File size: 1,232 Bytes
cd99321
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
import { useEffect, useRef, useState } from 'react'

const isTestEnv = typeof navigator !== 'undefined' && /jsdom/i.test(navigator.userAgent ?? '')

// Zählt beim Mount von 0 auf target hoch. Feste Dauer mit ease-out-quint.
export function useCountUp(target: number, duration = 800): number {
  const [value, setValue] = useState(() => isTestEnv || target <= 0 ? target : 0)
  const startRef = useRef<number | null>(null)
  const frameRef = useRef<number | null>(null)

  useEffect(() => {
    const reduced = window.matchMedia?.('(prefers-reduced-motion: reduce)')?.matches ?? false
    if (reduced || isTestEnv || target <= 0) { setValue(target); return }

    startRef.current = null
    const step = (now: number) => {
      if (startRef.current == null) startRef.current = now
      const elapsed = now - startRef.current
      const t = Math.min(elapsed / duration, 1)
      // ease-out-quint
      const eased = 1 - Math.pow(1 - t, 5)
      setValue(Math.round(target * eased))
      if (t < 1) frameRef.current = requestAnimationFrame(step)
    }
    frameRef.current = requestAnimationFrame(step)
    return () => { if (frameRef.current) cancelAnimationFrame(frameRef.current) }
  }, [target, duration])

  return value
}