CounterAnimation
counteranimation-1779379195909.tsx
'use client'
import { useState, useEffect, useRef } from 'react'
interface AnimatedNumberProps {
target: number
duration?: number
prefix?: string
suffix?: string
decimals?: number
onComplete?: () => void
}
function AnimatedNumber({ target, duration = 2000, prefix = '', suffix = '', decimals = 0, onComplete }: AnimatedNumberProps) {
const [current, setCurrent] = useState(0)
const [running, setRunning] = useState(false)
const startRef = useRef(0)
const startTimeRef = useRef(0)
const rafRef = useRef<number>(0)
function start() {
startRef.current = current
startTimeRef.current = performance.now()
setRunning(true)
function tick(now: number) {
const elapsed = now - startTimeRef.current
const progress = Math.min(elapsed / duration, 1)
const eased = 1 - Math.pow(1 - progress, 4)
const val = startRef.current + (target - startRef.current) * eased
setCurrent(val)
if (progress < 1) {
rafRef.current = requestAnimationFrame(tick)
} else {
setRunning(false)
onComplete?.()
}
}
rafRef.current = requestAnimationFrame(tick)
}
useEffect(() => () => cancelAnimationFrame(rafRef.current), [])
const fmt = current >= 1000000 ? `${(current / 1000000).toFixed(1)}M` : current >= 1000 ? `${(current / 1000).toFixed(1)}k` : current.toFixed(decimals)
return (
<div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 8 }}>
<div style={{ fontSize: 48, fontWeight: 900, color: '#F5F5F0', fontVariantNumeric: 'tabular-nums', letterSpacing: '-0.03em' }}>
<span style={{ color: '#C9A84C', fontSize: 28 }}>{prefix}</span>{fmt}<span style={{ color: '#C9A84C', fontSize: 28 }}>{suffix}</span>
</div>
<button onClick={running ? undefined : start} disabled={running} style={{ background: running ? 'rgba(255,255,255,0.06)' : '#C9A84C', color: running ? 'rgba(255,255,255,0.4)' : '#0A0A0A', border: 'none', borderRadius: 8, padding: '8px 20px', cursor: running ? 'default' : 'pointer', fontWeight: 700, fontSize: 13, transition: 'all 0.2s' }}>
{running ? 'Counting...' : current === 0 ? 'Start' : 'Again'}
</button>
</div>
)
}
export default function CounterAnimation() {
const [confetti, setConfetti] = useState(false)
const COUNTERS = [
{ label: 'Components', target: 17500, prefix: '', suffix: '+', decimals: 0 },
{ label: 'MCP Requests', target: 1240000, prefix: '', suffix: '', decimals: 0 },
{ label: 'Avg Quality', target: 97.3, prefix: '', suffix: '%', decimals: 1 },
{ label: 'Revenue', target: 48320, prefix: '$', suffix: '', decimals: 0 },
]
return (
<div style={{ padding: 40, background: '#0A0A0A', display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 32 }}>
{confetti && <div style={{ position: 'absolute', top: 0, left: 0, right: 0, textAlign: 'center', fontSize: 32, pointerEvents: 'none', animation: 'fall 1s ease' }}>🎉🎊✨🎉</div>}
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(2, 1fr)', gap: 24, width: '100%', maxWidth: 480 }}>
{COUNTERS.map(c => (
<div key={c.label} style={{ background: '#111', border: '1px solid rgba(255,255,255,0.06)', borderRadius: 14, padding: 24, display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 8 }}>
<AnimatedNumber {...c} onComplete={() => setConfetti(true)} />
<div style={{ color: 'rgba(255,255,255,0.35)', fontSize: 12 }}>{c.label}</div>
</div>
))}
</div>
<style>{`@keyframes fall { from { transform:translateY(-20px); opacity:1 } to { transform:translateY(20px); opacity:0 } }`}</style>
</div>
)
}Component info
CategoryAnimations & Cursors
Frameworkreact
TierFREE
Views0
Copies0
About
Animated number counter with easing, formatting options, milestone celebrations, and reset
More from Animations & Cursors
import React from 'react';
import { motion } from 'framer-motion';
interface MorphCursorProps {
children: React.ReactNode;
}
const MorphCursor: React.FC<MorphCursorProps> = ({ children }) => {
const [cursorType, setCursorType] = React.useState(MorphCursor
Animations & Cursors
import { useEffect, useState } from 'react';
interface Dot {
x: number;
y: number;
opacity: number;
}
const TrailCursor = () => {
const [dots, setDots] = useState<Dot[]>([]);
const [mousePosition, setMousePosition] = useState({ x: 0, y: 0TrailCursor
Animations & Cursors
import React, { useEffect, useState } from 'react';
import './NeonGlowCursor.css';
interface Props {
children: React.ReactNode;
}
const NeonGlowCursor: React.FC<Props> = ({ children }) => {
const [cursorX, setCursorX] = useState<number>(0);
cNeonGlowCursor
Animations & Cursors
import { useState, useEffect } from 'react';
import { motion } from 'framer-motion';
interface PixelCursorProps {
color: string;
}
const PixelCursor: React.FC<PixelCursorProps> = ({ color }) => {
const [mousePosition, setMousePosition] = useStaPixelCursor
Animations & Cursors