import { useEffect, useRef } from 'react'; import { animate, useInView } from 'motion/react'; interface AnimatedCounterProps { value: number; from?: number; suffix?: string; prefix?: string; duration?: number; // duration in seconds } export default function AnimatedCounter({ value, from = 0, suffix = '', prefix = '', duration = 2.5 }: AnimatedCounterProps) { const ref = useRef(null); // Trigger when 10% of the element is visible const isInView = useInView(ref, { once: true, margin: "-50px" }); useEffect(() => { if (!isInView) return; const node = ref.current; if (!node) return; const controls = animate(from, value, { duration: duration, ease: [0.16, 1, 0.3, 1], // Custom ultra-smooth easeOutExpo onUpdate(latest) { node.textContent = `${prefix}${Math.floor(latest)}${suffix}`; }, }); return () => controls.stop(); }, [value, from, isInView, prefix, suffix, duration]); // Fallback / Initial display before entering viewport return {prefix}{from}{suffix}; }