|
1 | | -# ScrollToSmooth |
2 | | - |
3 | | -[](https://www.codefactor.io/repository/github/bfiessinger/scrolltosmooth) |
4 | | -[](https://bundlephobia.com/result?p=scrolltosmooth) |
5 | | - |
6 | | -[](https://www.jsdelivr.com/package/gh/bfiessinger/scrollToSmooth) |
7 | | - |
8 | | -Lightweight **Vanilla JavaScript smooth scrolling library** with zero dependencies. |
9 | | - |
10 | | -ScrollToSmooth provides fully customizable scroll animations powered by `requestAnimationFrame`. |
11 | | -The core stays minimal while advanced functionality can be added through a **tree-shakeable plugin system**. |
| 1 | +<p align="center"> |
| 2 | + <br /> |
| 3 | + <strong style="font-size: 2em;">ScrollToSmooth</strong> |
| 4 | + <br /> |
| 5 | + <em>Buttery-smooth scroll animations for the web — zero dependencies, fully tree-shakeable, plugin-powered.</em> |
| 6 | + <br /><br /> |
| 7 | +</p> |
| 8 | + |
| 9 | +<p align="center"> |
| 10 | + <a href="https://www.codefactor.io/repository/github/bfiessinger/scrolltosmooth"><img src="https://img.shields.io/codefactor/grade/github/bfiessinger/scrolltosmooth?style=for-the-badge" alt="CodeFactor" /></a> |
| 11 | + <a href="https://bundlephobia.com/result?p=scrolltosmooth"><img src="https://img.shields.io/bundlephobia/minzip/scrolltosmooth?style=for-the-badge" alt="Bundle Size" /></a> |
| 12 | + <img src="https://img.shields.io/github/v/release/bfiessinger/scrollToSmooth?include_prereleases&style=for-the-badge" alt="Version" /> |
| 13 | + <a href="https://www.jsdelivr.com/package/gh/bfiessinger/scrollToSmooth"><img src="https://img.shields.io/jsdelivr/gh/hy/bfiessinger/scrolltosmooth?style=for-the-badge" alt="jsDelivr" /></a> |
| 14 | +</p> |
| 15 | + |
| 16 | +<p align="center"> |
| 17 | + <a href="#quick-start">Quick Start</a> · <a href="docs/api.md">API</a> · <a href="docs/plugins.md">Plugins</a> · <a href="docs/easings.md">Easings</a> · <a href="https://bfiessinger.github.io/scrollToSmooth/">Live Demo</a> |
| 18 | +</p> |
12 | 19 |
|
13 | 20 | --- |
14 | 21 |
|
15 | | -## Features |
16 | | - |
17 | | -- Smooth animated scrolling |
18 | | -- Custom easing functions (import only what you need) |
19 | | -- Programmatic scrolling API |
20 | | -- Fixed header offset support |
21 | | -- Scroll lifecycle callbacks |
22 | | -- Plugin architecture |
23 | | -- Tree-shakeable modules |
24 | | -- No dependencies |
| 22 | +ScrollToSmooth gives you complete control over scroll animations using `requestAnimationFrame`. Pick from **31 built-in easing functions**, scroll in any direction, queue animations, snap to sections, add touch-momentum — and ship only the code you actually use. |
| 23 | + |
| 24 | +## Highlights |
| 25 | + |
| 26 | +| | Feature | What it does | |
| 27 | +|-|---------|-------------| |
| 28 | +| **🎯** | **Scroll anywhere** | Scroll to elements, CSS selectors, pixel offsets, percentages (`'50%'`), viewport units (`'25vh'`), or `{ x, y }` coordinates | |
| 29 | +| **🎨** | **31 easing functions** | From subtle `easeOutCubic` to bouncy `easeOutBounce` — import only the ones you need | |
| 30 | +| **📐** | **Horizontal & 2D scrolling** | Scroll on the x-axis, y-axis, or both simultaneously via the Horizontal plugin | |
| 31 | +| **📸** | **Section snapping** | Auto-snap to the nearest section after the user stops scrolling | |
| 32 | +| **📱** | **Touch momentum** | Inertia scrolling on touch devices — swipe and let momentum carry you | |
| 33 | +| **🔗** | **Scroll queue** | Chain multiple scroll targets and execute them in sequence | |
| 34 | +| **🧩** | **Plugin system** | Add only what you need — every plugin is tree-shakeable | |
| 35 | +| **⚡** | **CSS custom properties** | `--sts-scroll-y` and `--sts-scroll-x` update every frame — drive progress bars, parallax, or any reactive styling from scroll position | |
| 36 | +| **🎛** | **Callbacks & events** | `onScrollStart` / `onScrollUpdate` / `onScrollEnd` callbacks + `scrolltosmooth:*` CustomEvents that bubble up | |
| 37 | +| **📏** | **Smart offsets** | Fixed-header offset via element selector, pixels, `%`, or `vh` — auto-recalculates on resize | |
| 38 | +| **🌐** | **Native fallback** | Set `useNative: true` to delegate to the browser's `scroll-behavior: smooth` when supported | |
| 39 | +| **🪶** | **Tiny core** | ~3 KB min+gzip with zero dependencies | |
25 | 40 |
|
26 | 41 | --- |
27 | 42 |
|
28 | | -## Demo |
| 43 | +## Live Demo |
| 44 | + |
| 45 | +**[→ bfiessinger.github.io/scrollToSmooth](https://bfiessinger.github.io/scrollToSmooth/)** |
29 | 46 |
|
30 | | -https://bfiessinger.github.io/scrollToSmooth/ |
| 47 | +See vertical scrolling, horizontal scrolling, 2D board navigation, section snapping, scroll queuing, and progress tracking in action. |
31 | 48 |
|
32 | 49 | --- |
33 | 50 |
|
34 | 51 | ## Installation |
35 | 52 |
|
36 | | -### NPM |
37 | | - |
38 | 53 | ```bash |
39 | 54 | npm install scrolltosmooth |
40 | 55 | ``` |
41 | 56 |
|
42 | | -### CDN |
43 | | - |
44 | | -All‑in‑one build (Do not use for production): |
| 57 | +Or use a CDN — pick the build that fits your needs: |
45 | 58 |
|
46 | 59 | ```html |
| 60 | +<!-- Minimal core (linear easing only, ~3 KB) --> |
| 61 | +<script src="https://cdn.jsdelivr.net/npm/scrolltosmooth/dist/scrolltosmooth.min.js"></script> |
| 62 | + |
| 63 | +<!-- Full bundle with all easings + plugins pre-registered --> |
47 | 64 | <script src="https://cdn.jsdelivr.net/npm/scrolltosmooth/dist/scrolltosmooth.pkgd.min.js"></script> |
48 | 65 | ``` |
49 | 66 |
|
50 | | -Minimal build (linear easing only): |
| 67 | +> **Tip:** The core IIFE only includes the `linear` easing. Use the `pkgd` build for quick prototyping; for production, import individual easings and plugins to keep bundles lean. |
51 | 68 |
|
52 | | -```html |
53 | | -<script src="https://cdn.jsdelivr.net/npm/scrolltosmooth/dist/scrolltosmooth.min.js"></script> |
| 69 | +--- |
| 70 | + |
| 71 | +## Quick Start |
| 72 | + |
| 73 | +```js |
| 74 | +import ScrollToSmooth from 'scrolltosmooth' |
| 75 | +import { easeOutCubic } from 'scrolltosmooth/easings/easeOutCubic' |
| 76 | + |
| 77 | +const scroller = new ScrollToSmooth('a', { |
| 78 | + duration: 600, |
| 79 | + easing: easeOutCubic, |
| 80 | + offset: '#header', // compensate for a fixed nav |
| 81 | +}) |
| 82 | + |
| 83 | +scroller.init() |
| 84 | +``` |
| 85 | + |
| 86 | +That's it — every `<a href="#...">` on the page now scrolls smoothly, offset by the height of `#header`. |
| 87 | + |
| 88 | +### Programmatic scrolling |
| 89 | + |
| 90 | +```js |
| 91 | +// Scroll to an element |
| 92 | +scroller.scrollTo('#features') |
| 93 | + |
| 94 | +// Scroll to a pixel position |
| 95 | +scroller.scrollTo(500) |
| 96 | + |
| 97 | +// Scroll to 50% of the document |
| 98 | +scroller.scrollTo('50%') |
| 99 | + |
| 100 | +// Scroll by a relative offset |
| 101 | +scroller.scrollBy(200) |
54 | 102 | ``` |
55 | 103 |
|
56 | | -> **Note:** the iife/minified core file only includes the linear easing and |
57 | | -> the class itself. the `pkgd` build exposes every easing on `window` but |
58 | | -> is much larger; use it only when you need the full set with one script tag. |
| 104 | +### Scroll queue |
| 105 | + |
| 106 | +Chain targets and let them play one after another: |
| 107 | + |
| 108 | +```js |
| 109 | +scroller.queueScroll('#section-1') |
| 110 | +scroller.queueScroll('#section-2') |
| 111 | +scroller.queueScroll('#section-3') |
| 112 | +// Scrolls to each section in sequence |
| 113 | +``` |
59 | 114 |
|
60 | 115 | --- |
61 | 116 |
|
62 | | -## Quick Start |
| 117 | +## Plugins |
63 | 118 |
|
64 | | -### Easing import example |
| 119 | +Plugins keep the core lean. Import only what you need: |
65 | 120 |
|
66 | | -By default the core class only ships with a single `linear` easing. import |
67 | | -other functions explicitly to keep your bundle small: |
| 121 | +### Horizontal & 2D Scrolling |
68 | 122 |
|
69 | 123 | ```js |
70 | | -import ScrollToSmooth from 'scrolltosmooth'; |
71 | | -import { easeOutCubic } from 'scrolltosmooth/easings/easeOutCubic'; |
| 124 | +import { HorizontalScrollPlugin } from 'scrolltosmooth/plugins/horizontal' |
72 | 125 |
|
73 | | -const scroller = new ScrollToSmooth('a', { |
74 | | - duration: 600, |
75 | | - easing: easeOutCubic |
76 | | -}); |
| 126 | +ScrollToSmooth.use(HorizontalScrollPlugin) |
| 127 | + |
| 128 | +const scroller = new ScrollToSmooth('.nav-link', { axis: 'x' }) |
| 129 | +scroller.init() |
| 130 | + |
| 131 | +scroller.scrollToX('#slide-3') // horizontal |
| 132 | +scroller.scrollToBoth(800, 400) // both axes at once |
77 | 133 | ``` |
78 | 134 |
|
79 | | -If you still prefer passing a string name you can resolve it yourself: |
| 135 | +### Section Snapping |
80 | 136 |
|
81 | 137 | ```js |
82 | | -import { getEasing } from 'scrolltosmooth/easings'; |
| 138 | +import { SnapPlugin } from 'scrolltosmooth/plugins/snap' |
| 139 | + |
| 140 | +ScrollToSmooth.use(SnapPlugin) |
83 | 141 |
|
84 | 142 | const scroller = new ScrollToSmooth('a', { |
85 | | - easing: getEasing('easeOutCubic') |
86 | | -}); |
| 143 | + snap: true, |
| 144 | + snapSelector: '.section', // which elements to snap to |
| 145 | + snapDebounce: 150, // ms of inactivity before snapping |
| 146 | +}) |
| 147 | +scroller.init() |
87 | 148 | ``` |
88 | 149 |
|
89 | | -(The core will warn and fall back to `linear` if a string is given.) |
| 150 | +### Touch Momentum |
90 | 151 |
|
91 | | -## Quick Start |
| 152 | +```js |
| 153 | +import { TouchMomentumPlugin } from 'scrolltosmooth/plugins/touch-momentum' |
92 | 154 |
|
93 | | -```javascript |
94 | | -import ScrollToSmooth from 'scrolltosmooth' |
| 155 | +ScrollToSmooth.use(TouchMomentumPlugin) |
95 | 156 |
|
96 | 157 | const scroller = new ScrollToSmooth('a', { |
97 | | - duration: 400 |
| 158 | + touchMomentum: true, |
| 159 | + touchMomentumFactor: 300, |
| 160 | + touchMomentumMinVelocity: 0.3, |
98 | 161 | }) |
99 | | - |
100 | 162 | scroller.init() |
101 | 163 | ``` |
102 | 164 |
|
103 | | -Scroll to element: |
| 165 | +[→ Full plugin docs](docs/plugins.md) |
| 166 | + |
| 167 | +--- |
| 168 | + |
| 169 | +## Easing Functions |
| 170 | + |
| 171 | +31 easing functions, all individually importable: |
104 | 172 |
|
105 | | -```javascript |
106 | | -scroller.scrollTo('#section') |
| 173 | +```js |
| 174 | +import { easeOutBounce } from 'scrolltosmooth/easings/easeOutBounce' |
107 | 175 | ``` |
108 | 176 |
|
109 | | -Scroll by distance: |
| 177 | +| Family | In | Out | InOut | |
| 178 | +|--------|----|-----|-------| |
| 179 | +| Quad | `easeInQuad` | `easeOutQuad` | `easeInOutQuad` | |
| 180 | +| Cubic | `easeInCubic` | `easeOutCubic` | `easeInOutCubic` | |
| 181 | +| Quart | `easeInQuart` | `easeOutQuart` | `easeInOutQuart` | |
| 182 | +| Quint | `easeInQuint` | `easeOutQuint` | `easeInOutQuint` | |
| 183 | +| Sine | `easeInSine` | `easeOutSine` | `easeInOutSine` | |
| 184 | +| Expo | `easeInExpo` | `easeOutExpo` | `easeInOutExpo` | |
| 185 | +| Circ | `easeInCirc` | `easeOutCirc` | `easeInOutCirc` | |
| 186 | +| Elastic | `easeInElastic` | `easeOutElastic` | `easeInOutElastic` | |
| 187 | +| Back | `easeInBack` | `easeOutBack` | `easeInOutBack` | |
| 188 | +| Bounce | `easeInBounce` | `easeOutBounce` | `easeInOutBounce` | |
110 | 189 |
|
111 | | -```javascript |
112 | | -scroller.scrollBy(200) |
| 190 | +Plus `linear`. Or bring your own: `easing: (t) => t * t` |
| 191 | + |
| 192 | +[→ Full easing reference](docs/easings.md) |
| 193 | + |
| 194 | +--- |
| 195 | + |
| 196 | +## Reactive CSS Custom Properties |
| 197 | + |
| 198 | +ScrollToSmooth writes `--sts-scroll-y` (and `--sts-scroll-x` with the Horizontal plugin) to the scroll container on every animation frame. Use them in CSS for zero-JS reactive styling: |
| 199 | + |
| 200 | +```css |
| 201 | +/* Parallax background tied to scroll */ |
| 202 | +.hero-bg { |
| 203 | + transform: translateY(calc(var(--sts-scroll-y, 0) * 0.3px)); |
| 204 | +} |
| 205 | + |
| 206 | +/* Progress bar driven by scroll position */ |
| 207 | +.progress-bar { |
| 208 | + width: calc(var(--sts-scroll-y, 0) / var(--doc-height) * 100%); |
| 209 | +} |
113 | 210 | ``` |
114 | 211 |
|
115 | 212 | --- |
116 | 213 |
|
117 | | -## Documentation |
| 214 | +## Events & Callbacks |
118 | 215 |
|
119 | | -Full documentation is located in the `/docs` directory. |
| 216 | +Track every phase of a scroll animation: |
120 | 217 |
|
121 | | -- [Usage](docs/usage.md) |
122 | | -- [API Reference](docs/api.md) |
123 | | -- [Plugins](docs/plugins.md) |
124 | | -- [Easing Functions](docs/easings.md) |
125 | | -- [Architecture](docs/architecture.md) |
| 218 | +```js |
| 219 | +const scroller = new ScrollToSmooth('a', { |
| 220 | + onScrollStart: ({ startPosition, endPosition }) => { |
| 221 | + console.log(`Scrolling from ${startPosition}px to ${endPosition}px`) |
| 222 | + }, |
| 223 | + onScrollUpdate: ({ currentPosition, progress }) => { |
| 224 | + progressBar.style.width = `${progress * 100}%` |
| 225 | + }, |
| 226 | + onScrollEnd: ({ endPosition }) => { |
| 227 | + analytics.track('scroll_complete', { position: endPosition }) |
| 228 | + }, |
| 229 | +}) |
| 230 | +``` |
| 231 | + |
| 232 | +Or use CustomEvents that bubble through the DOM: |
| 233 | + |
| 234 | +```js |
| 235 | +document.addEventListener('scrolltosmooth:start', (e) => { |
| 236 | + console.log(e.detail.startPosition, '→', e.detail.endPosition) |
| 237 | +}) |
| 238 | +``` |
126 | 239 |
|
127 | 240 | --- |
128 | 241 |
|
129 | | -## Why not just CSS smooth scrolling? |
| 242 | +## Why not just CSS `scroll-behavior: smooth`? |
130 | 243 |
|
131 | | -Native CSS smooth scrolling works well for simple cases. |
132 | | -ScrollToSmooth adds functionality when you need: |
| 244 | +CSS smooth scrolling is great for simple use cases. ScrollToSmooth steps in when you need more control: |
133 | 245 |
|
134 | | -- custom easing functions |
135 | | -- scroll lifecycle events |
136 | | -- dynamic duration control |
137 | | -- fixed header offsets |
138 | | -- programmatic scroll control |
139 | | -- extendable plugin system |
| 246 | +| Feature | CSS Native | ScrollToSmooth | |
| 247 | +|---------|-----------|----------------| |
| 248 | +| Custom easing curves | ❌ | ✅ 31 built-in + custom | |
| 249 | +| Scroll lifecycle events | ❌ | ✅ start / update / end | |
| 250 | +| Dynamic duration | ❌ | ✅ fixed, relative, min/max | |
| 251 | +| Fixed header offset | ❌ | ✅ element, px, %, vh | |
| 252 | +| Horizontal / 2D scrolling | ❌ | ✅ via plugin | |
| 253 | +| Section snapping | Partial | ✅ customizable debounce | |
| 254 | +| Touch momentum | ❌ | ✅ via plugin | |
| 255 | +| Scroll queue | ❌ | ✅ chained animations | |
| 256 | +| CSS custom properties | ❌ | ✅ per-frame updates | |
140 | 257 |
|
141 | 258 | --- |
142 | 259 |
|
143 | | -## Contributing |
| 260 | +## Documentation |
144 | 261 |
|
145 | | -Contributions are welcome. |
| 262 | +- **[Usage Guide](docs/usage.md)** — Setup, triggers, offsets, containers, and common patterns |
| 263 | +- **[API Reference](docs/api.md)** — Every method, option, event, and type |
| 264 | +- **[Plugins](docs/plugins.md)** — Horizontal, Snap, Touch Momentum + writing your own |
| 265 | +- **[Easing Functions](docs/easings.md)** — All 31 easings with import examples |
| 266 | +- **[Architecture](docs/architecture.md)** — Build system, internals, and design decisions |
146 | 267 |
|
147 | | -Project structure: |
| 268 | +--- |
148 | 269 |
|
149 | | -```text |
150 | | -src |
151 | | - core |
152 | | - easings |
153 | | - plugins |
154 | | -``` |
| 270 | +## Contributing |
155 | 271 |
|
156 | | -Architecture documentation: `docs/architecture.md` |
| 272 | +Contributions are welcome! See the [architecture docs](docs/architecture.md) for project structure and build details. |
| 273 | + |
| 274 | +```bash |
| 275 | +npm run build # build everything |
| 276 | +npm run lint # lint TypeScript |
| 277 | +``` |
157 | 278 |
|
158 | 279 | --- |
159 | 280 |
|
160 | 281 | ## Support |
161 | 282 |
|
162 | | -If you find this project useful: |
| 283 | +If you find this project useful, consider supporting it: |
| 284 | + |
| 285 | +[→ paypal.me/bfiessinger](https://www.paypal.me/bfiessinger) |
| 286 | + |
| 287 | +--- |
| 288 | + |
| 289 | +## License |
163 | 290 |
|
164 | | -https://www.paypal.me/bfiessinger |
| 291 | +[MIT](LICENSE) |
0 commit comments