"use client"; import { useEffect, useRef, useState } from "react"; type Props = { value: number; className?: string; }; export function Counter({ value, className = "" }: Props) { const [n, setN] = useState(0); const ref = useRef(null); const started = useRef(false); useEffect(() => { const el = ref.current; if (!el || started.current) return; const run = () => { if (started.current) return; started.current = true; const duration = 1400; const start = performance.now(); const tick = (now: number) => { const t = Math.min(1, (now - start) / duration); const eased = 1 - Math.pow(1 - t, 3); setN(Math.round(value * eased)); if (t < 1) requestAnimationFrame(tick); else setN(value); }; requestAnimationFrame(tick); }; const isVisible = () => { const rect = el.getBoundingClientRect(); const vh = window.innerHeight || document.documentElement.clientHeight; return rect.top < vh * 0.92 && rect.bottom > vh * 0.08; }; if (isVisible()) { run(); return; } const observer = new IntersectionObserver( ([entry]) => { if (entry.isIntersecting) { run(); observer.disconnect(); } }, { threshold: 0.2 }, ); observer.observe(el); return () => observer.disconnect(); }, [value]); return ( {n.toLocaleString("en-US")} ); }