Most animated counters move every changing digit at once. A real odometer doesn't do that. Going from 1,099 to 1,100, the ones drum turns and the tens and hundreds only get pulled along at the end.
I wanted that on the web so I made one. It's free, MIT, on npm as tactile-odometer.
how it carries
Each drum is a ten-sided prism in CSS 3D. Only the last drum is driven, by one spring on a single number. Every other drum works out its angle from that number. It stays put while the drum to its right goes 0 to 9, and turns when that drum goes 9 to 0.
In the 1,099 to 1,100 test the ones drum is most of the way round before the tens and hundreds start (268 ms in), and all three land together at 417 ms. I timed odometer.js and NumberFlow the same way in Chromium, and both start every changing column on the same frame.
the small stuff
- the faces are real text, not images
- past about 24 digits a second a drum draws as a blur instead of strobing
- screen readers hear the number once when it lands
- with reduced motion on, the number just changes
- Intl.NumberFormat, up to 6 decimals, a prefix and a suffix, and a tally mode that counts up or down
using it
The quickest way in is the shadcn registry, no account needed:
npx shadcn add @tactile/odometer
or from npm:
import { Odometer } from 'tactile-odometer';
import 'tactile-odometer/odometer.css';
const odo = new Odometer(document.getElementById('counter'), { value: 1099 });
odo.setValue(1100); // rolls
odo.setValue(1100, { instant: true }); // jumps
odo.count(1); // live tally, odo.count(0) stops it
There's also a custom element if you'd rather drop it in as a tag.
what it costs
About 8 KB gzipped of script, 4.2 KB of CSS and a 20.7 KB numerals font, so it's heavier than the others. There's no React or Vue package yet (in React, create it in an effect and destroy it on unmount), and the bundled numerals are Latin only.
Source: github.com/cipriantitire/tactile-ui-free
Live, to play with: tactileui.dev/objects/odometer
It's one piece of Tactile UI, the library I'm launching on Tuesday. If this looks wrong next to a real odometer, tell me where.
Top comments (0)