← Components/Animations & Cursors

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: 0
TrailCursor
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);
  c
NeonGlowCursor
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] = useSta
PixelCursor
Animations & Cursors