Files
EpicNext-Cms/src/components/animated-counter.tsx
T
openhands 3827f3e686
Deploy / release (push) Skipped
Deploy / deploy (push) Successful in 2m4s
Migrate from framer-motion to motion/react
2026-07-28 18:49:25 +02:00

48 lines
1.0 KiB
TypeScript

"use client";
import { motion, useInView } from "motion/react";
import { useEffect, useRef, useState } from "react";
export function AnimatedCounter({
value,
suffix = "",
}: {
value: number;
suffix?: string;
}) {
const ref = useRef<HTMLSpanElement>(null);
const isInView = useInView(ref, { once: true, margin: "-50px" });
const [display, setDisplay] = useState(0);
useEffect(() => {
if (!isInView) return;
const duration = 1200;
const steps = 30;
const increment = value / steps;
let current = 0;
let step = 0;
const id = setInterval(() => {
step++;
current = Math.min(Math.round(increment * step), value);
setDisplay(current);
if (step >= steps) {
clearInterval(id);
setDisplay(value);
}
}, duration / steps);
return () => clearInterval(id);
}, [isInView, value]);
return (
<motion.span
ref={ref}
initial={{ opacity: 0, y: 12 }}
animate={isInView ? { opacity: 1, y: 0 } : {}}
transition={{ duration: 0.5, ease: "easeOut" }}
>
{display.toLocaleString()}
{suffix}
</motion.span>
);
}