rune-scroller 2.2.1 → 2.2.2
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/README.md +61 -417
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,8 +1,4 @@
|
|
|
1
|
-
# ⚡ Rune Scroller
|
|
2
|
-
|
|
3
|
-
**📚 Complete API Reference** — Detailed documentation for all features and options.
|
|
4
|
-
|
|
5
|
-
---
|
|
1
|
+
# ⚡ Rune Scroller
|
|
6
2
|
|
|
7
3
|
<div align="center">
|
|
8
4
|
<img src="./logo.png" alt="Rune Scroller Logo" width="200" />
|
|
@@ -26,177 +22,20 @@
|
|
|
26
22
|
|
|
27
23
|
## ✨ Features
|
|
28
24
|
|
|
29
|
-
- **
|
|
25
|
+
- **5.7KB gzipped** (JS+CSS) - Minimal overhead
|
|
30
26
|
- **Zero dependencies** - Pure Svelte 5 + IntersectionObserver
|
|
31
|
-
- **14 animations** - Fade, Zoom, Flip, Slide, Bounce
|
|
32
|
-
- **
|
|
27
|
+
- **14 animations** - Fade, Zoom, Flip, Slide, Bounce
|
|
28
|
+
- **TypeScript support** - Full type definitions
|
|
33
29
|
- **SSR-ready** - SvelteKit compatible
|
|
34
30
|
- **GPU-accelerated** - Pure CSS transforms
|
|
35
31
|
- **Accessible** - Respects `prefers-reduced-motion`
|
|
36
|
-
- **v2.0.0 New** - `onVisible` callback, ResizeObserver support, animation validation, sentinel customization
|
|
37
|
-
- **✨ Latest** - `useIntersection` migrated to Svelte 5 `$effect` rune for better lifecycle management
|
|
38
|
-
- **🚀 Bundle optimized** - CSS with custom properties, production build minification
|
|
39
|
-
- **🚀 v2.2.0** - Cache CSS check to eliminate 99% of reflows
|
|
40
32
|
|
|
41
33
|
---
|
|
42
|
-
|
|
43
|
-
## 🚀 Performance
|
|
44
|
-
|
|
45
|
-
### Cache CSS Validation (v2.2.0)
|
|
46
|
-
|
|
47
|
-
**Problem:**
|
|
48
|
-
- `checkAndWarnIfCSSNotLoaded()` was called for EVERY element
|
|
49
|
-
- Each call did:
|
|
50
|
-
- `document.createElement('div')`
|
|
51
|
-
- `document.body.appendChild(test)`
|
|
52
|
-
- `getComputedStyle(test)` ⚠️ **Expensive!** Forces full page reflow
|
|
53
|
-
- `test.remove()`
|
|
54
|
-
- For 100 animated elements = **100 reflows + 100 DOM operations**
|
|
55
|
-
|
|
56
|
-
**Solution:**
|
|
57
|
-
```javascript
|
|
58
|
-
// Cache to check only once per page load
|
|
59
|
-
let cssCheckResult = null;
|
|
60
|
-
|
|
61
|
-
export function checkAndWarnIfCSSNotLoaded() {
|
|
62
|
-
if (cssCheckResult !== null) return cssCheckResult;
|
|
63
|
-
// ... expensive check ...
|
|
64
|
-
cssCheckResult = hasAnimation;
|
|
65
|
-
return hasAnimation;
|
|
66
|
-
}
|
|
67
|
-
```
|
|
68
|
-
|
|
69
|
-
**Impact:**
|
|
70
|
-
- Validation runs ONLY ONCE per page load
|
|
71
|
-
- Eliminates layout thrashing from repeated `getComputedStyle()` calls
|
|
72
|
-
- For 100 elements: **99 fewer reflows** (100 → 1)
|
|
73
|
-
- Zero memory overhead (single boolean)
|
|
74
|
-
|
|
75
|
-
### Current Performance Metrics
|
|
76
|
-
|
|
77
|
-
| Metric | Value |
|
|
78
|
-
|---------|--------|
|
|
79
|
-
| **Bundle size** | 12.4KB (compressed), 40.3KB (unpacked) |
|
|
80
|
-
| **Initialization** | ~1-2ms per element |
|
|
81
|
-
| **Observer callback** | <0.5ms per frame |
|
|
82
|
-
| **CSS validation** | ~0.1ms total (v2.2.0, with cache) |
|
|
83
|
-
| **Memory per observer** | ~1.2KB |
|
|
84
|
-
| **Animation performance** | 60fps maintained |
|
|
85
|
-
| **Memory leaks** | 0 detected |
|
|
86
|
-
|
|
87
|
-
### Why Performance Matters
|
|
88
|
-
|
|
89
|
-
**Layout Thrashing:**
|
|
90
|
-
- Synchronous reflows block the main thread
|
|
91
|
-
- Each reflow can take 10-20ms
|
|
92
|
-
- For 100 elements = **1-2 seconds blocked**
|
|
93
|
-
- User sees stuttering/jank while scrolling
|
|
94
|
-
|
|
95
|
-
**Solution:**
|
|
96
|
-
- Cache = 1 reflow instead of N
|
|
97
|
-
- 99% improvement on pages with many animations
|
|
98
|
-
- Smoother scrolling, better UX
|
|
99
|
-
|
|
100
|
-
### Optimized Code Patterns
|
|
101
|
-
|
|
102
|
-
**IntersectionObserver:**
|
|
103
|
-
- Native API (no scroll listeners)
|
|
104
|
-
- Fast callback (<0.5ms per frame)
|
|
105
|
-
- No debounce needed (browser handles this efficiently)
|
|
106
|
-
|
|
107
|
-
**CSS Animations:**
|
|
108
|
-
- Transforms only (GPU-accelerated)
|
|
109
|
-
- No layout/repaint during animation
|
|
110
|
-
- `will-change` on visible elements only
|
|
111
|
-
|
|
112
|
-
**DOM Operations:**
|
|
113
|
-
- `insertAdjacentElement('beforebegin')` instead of `insertBefore`
|
|
114
|
-
- `offsetHeight` instead of `getBoundingClientRect()` (avoids transform issues)
|
|
115
|
-
- Complete cleanup on destroy
|
|
116
|
-
|
|
117
|
-
**Memory Management:**
|
|
118
|
-
- All observers disconnected
|
|
119
|
-
- Sentinel and wrapper removed
|
|
120
|
-
- State prevents double-disconnects
|
|
121
|
-
- 0 memory leaks detected (121/121 tests)
|
|
122
|
-
|
|
123
|
-
### Future Considerations
|
|
124
|
-
|
|
125
|
-
**1. `will-change` Timing**
|
|
126
|
-
- Currently: `.is-visible { will-change: transform, opacity; }`
|
|
127
|
-
- Trade-off: Stays active after animation (consumes GPU memory)
|
|
128
|
-
- Consideration: Use `transitionend` event to remove `will-change`
|
|
129
|
-
- Recommendation: Keep current (GPU memory is cheap)
|
|
130
|
-
|
|
131
|
-
**2. Threshold Tuning**
|
|
132
|
-
- Current: `threshold: 0` (triggers as soon as 1px is visible)
|
|
133
|
-
- Alternative: `threshold: 0.1` or `threshold: 0.25`
|
|
134
|
-
- Trade-off: Higher threshold = later trigger = smoother stagger
|
|
135
|
-
- Recommendation: Keep `threshold: 0` for immediate feedback
|
|
136
|
-
|
|
137
|
-
**3. requestIdleCallback**
|
|
138
|
-
- Potential: Defer non-critical setup to browser idle time
|
|
139
|
-
- Trade-off: Complex to implement, marginal benefit
|
|
140
|
-
- Recommendation: Not needed (current performance is excellent)
|
|
141
|
-
|
|
142
|
-
**4. Testing on Low-End Devices**
|
|
143
|
-
- Test on mobile phones, older browsers
|
|
144
|
-
- Use DevTools CPU throttling
|
|
145
|
-
- Consider Lighthouse/Puppeteer for automated testing
|
|
146
|
-
- Ensure 60fps maintained on real devices
|
|
147
|
-
|
|
148
|
-
### What NOT to Optimize
|
|
149
|
-
|
|
150
|
-
**Anti-patterns to avoid:**
|
|
151
|
-
|
|
152
|
-
1. ❌ **Premature optimization**
|
|
153
|
-
- Don't optimize without measurements
|
|
154
|
-
- Profile first, optimize later
|
|
155
|
-
- "Premature optimization is the root of all evil"
|
|
156
|
-
|
|
157
|
-
2. ❌ **Over-engineering**
|
|
158
|
-
- Complex solutions for small gains
|
|
159
|
-
- Keep it simple when possible
|
|
160
|
-
- Don't sacrifice readability for micro-optimizations
|
|
161
|
-
|
|
162
|
-
3. ❌ **Breaking performance for size**
|
|
163
|
-
- Bundle size matters (12.4KB is excellent)
|
|
164
|
-
- Don't add huge dependencies for minor improvements
|
|
165
|
-
|
|
166
|
-
4. ❌ **Optimizing unused paths**
|
|
167
|
-
- Focus on hot paths (element creation, scroll, intersection)
|
|
168
|
-
- Cold paths (initialization, destroy) less critical
|
|
169
|
-
|
|
170
|
-
5. ❌ **Sacrificing maintainability**
|
|
171
|
-
- Don't sacrifice code clarity for micro-optimizations
|
|
172
|
-
- Comments should explain WHY, not just WHAT
|
|
173
|
-
- Keep code simple and understandable
|
|
174
|
-
|
|
175
|
-
### Performance Testing
|
|
176
|
-
|
|
177
|
-
**Recommended approach:**
|
|
178
|
-
1. Create a benchmark with 100-1000 animated elements
|
|
179
|
-
2. Measure: initialization, first animation, scroll performance, cleanup
|
|
180
|
-
3. Profile with DevTools Performance tab
|
|
181
|
-
4. Test on real pages (not just benchmarks)
|
|
182
|
-
5. Verify 60fps is maintained during scroll
|
|
183
|
-
|
|
184
|
-
**Tools:**
|
|
185
|
-
- Chrome DevTools Performance tab
|
|
186
|
-
- Firefox Performance Profiler
|
|
187
|
-
- Web Inspector (Safari)
|
|
188
|
-
- Lighthouse (PageSpeed, accessibility, best practices)
|
|
189
34
|
|
|
190
|
-
---
|
|
191
|
-
|
|
192
35
|
## 📦 Installation
|
|
193
36
|
|
|
194
37
|
```bash
|
|
195
38
|
npm install rune-scroller
|
|
196
|
-
# or
|
|
197
|
-
pnpm add rune-scroller
|
|
198
|
-
# or
|
|
199
|
-
yarn add rune-scroller
|
|
200
39
|
```
|
|
201
40
|
|
|
202
41
|
---
|
|
@@ -224,51 +63,12 @@ yarn add rune-scroller
|
|
|
224
63
|
</div>
|
|
225
64
|
```
|
|
226
65
|
|
|
227
|
-
That's it! The CSS animations are included automatically when you import rune-scroller.
|
|
228
|
-
|
|
229
|
-
### Option 2: Manual CSS Import
|
|
230
|
-
|
|
231
|
-
For fine-grained control, import CSS manually:
|
|
232
|
-
|
|
233
|
-
**Step 1: Import CSS in your root layout (recommended for SvelteKit):**
|
|
234
|
-
|
|
235
|
-
```svelte
|
|
236
|
-
<!-- src/routes/+layout.svelte -->
|
|
237
|
-
<script>
|
|
238
|
-
import 'rune-scroller/animations.css';
|
|
239
|
-
let { children } = $props();
|
|
240
|
-
</script>
|
|
241
|
-
|
|
242
|
-
{@render children()}
|
|
243
|
-
```
|
|
244
|
-
|
|
245
|
-
**Or import in each component:**
|
|
246
|
-
|
|
247
|
-
```svelte
|
|
248
|
-
<script>
|
|
249
|
-
import runeScroller from 'rune-scroller';
|
|
250
|
-
import 'rune-scroller/animations.css';
|
|
251
|
-
</script>
|
|
252
|
-
```
|
|
253
|
-
|
|
254
|
-
**Step 2: Use the animations**
|
|
255
|
-
|
|
256
|
-
```svelte
|
|
257
|
-
<script>
|
|
258
|
-
import runeScroller from 'rune-scroller';
|
|
259
|
-
// CSS already imported in layout or above
|
|
260
|
-
</script>
|
|
261
|
-
|
|
262
|
-
<div use:runeScroller={{ animation: 'fade-in' }}>
|
|
263
|
-
Animated content
|
|
264
|
-
</div>
|
|
265
|
-
```
|
|
266
|
-
|
|
267
66
|
---
|
|
268
67
|
|
|
269
68
|
## 🎨 Available Animations
|
|
270
69
|
|
|
271
70
|
### Fade (5)
|
|
71
|
+
|
|
272
72
|
- `fade-in` - Simple opacity fade
|
|
273
73
|
- `fade-in-up` - Fade + move up 300px
|
|
274
74
|
- `fade-in-down` - Fade + move down 300px
|
|
@@ -276,6 +76,7 @@ For fine-grained control, import CSS manually:
|
|
|
276
76
|
- `fade-in-right` - Fade + move from left 300px
|
|
277
77
|
|
|
278
78
|
### Zoom (5)
|
|
79
|
+
|
|
279
80
|
- `zoom-in` - Scale from 0.3 to 1
|
|
280
81
|
- `zoom-out` - Scale from 2 to 1
|
|
281
82
|
- `zoom-in-up` - Zoom (0.5→1) + move up 300px
|
|
@@ -283,6 +84,7 @@ For fine-grained control, import CSS manually:
|
|
|
283
84
|
- `zoom-in-right` - Zoom (0.5→1) + move from left 300px
|
|
284
85
|
|
|
285
86
|
### Others (4)
|
|
87
|
+
|
|
286
88
|
- `flip` - 3D flip on Y-axis
|
|
287
89
|
- `flip-x` - 3D flip on X-axis
|
|
288
90
|
- `slide-rotate` - Slide + rotate 10°
|
|
@@ -294,114 +96,45 @@ For fine-grained control, import CSS manually:
|
|
|
294
96
|
|
|
295
97
|
```typescript
|
|
296
98
|
interface RuneScrollerOptions {
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
99
|
+
animation?: AnimationType // Animation name (default: 'fade-in')
|
|
100
|
+
duration?: number // Duration in ms (default: 800)
|
|
101
|
+
repeat?: boolean // Repeat on scroll (default: false)
|
|
102
|
+
debug?: boolean // Show sentinel as visible line (default: false)
|
|
103
|
+
offset?: number // Sentinel offset in px (default: 0, negative = earlier)
|
|
104
|
+
onVisible?: (element: HTMLElement) => void // Callback when visible
|
|
105
|
+
sentinelColor?: string // Debug sentinel color (e.g. '#ff6b6b')
|
|
106
|
+
sentinelId?: string // Custom sentinel ID
|
|
305
107
|
}
|
|
306
108
|
```
|
|
307
109
|
|
|
308
|
-
### Option Details
|
|
309
|
-
|
|
310
|
-
- **`animation`** - Type of animation to play. Choose from 14 built-in animations listed above. Invalid animations automatically fallback to 'fade-in' with a console warning.
|
|
311
|
-
- **`duration`** - How long the animation lasts in milliseconds (default: 800ms).
|
|
312
|
-
- **`repeat`** - If `true`, animation plays every time sentinel enters viewport. If `false`, plays only once.
|
|
313
|
-
- **`debug`** - If `true`, displays the sentinel element as a visible line below your element. Useful for seeing exactly when animations trigger. Default color is cyan (#00e0ff), customize with `sentinelColor`.
|
|
314
|
-
- **`offset`** - Offset of the sentinel in pixels. Positive values move sentinel down (delays animation), negative values move it up (triggers earlier). Useful for large elements where you want animation to trigger before the entire element is visible.
|
|
315
|
-
- **`onVisible`** *(v2.0.0+)* - Callback function triggered when the animation becomes visible. Receives the animated element as parameter. Useful for analytics, lazy loading, or triggering custom effects.
|
|
316
|
-
- **`sentinelColor`** *(v2.0.0+)* - Customize the debug sentinel color (e.g., '#ff6b6b' for red). Only visible when `debug: true`. Useful for distinguishing multiple sentinels on the same page.
|
|
317
|
-
- **`sentinelId`** *(v2.0.0+)* - Set a custom ID for the sentinel element. If not provided, an auto-ID is generated (`sentinel-1`, `sentinel-2`, etc.). Useful for identifying sentinels in DevTools and tracking which element owns which sentinel.
|
|
318
|
-
|
|
319
110
|
### Examples
|
|
320
111
|
|
|
321
112
|
```svelte
|
|
322
113
|
<!-- Basic -->
|
|
323
|
-
<div use:runeScroller={{ animation: 'zoom-in' }}>
|
|
324
|
-
Content
|
|
325
|
-
</div>
|
|
114
|
+
<div use:runeScroller={{ animation: 'zoom-in' }}>Content</div>
|
|
326
115
|
|
|
327
116
|
<!-- Custom duration -->
|
|
328
|
-
<div use:runeScroller={{ animation: 'fade-in-up', duration: 1000 }}>
|
|
329
|
-
Fast animation
|
|
330
|
-
</div>
|
|
117
|
+
<div use:runeScroller={{ animation: 'fade-in-up', duration: 1000 }}>Fast</div>
|
|
331
118
|
|
|
332
119
|
<!-- Repeat mode -->
|
|
333
|
-
<div use:runeScroller={{ animation: 'bounce-in', repeat: true }}>
|
|
334
|
-
Repeats every time you scroll
|
|
335
|
-
</div>
|
|
120
|
+
<div use:runeScroller={{ animation: 'bounce-in', repeat: true }}>Repeats</div>
|
|
336
121
|
|
|
337
|
-
<!-- Debug mode
|
|
338
|
-
<div use:runeScroller={{ animation: 'fade-in', debug: true }}>
|
|
339
|
-
The cyan line below this shows when animation will trigger
|
|
340
|
-
</div>
|
|
122
|
+
<!-- Debug mode -->
|
|
123
|
+
<div use:runeScroller={{ animation: 'fade-in', debug: true }}>Debug</div>
|
|
341
124
|
|
|
342
|
-
<!--
|
|
343
|
-
<div use:runeScroller={{
|
|
344
|
-
|
|
345
|
-
duration: 1200,
|
|
346
|
-
repeat: true,
|
|
347
|
-
debug: true
|
|
348
|
-
}}>
|
|
349
|
-
Full featured example
|
|
125
|
+
<!-- Trigger earlier with negative offset -->
|
|
126
|
+
<div use:runeScroller={{ animation: 'fade-in-up', offset: -200 }}>
|
|
127
|
+
Triggers 200px before element bottom
|
|
350
128
|
</div>
|
|
351
129
|
|
|
352
|
-
<!--
|
|
353
|
-
<div use:runeScroller={{
|
|
354
|
-
animation: 'fade-in-up',
|
|
355
|
-
offset: -200 // Trigger 200px before element bottom
|
|
356
|
-
}}>
|
|
357
|
-
Large content that needs early triggering
|
|
358
|
-
</div>
|
|
359
|
-
|
|
360
|
-
<!-- Delay animation by moving sentinel down -->
|
|
361
|
-
<div use:runeScroller={{
|
|
362
|
-
animation: 'zoom-in',
|
|
363
|
-
offset: 300 // Trigger 300px after element bottom
|
|
364
|
-
}}>
|
|
365
|
-
Content with delayed animation
|
|
366
|
-
</div>
|
|
367
|
-
|
|
368
|
-
<!-- v2.0.0: onVisible callback for analytics tracking -->
|
|
130
|
+
<!-- onVisible callback for analytics -->
|
|
369
131
|
<div use:runeScroller={{
|
|
370
132
|
animation: 'fade-in-up',
|
|
371
133
|
onVisible: (el) => {
|
|
372
|
-
|
|
373
|
-
// Track analytics, load images, trigger API calls, etc.
|
|
374
|
-
window.gtag?.('event', 'animation_visible', { element: el.id });
|
|
134
|
+
window.gtag?.('event', 'section_viewed', { id: el.id });
|
|
375
135
|
}
|
|
376
136
|
}}>
|
|
377
|
-
Tracked
|
|
378
|
-
</div>
|
|
379
|
-
|
|
380
|
-
<!-- v2.0.0: Custom sentinel color for debugging -->
|
|
381
|
-
<div use:runeScroller={{
|
|
382
|
-
animation: 'fade-in',
|
|
383
|
-
debug: true,
|
|
384
|
-
sentinelColor: '#ff6b6b' // Red instead of default cyan
|
|
385
|
-
}}>
|
|
386
|
-
Red debug sentinel
|
|
387
|
-
</div>
|
|
388
|
-
|
|
389
|
-
<!-- v2.0.0: Custom sentinel ID for identification -->
|
|
390
|
-
<div use:runeScroller={{
|
|
391
|
-
animation: 'zoom-in',
|
|
392
|
-
sentinelId: 'hero-zoom',
|
|
393
|
-
debug: true
|
|
394
|
-
}}>
|
|
395
|
-
Identified sentinel (shows "hero-zoom" in debug mode)
|
|
396
|
-
</div>
|
|
397
|
-
|
|
398
|
-
<!-- v2.0.0: Auto-ID (sentinel-1, sentinel-2, etc) -->
|
|
399
|
-
<div use:runeScroller={{
|
|
400
|
-
animation: 'fade-in-up',
|
|
401
|
-
debug: true
|
|
402
|
-
// sentinelId omitted → auto generates "sentinel-1", "sentinel-2", etc
|
|
403
|
-
}}>
|
|
404
|
-
Auto-identified sentinel
|
|
137
|
+
Tracked section
|
|
405
138
|
</div>
|
|
406
139
|
```
|
|
407
140
|
|
|
@@ -409,31 +142,25 @@ interface RuneScrollerOptions {
|
|
|
409
142
|
|
|
410
143
|
## 🎯 How It Works
|
|
411
144
|
|
|
412
|
-
|
|
145
|
+
**Sentinel-based triggering:**
|
|
413
146
|
|
|
414
|
-
1.
|
|
415
|
-
2. When
|
|
416
|
-
3.
|
|
417
|
-
4.
|
|
418
|
-
5.
|
|
419
|
-
6. *(v2.0.0)* Sentinel automatically repositions on element resize via ResizeObserver
|
|
147
|
+
1. Invisible 1px sentinel created below your element
|
|
148
|
+
2. When sentinel enters viewport, animation triggers
|
|
149
|
+
3. Uses native IntersectionObserver for performance
|
|
150
|
+
4. Pure CSS animations (GPU-accelerated)
|
|
151
|
+
5. ResizeObserver auto-repositions sentinel
|
|
420
152
|
|
|
421
153
|
**Why sentinels?**
|
|
154
|
+
|
|
422
155
|
- Accurate timing across all screen sizes
|
|
423
156
|
- No complex offset calculations
|
|
424
|
-
-
|
|
425
|
-
- Sentinel stays fixed while element animates (no observer confusion with transforms)
|
|
426
|
-
|
|
427
|
-
**Automatic ResizeObserver** *(v2.0.0+)*
|
|
428
|
-
- Sentinel repositions automatically when element resizes
|
|
429
|
-
- Works with responsive layouts and dynamic content
|
|
430
|
-
- No configuration needed—it just works
|
|
157
|
+
- Works with animated elements (transforms don't affect observer)
|
|
431
158
|
|
|
432
159
|
---
|
|
433
160
|
|
|
434
161
|
## 🌐 SSR Compatibility
|
|
435
162
|
|
|
436
|
-
Works seamlessly with SvelteKit
|
|
163
|
+
Works seamlessly with SvelteKit:
|
|
437
164
|
|
|
438
165
|
```svelte
|
|
439
166
|
<!-- src/routes/+layout.svelte -->
|
|
@@ -445,22 +172,6 @@ Works seamlessly with SvelteKit. Simply import rune-scroller in your root layout
|
|
|
445
172
|
{@render children()}
|
|
446
173
|
```
|
|
447
174
|
|
|
448
|
-
Then use animations anywhere in your app:
|
|
449
|
-
|
|
450
|
-
```svelte
|
|
451
|
-
<!-- src/routes/+page.svelte -->
|
|
452
|
-
<script>
|
|
453
|
-
import runeScroller from 'rune-scroller';
|
|
454
|
-
</script>
|
|
455
|
-
|
|
456
|
-
<!-- No special handling needed -->
|
|
457
|
-
<div use:runeScroller={{ animation: 'fade-in-up' }}>
|
|
458
|
-
Works in SvelteKit SSR!
|
|
459
|
-
</div>
|
|
460
|
-
```
|
|
461
|
-
|
|
462
|
-
The library checks for browser environment and gracefully handles server-side rendering.
|
|
463
|
-
|
|
464
175
|
---
|
|
465
176
|
|
|
466
177
|
## ♿ Accessibility
|
|
@@ -468,73 +179,32 @@ The library checks for browser environment and gracefully handles server-side re
|
|
|
468
179
|
Respects `prefers-reduced-motion`:
|
|
469
180
|
|
|
470
181
|
```css
|
|
471
|
-
/* In animations.css */
|
|
472
182
|
@media (prefers-reduced-motion: reduce) {
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
183
|
+
.scroll-animate {
|
|
184
|
+
animation: none !important;
|
|
185
|
+
opacity: 1 !important;
|
|
186
|
+
transform: none !important;
|
|
187
|
+
}
|
|
478
188
|
}
|
|
479
189
|
```
|
|
480
190
|
|
|
481
|
-
Users who prefer reduced motion will see content without animations.
|
|
482
|
-
|
|
483
191
|
---
|
|
484
192
|
|
|
485
193
|
## 📚 API Reference
|
|
486
|
-
### Public API
|
|
487
|
-
|
|
488
|
-
Rune Scroller exports a **single action-based API** (no components):
|
|
489
|
-
|
|
490
|
-
1. **`runeScroller`** (default) - Sentinel-based, simple, powerful
|
|
491
|
-
|
|
492
|
-
**Why actions instead of components?**
|
|
493
|
-
- Actions are lightweight directives
|
|
494
|
-
- No DOM wrapper overhead
|
|
495
|
-
- Better performance
|
|
496
|
-
- More flexible
|
|
497
|
-
|
|
498
|
-
### Main Export
|
|
499
194
|
|
|
500
195
|
```typescript
|
|
501
|
-
//
|
|
502
|
-
import runeScroller from
|
|
196
|
+
// Default export
|
|
197
|
+
import runeScroller from "rune-scroller"
|
|
503
198
|
|
|
504
199
|
// Named exports
|
|
505
200
|
import {
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
} from
|
|
201
|
+
useIntersection, // Composable
|
|
202
|
+
useIntersectionOnce, // Composable
|
|
203
|
+
calculateRootMargin, // Utility
|
|
204
|
+
} from "rune-scroller"
|
|
510
205
|
|
|
511
206
|
// Types
|
|
512
|
-
import type {
|
|
513
|
-
AnimationType,
|
|
514
|
-
RuneScrollerOptions,
|
|
515
|
-
IntersectionOptions,
|
|
516
|
-
UseIntersectionReturn
|
|
517
|
-
} from 'rune-scroller';
|
|
518
|
-
```
|
|
519
|
-
|
|
520
|
-
### TypeScript Types
|
|
521
|
-
|
|
522
|
-
```typescript
|
|
523
|
-
type AnimationType =
|
|
524
|
-
| 'fade-in' | 'fade-in-up' | 'fade-in-down' | 'fade-in-left' | 'fade-in-right'
|
|
525
|
-
| 'zoom-in' | 'zoom-out' | 'zoom-in-up' | 'zoom-in-left' | 'zoom-in-right'
|
|
526
|
-
| 'flip' | 'flip-x' | 'slide-rotate' | 'bounce-in';
|
|
527
|
-
|
|
528
|
-
interface RuneScrollerOptions {
|
|
529
|
-
animation?: AnimationType;
|
|
530
|
-
duration?: number;
|
|
531
|
-
repeat?: boolean;
|
|
532
|
-
debug?: boolean;
|
|
533
|
-
offset?: number;
|
|
534
|
-
onVisible?: (element: HTMLElement) => void; // v2.0.0+
|
|
535
|
-
sentinelColor?: string; // v2.0.0+
|
|
536
|
-
sentinelId?: string; // v2.0.0+
|
|
537
|
-
}
|
|
207
|
+
import type { AnimationType, RuneScrollerOptions } from "rune-scroller"
|
|
538
208
|
```
|
|
539
209
|
|
|
540
210
|
---
|
|
@@ -546,45 +216,33 @@ interface RuneScrollerOptions {
|
|
|
546
216
|
```svelte
|
|
547
217
|
<script>
|
|
548
218
|
import runeScroller from 'rune-scroller';
|
|
549
|
-
|
|
550
|
-
const items = [
|
|
551
|
-
{ title: 'Feature 1', description: 'Description 1' },
|
|
552
|
-
{ title: 'Feature 2', description: 'Description 2' },
|
|
553
|
-
{ title: 'Feature 3', description: 'Description 3' }
|
|
554
|
-
];
|
|
219
|
+
const items = ['Item 1', 'Item 2', 'Item 3'];
|
|
555
220
|
</script>
|
|
556
221
|
|
|
557
|
-
|
|
558
|
-
{
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
</div>
|
|
222
|
+
{#each items as item, i}
|
|
223
|
+
<div use:runeScroller={{
|
|
224
|
+
animation: 'fade-in-up',
|
|
225
|
+
duration: 800,
|
|
226
|
+
style: `--delay: ${i * 100}ms`
|
|
227
|
+
}}>
|
|
228
|
+
{item}
|
|
229
|
+
</div>
|
|
230
|
+
{/each}
|
|
565
231
|
```
|
|
566
232
|
|
|
567
233
|
### Hero Section
|
|
568
234
|
|
|
569
235
|
```svelte
|
|
570
|
-
<
|
|
571
|
-
|
|
572
|
-
</
|
|
573
|
-
|
|
574
|
-
<div use:runeScroller={{ animation: 'fade-in-up', duration: 1200 }}>
|
|
575
|
-
<p>Engaging content</p>
|
|
576
|
-
</div>
|
|
577
|
-
|
|
578
|
-
<div use:runeScroller={{ animation: 'zoom-in', duration: 1000 }}>
|
|
579
|
-
<button>Get Started</button>
|
|
580
|
-
</div>
|
|
236
|
+
<h1 use:runeScroller={{ animation: 'fade-in-down', duration: 1000 }}>Welcome</h1>
|
|
237
|
+
<p use:runeScroller={{ animation: 'fade-in-up', duration: 1200 }}>Subtitle</p>
|
|
238
|
+
<button use:runeScroller={{ animation: 'zoom-in', duration: 800 }}>Get Started</button>
|
|
581
239
|
```
|
|
582
240
|
|
|
583
241
|
---
|
|
584
242
|
|
|
585
243
|
## 🔗 Links
|
|
586
244
|
|
|
587
|
-
- **npm
|
|
245
|
+
- **npm**: [rune-scroller](https://www.npmjs.com/package/rune-scroller)
|
|
588
246
|
- **GitHub**: [lelabdev/rune-scroller](https://github.com/lelabdev/rune-scroller)
|
|
589
247
|
- **Changelog**: [CHANGELOG.md](https://github.com/lelabdev/rune-scroller/blob/main/lib/CHANGELOG.md)
|
|
590
248
|
|
|
@@ -596,18 +254,4 @@ MIT © [ludoloops](https://github.com/ludoloops)
|
|
|
596
254
|
|
|
597
255
|
---
|
|
598
256
|
|
|
599
|
-
## 🤝 Contributing
|
|
600
|
-
|
|
601
|
-
Contributions welcome! Please open an issue or PR on GitHub.
|
|
602
|
-
|
|
603
|
-
```bash
|
|
604
|
-
# Development
|
|
605
|
-
bun install
|
|
606
|
-
bun run dev
|
|
607
|
-
bun test
|
|
608
|
-
bun run build
|
|
609
|
-
```
|
|
610
|
-
|
|
611
|
-
---
|
|
612
|
-
|
|
613
257
|
Made with ❤️ by [LeLab.dev](https://lelab.dev)
|