diff --git a/client/src/components/Journey/MobileMapTimeline.tsx b/client/src/components/Journey/MobileMapTimeline.tsx index cd543a91..27ead4b0 100644 --- a/client/src/components/Journey/MobileMapTimeline.tsx +++ b/client/src/components/Journey/MobileMapTimeline.tsx @@ -53,9 +53,6 @@ export default function MobileMapTimeline({ }) }, [entries]) const cardRefs = useRef>(new Map()) - const activeIndexRef = useRef(activeIndex) - useEffect(() => { activeIndexRef.current = activeIndex }, [activeIndex]) - // Sync map focus when carousel scrolls (with guard for uninitialized map) const syncMapToCarousel = useCallback((index: number) => { const entry = entries[index] @@ -90,29 +87,19 @@ export default function MobileMapTimeline({ }) }, [syncMapToCarousel]) - // Track scroll; debounce to re-center the active card when the user stops. + // Defer all state updates until scrolling settles — updating activeIndex + // mid-swipe resizes cards (240→320px), causing layout reflow every frame. useEffect(() => { const el = carouselRef.current if (!el || entries.length === 0) return - let rafId: number | null = null let settleTimer: number | null = null const onScroll = () => { - if (rafId != null) return - rafId = requestAnimationFrame(() => { - pickNearestCard() - rafId = null - }) if (settleTimer != null) window.clearTimeout(settleTimer) - settleTimer = window.setTimeout(() => { - // Ensure the active card sits at the center once the user settles. - const card = cardRefs.current.get(activeIndexRef.current) - card?.scrollIntoView({ behavior: 'smooth', inline: 'center', block: 'nearest' }) - }, 180) + settleTimer = window.setTimeout(pickNearestCard, 150) } el.addEventListener('scroll', onScroll, { passive: true }) return () => { el.removeEventListener('scroll', onScroll) - if (rafId != null) cancelAnimationFrame(rafId) if (settleTimer != null) window.clearTimeout(settleTimer) } }, [entries.length, pickNearestCard]) @@ -210,9 +197,9 @@ export default function MobileMapTimeline({ >