Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to make odometer styled transistion without using any external package in Reactjs?

So I have many of the sites for the solution everyone is some package but i dont need any of the package its should be only using pure js in Reactjs <div className="odometer-value">100</div>

current not able to find one without package

like image 992
RamkumarR Avatar asked Oct 25 '25 05:10

RamkumarR


1 Answers

There are bunch of libraries which provides it by default with cool visual representation, But as you said you are looking for plain implementation so I just wrote something in couple of minutes.

Assuming you are not looking for fancy animations and just a counter counting up/down on value change is fine, below code should work for you.

import { useEffect, useState } from "react";
import "./SpeedoMeter.css";

export default function SpeedoMeter({ value }) {
  // Current shown value
  const [currentValue, setCurrentValue] = useState(0);
  // Target Value to reach
  const [targetValue, setTargetValue] = useState(0);
  // Number of step to reach
  const steps = 10;
  // Latency between each step, if this is 0 then the count up/down is very instant and wont work
  const lag = 1;

  // Updating target value
  useEffect(() => {
    setTargetValue(value);
  }, [value]);

  // Updating Current value to follow target value, a feedback loop if made so that useEffect depencency can be leveraged.
  // Note: This is not super accurate in timing/animations
  useEffect(() => {
    if (currentValue !== targetValue) {
      setTimeout(() => {
        setCurrentValue((prevCurr) => {
          let distance = Math.abs(targetValue - currentValue);
          let stepSize = Math.ceil(distance / steps);
          return currentValue < targetValue
            ? currentValue + stepSize
            : currentValue - stepSize;
        });
      }, lag);
    }
  }, [currentValue, targetValue]);

  return (
    <div className="speedo-wrap">
      {(currentValue + "").split("").map((val, idx) => (
        <div className="speedo-digit" style={{ marginTop: `-${val}em` }}>
          <div data-val="0">0</div>
          <div data-val="1">1</div>
          <div data-val="2">2</div>
          <div data-val="3">3</div>
          <div data-val="4">4</div>
          <div data-val="5">5</div>
          <div data-val="6">6</div>
          <div data-val="7">7</div>
          <div data-val="8">8</div>
          <div data-val="9">9</div>
        </div>
      ))}
    </div>
  );
}

CSS

.speedo-wrap {
  display: flex;
  justify-content: center;
  height: 1rem;
  font-size: 1rem;
  line-height: 1rem;
  overflow: hidden;
}

.speedo-digit {
  transition: 1s all;
}

USAGE

<SpeedoMeter value={10000} />

Adding CodeSandbox for same: https://codesandbox.io/s/basic-react-speedometer-gsv5yc?file=/src/SpeedoMeter.js

Additionally, animations can be easily controlled with cubic-bezier, visual representation of digits can also be changes with minimal side-effect, speed/steps can be controlled.

EDIT: Created a library with same

Github NPM

like image 165
5 revsVikas Saini Avatar answered Oct 26 '25 18:10

5 revsVikas Saini