Example
Countdown
0123456789
0123456789
:0123456789
0123456789
:0123456789
0123456789
Installation
Copy and paste the source code into your project.
"use client";
import { useEffect } from "react";
import { motion, useSpring, useTransform, MotionValue } from "framer-motion";
import { cn } from "@/lib/utils";
type Sizing = "sm" | "md" | "lg";
function calculateDimensions(size: Sizing) {
switch (size) {
case "sm":
return { fontSize: 8, padding: 2 };
case "md":
return { fontSize: 12, padding: 2 };
case "lg":
return { fontSize: 16, padding: 1 };
default:
return { fontSize: 12, padding: 2 };
}
}
function Number({
mv,
number,
height,
}: {
mv: MotionValue;
number: number;
height: number;
}) {
let y = useTransform(mv, (latest) => {
let offset = (10 + number - latest) % 10;
let memo = offset * height;
if (offset > 5) {
memo -= 10 * height;
}
return memo;
});
return (
<motion.span
style={{ y }}
className="absolute inset-0 flex items-center justify-center"
>
{number}
</motion.span>
);
}
type Props = {
place: number;
value: number;
size?: Sizing;
};
/**
* AnimatedNumber component.
*
* An animated number component that animates the transition between two numbers.
*
* @param {number} [props.place] - The place value of the digit (10s, 1s, etc.)
* @param {number} [props.value] - The value of the digit (0-9)
* @param {Sizing} [props.size] - The size of the number ('sm', 'md', 'lg')
*/
export function AnimatedNumber({ place, value, size = "lg" }: Props) {
const { fontSize, padding } = calculateDimensions(size);
const height = fontSize + padding;
const width = fontSize + padding;
const valueRoundedToPlace = Math.floor(value / place) % 10;
const animatedValue = useSpring(valueRoundedToPlace, {
stiffness: 200,
damping: 20,
});
useEffect(() => {
animatedValue.set(valueRoundedToPlace);
}, [animatedValue, valueRoundedToPlace]);
return (
<motion.div
className={cn(`relative overflow-hidden`)}
style={{ width: `${width}px`, height: `${height}px` }}
>
{Array.from(Array(10).keys()).map((i) => (
<Number key={i} mv={animatedValue} number={i} height={height} />
))}
</motion.div>
);
}
Update the import paths to match your project setup.
More Examples
Counter
0123456789
0123456789
0123456789
0123456789
Scramble Counter
0123456789
0123456789
0123456789
0123456789
Props
Prop | Type | Default | Description |
---|---|---|---|
place | number | - | The place value of the digit (10s, 1s, etc.) |
value | number | - | The value of the digit (0-9) |
size | Sizing | "sm" | The size of the number ('sm', 'md', 'lg') |