meh
This commit is contained in:
36
src/components/shared/CountUp.jsx
Normal file
36
src/components/shared/CountUp.jsx
Normal file
@@ -0,0 +1,36 @@
|
||||
import { useEffect, useRef, useState } from 'react'
|
||||
|
||||
export default function CountUp({ end, duration = 1000, suffix = '' }) {
|
||||
const [display, setDisplay] = useState(0)
|
||||
const ref = useRef(null)
|
||||
const started = useRef(false)
|
||||
|
||||
useEffect(() => {
|
||||
const el = ref.current
|
||||
if (!el) return
|
||||
|
||||
const observer = new IntersectionObserver(([entry]) => {
|
||||
if (entry.isIntersecting && !started.current) {
|
||||
started.current = true
|
||||
const startTime = performance.now()
|
||||
|
||||
const animate = (now) => {
|
||||
const elapsed = now - startTime
|
||||
const progress = Math.min(elapsed / duration, 1)
|
||||
// ease-out quad
|
||||
const eased = 1 - (1 - progress) * (1 - progress)
|
||||
setDisplay(Math.round(eased * end))
|
||||
if (progress < 1) requestAnimationFrame(animate)
|
||||
}
|
||||
|
||||
requestAnimationFrame(animate)
|
||||
observer.disconnect()
|
||||
}
|
||||
}, { threshold: 0.3 })
|
||||
|
||||
observer.observe(el)
|
||||
return () => observer.disconnect()
|
||||
}, [end, duration])
|
||||
|
||||
return <span ref={ref}>{display}{suffix}</span>
|
||||
}
|
||||
Reference in New Issue
Block a user