How to translate element to act like a odometer

odometer js example
odometer js on scroll
react odometer
npm odometer
odometer js alternative
odometer codepen
odometer-theme
number transition animation

I've got code:

  <div class="wrap2" id="wrap" data-num="0">
    <span>0</span><span>1</span>...

CSS:

.wrap2[data-num="0"] {
  transfom:translate(0, 0);
}
.wrap2[data-num="1"] {
  transform:translate(0, -30px);
  }

https://jsfiddle.net/9t4zsuov/2/

But i want to act like a odometer - numbers have to roll only to top, not bottom. Any ideas, how to do that ?

As @codyThompsonDev said, a rollover area is the best way to implement this. Something I think he missed though, is what happens when you go from a rollover number to a non-rollover number.

For example, let's say that the odometer randomly tries to roll to 4, then 3, then 1. The first time, it can roll to 4 no problem. The second time, it has to roll to "13", in the rollover zone. But then, it tries to roll to "11" which is also in the rollover zone, causing it to roll backwards.

To achieve this effect under those circumstances, you must snap the dial back out of the rollover zone, then roll forward again. I would implement this using window.requestAnimationFrame().

I've made a fiddle to demonstrate this: https://jsfiddle.net/tprobinson/8k125fmz/67/

Add the debugBackground class to dupa2 to see the rollover effect visually.

I would recommend generating the CSS classes with a preprocessor like Sass, as writing them by hand can be error prone as well.

document.getElementById("rand").addEventListener("click", randomize);

const debug = document.getElementById("debug");
const dupa = document.getElementById("cipa");

let animationInProgress = null

function setDebug(num) {
  debug.textContent = 'Number is really: ' + num
}

function animateOdometer(newNum) {
  // Add the smooth class and set the number to let it roll.
  dupa.classList.add('smooth')
  setDebug(newNum)
  dupa.dataset.num = newNum

  // In 1000 ms, remove the smooth class
  animationInProgress = window.setTimeout(() => {
    dupa.classList.remove('smooth')
    animationInProgress = null
  }, 1000)
}

function randomize() {

  let oldNum = Number.parseInt(dupa.dataset.num)
  if (oldNum === undefined || oldNum === null) {
    oldNum = 0
  }

  let newNum = Math.floor(Math.random() * 9) + 0;

  // If an animation is already in progress, cancel it
  if (animationInProgress) {
    window.clearTimeout(animationInProgress)
    dupa.classList.remove('smooth')
    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
    dupa.dataset.num = oldNum - 10
    setDebug(oldNum - 10)
    dupa.classList.remove('smooth')

    window.requestAnimationFrame(() => {
      // Wait for one frame to let the snapback happen
      window.requestAnimationFrame(() => {
        // Then roll forward
        animateOdometer(newNum)
      })
    })

    return
  }

  // Roll the dial
  animateOdometer(newNum)
}
#rand,
#debug {
  margin-top: 50px;
}

.dupa1 {
  height: 30px;
  width: 30px;
  border: 1px solid #000;
  overflow: hidden;
}

.dupa2.smooth {
  transition: all 1s ease;
}

.dupa2 span {
  height: 30px;
  width: 30px;
  display: block;
  text-align: center;
  line-height: 30px;
}

.dupa2.debugBackground {
  background: linear-gradient(to bottom, #ffffff 0%, #ffffff 50%, #207cca 51%, #207cca 100%);
}

.dupa2[data-num="0"] {
  transform: translate(0, 0);
}

.dupa2[data-num="1"] {
  transform: translate(0, -30px);
}

.dupa2[data-num="2"] {
  transform: translate(0, -60px);
}

.dupa2[data-num="3"] {
  transform: translate(0, -90px);
}

.dupa2[data-num="4"] {
  transform: translate(0, -120px);
}

.dupa2[data-num="5"] {
  transform: translate(0, -150px);
}

.dupa2[data-num="6"] {
  transform: translate(0, -180px);
}

.dupa2[data-num="7"] {
  transform: translate(0, -210px);
}

.dupa2[data-num="8"] {
  transform: translate(0, -240px);
}

.dupa2[data-num="9"] {
  transform: translate(0, -270px);
}

.dupa2[data-num="10"] {
  transform: translate(0, -300px);
}

.dupa2[data-num="11"] {
  transform: translate(0, -330px);
}

.dupa2[data-num="12"] {
  transform: translate(0, -360px);
}

.dupa2[data-num="13"] {
  transform: translate(0, -390px);
}

.dupa2[data-num="14"] {
  transform: translate(0, -420px);
}

.dupa2[data-num="15"] {
  transform: translate(0, -450px);
}

.dupa2[data-num="16"] {
  transform: translate(0, -480px);
}

.dupa2[data-num="17"] {
  transform: translate(0, -510px);
}

.dupa2[data-num="18"] {
  transform: translate(0, -540px);
}

.dupa2[data-num="19"] {
  transform: translate(0, -570px);
}
<div class="dupa1">
  <div class="dupa2" id="cipa" data-num="0">
    <span>0</span>
    <span>1</span>
    <span>2</span>
    <span>3</span>
    <span>4</span>
    <span>5</span>
    <span>6</span>
    <span>7</span>
    <span>8</span>
    <span>9</span>
    <span>0</span>
    <span>1</span>
    <span>2</span>
    <span>3</span>
    <span>4</span>
    <span>5</span>
    <span>6</span>
    <span>7</span>
    <span>8</span>
    <span>9</span>
  </div>
</div>

<div id="debug">
  Number is really: 0
</div>

<button id="rand">rand</button>

How to Create Smooth Numbers Transitioning For Your Site, Now any element that you wrap with the odometer class will be transformed into an odometer. In this example, I'm using a car-like odometer� Translate Odometer. See authoritative translations of Odometer in Spanish with example sentences and audio pronunciations.

You can use two sets of numbers and a little bit of extra javascript to achieve this effect.

If the new number is less than the current number, use a second set of numbers (digits 0-9) that are farther down. As the css animation transitions from the first set of numbers to the second, it will appear as if the odometer is "rolling over".

When the animation completes, switch back to the first set of numbers without animating (no transition class).

I've made a working example based off of your original jsfiddle.

NOTE: This makes use of the .classList property of DOM elements, and the tranistionend event. You may have to add vendor prefixes (i.e. webkitTransitionEnd) and implement your own version of .classList, depending on what browsers you need to support.

document.getElementById("rand").addEventListener("click", randomize);
document.getElementById("cipa").addEventListener("transitionend", transitionEnd);

function randomize() {
  setNumber(Math.floor(Math.random() * 9));
}

function setNumber(newNumber) {
  let dupa = document.getElementById("cipa");

  // assumes dupa.dataset.num always be a valid int
  let selected = parseInt(dupa.dataset.num);

  if (newNumber === selected) return; // if same as existing, don't do anything

  // if the new number is less than the old number
  // use the second set of numbers to avoid moving "backwards"
  if (newNumber < selected) dupa.classList.add("rolledover");

  // animate to the new position
  dupa.classList.add("transitioning");
  dupa.dataset.num = "" + newNumber;
}

function transitionEnd() {
  let dupa = document.getElementById("cipa");
  // don't animate
  dupa.classList.remove("transitioning");
  dupa.classList.remove("rolledover");
}
#rand {
  margin-top: 50px;
}

.dupa1 {
  height: 30px;
  width: 30px;
  border: 1px solid #000;
  overflow: hidden;
}

.dupa2.transitioning {
  transition: all 1s ease;
}

.dupa2 span {
  height: 30px;
  width: 30px;
  display: block;
  text-align: center;
  line-height: 30px;
}

.dupa2[data-num="0"] {
  transform: translate(0, 0);
}

.dupa2[data-num="1"] {
  transform: translate(0, -30px);
}

.dupa2[data-num="2"] {
  transform: translate(0, -60px);
}

.dupa2[data-num="3"] {
  transform: translate(0, -90px);
}

.dupa2[data-num="4"] {
  transform: translate(0, -120px);
}

.dupa2[data-num="5"] {
  transform: translate(0, -150px);
}

.dupa2[data-num="6"] {
  transform: translate(0, -180px);
}

.dupa2[data-num="7"] {
  transform: translate(0, -210px);
}

.dupa2[data-num="8"] {
  transform: translate(0, -240px);
}

.dupa2[data-num="9"] {
  transform: translate(0, -270px);
}

.rolledover.dupa2[data-num="0"] {
  transform: translate(0, -300px);
}

.rolledover.dupa2[data-num="1"] {
  transform: translate(0, -330px);
}

.rolledover.dupa2[data-num="2"] {
  transform: translate(0, -360px);
}

.rolledover.dupa2[data-num="3"] {
  transform: translate(0, -390px);
}

.rolledover.dupa2[data-num="4"] {
  transform: translate(0, -420px);
}

.rolledover.dupa2[data-num="5"] {
  transform: translate(0, -450px);
}

.rolledover.dupa2[data-num="6"] {
  transform: translate(0, -480px);
}

.rolledover.dupa2[data-num="7"] {
  transform: translate(0, -510px);
}

.rolledover.dupa2[data-num="8"] {
  transform: translate(0, -540px);
}

.rolledover.dupa2[data-num="9"] {
  transform: translate(0, -570px);
}
<div class="dupa1">
  <div class="dupa2" id="cipa" data-num="0">
    <span>0</span>
    <span>1</span>
    <span>2</span>
    <span>3</span>
    <span>4</span>
    <span>5</span>
    <span>6</span>
    <span>7</span>
    <span>8</span>
    <span>9</span>
    <span>0</span>
    <span>1</span>
    <span>2</span>
    <span>3</span>
    <span>4</span>
    <span>5</span>
    <span>6</span>
    <span>7</span>
    <span>8</span>
    <span>9</span>
  </div>
</div>

<button id="rand">rand</button>

Odometer — Transition numbers with ease, Odometer is a Javascript and CSS library for smoothly transitioning numbers. It is free and open source and was developed by HubSpot developers Adam� odometer translate: contachilometri, odometro. Learn more in the Cambridge English-Italian Dictionary.

Thank you a lot.

But I stuck in another problem similar, but with array.

I've made a fiddle for better view to the problem: https://jsfiddle.net/zr2dLbge/

<div class="wrap" id="wrap"></div>

.wrap{
  border:1px solid #000;
  display: inline-block;
  height:30px;
  border-right: none;
}
.numbers{
   width:30px;
  height:30px;
  display:inline-block;
  overflow: hidden;
  border-right: 1px solid #000;
}
.numbers span{
  display: block;
  width:30px;
  height:30px;
  line-height: 30px;
  text-align: center;
}

.numbers[data-num="0"] div{
  transform: translate(0, 0);
  transition: all 1s ease;
}

.numbers[data-num="1"] div{
  transform: translate(0, -30px);
  transition: all 1s ease;
}

.numbers[data-num="2"] div{
  transform: translate(0, -60px);
  transition: all 1s ease;
}

.numbers[data-num="3"] div{
  transform: translate(0, -90px);
  transition: all 1s ease;
}

.numbers[data-num="4"] div{
  transform: translate(0, -120px);
  transition: all 1s ease;
}

.numbers[data-num="5"] div{
  transform: translate(0, -150px);
  transition: all 1s ease;
}

.numbers[data-num="6"] div{
  transform: translate(0, -180px);
  transition: all 1s ease;
}

.numbers[data-num="7"] div{
  transform: translate(0, -210px);
  transition: all 1s ease;
}

.numbers[data-num="8"] div{
  transform: translate(0, -240px);
  transition: all 1s ease;
}

.numbers[data-num="9"] div{
  transform: translate(0, -270px);
  transition: all 1s ease;
}

let arr = [];
var numbers = 1234561234;
const wrap = document.getElementById("wrap");

function toArray (val) {

  return (val).toString().split('');
}

arr = toArray(numbers);

for (let i = 0; i < arr.length; i++) {
    div = document.createElement('div'),
    div.className = "numbers";
    div.dataset.num = arr[i];
    div.dataset.x = i;
    div.innerHTML = "<div><span>0</span><span>1</span><span>2</span><span>3</span><span>4</span><span>5</span><span>6</span><span>7</span><span>8</span><span>9</span></div>"
    wrap.appendChild(div);
}


setInterval(function(){ 
arr.forEach( (k) => {
  arr[k] = Math.floor(Math.random() * 9) + 0;
})

for (let i = 0; i < arr.length; i++) {
    document.querySelector('.numbers[data-x="'+i+'"]').dataset.num = arr[i];
}
 }, 1000);

unfortunately window.requestAnimationFrame() doesn't work for me in this case

odometer, Add the odometer class to any numbers you'd like to animate on change. don't update by erasing and rerendering the odometer element, will work just fine. Translation of Odometer in English. Translate Odometer in English online and download now our free translator to use any time at no charge.

translate(), The translate() CSS function repositions an element in the horizontal and/or The ordinate (vertical, y-coordinate) of the translating vector will be set to 0 . A percentage as first value refers to the width, as second part to the� The ICO Rallye MAX-G is a race-ready compass repeater and an excellent backup odometer. El Rallye MAX-G es un repetidor de CAP listo para los rally y un excelente odómetro de backup. Reset your car odometer to zero.

transform, Even with a declared height and width, this element will now be skewX() and skewY() : Tilts an element to the left or right, like turning a� Odometer definition: An odometer is a device in a vehicle which shows how far the vehicle has travelled. | Meaning, pronunciation, translations and examples

Symmetry - Odometer is 088880 on My Honda Element, My Honda Element Odometer goes to 088880 for the very first Time, never before documented Duration: 1:11 Posted: Aug 31, 2015 Charges in odometer tampering cases, therefore, often include allegations that these statutes have also been violated. Such charges more accurately depict the totality of the illegal conduct than odometer tampering charges standing alone. The general conspiracy statute is commonly used in tampering cases.

Comments
  • The code in jsfiddle uses a random number. You want it to go up one by one?
  • Yup, it have to be randomly
  • This is not bad, only other way I can think is using jQuery and generating lists dynamically...
  • thanks a lot, it solved my problem. @Bedir i want to do it without jquery, just vanilla js