--- title: "EDM115 - Project EDM115/light-odometer" canonical_url: "https://edm115.dev/projects/light-odometer" last_updated: "2026-08-30T03:11:55.954Z" meta: description: "For personal use only. Use mtmarco87/tm-odometer instead" "og:description": "For personal use only. Use mtmarco87/tm-odometer instead" "og:title": "EDM115 - Project EDM115/light-odometer" --- Home # light-odometer ![NPM Version](https://img.shields.io/npm/v/light-odometer)![NPM Downloads](https://img.shields.io/npm/dt/light-odometer) [![More info](https://img.shields.io/badge/npmx-More_info-orange?logo=npm)](https://npmx.dev/light-odometer) This project is made only for personal use, as a small and lightweight build of [tm-odometer](https://github.com/mtmarco87/tm-odometer), which is itself a fork of [HubSpot’s odometer](https://github.com/HubSpot/odometer). ⚠️ Do not use this, use [@mtmarco87](https://github.com/mtmarco87)’s package instead. Huge props to him for this TypeScript refactor ! No theme is shipped here, no docs either. ## What’s changed ? For a quick overview in a real-world usage, see https://github.com/EDM115/website/blob/master/app/components/ui/Odometer.vue ### Breaking changes - Renamed from `TmOdometer` to `LightOdometer` - Removal of all built artifacts, themes, demo, screenshots and CoffeeScript code - Switch from Rollup to tsdown (Rolldown) - Remove compatibility layers (Internet Explorer, jQuery) - Keep only the ESM build and switch to a default export ### New features - SSR friendly (you can import it top-level without blowing up) and use safe fallbacks for browser-land functions - Each instance can now have an `id` - Ability to customize the frameratets ``` const odo = new LightOdometer({ ..., framerate: 20 }) // default is 30, going above isn't recommended // use countFramerate if you use the `count` mode instead of `slide` ``` - Ability to render once and destroy the instancets ``` const odo = new LightOdometer({ ..., value: 0 }) odo.animateOnceAndDisconnect(12345) ``` - Expose `duration` as a CSS property to sync up JS and CSS animationsts ``` const odo = new LightOdometer({ ..., duration: 5000 }) console.log(getComputedStyle(odo.el).getPropertyValue("--odometer-duration")) // "5000ms" ``` - Get and mutate instance and global optionsts ``` const odo = new LightOdometer({ ... }) console.log(odo.getOptions().duration) odo.setOptions({ duration: 2000 }) console.log(LightOdometer.getGlobalOptions.selector) LightOdometer.setGlobalOptions({ selector: ".my-odometer" }) ``` - Add per-instance subscriptable animation start/end events, they give back the instance id, el, instance, value, oldValue, optionsts ``` const odo = new LightOdometer({ ... }) console.log(odo.isAnimating) function onStart(e: Event) { const { detail } = e as CustomEvent // detail.id, detail.value, detail.instance.isAnimating, ... console.log(`started animating to ${detail.value}`) } function onDone(e: Event) { const { detail } = e as CustomEvent console.log(`finished animating to ${detail.value}`) } odo.on("odometerstart", onStart) odo.on("odometerdone", onDone) // later odo.off("odometerstart", onStart) odo.off("odometerdone", onDone) ``` - Print the instance as a JSON-friendly stringts ``` const odo = new LightOdometer({ ... }) console.log(odo.toString()) ```json ``` { "id": 2, "value": 157, "options": { "id": 2, "value": 0, "animation": "slide", "duration": 8000, "format": "( ddd)", "framerate": 20 }, "globalOptions": {}, "watchMutations": false, "transitionEndBound": true, "destroyed": false, "format": { "repeating": " ", "precision": 0 }, "isAnimating": false } ``` ### Improvements - `value` is now stripped from spaces - Multiple performance improvements - Use overall more optimized methods - Stabilize and simplify the interfaces ### Other changes - No more const functions - Better TS config and stricter types (no more `any` nor `as`) - Use classes private elements - Added linting and formatting (OxLint + ESLint Stylistic) - Target ES2023 instead of ES2015