import { useEffect, useRef, useState } from "react"; import { prefersReducedMotion } from "@/lib/reduced-motion"; interface AnimateInProps { children: React.ReactNode; delay?: number; threshold?: number; } export function AnimateIn({ children, delay = 0, threshold = 0.15 }: AnimateInProps) { const ref = useRef(null); const [visible, setVisible] = useState(false); const [skip, setSkip] = useState(false); useEffect(() => { const el = ref.current; if (!el) return; if (prefersReducedMotion()) { setSkip(true); setVisible(true); return; } const rect = el.getBoundingClientRect(); const inView = rect.top < window.innerHeight && rect.bottom > 0; const isReload = (performance.getEntriesByType?.("navigation")?.[0] as PerformanceNavigationTiming)?.type === "reload"; const isSpaNav = !!(window as any).__astroNavigation; if (inView && (isReload || isSpaNav)) { setSkip(true); setVisible(true); return; } if (inView) { requestAnimationFrame(() => setVisible(true)); return; } const observer = new IntersectionObserver( ([entry]) => { if (entry.isIntersecting) { setVisible(true); observer.disconnect(); } }, { threshold } ); observer.observe(el); return () => observer.disconnect(); }, [threshold]); return (
{children}
); }