hmm, the demo doesn't seem to work.
the reference i'm looking at that i'm looking to replicate has this as it's final markup, maybe you can use it as reference and try and figure out what they did? i know they used motion to build it though.
reference markup: <span class="number-section-integer" style="display: inline-flex; justify-content: right; width: 1.08008em;"><span style="display:inline-flex;justify-content:inherit;position:relative"><span style="display: inline-flex; justify-content: center; opacity: 1; width: 0.54004em; transform: none; transform-origin: 50% 50% 0px;"><span style="display: inline-flex; justify-content: center; flex-direction: column; align-items: center; position: relative; width: 0.54004em; transform: none;"><span style="display: flex; flex-direction: column; align-items: center; position: absolute; width: 100%; bottom: 100%; left: 0px;"><span style="display: inline-block; padding: calc(var(--mask-height, 0.15em)/2) 0;">0</span></span><span style="display: inline-block; padding: calc(var(--mask-height, 0.15em)/2) 0;">1</span><span style="display: flex; flex-direction: column; align-items: center; position: absolute; width: 100%; top: 100%; left: 0px;"><span style="display: inline-block; padding: calc(var(--mask-height, 0.15em)/2) 0;">2</span><span style="display: inline-block; padding: calc(var(--mask-height, 0.15em)/2) 0;">3</span><span style="display: inline-block; padding: calc(var(--mask-height, 0.15em)/2) 0;">4</span><span style="display: inline-block; padding: calc(var(--mask-height, 0.15em)/2) 0;">5</span><span style="display: inline-block; padding: calc(var(--mask-height, 0.15em)/2) 0;">6</span><span style="display: inline-block; padding: calc(var(--mask-height, 0.15em)/2) 0;">7</span><span style="display: inline-block; padding: calc(var(--mask-height, 0.15em)/2) 0;">8</span><span style="display: inline-block; padding: calc(var(--mask-height, 0.15em)/2) 0;">9</span></span></span></span><span style="display: inline-flex; justify-content: center; opacity: 1; width: 0.54004em; transform: none; transform-origin: 50% 50% 0px;"><span style="display: inline-flex; justify-content: center; flex-direction: column; align-items: center; position: relative; width: 0.54004em; transform: none;"><span style="display: flex; flex-direction: column; align-items: center; position: absolute; width: 100%; bottom: 100%; left: 0px;"><span style="display: inline-block; padding: calc(var(--mask-height, 0.15em)/2) 0;">0</span><span style="display: inline-block; padding: calc(var(--mask-height, 0.15em)/2) 0;">1</span><span style="display: inline-block; padding: calc(var(--mask-height, 0.15em)/2) 0;">2</span><span style="display: inline-block; padding: calc(var(--mask-height, 0.15em)/2) 0;">3</span><span style="display: inline-block; padding: calc(var(--mask-height, 0.15em)/2) 0;">4</span><span style="display: inline-block; padding: calc(var(--mask-height, 0.15em)/2) 0;">5</span><span style="display: inline-block; padding: calc(var(--mask-height, 0.15em)/2) 0;">6</span><span style="display: inline-block; padding: calc(var(--mask-height, 0.15em)/2) 0;">7</span><span style="display: inline-block; padding: calc(var(--mask-height, 0.15em)/2) 0;">8</span></span><span style="display: inline-block; padding: calc(var(--mask-height, 0.15em)/2) 0;">9</span></span></span></span></span>
You're right, and thank you for providing the reference markup. I can see that the implementation you're looking for is more complex and involves animating individual digits in a vertical scrolling manner. Let's create a new AnimateNumber component that more closely matches this behavior.
Here's an improved version that should replicate the effect you're looking for:
This new implementation more closely matches the reference markup and behavior you provided. Here's a breakdown of the changes:
Digit component that handles the animation for each individual digit.Digit component uses Framer Motion's useSpring to create a smooth scrolling effect for each digit change.AnimatePresence component to handle the entering and exiting of digits smoothly.AnimateNumber component now splits the input number into individual digits and renders a Digit component for each.This implementation should provide a much closer match to the reference you provided. The digits will scroll vertically when changing, creating a smooth and engaging animation effect.
To use this component in your project, you can import and use it like this:
import { AnimateNumber } from './animate-number-scroll'
function MyComponent() {
const [value, setValue] = useState(0)
return (
<div>
<AnimateNumber value={value} />
<button onClick={() => setValue((prev) => (prev + 1) % 100)}>Increment</button>
</div>
)
}This implementation should now correctly animate the numbers with a vertical scrolling effect, similar to the reference markup you provided.
Let me fix the animation implementation. The issue seems to be with how we're handling the digit transitions. Here's an improved version:
This new implementation:
useMotionValue and useTransform hooks to create smooth transitions between numbersThe animation now works by:
Try clicking the increment button now - you should see smooth transitions between numbers without any glitches or stuck states.