@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
Markdown
# 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.