Skip to content Skip to sidebar Skip to footer

Translating Div To Behave Like Odometer

I've been trying to create an odometer like animation using React, and vanilla css. So far it's working where when number is incremented, a translationY upwards occurs like an actu

Solution 1:

I spent way too much time looking at solutions for this. First off, there are a ton of libraries that would make something like this trivial, however I enjoyed the learning process of finding a solution.

I did not create a react specific solution, but my vanilla javascript demo should be more than enough to easily port it into a react solution.

To accomplish this task I first tried to create an element each time there was a change, with the bottom number being the starting number, and the top number being the landing number, and slide it down until the desired number was hit, and make the old element disappear. However this ended up looking choppy and had some unintended effects when the element was changed rapidly.

After stumbling across a few demos, I realized that 3d css might be the perfect solution. Instead of having a 2d element we transition up and down, we could create a 3d element that was spinning on a wheel. Js could calculate the degree needed for rotating the element to always be spinning forward.

Please enjoy my small demo, and if you have any questions please ask.

const$ = (str, dom = document) => [...dom.querySelectorAll(str)];

const panels = $(".panel");
panels.forEach((panel, i) => {
  panel.style.setProperty("--angle", `${360 / panels.length * i}deg`)
});
const ring = $(".ring")[0];
const nums = $(".num");
nums.forEach((num, i) => {
  num.addEventListener("click", () => {
    const numAngle = 36 * i;
    const currentAngle =
      ring.style.getPropertyValue("--deg")
      .replace(/\D/g, "");
    let nextAngle = numAngle;
    while (nextAngle < currentAngle) {
      nextAngle += 360;
    }
    ring.style.setProperty("--deg", `-${nextAngle}deg`)
  });
});
* {
 margin: 0;
 padding: 0;
 box-sizing: border-box;
 font-family: monospace;
}

body {
 display: grid;
 place-items: center;
 min-height: 100vh;
 perspective: 500px;
 perspective-origin: 50%50%;
}

.ring {
  transform-style: preserve-3d;
  transition: transform 1s;
  transform: rotateX(var(--deg));
}

.panel {
  position: absolute;
  transform:
    translate(-50%, -50%)
    rotateX(var(--angle))
    translateZ(22.5px);
  border-bottom: 1px solid black;
  background-color: white;
}

.numPanel {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  display: flex;
  justify-content: center;
  gap: 1rem;
  user-select: none;
}

.num {
  font-size: 2rem;
  padding: 0.5rem;
  cursor: pointer;
}

.num:hover {
  background-color: lightblue;
}
<divclass="numPanel"><divclass="num">0</div><divclass="num">1</div><divclass="num">2</div><divclass="num">3</div><divclass="num">4</div><divclass="num">5</div><divclass="num">6</div><divclass="num">7</div><divclass="num">8</div><divclass="num">9</div></div><divclass="ring"><divclass="panel">0</div><divclass="panel">1</div><divclass="panel">2</div><divclass="panel">3</div><divclass="panel">4</div><divclass="panel">5</div><divclass="panel">6</div><divclass="panel">7</div><divclass="panel">8</div><divclass="panel">9</div></div>

Post a Comment for "Translating Div To Behave Like Odometer"