diff --git a/src/components/animated-counter.tsx b/src/components/animated-counter.tsx index 62977c82..e7972585 100644 --- a/src/components/animated-counter.tsx +++ b/src/components/animated-counter.tsx @@ -12,25 +12,35 @@ export function AnimatedCounter({ }) { const ref = useRef(null); const isInView = useInView(ref, { once: true, margin: "-50px" }); + const started = useRef(false); const [display, setDisplay] = useState(0); useEffect(() => { if (!isInView) return; + + if (started.current) { + setDisplay(value); + return; + } + started.current = true; + const duration = 1200; - const steps = 30; - const increment = value / steps; - let current = 0; - let step = 0; - const id = setInterval(() => { - step++; - current = Math.min(Math.round(increment * step), value); - setDisplay(current); - if (step >= steps) { - clearInterval(id); + const start = performance.now(); + let raf = 0; + + const tick = (now: number) => { + const progress = Math.min((now - start) / duration, 1); + const eased = 1 - (1 - progress) ** 3; + setDisplay(Math.round(eased * value)); + if (progress < 1) { + raf = requestAnimationFrame(tick); + } else { setDisplay(value); } - }, duration / steps); - return () => clearInterval(id); + }; + + raf = requestAnimationFrame(tick); + return () => cancelAnimationFrame(raf); }, [isInView, value]); return (