const inView = useInView(ref, { once: true })
const [n, setN] = useState(isCanvas ? value : from)
useEffect(() => {
if (isCanvas) { setN(value); return }
if (!inView) return
let timer: ReturnType<typeof setInterval> | undefined
let base = value
const controls = animate(from, value, {
duration, delay, ease: [0.22, 1, 0.36, 1],
onUpdate: (v) => setN(v),
onComplete: () => {
if (!drift && !jitter) return
// Keep ticking like live data after the count-up lands.
timer = setInterval(() => {
base += drift * (0.6 + Math.random() * 0.8)
setN(base + (jitter ? (Math.random() * 2 - 1) * jitter : 0))
}, Math.max(0.5, interval) * 1000)
},
})
return () => { controls.stop(); if (timer) clearInterval(timer) }
}, [inView, isCanvas, value, from, duration, delay, drift, jitter, interval])