first commit
This commit is contained in:
@@ -0,0 +1,50 @@
|
||||
"use client";
|
||||
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
import { useInView } from "framer-motion";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
interface AnimatedNumberProps {
|
||||
value: number;
|
||||
suffix?: string;
|
||||
prefix?: string;
|
||||
duration?: number;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
export function AnimatedNumber({
|
||||
value,
|
||||
suffix = "",
|
||||
prefix = "",
|
||||
duration = 1800,
|
||||
className,
|
||||
}: AnimatedNumberProps) {
|
||||
const [count, setCount] = useState(0);
|
||||
const ref = useRef<HTMLSpanElement>(null);
|
||||
const isInView = useInView(ref, { once: true, margin: "-80px" });
|
||||
|
||||
useEffect(() => {
|
||||
if (!isInView) return;
|
||||
|
||||
let rafId: number;
|
||||
let startTime: number | null = null;
|
||||
|
||||
const step = (ts: number) => {
|
||||
if (!startTime) startTime = ts;
|
||||
const progress = Math.min((ts - startTime) / duration, 1);
|
||||
// Ease-out cubic
|
||||
const eased = 1 - Math.pow(1 - progress, 3);
|
||||
setCount(Math.round(eased * value));
|
||||
if (progress < 1) rafId = requestAnimationFrame(step);
|
||||
};
|
||||
|
||||
rafId = requestAnimationFrame(step);
|
||||
return () => cancelAnimationFrame(rafId);
|
||||
}, [isInView, value, duration]);
|
||||
|
||||
return (
|
||||
<span ref={ref} className={cn(className)}>
|
||||
{prefix}{count}{suffix}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user