Some checks failed
Build & Deploy / 🔍 Prepare (push) Successful in 23s
Build & Deploy / 🚀 Deploy (push) Has been skipped
Build & Deploy / 🧪 Post-Deploy Verification (push) Has been skipped
Build & Deploy / 🧪 QA (push) Successful in 1m6s
Build & Deploy / 🏗️ Build (push) Failing after 2m52s
Build & Deploy / 🔔 Notify (push) Successful in 1s
- Removed confusing SVG lines from map - Restored distinct clickable markers for major references - Fixed corepack failing to install pnpm v11 in Docker by explicitly forcing v10 - Added direct port mapping 3001:3001 to bypass proxy issues
49 lines
1.1 KiB
TypeScript
49 lines
1.1 KiB
TypeScript
'use client';
|
|
|
|
import React, { useEffect, useState } from 'react';
|
|
import { motion, useMotionValue, useTransform, animate, useInView } 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 (
|
|
<motion.span ref={ref} className={className}>
|
|
{prefix}<motion.span>{rounded}</motion.span>{suffix}
|
|
</motion.span>
|
|
);
|
|
}
|