react-scroll-media 1.0.0
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.
- package/LICENSE +21 -0
- package/README.md +404 -0
- package/dist/index.d.mts +249 -0
- package/dist/index.d.ts +249 -0
- package/dist/index.js +843 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +799 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +59 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 Thanniru Sai Teja
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,404 @@
|
|
|
1
|
+
# React Scroll Media 🎬
|
|
2
|
+
|
|
3
|
+
> **Production-ready, cinematic scroll sequences for React.**
|
|
4
|
+
> Zero scroll-jacking. Pure sticky positioning. 60fps performance.
|
|
5
|
+
|
|
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?
|
|
31
|
+
|
|
32
|
+
Feature
|
|
33
|
+
|
|
34
|
+
Video (`<video>`)
|
|
35
|
+
|
|
36
|
+
Scroll Sequence (`react-scroll-media`)
|
|
37
|
+
|
|
38
|
+
**Quality**
|
|
39
|
+
|
|
40
|
+
Compressed (artifacts)
|
|
41
|
+
|
|
42
|
+
Lossless / Exact Frames (CRISP)
|
|
43
|
+
|
|
44
|
+
**Transparency**
|
|
45
|
+
|
|
46
|
+
Difficult (needs webm/hevc)
|
|
47
|
+
|
|
48
|
+
Native PNG/WebP Transparency (Easy)
|
|
49
|
+
|
|
50
|
+
**Scrubbing**
|
|
51
|
+
|
|
52
|
+
Janky (keyframe dependency)
|
|
53
|
+
|
|
54
|
+
1:1 Instant Scrubbing
|
|
55
|
+
|
|
56
|
+
**Mobile**
|
|
57
|
+
|
|
58
|
+
Auto-play often blocked
|
|
59
|
+
|
|
60
|
+
Works everywhere
|
|
61
|
+
|
|
62
|
+
**File Size**
|
|
63
|
+
|
|
64
|
+
Small
|
|
65
|
+
|
|
66
|
+
Large (requires optimization/lazy loading)
|
|
67
|
+
|
|
68
|
+
Use **Scroll Sequence** when you need perfect interaction, transparency, or crystal-clear product visuals (like Apple). Use **Video** for long, non-interactive backgrounds.
|
|
69
|
+
|
|
70
|
+
---
|
|
71
|
+
|
|
72
|
+
## 📦 Installation
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
npm install react-scroll-media# oryarn add react-scroll-media
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
---
|
|
79
|
+
|
|
80
|
+
## 🚀 Usage
|
|
81
|
+
|
|
82
|
+
### Basic Example
|
|
83
|
+
|
|
84
|
+
The simplest way to use it is with the `ScrollSequence` component.
|
|
85
|
+
|
|
86
|
+
```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> );}
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
### ✨ Scrollytelling & Composition
|
|
91
|
+
|
|
92
|
+
You can nest components inside `ScrollSequence`. They will be placed in the sticky container and can react to the timeline.
|
|
93
|
+
|
|
94
|
+
#### Animated Text (`ScrollText`)
|
|
95
|
+
|
|
96
|
+
Animate opacity and position based on scroll progress (0 to 1). Supports enter and exit phases.
|
|
97
|
+
|
|
98
|
+
```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>
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
#### Word Reveal (`ScrollWordReveal`)
|
|
103
|
+
|
|
104
|
+
Reveals text word-by-word as you scroll.
|
|
105
|
+
|
|
106
|
+
```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' }}/>
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
### Advanced: Custom Hooks
|
|
111
|
+
|
|
112
|
+
For full control over the specialized UI, use the headless hooks.
|
|
113
|
+
|
|
114
|
+
#### `useScrollSequence`
|
|
115
|
+
|
|
116
|
+
Manages the canvas image controller.
|
|
117
|
+
|
|
118
|
+
```tsx
|
|
119
|
+
import { useScrollSequence } from 'react-scroll-media';const CustomScroller = () => { // ... setup refs const { containerRef, canvasRef, isLoaded } = useScrollSequence({ ... }); // ... render custom structure};
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
#### `useScrollTimeline`
|
|
123
|
+
|
|
124
|
+
Subscribe to the scroll timeline in any component.
|
|
125
|
+
|
|
126
|
+
```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
|
|
217
|
+
|
|
218
|
+
Browser
|
|
219
|
+
|
|
220
|
+
Status
|
|
221
|
+
|
|
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`
|
|
283
|
+
|
|
284
|
+
~250MB
|
|
285
|
+
|
|
286
|
+
High RAM usage.
|
|
287
|
+
|
|
288
|
+
500+
|
|
289
|
+
|
|
290
|
+
`lazy`
|
|
291
|
+
|
|
292
|
+
~20MB
|
|
293
|
+
|
|
294
|
+
**Recommended**. Kept flat constant.
|
|
295
|
+
|
|
296
|
+
### Error Handling & Fallbacks
|
|
297
|
+
|
|
298
|
+
Network errors are handled gracefully. You can provide a fallback UI that displays while images are loading or if they fail.
|
|
299
|
+
|
|
300
|
+
```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)}/>
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
### Error Boundaries
|
|
305
|
+
|
|
306
|
+
For robust production apps, wrap `ScrollSequence` in an Error Boundary to catch unexpected crashes:
|
|
307
|
+
|
|
308
|
+
```tsx
|
|
309
|
+
```tsx
|
|
310
|
+
class ErrorBoundary extends React.Component<{ fallback: React.ReactNode, children: React.ReactNode }, { hasError: boolean }> {
|
|
311
|
+
state = { hasError: false };
|
|
312
|
+
static getDerivedStateFromError() { return { hasError: true }; }
|
|
313
|
+
render() {
|
|
314
|
+
if (this.state.hasError) return this.props.fallback;
|
|
315
|
+
return this.props.children;
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
// Usage
|
|
320
|
+
<ErrorBoundary fallback={<div>Something went wrong</div>}>
|
|
321
|
+
<ScrollSequence source={...} />
|
|
322
|
+
</ErrorBoundary>
|
|
323
|
+
```
|
|
324
|
+
```
|
|
325
|
+
|
|
326
|
+
### Multi-Instance & Nested Scroll
|
|
327
|
+
|
|
328
|
+
`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.
|
|
332
|
+
|
|
333
|
+
---
|
|
334
|
+
|
|
335
|
+
## 🏗️ Architecture
|
|
336
|
+
|
|
337
|
+
### `SequenceSource` Options
|
|
338
|
+
|
|
339
|
+
**1. Manual Mode** (Pass array directly)
|
|
340
|
+
|
|
341
|
+
```ts
|
|
342
|
+
{ type: 'manual', frames: ['/img/1.jpg', '/img/2.jpg']}
|
|
343
|
+
```
|
|
344
|
+
|
|
345
|
+
**2. Pattern Mode** (Generate URLs)
|
|
346
|
+
|
|
347
|
+
```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)}
|
|
349
|
+
```
|
|
350
|
+
|
|
351
|
+
**3. Manifest Mode** (Fetch JSON)
|
|
352
|
+
|
|
353
|
+
```ts
|
|
354
|
+
{ type: 'manifest', url: '/sequence.json' }// JSON format: { "frames": ["url1", "url2"] } OR pattern config
|
|
355
|
+
```
|
|
356
|
+
|
|
357
|
+
> **Note**: Manifests are cached in memory by URL. To force a refresh, append a query param (e.g. `?v=2`).
|
|
358
|
+
|
|
359
|
+
---
|
|
360
|
+
|
|
361
|
+
## 🏗️ Architecture
|
|
362
|
+
|
|
363
|
+
### How it Works (The "Sticky" Technique)
|
|
364
|
+
|
|
365
|
+
Unlike libraries that use `position: fixed` or JS-based scroll locking (which breaks refreshing and feels unnatural), we use **CSS Sticky Positioning**.
|
|
366
|
+
|
|
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.
|
|
377
|
+
|
|
378
|
+
### Memory Strategy
|
|
379
|
+
|
|
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`.
|
|
383
|
+
|
|
384
|
+
---
|
|
385
|
+
|
|
386
|
+
## 🐛 Debugging
|
|
387
|
+
|
|
388
|
+
Enable the debug overlay to inspect your sequence in production:
|
|
389
|
+
|
|
390
|
+
```tsx
|
|
391
|
+
<ScrollSequence source={...} debug={true} />
|
|
392
|
+
```
|
|
393
|
+
|
|
394
|
+
**Output:**
|
|
395
|
+
|
|
396
|
+
```
|
|
397
|
+
Progress: 0.45Frame: 45 / 100
|
|
398
|
+
```
|
|
399
|
+
|
|
400
|
+
This overlay is updated directly via DOM manipulation (bypassing React renders) for zero overhead.
|
|
401
|
+
|
|
402
|
+
---
|
|
403
|
+
|
|
404
|
+
MIT © 2026 Thanniru Sai Teja
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
import * as React$1 from 'react';
|
|
2
|
+
import React__default from 'react';
|
|
3
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Public type definitions for react-scroll-media
|
|
7
|
+
*/
|
|
8
|
+
type SequenceSource = {
|
|
9
|
+
type: 'manual';
|
|
10
|
+
frames: string[];
|
|
11
|
+
} | {
|
|
12
|
+
type: 'pattern';
|
|
13
|
+
url: string;
|
|
14
|
+
start?: number;
|
|
15
|
+
end: number;
|
|
16
|
+
pad?: number;
|
|
17
|
+
} | {
|
|
18
|
+
type: 'manifest';
|
|
19
|
+
url: string;
|
|
20
|
+
};
|
|
21
|
+
interface ScrollSequenceProps {
|
|
22
|
+
/**
|
|
23
|
+
* Source configuration for the image sequence.
|
|
24
|
+
* Can be 'manual', 'pattern', or 'manifest'.
|
|
25
|
+
*/
|
|
26
|
+
source: SequenceSource;
|
|
27
|
+
/** If true, shows a debug overlay with progress and frame info. */
|
|
28
|
+
debug?: boolean;
|
|
29
|
+
/**
|
|
30
|
+
* Memory management strategy.
|
|
31
|
+
* 'eager' (default): Preloads all images on mount. Smooth playback, higher memory.
|
|
32
|
+
* 'lazy': Loads images only when needed (curren frame ±3). Saves memory, may stutter on slow networks.
|
|
33
|
+
*/
|
|
34
|
+
memoryStrategy?: 'eager' | 'lazy';
|
|
35
|
+
/**
|
|
36
|
+
* Buffer size for lazy loading (default 10).
|
|
37
|
+
* Number of frames to keep loaded around the current frame.
|
|
38
|
+
*/
|
|
39
|
+
lazyBuffer?: number;
|
|
40
|
+
/** CSS height for the scroll container. Defines the total scroll distance. */
|
|
41
|
+
scrollLength?: string;
|
|
42
|
+
/** CSS class name for the container div. */
|
|
43
|
+
className?: string;
|
|
44
|
+
/** Optional children to render inside the sticky container (e.g. ScrollText). */
|
|
45
|
+
children?: React.ReactNode;
|
|
46
|
+
/** Component to render while the sequence is loading. */
|
|
47
|
+
fallback?: React.ReactNode;
|
|
48
|
+
/** Accessibility label for the canvas (role="img"). Defaults to "Scroll sequence". */
|
|
49
|
+
accessibilityLabel?: string;
|
|
50
|
+
/** Callback fired when an error occurs (e.g. image load failure). */
|
|
51
|
+
onError?: (error: Error) => void;
|
|
52
|
+
}
|
|
53
|
+
interface ResolvedSequence {
|
|
54
|
+
/** Sorted array of frame URLs (sorted numerically by filename). */
|
|
55
|
+
frames: string[];
|
|
56
|
+
/** Total number of frames. */
|
|
57
|
+
frameCount: number;
|
|
58
|
+
}
|
|
59
|
+
interface ScrollProgress {
|
|
60
|
+
/** Progress value between 0 and 1. */
|
|
61
|
+
progress: number;
|
|
62
|
+
/** Current scroll position in pixels. */
|
|
63
|
+
scrollTop: number;
|
|
64
|
+
/** Total scrollable height in pixels. */
|
|
65
|
+
scrollHeight: number;
|
|
66
|
+
/** Viewport height in pixels. */
|
|
67
|
+
viewportHeight: number;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
declare const ScrollSequence: React__default.ForwardRefExoticComponent<ScrollSequenceProps & React__default.RefAttributes<HTMLDivElement>>;
|
|
71
|
+
|
|
72
|
+
interface UseScrollSequenceParams {
|
|
73
|
+
source: ScrollSequenceProps['source'];
|
|
74
|
+
debugRef?: React.MutableRefObject<HTMLDivElement | null>;
|
|
75
|
+
memoryStrategy?: 'eager' | 'lazy';
|
|
76
|
+
lazyBuffer?: number;
|
|
77
|
+
onError?: (error: Error) => void;
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Hook to manage image sequence in a timeline context.
|
|
81
|
+
* MUST be used inside ScrollTimelineProvider.
|
|
82
|
+
*/
|
|
83
|
+
declare function useScrollSequence({ source, debugRef, memoryStrategy, lazyBuffer, onError, }: UseScrollSequenceParams): {
|
|
84
|
+
canvasRef: React$1.RefObject<HTMLCanvasElement>;
|
|
85
|
+
isLoaded: boolean;
|
|
86
|
+
error: Error | null;
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
interface ScrollTimelineProviderProps {
|
|
90
|
+
children: React__default.ReactNode;
|
|
91
|
+
/** CSS height for the scroll container (e.g., "300vh"). */
|
|
92
|
+
scrollLength?: string;
|
|
93
|
+
className?: string;
|
|
94
|
+
style?: React__default.CSSProperties;
|
|
95
|
+
}
|
|
96
|
+
declare function ScrollTimelineProvider({ children, scrollLength, className, style, }: ScrollTimelineProviderProps): react_jsx_runtime.JSX.Element;
|
|
97
|
+
|
|
98
|
+
interface ScrollTextProps {
|
|
99
|
+
children: React__default.ReactNode;
|
|
100
|
+
/** Progress start (0-1) where ENTRANCE animation begins */
|
|
101
|
+
start?: number;
|
|
102
|
+
/** Progress end (0-1) where ENTRANCE animation ends */
|
|
103
|
+
end?: number;
|
|
104
|
+
/** Progress start (0-1) where EXIT animation begins. If omitted, element stays visible. */
|
|
105
|
+
exitStart?: number;
|
|
106
|
+
/** Progress end (0-1) where EXIT animation ends */
|
|
107
|
+
exitEnd?: number;
|
|
108
|
+
/** Initial opacity */
|
|
109
|
+
initialOpacity?: number;
|
|
110
|
+
/** Target opacity (when entered) */
|
|
111
|
+
targetOpacity?: number;
|
|
112
|
+
/** Final opacity (after exit) */
|
|
113
|
+
finalOpacity?: number;
|
|
114
|
+
/** Y-axis translation range in pixels (e.g., 50 means move down 50px) */
|
|
115
|
+
translateY?: number;
|
|
116
|
+
style?: React__default.CSSProperties;
|
|
117
|
+
className?: string;
|
|
118
|
+
}
|
|
119
|
+
declare function ScrollText({ children, start, end, exitStart, exitEnd, initialOpacity, targetOpacity, finalOpacity, translateY, style, className, }: ScrollTextProps): react_jsx_runtime.JSX.Element;
|
|
120
|
+
|
|
121
|
+
interface ScrollWordRevealProps {
|
|
122
|
+
text: string;
|
|
123
|
+
/** Progress start (0-1) */
|
|
124
|
+
start?: number;
|
|
125
|
+
/** Progress end (0-1) */
|
|
126
|
+
end?: number;
|
|
127
|
+
className?: string;
|
|
128
|
+
style?: React__default.CSSProperties;
|
|
129
|
+
}
|
|
130
|
+
declare function ScrollWordReveal({ text, start, end, className, style }: ScrollWordRevealProps): react_jsx_runtime.JSX.Element;
|
|
131
|
+
|
|
132
|
+
type TimelineCallback = (progress: number) => void;
|
|
133
|
+
declare class ScrollTimeline {
|
|
134
|
+
private container;
|
|
135
|
+
private subscribers;
|
|
136
|
+
private currentProgress;
|
|
137
|
+
private cachedRect;
|
|
138
|
+
private cachedScrollParent;
|
|
139
|
+
private cachedScrollParentRect;
|
|
140
|
+
private cachedViewportHeight;
|
|
141
|
+
private cachedOffsetTop;
|
|
142
|
+
private isLayoutDirty;
|
|
143
|
+
private resizeObserver;
|
|
144
|
+
readonly id: string;
|
|
145
|
+
constructor(container: Element);
|
|
146
|
+
private onWindowResize;
|
|
147
|
+
/**
|
|
148
|
+
* Subscribe to progress updates.
|
|
149
|
+
* Returns an unsubscribe function.
|
|
150
|
+
*/
|
|
151
|
+
subscribe(callback: TimelineCallback): () => void;
|
|
152
|
+
unsubscribe(callback: TimelineCallback): void;
|
|
153
|
+
/**
|
|
154
|
+
* Start is now handled by LoopManager via subscriptions
|
|
155
|
+
* Deprecated but kept for API stability if needed.
|
|
156
|
+
*/
|
|
157
|
+
start(): void;
|
|
158
|
+
stop(): void;
|
|
159
|
+
private tick;
|
|
160
|
+
private notify;
|
|
161
|
+
private updateCache;
|
|
162
|
+
private calculateProgress;
|
|
163
|
+
private getScrollParent;
|
|
164
|
+
destroy(): void;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
interface UseScrollTimelineResult {
|
|
168
|
+
/**
|
|
169
|
+
* Manual subscription to the timeline.
|
|
170
|
+
* Useful for low-level DOM updates (refs) without re-rendering.
|
|
171
|
+
*/
|
|
172
|
+
subscribe: (callback: TimelineCallback) => () => void;
|
|
173
|
+
/** The raw timeline instance (for advanced usage) */
|
|
174
|
+
timeline: ScrollTimeline | null;
|
|
175
|
+
}
|
|
176
|
+
declare function useScrollTimeline(): UseScrollTimelineResult;
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Clamps a value between a minimum and maximum.
|
|
180
|
+
* Default range is [0, 1], suitable for progress values.
|
|
181
|
+
*
|
|
182
|
+
* @param value - The value to clamp
|
|
183
|
+
* @param min - Minimum value (default: 0)
|
|
184
|
+
* @param max - Maximum value (default: 1)
|
|
185
|
+
* @returns The clamped value
|
|
186
|
+
*/
|
|
187
|
+
declare function clamp(value: number, min?: number, max?: number): number;
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* SequenceResolver
|
|
191
|
+
* Handles intelligent frame resolution from multiple input sources:
|
|
192
|
+
* - Manual frame list (frames[])
|
|
193
|
+
* - Pattern generation (pattern, start, end, pad)
|
|
194
|
+
* - Remote manifest (manifest URL)
|
|
195
|
+
*/
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Resolves frame sequence from props.
|
|
199
|
+
* Prioritizes: frames > pattern > manifest
|
|
200
|
+
*/
|
|
201
|
+
/**
|
|
202
|
+
* Resolves frame sequence from props.
|
|
203
|
+
* Handles 'manual', 'pattern', and 'manifest' sources.
|
|
204
|
+
*/
|
|
205
|
+
declare function resolveSequence(source: ScrollSequenceProps['source']): Promise<ResolvedSequence>;
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* ImageController
|
|
209
|
+
* Manages canvas rendering, image loading, and frame-by-frame drawing.
|
|
210
|
+
* Handles preloading and caching to minimize redraws.
|
|
211
|
+
*/
|
|
212
|
+
interface ImageControllerConfig {
|
|
213
|
+
/** HTMLCanvasElement to draw on */
|
|
214
|
+
canvas: HTMLCanvasElement;
|
|
215
|
+
/** Array of sorted frame URLs */
|
|
216
|
+
frames: string[];
|
|
217
|
+
/** Memory management strategy */
|
|
218
|
+
strategy?: 'eager' | 'lazy';
|
|
219
|
+
/** Lazy load buffer size (default 10) */
|
|
220
|
+
bufferSize?: number;
|
|
221
|
+
}
|
|
222
|
+
declare class ImageController {
|
|
223
|
+
private canvas;
|
|
224
|
+
private ctx;
|
|
225
|
+
private frames;
|
|
226
|
+
private imageCache;
|
|
227
|
+
private loadingPromises;
|
|
228
|
+
private currentFrameIndex;
|
|
229
|
+
private strategy;
|
|
230
|
+
private bufferSize;
|
|
231
|
+
/**
|
|
232
|
+
* Create a new ImageController instance.
|
|
233
|
+
*
|
|
234
|
+
* @param config - Configuration object
|
|
235
|
+
* @throws If canvas doesn't support 2D context
|
|
236
|
+
*/
|
|
237
|
+
private isDestroyed;
|
|
238
|
+
constructor(config: ImageControllerConfig);
|
|
239
|
+
private preloadAll;
|
|
240
|
+
private ensureFrameWindow;
|
|
241
|
+
preloadFrame(index: number): Promise<void>;
|
|
242
|
+
private loadImage;
|
|
243
|
+
update(progress: number): void;
|
|
244
|
+
private drawFrame;
|
|
245
|
+
setCanvasSize(width: number, height: number): void;
|
|
246
|
+
destroy(): void;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
export { ImageController, type ImageControllerConfig, type ResolvedSequence, type ScrollProgress, ScrollSequence, type ScrollSequenceProps, ScrollText, ScrollTimeline, ScrollTimelineProvider, ScrollWordReveal, clamp, resolveSequence, useScrollSequence, useScrollTimeline };
|