Files
e-tib.com/components/ui/AnimatedCounter.tsx
Marc Mintel 992a07f54a
Some checks failed
Build & Deploy / 🔍 Prepare (push) Successful in 1m25s
Build & Deploy / 🧪 QA (push) Failing after 1m25s
Build & Deploy / 🏗️ Build (push) Has been skipped
Build & Deploy / 🚀 Deploy (push) Has been skipped
Build & Deploy / 🧪 Post-Deploy Verification (push) Has been skipped
Build & Deploy / 🔔 Notify (push) Successful in 3s
perf(motion): migrate global framer-motion imports to LazyMotion
- Replaced synchronous `motion` imports with `m` and `LazyMotion` across all components
- Removed 1-second `animate-in` delay on LCP element in HeroVideo
- Added AVIF image format support to next.config.mjs
- Fixed ReferencesSlider carousel left padding alignment
- Dropped Total Blocking Time (TBT) to 0ms
2026-06-23 12:18:54 +02:00

49 lines
1.1 KiB
TypeScript

'use client';
import React, { useEffect, useState } from 'react';
import { useMotionValue, useTransform, animate, useInView, m, LazyMotion, domAnimation } from 'framer-motion';
interface AnimatedCounterProps {
value: number;
duration?: number;
delay?: number;
suffix?: string;
prefix?: string;
className?: string;
}
export function AnimatedCounter({
value,
duration = 2,
delay = 0,
suffix = '',
prefix = '',
className = '',
}: AnimatedCounterProps) {
const count = useMotionValue(0);
const rounded = useTransform(count, (latest) => {
// Format with thousands separator
return new Intl.NumberFormat('de-DE').format(Math.round(latest));
});
const ref = React.useRef(null);
const inView = useInView(ref, { once: true, margin: "-100px" });
useEffect(() => {
if (inView) {
const controls = animate(count, value, {
duration: duration,
delay: delay,
ease: 'easeOut',
});
return controls.stop;
}
}, [inView, value, duration, delay, count]);
return (
<m.span ref={ref} className={className}>
{prefix}<m.span>{rounded}</m.span>{suffix}
</m.span>
);
}