colab-smooth-scroll
v0.2.0
Published
Lerp-based smooth scrolling for the web. Vanilla JS, zero dependencies.
Maintainers
Readme
colab-smooth-scroll
Lerp-based smooth scrolling. Vanilla JS, zero dependencies.
Wheel input feeds a virtual scroll target that the real scroll position eases
toward each frame, so the page glides instead of stepping. Touch drags,
keyboard, scrollbar, and anchor jumps stay native — a scroll listener adopts
any position the library didn't set itself. Respects prefers-reduced-motion.
Install
npm install colab-smooth-scrollUsage
ESM / bundler
import { initSmoothScroll } from 'colab-smooth-scroll';
const scroller = initSmoothScroll({ smoothness: 0.07 });
scroller.stop(); // freeze wheel-driven scrolling
scroller.start(); // resume it
scroller.scrollTo(0, { duration: 1.4 }); // animate to a position
scroller.on('scroll', ({ scroll, velocity }) => { /* ... */ });
// later, if needed
scroller.destroy();CommonJS
const { initSmoothScroll } = require('colab-smooth-scroll');Plain <script>
<script src="https://unpkg.com/colab-smooth-scroll/dist/index.global.js"></script>
<script>
const scroller = window.SmoothScroll.initSmoothScroll({ smoothness: 0.07 });
</script>Options
| Option | Type | Default | Description |
| ------------------ | -------- | ---------------- | -------------------------------------------------------------------- |
| smoothness | number | 0.07 | Wheel easing rate per frame — higher is snappier. |
| wheelMultiplier | number | 1 | Scales the wheel delta before it's applied. |
| scrollToDuration | number | 1.2 | Default duration (seconds) for scrollTo() calls that don't set one.|
| scrollToEasing | function | ease-out expo | Default easing for scrollTo() calls that don't set one. |
Instance methods
initSmoothScroll() returns an instance (or an inert no-op instance if
prefers-reduced-motion is on):
stop()/start()— freeze/resume wheel-driven scrolling. While stopped, wheel input is swallowed (preventDefault, no movement) except on elements opted out viadata-wheel-native. Togglesis-smooth-scroll-stoppedon<html>.scrollTo(y, { duration, easing })— animates to positiony.duration: 0jumps instantly. A wheel event mid-animation interrupts it and resumes normal wheel-driven scrolling from wherever it was.on('scroll', fn)/off('scroll', fn)—fnis called each frame the position changes with{ scroll, velocity }(velocityis the pixel delta since the previous frame).destroy()— removes all listeners and stops the animation loop.
<html> carries is-smooth-scroll for as long as the instance is active
(added on init, removed on destroy()), plus two transient classes:
is-smooth-scrolling while a scroll animation (wheel-driven or
scrollTo()) is in flight, and is-smooth-scroll-stopped while stop() is
active. Compose .is-smooth-scroll.is-smooth-scroll-stopped (two classes)
in consumer CSS when a rule needs to outrank another same-specificity rule
without depending on source order.
Opting an element out
Elements that scroll themselves (e.g. a fixed thumb rail, or a modal panel with its own overflow) can opt their wheel input out of the page-scroll hijack:
<div data-wheel-native>...</div>Build
npm run buildBundling: math.js, ticker.js, and index.js have no circular deps, so
build.js strips their import/export lines and concatenates them in
dependency order, then runs the result through terser — the same minifier
d8e uses for JS — for the ESM, CJS, and
global (<script>) dist builds. terser is the only build-time dependency.
Test
npm testUses Node's built-in test runner (node --test), zero test dependencies. The
library only touches a handful of window/document members, so
test-helper/browser-env.js hand-rolls a minimal stub rather than pulling in
jsdom.
