Multi-field odometer with Rollover
Add the 'debugBackground' class to 'dupa2' to see the rollover zone in blue
by tprobinson
HTML
<div id="root"></div>
<button id="rand">rand</button>
CSS
#rand {
margin-top: 50px;
}
#root {
display: flex:
}
.field {
height: 30px;
width: 30px;
border: 1px solid #000;
overflow: hidden;
}
.field.smooth {
transition: all 1s ease;
}
.field span {
height: 30px;
width: 30px;
display: block;
text-align: center;
line-height: 30px;
}
.field.debugBackground {
background: linear-gradient(to bottom, #ffffff 0%, #ffffff 50%, #207cca 51%, #207cca 100%);
}
.field[data-num="0"] {
transform: translate(0, 0);
}
.field[data-num="1"] {
transform: translate(0, -30px);
}
.field[data-num="2"] {
transform: translate(0, -60px);
}
.field[data-num="3"] {
transform: translate(0, -90px);
}
.field[data-num="4"] {
transform: translate(0, -120px);
}
.field[data-num="5"] {
transform: translate(0, -150px);
}
.field[data-num="6"] {
transform: translate(0, -180px);
}
.field[data-num="7"] {
transform: translate(0, -210px);
}
.field[data-num="8"] {
transform: translate(0, -240px);
}
.field[data-num="9"] {
transform: translate(0, -270px);
}
.field[data-num="10"] {
transform: translate(0, -300px);
}
.field[data-num="11"] {
transform: translate(0, -330px);
}
.field[data-num="12"] {
transform: translate(0, -360px);
}
.field[data-num="13"] {
transform: translate(0, -390px);
}
.field[data-num="14"] {
transform: translate(0, -420px);
}
.field[data-num="15"] {
transform: translate(0, -450px);
}
.field[data-num="16"] {
transform: translate(0, -480px);
}
.field[data-num="17"] {
transform: translate(0, -510px);
}
.field[data-num="18"] {
transform: translate(0, -540px);
}
.field[data-num="19"] {
transform: translate(0, -570px);
}
JavaScript
// Generate this ahead of time
const fieldInnerHtml = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9].map(x => `<span>${x}</span>`)
class RolloverField {
constructor() {
this.animationInProgress = null
this.element = document.createElement('div')
this.element.addClass('field')
this.element.dataset.num = 0
this.element.innerHtml = fieldInnerHtml
}
animate(num) {
// Add the smooth class and set the number to let it roll.
this.element.classList.add('smooth')
this.element.dataset.num = num
// In 1000 ms, remove the smooth class
animationInProgress = window.setTimeout(() => {
this.element.classList.remove('smooth')
this.animationInProgress = null
}, 1000)
}
setNum(num) {
let oldNum = Number.parseInt(this.element.dataset.num)
if (oldNum === undefined || oldNum === null) {
oldNum = 0
}
// If an animation is already in progress, cancel it
if (this.animationInProgress) {
window.clearTimeout(this.animationInProgress)
this.element.classList.remove('smooth')
this.animationInProgress = null
}
// If the new number is before our old number
// we have to force a roll forwards
if (newNum < oldNum) {
newNum += 10
}
if (oldNum > 9) {
// The dial was already rolled over. We need to
// snap the dial back before rolling again.
// Wait for a frame so we can snap the dial back
this.element.dataset.num = oldNum - 10
this.element.classList.remove('smooth')
window.requestAnimationFrame(() => {
// Wait for one frame to let the snapback happen
window.requestAnimationFrame(() => {
// Then roll forward
this.animate(newNum)
})
})
return
}
// Roll the dial
this.animate(newNum)
}
}
// Generate all our HTML
const root = document.getElementById("root");
console.log(root)
const elArray = []
while (elArray.length < 9) {
const newEl =...