UNPKG

@magic-spells/scroll-velocity

Version:

High-performance scroll velocity tracker with physics-based friction and CSS variable output for velocity-driven animations.

179 lines (126 loc) 5.93 kB
# Scroll Velocity Tracker High-performance scroll velocity tracker with physics-based friction and CSS variable output for velocity-driven animations. [**Live Demo**](https://magic-spells.github.io/scroll-velocity/demo/) ## Features - **High-performance tracking** using `requestAnimationFrame` (no scroll event performance issues) - 🎯 **Physics-based simulation** with configurable dampening, friction, and attraction to zero - 🕵️‍♂️ **Smart sampling modes** - delta-based for punchy feel or time-based for consistent velocity - 🌊 **Multiple CSS variables** exposed: normalized, absolute, power-curve, and raw velocity values - 🌐 **Framework agnostic** works with any frontend framework or vanilla JS - 📦 **Lightweight & zero dependencies** - Optimized for performance - 🔧 **Simple API** with start/stop controls and runtime option updates - **Accessibility-aware** - respects `prefers-reduced-motion` preference ## Installation ```bash npm install @magic-spells/scroll-velocity ``` ```javascript // Import the class in your JS entry point import { ScrollVelocity } from '@magic-spells/scroll-velocity'; ``` Or include directly via CDN: ```html <script src="https://unpkg.com/@magic-spells/scroll-velocity"></script> ``` ## Usage Create and start the velocity tracker: ```javascript import { ScrollVelocity } from '@magic-spells/scroll-velocity'; const tracker = new ScrollVelocity({ target: document.body, // element that receives CSS variables sampleMode: 'delta', // 'delta' for punchy feel, 'time' for consistent velocity dampening: 0.35, // higher = snappier response to velocity changes friction: 0.95, // velocity persistence per frame (0-1) attraction: 0.96, // pull toward zero each frame (0-1) threshold: 0.02, // stop when velocity drops below this maxVelocity: 200, // clamp raw velocity, used for normalization writeCSSVariables: true, // set to false for programmatic-only usage respectReducedMotion: true // disable when user prefers reduced motion }); tracker.start(); ``` --- ## API ### Constructor Options All options are optional with sensible defaults: - `target` _(HTMLElement)_ - Element to receive CSS variables (default: `document.body`) - `sampleMode` _('delta'|'time')_ - Sampling method; 'delta' for punchy old-school feel, 'time' for consistent px/ms - `dampening` _(number)_ - Blend factor toward target velocity; higher = snappier (default: 0.35) - `friction` _(number)_ - Velocity decay per frame, 0-1 (default: 0.92) - `attraction` _(number)_ - Pull toward zero per frame, 0-1 (default: 0.96) - `threshold` _(number)_ - Stop threshold for absolute velocity (default: 0.02) - `maxVelocity` _(number)_ - Clamp for raw velocity, used for normalization (default: 200) - `writeCSSVariables` _(boolean)_ - Whether to write CSS custom properties (default: true) - `respectReducedMotion` _(boolean)_ - Disable when user prefers reduced motion (default: true) ### Public Methods - `start()` - Begin tracking scroll velocity - `stop()` - Stop tracking and reset velocity to zero - `getVelocity()` - Returns current raw velocity (signed number) - `getNormalizedVelocity()` - Returns velocity normalized to [-1, 1] range - `setOptions(options)` - Update configuration without recreating instance --- ## CSS Variables The tracker exposes these CSS custom properties on the target element: - `--scroll-velocity` Normalized velocity between -1 and 1 - `--scroll-velocity-abs` Absolute value of normalized velocity (0 to 1) - `--scroll-velocity-pow` Power-curve transformed absolute velocity for enhanced mid-range perception - `--scroll-velocity-raw` Raw velocity value in pixels (clamped by maxVelocity) Use these properties to drive CSS animations: ```css .velocity-element { transform: translateX(calc(var(--scroll-velocity) * 100px)); filter: blur(calc(var(--scroll-velocity-abs) * 5px)); background-color: hsl(0 100% calc(50% + var(--scroll-velocity-pow) * 30%)); } .skew-on-scroll { transform: skewX(calc(var(--scroll-velocity) * 15deg)); } .scale-with-speed { transform: scale(calc(1 + var(--scroll-velocity-abs) * 0.5)); } ``` --- ## How It Works - **Smart Sampling**: Choose between delta-based sampling (punchy, immediate) or time-based sampling (consistent velocity regardless of scroll event frequency) - **Physics Simulation**: Applies dampening to chase velocity peaks, then friction and attraction-to-zero for natural decay - **Performance Optimized**: Uses `requestAnimationFrame` for smooth updates only when velocity is non-zero - **Accessibility**: Automatically disables velocity when user has `prefers-reduced-motion: reduce` set - **CSS-Driven Animations**: Exposes multiple velocity representations as CSS variables for GPU-accelerated animations The physics simulation creates natural-feeling velocity curves that start responsive and decay smoothly to zero. --- ## Integration Example ```javascript const tracker = new ScrollVelocity({ maxVelocity: 150, dampening: 0.4 }); tracker.start(); // Read velocity programmatically function onAnimationFrame() { const velocity = tracker.getVelocity(); const normalized = tracker.getNormalizedVelocity(); console.log('Raw velocity:', velocity); console.log('Normalized velocity:', normalized); requestAnimationFrame(onAnimationFrame); } requestAnimationFrame(onAnimationFrame); // Update settings at runtime tracker.setOptions({ dampening: 0.5, friction: 0.95 }); ``` --- ## Browser Support Supports all modern browsers with `requestAnimationFrame` and `matchMedia`: - Chrome 10+ - Firefox 4+ - Safari 6+ - Edge 12+ --- ## License MIT --- ## Repository & Issues [https://github.com/magic-spells/scroll-velocity](https://github.com/magic-spells/scroll-velocity) Report bugs and request features via GitHub issues.