JSFiddle - React, Tailwind, and code Playground
by mehulkar
HTML
<div data-count="1" id="hi" style="--size: 2.5rem;">0</div>
<button onclick="dothing(this)">add random number</button>
CSS
body {
margin: 0 auto;
text-align: center;
}
.vertical-number-wrapper {
display: flex;
flex-direction: column;
overflow: hidden;
height: 2.5rem;
line-height: 2.5rem;
--rotate-val: 0;
--rotate-delay: 0;
}
.vertical-number {
transition: transform 0.2s ease-in;
transition-delay: var(--rotate-delay);
transform: translateY(var(--rotate-val));
}
#hi {
font-size: var(--size);
margin: 0 auto 1rem;
display: flex;
align-items: center;
justify-content: center;
}
JavaScript
function createRotatingDigitElement() {
const WRAPPER_CLASS = 'vertical-number-wrapper';
const INDIVIDUAL_NUM_CLASS = 'vertical-number';
function createDigitOptions() {
return [0, 1, 2, 3, 4, 5, 6, 7, 8, 9].map((x) => {
const span = document.createElement('span');
span.classList.add(INDIVIDUAL_NUM_CLASS);
span.textContent = x;
return span;
});
}
const rotatingDigitElement = document.createElement('span');
rotatingDigitElement.classList.add(WRAPPER_CLASS);
const digitSpans = createDigitOptions();
for (const el of digitSpans) {
rotatingDigitElement.appendChild(el);
}
return rotatingDigitElement;
}
function animateUpdateNumber(element, newValue) {
// update our source of truth in the data attribute
const currentCount = element.dataset.count;
element.dataset.count = newValue;
// replace the textContent with HTML nodes that will do the animation
// Only do this once, and set a data attribute to indicate that it is "initialized".
if (!element.dataset.rotateInitialized) {
element.innerHTML = ''; // clear existing html
// For each digit, create a rotating digit element and append
// it to the main element. (replacing the existing HTML)
for (let i = 0; i < currentCount.length; i++) {
element.appendChild(createRotatingDigitElement());
}
element.dataset.rotateInitialized = true;
}
// takes a number, and splits it into digits. e.g. 132 => [1, 3, 2].
const newValDigits = String(newValue)
.split('')
.map((x) => +x);
// Add more rotating digits if we don't have enough for the newValue.
// Warning: This assumes that new value will never have fewer digits than the
// existing number of digits. If we add a subtract button, this could break.
const numberOfMissingDigits = newValDigits.length - element.childElementCount;
for (let i = 0; i < numberOfMissingDigits; i++) {
element.prepend(createRotatingDigitElement());
}
// Starting from the end...