Browse Source

fix: fix viewport coords to use page coordinates

master
Muthu Kumar 1 month ago
parent
commit
f7d1573746
Failed to extract signature
  1. 45
      src/draggable.attempts/6/Draggable.ts
  2. 12
      src/draggable.attempts/6/Draggable2.tsx
  3. 34
      src/pages/main/Contact.tsx

45
src/draggable.attempts/6/Draggable.ts

@ -12,8 +12,8 @@ interface Vec2 {
export interface DraggableOpts {
initialRotation?: number;
onViewportExit?: () => void;
onViewportEnter?: () => void;
onPageExit?: () => void;
onPageEnter?: () => void;
}
export function makeDraggable(card: HTMLElement, opts: DraggableOpts = {}) {
@ -21,8 +21,8 @@ export function makeDraggable(card: HTMLElement, opts: DraggableOpts = {}) {
const calculateInitialCenter = (): Vec2 => {
const rect = card.getBoundingClientRect();
return {
x: rect.left + rect.width / 2,
y: rect.top + rect.height / 2,
x: rect.left + rect.width / 2 + window.scrollX,
y: rect.top + rect.height / 2 + window.scrollY,
};
};
@ -55,25 +55,29 @@ export function makeDraggable(card: HTMLElement, opts: DraggableOpts = {}) {
let lastMousePosition: Vec2 = { x: 0, y: 0 };
let activePointerId: number | null = null;
let animationFrameId: number | null = null;
let isOutsideViewport = false;
let isOutsideBounds = false;
// --- Helpers ---
const checkViewportExit = throttle(() => {
// Don't check if we're dragging, user may still be able to move the card back into view
const checkPageBounds = throttle(() => {
if (state.dragging) return;
const rect = card.getBoundingClientRect();
const pageLeft = rect.left + window.scrollX;
const pageTop = rect.top + window.scrollY;
const pageRight = rect.right + window.scrollX;
const pageBottom = rect.bottom + window.scrollY;
const outside =
rect.right < 0 ||
rect.bottom < 0 ||
rect.left > window.innerWidth ||
rect.top > window.innerHeight;
if (outside !== isOutsideViewport) {
isOutsideViewport = outside;
if (isOutsideViewport) opts.onViewportExit?.();
else opts.onViewportEnter?.();
pageRight < 0 ||
pageBottom < 0 ||
pageLeft > document.documentElement.scrollWidth ||
pageTop > document.documentElement.scrollHeight;
if (outside !== isOutsideBounds) {
isOutsideBounds = outside;
if (isOutsideBounds) opts.onPageExit?.();
else opts.onPageEnter?.();
}
}, VIEWPORT_CHECK_INTERVAL_MS);
@ -158,10 +162,8 @@ export function makeDraggable(card: HTMLElement, opts: DraggableOpts = {}) {
// 1. Store current visual state relative to the *old* initialCenter
const currentDeltaX = center.x - initialCenter.x;
const currentDeltaY = center.y - initialCenter.y;
const currentRotation = rotation; // Rotation doesn't depend on initialCenter
// 2. Temporarily remove the transform
card.style.transition = "none"; // Disable transitions during adjustment
card.style.transform = "none";
// 3. Force browser reflow to get the *untouched* layout position
@ -180,10 +182,7 @@ export function makeDraggable(card: HTMLElement, opts: DraggableOpts = {}) {
// 6. Reapply the transform immediately before the next paint
// Use the *stored* delta and rotation to put it back visually where it was
card.style.transform = `translate(${currentDeltaX}px, ${currentDeltaY}px) rotate(${currentRotation}rad)`;
// 7. Re-enable transitions if they were used
card.style.transition = ""; // Or restore previous transition style if needed
card.style.transform = `translate(${currentDeltaX}px, ${currentDeltaY}px) rotate(${rotation}rad)`;
// The render loop will continue from this adjusted state.
}, RESIZE_DEBOUNCE_MS); // Apply debouncing
@ -231,7 +230,7 @@ export function makeDraggable(card: HTMLElement, opts: DraggableOpts = {}) {
rotate(${rotation}rad)
`;
checkViewportExit();
checkPageBounds();
animationFrameId = requestAnimationFrame(render);
}

12
src/draggable.attempts/6/Draggable2.tsx

@ -5,8 +5,8 @@ import { css, cx } from "@emotion/css";
export type DraggableProps = React.HtmlHTMLAttributes<any> & {
as?: React.ElementType;
onViewportEnter?: () => void;
onViewportExit?: () => void;
onPageEnter?: () => void;
onPageExit?: () => void;
children: React.ReactNode;
initialRotation?: number;
};
@ -17,8 +17,8 @@ export const Draggable = forwardRef<HTMLElement, DraggableProps>(
as: Comp = "div",
children,
className,
onViewportEnter,
onViewportExit,
onPageEnter,
onPageExit,
initialRotation,
...props
}: DraggableProps,
@ -29,8 +29,8 @@ export const Draggable = forwardRef<HTMLElement, DraggableProps>(
useEffect(() => {
if (!cardRef.current) return;
return makeDraggable(cardRef.current, {
onViewportEnter,
onViewportExit,
onPageEnter,
onPageExit,
initialRotation,
});
}, []);

34
src/pages/main/Contact.tsx

@ -96,13 +96,7 @@ const Contact: React.FC = () => {
}, []);
return (
<Container
className={css`
min-height: 50vh;
display: flex;
flex-direction: column;
position: relative;
`}>
<Container>
<h1>MKRhere</h1>
{visible < 1 && (
<AnimateEntry as="article" delay={500}>
@ -112,16 +106,31 @@ const Contact: React.FC = () => {
<a href="/">Start over?</a>
</AnimateEntry>
)}
<AnimateEntry
as="main"
delay={200}
className={css`
width: 100%;
min-height: max(40vh, 11rem);
height: 100%;
position: relative;
`}>
{contactCards.map((rot, i) => (
<Draggable
key={i}
onViewportExit={() => setVisible(v => v - 1)}
onViewportEnter={() => setVisible(v => v + 1)}
onPageExit={() => setVisible(v => v - 1)}
onPageEnter={() => setVisible(v => v + 1)}
initialRotation={rot}
className={css`
width: 17rem;
width: 21rem;
height: 13rem;
font-size: 1rem;
@media screen and (max-width: 40rem) {
width: 18rem;
height: 11rem;
font-size: 0.8rem;
font-size: 0.85rem;
}
position: absolute;
bottom: 0;
@ -143,7 +152,7 @@ const Contact: React.FC = () => {
border-radius: 0.5rem;
background: var(--card-bg);
box-shadow: 0 0 50rem 0 rgba(0, 0, 0, 0.8);
box-shadow: 0 0 6rem 0 rgba(0, 0, 0, 0.7);
display: flex;
align-items: center;
@ -236,6 +245,7 @@ const Contact: React.FC = () => {
/>
</Draggable>
))}
</AnimateEntry>
</Container>
);
};

Loading…
Cancel
Save