Sliding Number
motion-primitives/sliding-numberFreeComponent
A component that transitions between numbers with a sliding animation.
Install it
npx shadcn@latest add https://raw.githubusercontent.com/ibelick/motion-primitives/HEAD/public/c/sliding-number.jsonSource
1'use client';2import { useEffect, useId } from 'react';3import {4 MotionValue,5 motion,6 useSpring,7 useTransform,8 motionValue,9} from 'motion/react';10import useMeasure from 'react-use-measure';1112const TRANSITION = {13 type: 'spring',14 stiffness: 280,15 damping: 18,16 mass: 0.3,17};1819function Digit({ value, place }: { value: number; place: number }) {20 const valueRoundedToPlace = Math.floor(value / place) % 10;21 const initial = motionValue(valueRoundedToPlace);22 const animatedValue = useSpring(initial, TRANSITION);2324 useEffect(() => {25 animatedValue.set(valueRoundedToPlace);26 }, [animatedValue, valueRoundedToPlace]);2728 return (29 <div className='relative inline-block w-[1ch] overflow-x-visible overflow-y-clip leading-none tabular-nums'>30 <div className='invisible'>0</div>31 {Array.from({ length: 10 }, (_, i) => (32 <Number key={i} mv={animatedValue} number={i} />33 ))}34 </div>35 );36}3738function Number({ mv, number }: { mv: MotionValue<number>; number: number }) {39 const uniqueId = useId();40 const [ref, bounds] = useMeasure();4142 const y = useTransform(mv, (latest) => {43 if (!bounds.height) return 0;44 const placeValue = latest % 10;45 const offset = (10 + number - placeValue) % 10;46 let memo = offset * bounds.height;4748 if (offset > 5) {49 memo -= 10 * bounds.height;50 }5152 return memo;53 });5455 // don't render the animated number until we know the height56 if (!bounds.height) {57 return (58 <span ref={ref} className='invisible absolute'>59 {number}60 </span>61 );62 }6364 return (65 <motion.span66 style={{ y }}67 layoutId={`${uniqueId}-${number}`}68 className='absolute inset-0 flex items-center justify-center'69 transition={TRANSITION}70 ref={ref}71 >72 {number}73 </motion.span>74 );75}7677type SlidingNumberProps = {78 value: number;79 padStart?: boolean;80 decimalSeparator?: string;81};8283export function SlidingNumber({84 value,85 padStart = false,86 decimalSeparator = '.',87}: SlidingNumberProps) {88 const absValue = Math.abs(value);89 const [integerPart, decimalPart] = absValue.toString().split('.');90 const integerValue = parseInt(integerPart, 10);91 const paddedInteger =92 padStart && integerValue < 10 ? `0${integerPart}` : integerPart;93 const integerDigits = paddedInteger.split('');94 const integerPlaces = integerDigits.map((_, i) =>95 Math.pow(10, integerDigits.length - i - 1)96 );9798 return (99 <div className='flex items-center'>100// … truncated
What it pulls in
npm packages
Files it writes
More from motion-primitives
All 33 items| Component | Registry | Kind | Access | Installs | Command |
|---|---|---|---|---|---|
| Accordionaccordion | motion-primitives | Components | Free | 1 dep | |
| Animated Backgroundanimated-background | motion-primitives | Components | Free | 1 dep | |
| Animated Groupanimated-group | motion-primitives | Components | Free | 1 dep | |
| Animated Numberanimated-number | motion-primitives | Components | Free | 1 dep | |
| Border Trailborder-trail | motion-primitives | Components | Free | 1 dep | |
| Carouselcarousel | motion-primitives | Components | Free | 1 dep | |
| Cursorcursor | motion-primitives | Components | Free | 1 dep | |
| Dialogdialog | motion-primitives | Components | Free | 1 dep · 2 files |
