I need help on the script for the counter to increment by one and have a animation of the incremented number, slide to top
something like that on the picture and this is what I have done so far.
.booking-wrapper .countdown-wrapper .countdown-container .order-list {
border: 5px solid #959595;
list-style-type: none;
padding-left: 0;
margin-bottom: 0;
overflow: hidden;
line-height: 1;
height: 56px;
margin-top: 8px;
}
.booking-wrapper .countdown-wrapper .countdown-container .order-list .order-list-item {
display: inline;
font-size: 40px;
font-weight: bold;
padding: 0 15px;
}
<div class="countdown-container">
<ul id="order-list" class="order-list">
<li class="order-list-item"><span>0</span></li>
<li class="order-list-item" style="border-left: 1px solid black;"><span>0</span></li>
<li class="order-list-item" style="border-left: 1px solid black; border-right: 1px solid black;"><span>8</span></li>
<li class="order-list-item"><span>1</span></li>
</ul>
</div>
You should probably use some existing library for that. But if you want to have your own implementation, then use the following elements:
transition
property and the transitionend
event.8<br>9<br>0
. They should form a sequence. Scroll the middle digit into view so that only that digit is visible. Perform the animation upward or downward as needed, and when the animation is complete, update the HTML and reset the scroll offset (which can be the top
CSS property).span
elements. Just put the content straight into the li
elements.await
So here is how you could do it:
// Utility functions returning promises
const delay = ms => new Promise(resolve => setTimeout(resolve, ms));
const nextFrame = () => new Promise(resolve => requestAnimationFrame(resolve));
const animate = (elem, prop, value, duration) => {
return nextFrame().then(() => new Promise(resolve => {
elem.style.transition = `${prop} ${duration}ms`;
elem.style[prop] = `${value}px`;
const done = () => {
elem.style.transition = `${prop} 0ms`;
resolve();
}
elem.addEventListener("transitionend", done, {once: true});
})).then(nextFrame);
};
// DOM element wrapper for the counter functionality
class Counter {
constructor(element, length = 4, upwards = true) {
this.element = element;
this._value = 0;
this.upwards = !!upwards;
this.digits = Array.from({length}, () => element.appendChild(document.createElement("li")));
}
get value() {
return this._value;
}
set value(value) {
this._value = value;
const numStr = value.toString().padStart(4, "0").slice(-this.digits.length);
// Display the current number in the counter element (no animation)
this.digits.forEach( (digit, i) => {
// Put three lines, each having a digit, where the middle one is the current one:
digit.innerHTML = `${(+numStr[i]+(this.upwards ? 9 : 1))%10}<br>${numStr[i]}<br>${(+numStr[i]+(this.upwards ? 1 : 9))%10}`;
digit.style.top = `${-this.element.clientHeight}px`; // scroll the middle digit into view
});
}
async roll(direction = 1, duration = 500) {
await nextFrame();
const numChangingDigits = Math.min(this.digits.length,
this.value.toString().length - this.value.toString().search(direction > 0 ? /9*$/ : /0*$/) + 1);
const numStr = this.value.toString().padStart(4, "0").slice(-numChangingDigits);
const promises = this.digits.slice(-numChangingDigits).map((digit, i) =>
animate(digit, "top", (direction > 0) === this.upwards ? -this.element.clientHeight*2 : 0, duration)
);
await Promise.all(promises);
this.value = this.value + direction;
await nextFrame();
}
async rollTo(target, duration = 500, pause = 300) {
const direction = Math.sign(target - this.value);
while (this.value !== target) {
await this.roll(direction, duration);
await delay(pause);
}
}
}
// Demo:
const counter = new Counter(document.getElementById("order-list"), 4, true);
counter.value = 9931;
counter.rollTo(10002, 500, 300);
.order-list {
border: 5px solid #999;
list-style-type: none;
padding-left: 0;
overflow: hidden;
height: 50px;
line-height: 1;
display: inline-block;
}
.order-list > li {
display: inline-block;
font-size: 50px;
font-weight: bold;
padding: 0 15px;
border-left: 1px solid black;
position: relative;
top: 0;
}
.order-list > li:first-child {
border-left: 0;
}
<ul id="order-list" class="order-list"></ul>
There are some arguments you can use to modify the speed of the animations. There is also an argument that determines whether the dials roll upward or downward for getting the next digit.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With