JSFiddle - React, Tailwind, and code Playground
by Mladen Mihajlovic
HTML
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Odometer</title>
<link rel="stylesheet" href="styles.css">
</head>
<body>
<div id="odometer" class="odometer">
<div class="digit"></div>
<div class="digit"></div>
<div class="digit"></div>
</div>
<button onclick="increment()">Increment</button>
<button onclick="decrement()">Decrement</button>
<script src="script.js"></script>
</body>
</html>
CSS
body {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
height: 100vh;
background-color: #f0f0f0;
font-family: Arial, sans-serif;
}
#odometer {
display: flex;
border: 2px solid #333;
padding: 10px;
background-color: #fff;
border-radius: 5px;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
}
.digit {
font-size: 2rem;
width: 2rem;
height: 3rem;
line-height: 3rem;
text-align: center;
overflow: hidden;
position: relative;
}
.digit div {
position: absolute;
width: 100%;
transition: transform 0.5s ease-in-out;
}
.digit .current {
transform: translateY(0);
}
.digit .new {
transform: translateY(100%);
}
button {
margin: 10px;
padding: 10px 20px;
font-size: 1rem;
border: none;
background-color: #333;
color: #fff;
border-radius: 5px;
cursor: pointer;
}
button:hover {
background-color: #555;
}
JavaScript
let currentNumber = 0;
function updateOdometer(number) {
const digits = document.querySelectorAll('.digit');
const numberStr = number.toString().padStart(digits.length, '0');
digits.forEach((digit, index) => {
const currentDigit = digit.querySelector('div.current');
const newDigitValue = numberStr[index];
if (currentDigit && currentDigit.textContent !== newDigitValue) {
const newDigit = document.createElement('div');
newDigit.textContent = newDigitValue;
newDigit.classList.add('new');
digit.appendChild(newDigit);
// Trigger reflow for the transition
newDigit.offsetHeight;
newDigit.style.transform = 'translateY(0)';
currentDigit.style.transform = 'translateY(-100%)';
setTimeout(() => {
if (currentDigit && currentDigit.parentNode === digit) {
digit.removeChild(currentDigit);
}
newDigit.classList.remove('new');
newDigit.classList.add('current');
}, 500);
} else if (!currentDigit) {
const newDigit = document.createElement('div');
newDigit.textContent = newDigitValue;
newDigit.classList.add('current');
digit.innerHTML = ''; // Clear any initial content
digit.appendChild(newDigit);
}
});
}
function increment() {
currentNumber++;
updateOdometer(currentNumber);
}
function decrement() {
if (currentNumber > 0) {
currentNumber--;
updateOdometer(currentNumber);
}
}
updateOdometer(currentNumber);