Files
moygroup/app/components/ui/animated-number.tsx
T
2026-06-03 16:13:15 +03:00

51 lines
1.2 KiB
TypeScript

"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>
);
}