react-scroll-media 1.0.0 → 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/README.md +463 -258
  2. package/package.json +1 -1
package/README.md CHANGED
@@ -1,315 +1,464 @@
1
- # React Scroll Media 🎬
1
+ <div align="center">
2
2
 
3
- > **Production-ready, cinematic scroll sequences for React.**
4
- > Zero scroll-jacking. Pure sticky positioning. 60fps performance.
3
+ # 🎬 React Scroll Media
5
4
 
6
- `react-scroll-media` is a lightweight library for creating Apple-style "scrollytelling" image sequences. It maps scroll progress to image frames deterministically, using standard CSS sticky positioning for a native, jank-free feel.
7
-
8
- ## ✨ Features
9
-
10
- - **🚀 Native Performance**:
11
- - Uses `requestAnimationFrame` for buttery smooth 60fps rendering.
12
- - **No Scroll Jacking**: We never hijack the scrollbar. It works with native scrolling.
13
- - **CSS Sticky**: Uses relatively positioned containers with sticky inner content.
14
- - **🖼️ Flexible Loading**:
15
- - **Manual**: Pass an array of image URLs.
16
- - **Pattern**: Generate sequences like `/img_{index}.jpg`.
17
- - **Manifest**: Load sequences from a JSON manifest.
18
- - **🧠 Smart Memory Management**:
19
- - **Lazy Mode**: Keeps only ±3 frames in memory for huge sequences (800+ frames).
20
- - **Eager Mode**: Preloads everything for maximum smoothness on smaller sequences.
21
- - **Decoding**: Uses `img.decode()` to prevent main-thread jank during painting.
22
- - **🛠️ Developer Experience**:
23
- - **Debug Overlay**: Visualize progress and frame index in real-time.
24
- - **Hooks**: Exported `useScrollSequence` for custom UI implementations.
25
- - **TypeScript**: First-class type definitions.
26
- - **SSR Safe**: Works perfectly with Next.js / Remix / Gatsby.
27
- - **A11y**: Built-in support for `prefers-reduced-motion` and ARIA attributes.
28
- - **Robust**: Error boundaries and callbacks for image load failures.
29
-
30
- ## 🤔 When to use this vs Video?
5
+ **Production-ready, cinematic scroll sequences for React.**
31
6
 
32
- Feature
7
+ [![npm version](https://img.shields.io/npm/v/react-scroll-media.svg)](https://www.npmjs.com/package/react-scroll-media)
8
+ [![npm downloads](https://img.shields.io/npm/dm/react-scroll-media.svg)](https://www.npmjs.com/package/react-scroll-media)
9
+ [![bundle size](https://img.shields.io/bundlephobia/minzip/react-scroll-media)](https://bundlephobia.com/package/react-scroll-media)
10
+ [![license](https://img.shields.io/npm/l/react-scroll-media.svg)](https://github.com/yourusername/react-scroll-media/blob/main/LICENSE)
33
11
 
34
- Video (`<video>`)
12
+ *Zero scroll-jacking • Pure sticky positioning • 60fps performance*
35
13
 
36
- Scroll Sequence (`react-scroll-media`)
14
+ [Installation](#-installation) [Usage](#-usage) • [API](#%EF%B8%8F-configuration) • [Examples](#-usage)
37
15
 
38
- **Quality**
16
+ </div>
39
17
 
40
- Compressed (artifacts)
41
-
42
- Lossless / Exact Frames (CRISP)
43
-
44
- **Transparency**
45
-
46
- Difficult (needs webm/hevc)
18
+ ---
47
19
 
48
- Native PNG/WebP Transparency (Easy)
20
+ ## 🌟 Overview
49
21
 
50
- **Scrubbing**
22
+ `react-scroll-media` is a lightweight library for creating Apple-style "scrollytelling" image sequences. It maps scroll progress to image frames deterministically, using standard CSS sticky positioning for a native, jank-free feel.
51
23
 
52
- Janky (keyframe dependency)
24
+ <br />
53
25
 
54
- 1:1 Instant Scrubbing
26
+ ## Features
55
27
 
56
- **Mobile**
28
+ ### 🚀 **Native Performance**
29
+ - Uses `requestAnimationFrame` for buttery smooth 60fps rendering
30
+ - **No Scroll Jacking** — We never hijack the scrollbar. It works with native scrolling
31
+ - **CSS Sticky** — Uses relatively positioned containers with sticky inner content
57
32
 
58
- Auto-play often blocked
33
+ ### 🖼️ **Flexible Loading**
34
+ - **Manual** — Pass an array of image URLs
35
+ - **Pattern** — Generate sequences like `/img_{index}.jpg`
36
+ - **Manifest** — Load sequences from a JSON manifest
59
37
 
60
- Works everywhere
38
+ ### 🧠 **Smart Memory Management**
39
+ - **Lazy Mode** — Keeps only ±3 frames in memory for huge sequences (800+ frames)
40
+ - **Eager Mode** — Preloads everything for maximum smoothness on smaller sequences
41
+ - **Decoding** — Uses `img.decode()` to prevent main-thread jank during painting
61
42
 
62
- **File Size**
43
+ ### 🛠️ **Developer Experience**
44
+ - **Debug Overlay** — Visualize progress and frame index in real-time
45
+ - **Hooks** — Exported `useScrollSequence` for custom UI implementations
46
+ - **TypeScript** — First-class type definitions
47
+ - **SSR Safe** — Works perfectly with Next.js / Remix / Gatsby
48
+ - **A11y** — Built-in support for `prefers-reduced-motion` and ARIA attributes
49
+ - **Robust** — Error boundaries and callbacks for image load failures
63
50
 
64
- Small
51
+ <br />
65
52
 
66
- Large (requires optimization/lazy loading)
53
+ ---
67
54
 
68
- Use **Scroll Sequence** when you need perfect interaction, transparency, or crystal-clear product visuals (like Apple). Use **Video** for long, non-interactive backgrounds.
55
+ ## 🤔 When to Use This vs Video?
56
+
57
+ <table>
58
+ <thead>
59
+ <tr>
60
+ <th>Feature</th>
61
+ <th>Video (<code>&lt;video&gt;</code>)</th>
62
+ <th>Scroll Sequence (<code>react-scroll-media</code>)</th>
63
+ </tr>
64
+ </thead>
65
+ <tbody>
66
+ <tr>
67
+ <td><strong>Quality</strong></td>
68
+ <td>Compressed (artifacts)</td>
69
+ <td>✨ Lossless / Exact Frames (CRISP)</td>
70
+ </tr>
71
+ <tr>
72
+ <td><strong>Transparency</strong></td>
73
+ <td>Difficult (needs webm/hevc)</td>
74
+ <td>✨ Native PNG/WebP Transparency (Easy)</td>
75
+ </tr>
76
+ <tr>
77
+ <td><strong>Scrubbing</strong></td>
78
+ <td>Janky (keyframe dependency)</td>
79
+ <td>✨ 1:1 Instant Scrubbing</td>
80
+ </tr>
81
+ <tr>
82
+ <td><strong>Mobile</strong></td>
83
+ <td>Auto-play often blocked</td>
84
+ <td>✨ Works everywhere</td>
85
+ </tr>
86
+ <tr>
87
+ <td><strong>File Size</strong></td>
88
+ <td>✨ Small</td>
89
+ <td>Large (requires optimization/lazy loading)</td>
90
+ </tr>
91
+ </tbody>
92
+ </table>
93
+
94
+ **💡 Use Scroll Sequence** when you need perfect interaction, transparency, or crystal-clear product visuals (like Apple).
95
+
96
+ **💡 Use Video** for long, non-interactive backgrounds.
97
+
98
+ <br />
69
99
 
70
100
  ---
71
101
 
72
102
  ## 📦 Installation
73
103
 
74
104
  ```bash
75
- npm install react-scroll-media# oryarn add react-scroll-media
105
+ npm install react-scroll-media
76
106
  ```
77
107
 
108
+ **or**
109
+
110
+ ```bash
111
+ yarn add react-scroll-media
112
+ ```
113
+
114
+ <br />
115
+
78
116
  ---
79
117
 
80
118
  ## 🚀 Usage
81
119
 
82
- ### Basic Example
120
+ ### 🎯 Basic Example
83
121
 
84
122
  The simplest way to use it is with the `ScrollSequence` component.
85
123
 
86
124
  ```tsx
87
- import { ScrollSequence } from 'react-scroll-media';const frames = [ '/images/frame_01.jpg', '/images/frame_02.jpg', // ...];export default function MyPage() { return ( <div style={{ height: '200vh' }}> <h1>Scroll Down</h1> <ScrollSequence source={{ type: 'manual', frames }} scrollLength="300vh" // Determines how long the sequence plays /> <h1>Continue Scrolling</h1> </div> );}
125
+ import { ScrollSequence } from 'react-scroll-media';
126
+
127
+ const frames = [
128
+ '/images/frame_01.jpg',
129
+ '/images/frame_02.jpg',
130
+ // ...
131
+ ];
132
+
133
+ export default function MyPage() {
134
+ return (
135
+ <div style={{ height: '200vh' }}>
136
+ <h1>Scroll Down</h1>
137
+
138
+ <ScrollSequence
139
+ source={{ type: 'manual', frames }}
140
+ scrollLength="300vh" // Determines how long the sequence plays
141
+ />
142
+
143
+ <h1>Continue Scrolling</h1>
144
+ </div>
145
+ );
146
+ }
88
147
  ```
89
148
 
149
+ <br />
150
+
90
151
  ### ✨ Scrollytelling & Composition
91
152
 
92
153
  You can nest components inside `ScrollSequence`. They will be placed in the sticky container and can react to the timeline.
93
154
 
94
- #### Animated Text (`ScrollText`)
155
+ <br />
156
+
157
+ #### 📝 Animated Text (`ScrollText`)
95
158
 
96
159
  Animate opacity and position based on scroll progress (0 to 1). Supports enter and exit phases.
97
160
 
98
161
  ```tsx
99
- import { ScrollSequence, ScrollText } from 'react-scroll-media';<ScrollSequence source={...} scrollLength="400vh"> {/* Fade In (0.1-0.2) -> Hold -> Fade Out (0.8-0.9) */} <ScrollText start={0.1} end={0.2} exitStart={0.8} exitEnd={0.9} translateY={50} className="my-text-overlay" > Cinematic Experience </ScrollText></ScrollSequence>
162
+ import { ScrollSequence, ScrollText } from 'react-scroll-media';
163
+
164
+ <ScrollSequence source={...} scrollLength="400vh">
165
+
166
+ {/* Fade In (0.1-0.2) -> Hold -> Fade Out (0.8-0.9) */}
167
+ <ScrollText
168
+ start={0.1}
169
+ end={0.2}
170
+ exitStart={0.8}
171
+ exitEnd={0.9}
172
+ translateY={50}
173
+ className="my-text-overlay"
174
+ >
175
+ Cinematic Experience
176
+ </ScrollText>
177
+
178
+ </ScrollSequence>
100
179
  ```
101
180
 
102
- #### Word Reveal (`ScrollWordReveal`)
181
+ <br />
182
+
183
+ #### 💬 Word Reveal (`ScrollWordReveal`)
103
184
 
104
185
  Reveals text word-by-word as you scroll.
105
186
 
106
187
  ```tsx
107
- import { ScrollWordReveal } from 'react-scroll-media';<ScrollWordReveal text="Experience the smooth cinematic scroll." start={0.4} end={0.6} style={{ fontSize: '2rem', color: 'white' }}/>
188
+ import { ScrollWordReveal } from 'react-scroll-media';
189
+
190
+ <ScrollWordReveal
191
+ text="Experience the smooth cinematic scroll."
192
+ start={0.4}
193
+ end={0.6}
194
+ style={{ fontSize: '2rem', color: 'white' }}
195
+ />
108
196
  ```
109
197
 
110
- ### Advanced: Custom Hooks
198
+ <br />
199
+
200
+ ### 🔧 Advanced: Custom Hooks
111
201
 
112
202
  For full control over the specialized UI, use the headless hooks.
113
203
 
204
+ <br />
205
+
114
206
  #### `useScrollSequence`
115
207
 
116
208
  Manages the canvas image controller.
117
209
 
118
210
  ```tsx
119
- import { useScrollSequence } from 'react-scroll-media';const CustomScroller = () => { // ... setup refs const { containerRef, canvasRef, isLoaded } = useScrollSequence({ ... }); // ... render custom structure};
211
+ import { useScrollSequence } from 'react-scroll-media';
212
+
213
+ const CustomScroller = () => {
214
+ // ... setup refs
215
+ const { containerRef, canvasRef, isLoaded } = useScrollSequence({ ... });
216
+ // ... render custom structure
217
+ };
120
218
  ```
121
219
 
220
+ <br />
221
+
122
222
  #### `useScrollTimeline`
123
223
 
124
224
  Subscribe to the scroll timeline in any component.
125
225
 
126
226
  ```tsx
127
- import { useScrollTimeline } from 'react-scroll-media';const MyComponent = () => { const { subscribe } = useScrollTimeline(); // Subscribe to progress (0-1) useEffect(() => subscribe((progress) => { console.log('Progress:', progress); }), [subscribe]); return <div>...</div>;};
128
- ```
129
-
130
- ---
131
-
132
- ## ⚙️ Configuration
133
-
134
- ### `ScrollSequence` Props
135
-
136
- Prop
137
-
138
- Type
139
-
140
- Default
141
-
142
- Description
143
-
144
- `source`
145
-
146
- `SequenceSource`
147
-
148
- **Required**
149
-
150
- Defines where images come from.
151
-
152
- `scrollLength`
153
-
154
- `string`
155
-
156
- `"300vh"`
157
-
158
- Height of the container (animation duration).
159
-
160
- `memoryStrategy`
161
-
162
- `"eager" | "lazy"`
163
-
164
- `"eager"`
165
-
166
- Optimization strategy.
167
-
168
- `lazyBuffer`
169
-
170
- `number`
171
-
172
- `10`
173
-
174
- Number of frames to keep loaded in lazy mode.
175
-
176
- `fallback`
177
-
178
- `ReactNode`
179
-
180
- `null`
181
-
182
- Loading state component.
183
-
184
- `accessibilityLabel`
185
-
186
- `string`
187
-
188
- `"Scroll sequence"`
189
-
190
- ARIA label for the canvas. Example: `"360 degree view of the product"`.
191
-
192
- `debug`
193
-
194
- `boolean`
195
-
196
- `false`
197
-
198
- Shows debug overlay.
199
-
200
- `onError`
201
-
202
- `(error: Error) => void`
203
-
204
- `undefined`
205
-
206
- Callback fired when an image fails to load or initialization errors occur.
207
-
208
- ## 📊 Performance & compatibility
209
-
210
- ### Bundle Size
211
-
212
- - **Minified**: ~22.0 kB
213
- - **Gzipped**: ~6.08 kB
214
- - Zero dependencies (uses native Canvas API, no heavyweight libraries).
215
-
216
- ### Browser Support
227
+ import { useScrollTimeline } from 'react-scroll-media';
217
228
 
218
- Browser
229
+ const MyComponent = () => {
230
+ const { subscribe } = useScrollTimeline();
219
231
 
220
- Status
232
+ // Subscribe to progress (0-1)
233
+ useEffect(() => subscribe((progress) => {
234
+ console.log('Progress:', progress);
235
+ }), [subscribe]);
221
236
 
222
- Note
223
-
224
- Chrome
225
-
226
-
227
-
228
- Full support (OffscreenCanvas enabled)
229
-
230
- Firefox
231
-
232
-
233
-
234
- Full support
235
-
236
- Safari
237
-
238
-
239
-
240
- Full support (Desktop & Mobile)
241
-
242
- Edge
243
-
244
-
245
-
246
- Full support
247
-
248
- IE11
249
-
250
-
251
-
252
- Not supported (Missing ES6/Canvas features)
253
-
254
- ### Accessibility (A11y)
255
-
256
- - **Keyboard Navigation**: Users can scrub through the sequence using standard keyboard controls (Arrow Keys, Spacebar, Page Up/Down) because it relies on native scrolling.
257
- - **Screen Readers**: Add `accessibilityLabel` to `ScrollSequence` to provide a description for the canvas. Canvas has `role="img"`.
258
- - **Reduced Motion**: Automatically detects `prefers-reduced-motion: reduce`. If enabled, `ScrollSequence` will disable the scroll animation and display the `fallback` content (if provided) or simply freeze the first frame to prevent motion sickness.
259
-
260
- ### Memory Usage (Benchmarks)
261
-
262
- Tested on 1080p frames.
263
-
264
- Frames
265
-
266
- Strategy
267
-
268
- Memory
269
-
270
- Recommendation
271
-
272
- 100
273
-
274
- `eager`
275
-
276
- ~50MB
277
-
278
- Instant seeking, smooth.
279
-
280
- 500
281
-
282
- `eager`
237
+ return <div>...</div>;
238
+ };
239
+ ```
283
240
 
284
- ~250MB
241
+ <br />
285
242
 
286
- High RAM usage.
243
+ ---
287
244
 
288
- 500+
245
+ ## ⚙️ Configuration
289
246
 
290
- `lazy`
247
+ ### `ScrollSequence` Props
291
248
 
292
- ~20MB
249
+ <table>
250
+ <thead>
251
+ <tr>
252
+ <th>Prop</th>
253
+ <th>Type</th>
254
+ <th>Default</th>
255
+ <th>Description</th>
256
+ </tr>
257
+ </thead>
258
+ <tbody>
259
+ <tr>
260
+ <td><code>source</code></td>
261
+ <td><code>SequenceSource</code></td>
262
+ <td><strong>Required</strong></td>
263
+ <td>Defines where images come from.</td>
264
+ </tr>
265
+ <tr>
266
+ <td><code>scrollLength</code></td>
267
+ <td><code>string</code></td>
268
+ <td><code>"300vh"</code></td>
269
+ <td>Height of the container (animation duration).</td>
270
+ </tr>
271
+ <tr>
272
+ <td><code>memoryStrategy</code></td>
273
+ <td><code>"eager" | "lazy"</code></td>
274
+ <td><code>"eager"</code></td>
275
+ <td>Optimization strategy.</td>
276
+ </tr>
277
+ <tr>
278
+ <td><code>lazyBuffer</code></td>
279
+ <td><code>number</code></td>
280
+ <td><code>10</code></td>
281
+ <td>Number of frames to keep loaded in lazy mode.</td>
282
+ </tr>
283
+ <tr>
284
+ <td><code>fallback</code></td>
285
+ <td><code>ReactNode</code></td>
286
+ <td><code>null</code></td>
287
+ <td>Loading state component.</td>
288
+ </tr>
289
+ <tr>
290
+ <td><code>accessibilityLabel</code></td>
291
+ <td><code>string</code></td>
292
+ <td><code>"Scroll sequence"</code></td>
293
+ <td>ARIA label for the canvas. Example: <code>"360 degree view of the product"</code>.</td>
294
+ </tr>
295
+ <tr>
296
+ <td><code>debug</code></td>
297
+ <td><code>boolean</code></td>
298
+ <td><code>false</code></td>
299
+ <td>Shows debug overlay.</td>
300
+ </tr>
301
+ <tr>
302
+ <td><code>onError</code></td>
303
+ <td><code>(error: Error) => void</code></td>
304
+ <td><code>undefined</code></td>
305
+ <td>Callback fired when an image fails to load or initialization errors occur.</td>
306
+ </tr>
307
+ </tbody>
308
+ </table>
309
+
310
+ <br />
293
311
 
294
- **Recommended**. Kept flat constant.
312
+ ---
295
313
 
296
- ### Error Handling & Fallbacks
314
+ ## 📊 Performance & Compatibility
315
+
316
+ ### 📦 Bundle Size
317
+
318
+ | Metric | Size |
319
+ |--------|------|
320
+ | **Minified** | ~22.0 kB |
321
+ | **Gzipped** | ~6.08 kB |
322
+
323
+ ✨ **Zero dependencies** — Uses native Canvas API, no heavyweight libraries.
324
+
325
+ <br />
326
+
327
+ ### 🌐 Browser Support
328
+
329
+ <table>
330
+ <thead>
331
+ <tr>
332
+ <th>Browser</th>
333
+ <th>Status</th>
334
+ <th>Note</th>
335
+ </tr>
336
+ </thead>
337
+ <tbody>
338
+ <tr>
339
+ <td>Chrome</td>
340
+ <td>✅</td>
341
+ <td>Full support (OffscreenCanvas enabled)</td>
342
+ </tr>
343
+ <tr>
344
+ <td>Firefox</td>
345
+ <td>✅</td>
346
+ <td>Full support</td>
347
+ </tr>
348
+ <tr>
349
+ <td>Safari</td>
350
+ <td>✅</td>
351
+ <td>Full support (Desktop & Mobile)</td>
352
+ </tr>
353
+ <tr>
354
+ <td>Edge</td>
355
+ <td>✅</td>
356
+ <td>Full support</td>
357
+ </tr>
358
+ <tr>
359
+ <td>IE11</td>
360
+ <td>❌</td>
361
+ <td>Not supported (Missing ES6/Canvas features)</td>
362
+ </tr>
363
+ </tbody>
364
+ </table>
365
+
366
+ <br />
367
+
368
+ ### ♿ Accessibility (A11y)
369
+
370
+ - **🎹 Keyboard Navigation** — Users can scrub through the sequence using standard keyboard controls (Arrow Keys, Spacebar, Page Up/Down) because it relies on native scrolling.
371
+
372
+ - **🔊 Screen Readers** — Add `accessibilityLabel` to `ScrollSequence` to provide a description for the canvas. Canvas has `role="img"`.
373
+
374
+ - **🎭 Reduced Motion** — Automatically detects `prefers-reduced-motion: reduce`. If enabled, `ScrollSequence` will disable the scroll animation and display the `fallback` content (if provided) or simply freeze the first frame to prevent motion sickness.
375
+
376
+ <br />
377
+
378
+ ### 💾 Memory Usage (Benchmarks)
379
+
380
+ *Tested on 1080p frames.*
381
+
382
+ <table>
383
+ <thead>
384
+ <tr>
385
+ <th>Frames</th>
386
+ <th>Strategy</th>
387
+ <th>Memory</th>
388
+ <th>Recommendation</th>
389
+ </tr>
390
+ </thead>
391
+ <tbody>
392
+ <tr>
393
+ <td>100</td>
394
+ <td><code>eager</code></td>
395
+ <td>30MB</td>
396
+ <td>Instant seeking, smooth.</td>
397
+ </tr>
398
+ <tr>
399
+ <td>500</td>
400
+ <td><code>eager</code></td>
401
+ <td>46MB</td>
402
+ <td>High RAM usage.</td>
403
+ </tr>
404
+ <tr>
405
+ <td>1000</td>
406
+ <td><code>eager</code></td>
407
+ <td>57MB</td>
408
+ <td>Very high RAM usage.</td>
409
+ </tr>
410
+ <tr>
411
+ <td>100</td>
412
+ <td><code>lazy</code></td>
413
+ <td>25MB</td>
414
+ <td>Low memory usage.</td>
415
+ </tr>
416
+ <tr>
417
+ <td>500</td>
418
+ <td><code>lazy</code></td>
419
+ <td>30MB</td>
420
+ <td>Low memory usage.</td>
421
+ </tr>
422
+ <tr>
423
+ <td>1000</td>
424
+ <td><code>lazy</code></td>
425
+ <td>45MB</td>
426
+ <td><strong>⭐ Recommended</strong>. Kept flat constant.</td>
427
+ </tr>
428
+ </tbody>
429
+ </table>
430
+
431
+ <br />
432
+
433
+ ### 🛡️ Error Handling & Fallbacks
297
434
 
298
435
  Network errors are handled gracefully. You can provide a fallback UI that displays while images are loading or if they fail.
299
436
 
300
437
  ```tsx
301
- <ScrollSequence source={{ type: 'manifest', url: '/bad_url.json' }} fallback={<div className="error">Failed to load sequence</div>} onError={(e) => console.error("Sequence error:", e)}/>
438
+ <ScrollSequence
439
+ source={{ type: 'manifest', url: '/bad_url.json' }}
440
+ fallback={<div className="error">Failed to load sequence</div>}
441
+ onError={(e) => console.error("Sequence error:", e)}
442
+ />
302
443
  ```
303
444
 
304
- ### Error Boundaries
445
+ <br />
446
+
447
+ ### 🚨 Error Boundaries
305
448
 
306
449
  For robust production apps, wrap `ScrollSequence` in an Error Boundary to catch unexpected crashes:
307
450
 
308
451
  ```tsx
309
- ```tsx
310
- class ErrorBoundary extends React.Component<{ fallback: React.ReactNode, children: React.ReactNode }, { hasError: boolean }> {
452
+ class ErrorBoundary extends React.Component<
453
+ { fallback: React.ReactNode, children: React.ReactNode },
454
+ { hasError: boolean }
455
+ > {
311
456
  state = { hasError: false };
312
- static getDerivedStateFromError() { return { hasError: true }; }
457
+
458
+ static getDerivedStateFromError() {
459
+ return { hasError: true };
460
+ }
461
+
313
462
  render() {
314
463
  if (this.state.hasError) return this.props.fallback;
315
464
  return this.props.children;
@@ -321,65 +470,101 @@ class ErrorBoundary extends React.Component<{ fallback: React.ReactNode, childre
321
470
  <ScrollSequence source={...} />
322
471
  </ErrorBoundary>
323
472
  ```
324
- ```
325
473
 
326
- ### Multi-Instance & Nested Scroll
474
+ <br />
475
+
476
+ ### 🔄 Multi-Instance & Nested Scroll
327
477
 
328
478
  `react-scroll-media` automatically handles multiple instances on the same page. Each instance:
329
- 1. Registers with a shared `RAF` loop (singleton) for optimal performance.
330
- 2. Calculates its own progress independently.
331
- 3. Should have a unique `scrollLength` or container setup.
479
+
480
+ 1. Registers with a shared `RAF` loop (singleton) for optimal performance.
481
+ 2. Calculates its own progress independently.
482
+ 3. Should have a unique `scrollLength` or container setup.
483
+
484
+ <br />
332
485
 
333
486
  ---
334
487
 
335
488
  ## 🏗️ Architecture
336
489
 
337
- ### `SequenceSource` Options
490
+ ### 📂 `SequenceSource` Options
338
491
 
339
- **1. Manual Mode** (Pass array directly)
492
+ <br />
493
+
494
+ #### **1. Manual Mode** (Pass array directly)
340
495
 
341
496
  ```ts
342
- { type: 'manual', frames: ['/img/1.jpg', '/img/2.jpg']}
497
+ {
498
+ type: 'manual',
499
+ frames: ['/img/1.jpg', '/img/2.jpg']
500
+ }
343
501
  ```
344
502
 
345
- **2. Pattern Mode** (Generate URLs)
503
+ <br />
504
+
505
+ #### **2. Pattern Mode** (Generate URLs)
346
506
 
347
507
  ```ts
348
- { type: 'pattern', url: '/assets/sequence_{index}.jpg', // {index} is replaced start: 1, // Start index end: 100, // End index pad: 4 // Zero padding (e.g. 1 -> 0001)}
508
+ {
509
+ type: 'pattern',
510
+ url: '/assets/sequence_{index}.jpg', // {index} is replaced
511
+ start: 1, // Start index
512
+ end: 100, // End index
513
+ pad: 4 // Zero padding (e.g. 1 -> 0001)
514
+ }
349
515
  ```
350
516
 
351
- **3. Manifest Mode** (Fetch JSON)
517
+ <br />
518
+
519
+ #### **3. Manifest Mode** (Fetch JSON)
352
520
 
353
521
  ```ts
354
- { type: 'manifest', url: '/sequence.json' }// JSON format: { "frames": ["url1", "url2"] } OR pattern config
522
+ {
523
+ type: 'manifest',
524
+ url: '/sequence.json'
525
+ }
526
+
527
+ // JSON format: { "frames": ["url1", "url2"] } OR pattern config
355
528
  ```
356
529
 
357
- > **Note**: Manifests are cached in memory by URL. To force a refresh, append a query param (e.g. `?v=2`).
530
+ > **💡 Note**: Manifests are cached in memory by URL. To force a refresh, append a query param (e.g. `?v=2`).
358
531
 
359
- ---
532
+ <br />
360
533
 
361
- ## 🏗️ Architecture
534
+ ---
362
535
 
363
- ### How it Works (The "Sticky" Technique)
536
+ ## 🎨 How it Works (The "Sticky" Technique)
364
537
 
365
538
  Unlike libraries that use `position: fixed` or JS-based scroll locking (which breaks refreshing and feels unnatural), we use **CSS Sticky Positioning**.
366
539
 
367
- 1. **Container (`relative`)**: This element has the height you specify (e.g., `300vh`). It occupies space in the document flow.
368
- 2. **Sticky Wrapper (`sticky`)**: Inside the container, we place a `div` that is `100vh` tall and `sticky` at `top: 0`.
369
- 3. **Canvas**: The `<canvas>` sits inside the sticky wrapper.
370
- 4. **Math**: As you scroll the container, the sticky wrapper stays pinned to the viewport. We calculate:
371
-
372
- ```ts
373
- progress = -containerRect.top / (containerHeight - viewportHeight)
374
- ```
375
-
376
- This gives a precise 0.0 to 1.0 value tied to the pixel position of the scrollbar.
540
+ <br />
541
+
542
+ ### 🔧 Technical Breakdown
543
+
544
+ 1. **Container (`relative`)** — This element has the height you specify (e.g., `300vh`). It occupies space in the document flow.
545
+
546
+ 2. **Sticky Wrapper (`sticky`)** Inside the container, we place a `div` that is `100vh` tall and `sticky` at `top: 0`.
377
547
 
378
- ### Memory Strategy
548
+ 3. **Canvas** — The `<canvas>` sits inside the sticky wrapper.
379
549
 
380
- - **"eager" (Default)**: Best for sequences < 200 frames. Preloads all images into `HTMLImageElement` instances. Instant seeking, smooth playback. High memory usage.
381
- - **"lazy"**: Best for long sequences (500+ frames). Only keeps the current frame and its neighbors in memory. Saves RAM, prevents crashes.
382
- - Buffer size defaults to ±10 frames but can be customized via `lazyBuffer`.
550
+ 4. **Math** As you scroll the container, the sticky wrapper stays pinned to the viewport. We calculate:
551
+
552
+ ```ts
553
+ progress = -containerRect.top / (containerHeight - viewportHeight)
554
+ ```
555
+
556
+ This gives a precise **0.0 to 1.0** value tied to the pixel position of the scrollbar.
557
+
558
+ <br />
559
+
560
+ ### 💡 Memory Strategy
561
+
562
+ - **"eager" (Default)** — Best for sequences < 200 frames. Preloads all images into `HTMLImageElement` instances. Instant seeking, smooth playback. High memory usage.
563
+
564
+ - **"lazy"** — Best for long sequences (500+ frames). Only keeps the current frame and its neighbors in memory. Saves RAM, prevents crashes.
565
+ - Buffer size defaults to ±10 frames but can be customized via `lazyBuffer`.
566
+
567
+ <br />
383
568
 
384
569
  ---
385
570
 
@@ -388,17 +573,37 @@ Unlike libraries that use `position: fixed` or JS-based scroll locking (which br
388
573
  Enable the debug overlay to inspect your sequence in production:
389
574
 
390
575
  ```tsx
391
- <ScrollSequence source={...} debug={true} />
576
+ <ScrollSequence
577
+ source={...}
578
+ debug={true}
579
+ />
392
580
  ```
393
581
 
582
+ <br />
583
+
394
584
  **Output:**
395
585
 
396
586
  ```
397
- Progress: 0.45Frame: 45 / 100
587
+ Progress: 0.45
588
+ Frame: 45 / 100
398
589
  ```
399
590
 
400
- This overlay is updated directly via DOM manipulation (bypassing React renders) for zero overhead.
591
+ This overlay is updated directly via DOM manipulation (bypassing React renders) for **zero overhead**.
592
+
593
+ <br />
401
594
 
402
595
  ---
403
596
 
404
- MIT © 2026 Thanniru Sai Teja
597
+ <div align="center">
598
+
599
+ ## 📄 License
600
+
601
+ **MIT** © 2026 **Thanniru Sai Teja**
602
+
603
+ <br />
604
+
605
+ Made with ❤️ for the React community
606
+
607
+ [⬆ Back to Top](#-react-scroll-media)
608
+
609
+ </div>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-scroll-media",
3
- "version": "1.0.0",
3
+ "version": "1.0.1",
4
4
  "description": "Production-ready scroll-driven image sequence rendering component for React",
5
5
  "license": "MIT",
6
6
  "author": "Thanniru Sai Teja",