rune-scroller 1.0.0 → 2.1.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/README.md +137 -59
- package/dist/animations.css +42 -139
- package/dist/animations.d.ts +12 -9
- package/dist/animations.js +31 -6
- package/dist/dom-utils.d.ts +29 -0
- package/dist/dom-utils.js +97 -0
- package/dist/index.d.ts +5 -6
- package/dist/index.js +17 -5
- package/dist/observer-utils.d.ts +21 -0
- package/dist/observer-utils.js +31 -0
- package/dist/runeScroller.d.ts +9 -0
- package/dist/runeScroller.js +166 -0
- package/dist/types.d.ts +62 -29
- package/dist/types.js +43 -0
- package/dist/useIntersection.svelte.d.ts +7 -16
- package/dist/useIntersection.svelte.js +75 -60
- package/package.json +14 -12
- package/dist/BaseAnimated.svelte +0 -48
- package/dist/BaseAnimated.svelte.d.ts +0 -16
- package/dist/RuneScroller.svelte +0 -37
- package/dist/RuneScroller.svelte.d.ts +0 -16
- package/dist/animate.svelte.d.ts +0 -14
- package/dist/animate.svelte.js +0 -79
- package/dist/animations.test.d.ts +0 -1
- package/dist/animations.test.js +0 -43
- package/dist/dom-utils.svelte.d.ts +0 -23
- package/dist/dom-utils.svelte.js +0 -48
- package/dist/runeScroller.svelte.d.ts +0 -24
- package/dist/runeScroller.svelte.js +0 -83
- package/dist/scroll-animate.test.d.ts +0 -1
- package/dist/scroll-animate.test.js +0 -57
package/README.md
CHANGED
|
@@ -1,4 +1,12 @@
|
|
|
1
|
-
# ⚡ Rune Scroller
|
|
1
|
+
# ⚡ Rune Scroller - Full Reference
|
|
2
|
+
|
|
3
|
+
**📚 Complete API Reference** — Detailed documentation for all features and options.
|
|
4
|
+
|
|
5
|
+
**Quick start?** See [README.md](../README.md) for a simpler introduction.
|
|
6
|
+
|
|
7
|
+
**Development?** See [CLAUDE.md](../CLAUDE.md) for the developer guide.
|
|
8
|
+
|
|
9
|
+
---
|
|
2
10
|
|
|
3
11
|
<div align="center">
|
|
4
12
|
<img src="./logo.png" alt="Rune Scroller Logo" width="200" />
|
|
@@ -9,17 +17,63 @@
|
|
|
9
17
|
> 🚀 **Open Source** by [ludoloops](https://github.com/ludoloops) at [LeLab.dev](https://lelab.dev)
|
|
10
18
|
> 📜 Licensed under **MIT**
|
|
11
19
|
|
|
20
|
+
<div align="center">
|
|
21
|
+
<a href="https://bundlephobia.com/package/rune-scroller">
|
|
22
|
+
<img src="https://img.shields.io/bundlephobia/minzip/rune-scroller" alt="minzipped size" />
|
|
23
|
+
</a>
|
|
24
|
+
<a href="https://bundlephobia.com/package/rune-scroller">
|
|
25
|
+
<img src="https://img.shields.io/bundlephobia/min/rune-scroller" alt="minified size" />
|
|
26
|
+
</a>
|
|
27
|
+
</div>
|
|
28
|
+
|
|
12
29
|
---
|
|
13
30
|
|
|
14
31
|
## ✨ Features
|
|
15
32
|
|
|
16
|
-
-
|
|
33
|
+
- **12.7KB gzipped** (40.3KB uncompressed) - Minimal overhead, optimized for production
|
|
17
34
|
- **Zero dependencies** - Pure Svelte 5 + IntersectionObserver
|
|
18
35
|
- **14 animations** - Fade, Zoom, Flip, Slide, Bounce variants
|
|
19
|
-
- **TypeScript** -
|
|
36
|
+
- **Full TypeScript support** - Type definitions generated from JSDoc
|
|
20
37
|
- **SSR-ready** - SvelteKit compatible
|
|
21
38
|
- **GPU-accelerated** - Pure CSS transforms
|
|
22
39
|
- **Accessible** - Respects `prefers-reduced-motion`
|
|
40
|
+
- **v2.0.0 New** - `onVisible` callback, ResizeObserver support, animation validation, sentinel customization
|
|
41
|
+
- **✨ Latest** - `useIntersection` migrated to Svelte 5 `$effect` rune for better lifecycle management
|
|
42
|
+
- **🚀 Bundle optimized** - CSS with custom properties, production build minification
|
|
43
|
+
|
|
44
|
+
---
|
|
45
|
+
|
|
46
|
+
## 📊 Performance & Quality
|
|
47
|
+
|
|
48
|
+
**Bundle Optimization (2026-01-12):**
|
|
49
|
+
- ✅ **121/121 tests passing** (100%)
|
|
50
|
+
- ✅ **Bundle size:** 12.7KB gzipped (-1.6KB from v2.0.0, -11.2%)
|
|
51
|
+
- ✅ **Unpacked size:** 40.3KB (-7.1KB from v2.0.0, -15.0%)
|
|
52
|
+
- ✅ **Type safety:** 0 errors (JSDoc + TypeScript)
|
|
53
|
+
- ✅ **Memory leaks:** 0 detected
|
|
54
|
+
- ✅ **Svelte 5 aligned:** Full runes support
|
|
55
|
+
- ✅ **Removed deprecated `animate` action** - Simplified API
|
|
56
|
+
- ✅ **Tests optimized for npm** - Excluded from package (tests/ directory)
|
|
57
|
+
- ✅ **CSS optimized:** CSS custom properties (-36% CSS reduction)
|
|
58
|
+
- ✅ **Production ready:** NODE_ENV guards for console warnings
|
|
59
|
+
|
|
60
|
+
**Bundle breakdown (unpacked, optimized):**
|
|
61
|
+
- `runeScroller.js`: 5.0KB (-12%)
|
|
62
|
+
- `dom-utils.js`: 3.4KB (-24%)
|
|
63
|
+
- `animations.css`: 2.5KB (-36%) ← Optimized with CSS custom properties
|
|
64
|
+
- `types.js`: 2.1KB
|
|
65
|
+
- `useIntersection.svelte.js`: 2.2KB (-18%)
|
|
66
|
+
- `observer-utils.js`: 773B (-52%)
|
|
67
|
+
- Type definitions (.d.ts): ~6KB (-33%)
|
|
68
|
+
- Other (index.js, LICENSE, package.json): 2.8KB
|
|
69
|
+
|
|
70
|
+
**Optimization details:**
|
|
71
|
+
- CSS custom properties eliminate repetitive rules
|
|
72
|
+
- Production builds strip console warnings via NODE_ENV
|
|
73
|
+
- JSDoc optimized (kept types, removed verbose descriptions)
|
|
74
|
+
- All features and animations remain unchanged
|
|
75
|
+
|
|
76
|
+
See [`MIGRATION_METRICS.md`](../MIGRATION_METRICS.md) for detailed performance benchmarks.
|
|
23
77
|
|
|
24
78
|
---
|
|
25
79
|
|
|
@@ -91,17 +145,17 @@ yarn add rune-scroller
|
|
|
91
145
|
|
|
92
146
|
### Fade (5)
|
|
93
147
|
- `fade-in` - Simple opacity fade
|
|
94
|
-
- `fade-in-up` - Fade + move up
|
|
95
|
-
- `fade-in-down` - Fade + move down
|
|
96
|
-
- `fade-in-left` - Fade + move from right
|
|
97
|
-
- `fade-in-right` - Fade + move from left
|
|
148
|
+
- `fade-in-up` - Fade + move up 300px
|
|
149
|
+
- `fade-in-down` - Fade + move down 300px
|
|
150
|
+
- `fade-in-left` - Fade + move from right 300px
|
|
151
|
+
- `fade-in-right` - Fade + move from left 300px
|
|
98
152
|
|
|
99
153
|
### Zoom (5)
|
|
100
|
-
- `zoom-in` - Scale from 0.
|
|
101
|
-
- `zoom-out` - Scale from
|
|
102
|
-
- `zoom-in-up` - Zoom + move up
|
|
103
|
-
- `zoom-in-left` - Zoom + move from right
|
|
104
|
-
- `zoom-in-right` - Zoom + move from left
|
|
154
|
+
- `zoom-in` - Scale from 0.3 to 1
|
|
155
|
+
- `zoom-out` - Scale from 2 to 1
|
|
156
|
+
- `zoom-in-up` - Zoom (0.5→1) + move up 300px
|
|
157
|
+
- `zoom-in-left` - Zoom (0.5→1) + move from right 300px
|
|
158
|
+
- `zoom-in-right` - Zoom (0.5→1) + move from left 300px
|
|
105
159
|
|
|
106
160
|
### Others (4)
|
|
107
161
|
- `flip` - 3D flip on Y-axis
|
|
@@ -116,20 +170,26 @@ yarn add rune-scroller
|
|
|
116
170
|
```typescript
|
|
117
171
|
interface RuneScrollerOptions {
|
|
118
172
|
animation?: AnimationType; // Animation name (default: 'fade-in')
|
|
119
|
-
duration?: number; // Duration in ms (default:
|
|
173
|
+
duration?: number; // Duration in ms (default: 800)
|
|
120
174
|
repeat?: boolean; // Repeat on scroll (default: false)
|
|
121
175
|
debug?: boolean; // Show sentinel as visible line (default: false)
|
|
122
176
|
offset?: number; // Sentinel offset in px (default: 0, negative = above)
|
|
177
|
+
onVisible?: (element: HTMLElement) => void; // Callback when animation triggers (v2.0.0+)
|
|
178
|
+
sentinelColor?: string; // Sentinel debug color, e.g. '#ff6b6b' (v2.0.0+)
|
|
179
|
+
sentinelId?: string; // Custom ID for sentinel identification (v2.0.0+)
|
|
123
180
|
}
|
|
124
181
|
```
|
|
125
182
|
|
|
126
183
|
### Option Details
|
|
127
184
|
|
|
128
|
-
- **`animation`** - Type of animation to play. Choose from 14 built-in animations listed above.
|
|
129
|
-
- **`duration`** - How long the animation lasts in milliseconds (default:
|
|
185
|
+
- **`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.
|
|
186
|
+
- **`duration`** - How long the animation lasts in milliseconds (default: 800ms).
|
|
130
187
|
- **`repeat`** - If `true`, animation plays every time sentinel enters viewport. If `false`, plays only once.
|
|
131
|
-
- **`debug`** - If `true`, displays the sentinel element as a visible
|
|
188
|
+
- **`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`.
|
|
132
189
|
- **`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.
|
|
190
|
+
- **`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.
|
|
191
|
+
- **`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.
|
|
192
|
+
- **`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.
|
|
133
193
|
|
|
134
194
|
### Examples
|
|
135
195
|
|
|
@@ -179,40 +239,50 @@ interface RuneScrollerOptions {
|
|
|
179
239
|
}}>
|
|
180
240
|
Content with delayed animation
|
|
181
241
|
</div>
|
|
182
|
-
```
|
|
183
242
|
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
243
|
+
<!-- v2.0.0: onVisible callback for analytics tracking -->
|
|
244
|
+
<div use:runeScroller={{
|
|
245
|
+
animation: 'fade-in-up',
|
|
246
|
+
onVisible: (el) => {
|
|
247
|
+
console.log('Animation visible!', el);
|
|
248
|
+
// Track analytics, load images, trigger API calls, etc.
|
|
249
|
+
window.gtag?.('event', 'animation_visible', { element: el.id });
|
|
250
|
+
}
|
|
251
|
+
}}>
|
|
252
|
+
Tracked animation
|
|
253
|
+
</div>
|
|
189
254
|
|
|
190
|
-
|
|
255
|
+
<!-- v2.0.0: Custom sentinel color for debugging -->
|
|
256
|
+
<div use:runeScroller={{
|
|
257
|
+
animation: 'fade-in',
|
|
258
|
+
debug: true,
|
|
259
|
+
sentinelColor: '#ff6b6b' // Red instead of default cyan
|
|
260
|
+
}}>
|
|
261
|
+
Red debug sentinel
|
|
262
|
+
</div>
|
|
191
263
|
|
|
192
|
-
|
|
193
|
-
<
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
264
|
+
<!-- v2.0.0: Custom sentinel ID for identification -->
|
|
265
|
+
<div use:runeScroller={{
|
|
266
|
+
animation: 'zoom-in',
|
|
267
|
+
sentinelId: 'hero-zoom',
|
|
268
|
+
debug: true
|
|
269
|
+
}}>
|
|
270
|
+
Identified sentinel (shows "hero-zoom" in debug mode)
|
|
271
|
+
</div>
|
|
197
272
|
|
|
198
|
-
|
|
273
|
+
<!-- v2.0.0: Auto-ID (sentinel-1, sentinel-2, etc) -->
|
|
274
|
+
<div use:runeScroller={{
|
|
199
275
|
animation: 'fade-in-up',
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
threshold: 0.5,
|
|
203
|
-
offset: 20,
|
|
204
|
-
once: true
|
|
276
|
+
debug: true
|
|
277
|
+
// sentinelId omitted → auto generates "sentinel-1", "sentinel-2", etc
|
|
205
278
|
}}>
|
|
206
|
-
|
|
279
|
+
Auto-identified sentinel
|
|
207
280
|
</div>
|
|
208
281
|
```
|
|
209
282
|
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
- `rootMargin` - Custom IntersectionObserver margin
|
|
214
|
-
- `delay` - Animation delay in ms
|
|
215
|
-
- `once` - Trigger only once
|
|
283
|
+
---
|
|
284
|
+
|
|
285
|
+
## 🔧 Advanced Usage
|
|
216
286
|
|
|
217
287
|
### Using Composables
|
|
218
288
|
|
|
@@ -245,11 +315,18 @@ Rune Scroller uses **sentinel-based triggering**:
|
|
|
245
315
|
3. This ensures precise timing regardless of element size
|
|
246
316
|
4. Uses native IntersectionObserver for performance
|
|
247
317
|
5. Pure CSS animations (GPU-accelerated)
|
|
318
|
+
6. *(v2.0.0)* Sentinel automatically repositions on element resize via ResizeObserver
|
|
248
319
|
|
|
249
320
|
**Why sentinels?**
|
|
250
321
|
- Accurate timing across all screen sizes
|
|
251
322
|
- No complex offset calculations
|
|
252
323
|
- Handles staggered animations naturally
|
|
324
|
+
- Sentinel stays fixed while element animates (no observer confusion with transforms)
|
|
325
|
+
|
|
326
|
+
**Automatic ResizeObserver** *(v2.0.0+)*
|
|
327
|
+
- Sentinel repositions automatically when element resizes
|
|
328
|
+
- Works with responsive layouts and dynamic content
|
|
329
|
+
- No configuration needed—it just works
|
|
253
330
|
|
|
254
331
|
---
|
|
255
332
|
|
|
@@ -304,16 +381,26 @@ Users who prefer reduced motion will see content without animations.
|
|
|
304
381
|
---
|
|
305
382
|
|
|
306
383
|
## 📚 API Reference
|
|
384
|
+
### Public API
|
|
385
|
+
|
|
386
|
+
Rune Scroller exports a **single action-based API** (no components):
|
|
387
|
+
|
|
388
|
+
1. **`runeScroller`** (default) - Sentinel-based, simple, powerful
|
|
389
|
+
|
|
390
|
+
**Why actions instead of components?**
|
|
391
|
+
- Actions are lightweight directives
|
|
392
|
+
- No DOM wrapper overhead
|
|
393
|
+
- Better performance
|
|
394
|
+
- More flexible
|
|
307
395
|
|
|
308
396
|
### Main Export
|
|
309
397
|
|
|
310
398
|
```typescript
|
|
311
|
-
// Default export
|
|
399
|
+
// Default export
|
|
312
400
|
import runeScroller from 'rune-scroller';
|
|
313
401
|
|
|
314
402
|
// Named exports
|
|
315
403
|
import {
|
|
316
|
-
animate, // Alternative action
|
|
317
404
|
useIntersection, // Composable
|
|
318
405
|
useIntersectionOnce, // Composable
|
|
319
406
|
calculateRootMargin // Utility
|
|
@@ -323,7 +410,6 @@ import {
|
|
|
323
410
|
import type {
|
|
324
411
|
AnimationType,
|
|
325
412
|
RuneScrollerOptions,
|
|
326
|
-
AnimateOptions,
|
|
327
413
|
IntersectionOptions,
|
|
328
414
|
UseIntersectionReturn
|
|
329
415
|
} from 'rune-scroller';
|
|
@@ -343,16 +429,9 @@ interface RuneScrollerOptions {
|
|
|
343
429
|
repeat?: boolean;
|
|
344
430
|
debug?: boolean;
|
|
345
431
|
offset?: number;
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
animation?: AnimationType;
|
|
350
|
-
duration?: number;
|
|
351
|
-
delay?: number;
|
|
352
|
-
threshold?: number;
|
|
353
|
-
rootMargin?: string;
|
|
354
|
-
offset?: number;
|
|
355
|
-
once?: boolean;
|
|
432
|
+
onVisible?: (element: HTMLElement) => void; // v2.0.0+
|
|
433
|
+
sentinelColor?: string; // v2.0.0+
|
|
434
|
+
sentinelId?: string; // v2.0.0+
|
|
356
435
|
}
|
|
357
436
|
```
|
|
358
437
|
|
|
@@ -406,7 +485,6 @@ interface AnimateOptions {
|
|
|
406
485
|
|
|
407
486
|
- **npm Package**: [rune-scroller](https://www.npmjs.com/package/rune-scroller)
|
|
408
487
|
- **GitHub**: [lelabdev/rune-scroller](https://github.com/lelabdev/rune-scroller)
|
|
409
|
-
- **Documentation**: [CLAUDE.md](./CLAUDE.md)
|
|
410
488
|
- **Changelog**: [CHANGELOG.md](./CHANGELOG.md)
|
|
411
489
|
|
|
412
490
|
---
|
|
@@ -423,10 +501,10 @@ Contributions welcome! Please open an issue or PR on GitHub.
|
|
|
423
501
|
|
|
424
502
|
```bash
|
|
425
503
|
# Development
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
504
|
+
bun install
|
|
505
|
+
bun run dev
|
|
506
|
+
bun test
|
|
507
|
+
bun run build
|
|
430
508
|
```
|
|
431
509
|
|
|
432
510
|
---
|
package/dist/animations.css
CHANGED
|
@@ -1,177 +1,80 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Reusable scroll animation styles
|
|
3
|
-
*
|
|
2
|
+
* Reusable scroll animation styles - Optimized with CSS custom properties
|
|
3
|
+
* Reduces bundle size while maintaining all 14 animations
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
/* Base animation container */
|
|
6
|
+
/* Base animation container with optimized transitions */
|
|
7
7
|
.scroll-animate,
|
|
8
8
|
.animated-element {
|
|
9
9
|
opacity: 0;
|
|
10
|
-
transition:
|
|
11
|
-
|
|
10
|
+
transition: opacity var(--duration, 2500ms) linear var(--delay, 0ms),
|
|
11
|
+
transform var(--duration, 2500ms) cubic-bezier(0.34, 1.56, 0.64, 1) var(--delay, 0ms);
|
|
12
|
+
transform: perspective(1000px) translate(var(--tx, 0), var(--ty, 0)) scale(var(--scale, 1)) rotateX(var(--rx, 0deg)) rotateY(var(--ry, 0deg)) rotate(var(--rotate, 0deg));
|
|
12
13
|
}
|
|
13
14
|
|
|
14
15
|
.scroll-animate.is-visible,
|
|
15
16
|
.animated-element.is-visible {
|
|
16
|
-
opacity: 1;
|
|
17
|
-
/* GPU acceleration only for visible elements to reduce initial memory pressure */
|
|
17
|
+
opacity: 1 !important;
|
|
18
18
|
will-change: transform, opacity;
|
|
19
|
+
transform: perspective(1000px) translate(var(--tx, 0), var(--ty, 0)) scale(var(--scale, 1)) rotateX(var(--rx, 0deg)) rotateY(var(--ry, 0deg)) rotate(var(--rotate, 0deg));
|
|
19
20
|
}
|
|
20
21
|
|
|
21
|
-
/*
|
|
22
|
-
|
|
22
|
+
/* Initial state - transform values before animation */
|
|
23
|
+
[data-animation='fade-in'] { --tx: 0; --ty: 0; }
|
|
24
|
+
[data-animation='fade-in-up'] { --ty: var(--translate-distance, 300px); }
|
|
25
|
+
[data-animation='fade-in-down'] { --ty: calc(-1 * var(--translate-distance, 300px)); }
|
|
26
|
+
[data-animation='fade-in-left'] { --tx: calc(-1 * var(--translate-distance, 300px)); }
|
|
27
|
+
[data-animation='fade-in-right'] { --tx: var(--translate-distance, 300px); }
|
|
23
28
|
|
|
24
|
-
|
|
25
|
-
[data-animation='
|
|
26
|
-
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
[data-animation='fade-in'].is-visible {
|
|
30
|
-
/* Inherits opacity: 1 from .scroll-animate.is-visible */
|
|
31
|
-
transform: none;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
/* Fade In Up */
|
|
35
|
-
[data-animation='fade-in-up'] {
|
|
36
|
-
transform: translateY(300px);
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
[data-animation='fade-in-up'].is-visible {
|
|
40
|
-
transform: translateY(0);
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
/* Fade In Down */
|
|
44
|
-
[data-animation='fade-in-down'] {
|
|
45
|
-
transform: translateY(-300px);
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
[data-animation='fade-in-down'].is-visible {
|
|
49
|
-
transform: translateY(0);
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
/* Fade In Left */
|
|
53
|
-
[data-animation='fade-in-left'] {
|
|
54
|
-
transform: translateX(-300px);
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
[data-animation='fade-in-left'].is-visible {
|
|
58
|
-
transform: translateX(0);
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
/* Fade In Right */
|
|
62
|
-
[data-animation='fade-in-right'] {
|
|
63
|
-
transform: translateX(300px);
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
[data-animation='fade-in-right'].is-visible {
|
|
67
|
-
transform: translateX(0);
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
/* Zoom In */
|
|
71
|
-
[data-animation='zoom-in'] {
|
|
72
|
-
transform: scale(0.3);
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
[data-animation='zoom-in'].is-visible {
|
|
76
|
-
transform: scale(1);
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
/* Zoom Out */
|
|
80
|
-
[data-animation='zoom-out'] {
|
|
81
|
-
transform: scale(2);
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
[data-animation='zoom-out'].is-visible {
|
|
85
|
-
transform: scale(1);
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
/* Zoom In Up */
|
|
89
|
-
[data-animation='zoom-in-up'] {
|
|
90
|
-
transform: scale(0.5) translateY(300px);
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
[data-animation='zoom-in-up'].is-visible {
|
|
94
|
-
transform: scale(1) translateY(0);
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
/* Zoom In Left */
|
|
98
|
-
[data-animation='zoom-in-left'] {
|
|
99
|
-
transform: scale(0.5) translateX(-300px);
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
[data-animation='zoom-in-left'].is-visible {
|
|
103
|
-
transform: scale(1) translateX(0);
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
/* Zoom In Right */
|
|
107
|
-
[data-animation='zoom-in-right'] {
|
|
108
|
-
transform: scale(0.5) translateX(300px);
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
[data-animation='zoom-in-right'].is-visible {
|
|
112
|
-
transform: scale(1) translateX(0);
|
|
113
|
-
}
|
|
29
|
+
[data-animation='zoom-in'] { --scale: 0.3; }
|
|
30
|
+
[data-animation='zoom-out'] { --scale: 2; }
|
|
31
|
+
[data-animation='zoom-in-up'] { --scale: 0.5; --ty: var(--translate-distance, 300px); }
|
|
32
|
+
[data-animation='zoom-in-left'] { --scale: 0.5; --tx: calc(-1 * var(--translate-distance, 300px)); }
|
|
33
|
+
[data-animation='zoom-in-right'] { --scale: 0.5; --tx: var(--translate-distance, 300px); }
|
|
114
34
|
|
|
115
|
-
|
|
116
|
-
[data-animation='flip'] {
|
|
117
|
-
transform: perspective(1000px) rotateY(90deg);
|
|
118
|
-
}
|
|
35
|
+
[data-animation='flip'] { --ry: 90deg; }
|
|
36
|
+
[data-animation='flip-x'] { --rx: 90deg; }
|
|
119
37
|
|
|
120
|
-
[data-animation='
|
|
121
|
-
|
|
122
|
-
}
|
|
38
|
+
[data-animation='slide-rotate'] { --tx: calc(-1 * var(--translate-distance, 300px)); --rotate: -45deg; }
|
|
39
|
+
[data-animation='bounce-in'] { --scale: 0; }
|
|
123
40
|
|
|
124
|
-
/*
|
|
125
|
-
[data-animation='
|
|
126
|
-
|
|
127
|
-
}
|
|
41
|
+
/* Visible state - reset transform values to final position */
|
|
42
|
+
[data-animation='fade-in-up'].is-visible { --ty: 0; }
|
|
43
|
+
[data-animation='fade-in-down'].is-visible { --ty: 0; }
|
|
44
|
+
[data-animation='fade-in-left'].is-visible { --tx: 0; }
|
|
45
|
+
[data-animation='fade-in-right'].is-visible { --tx: 0; }
|
|
128
46
|
|
|
129
|
-
[data-animation='
|
|
130
|
-
|
|
131
|
-
}
|
|
47
|
+
[data-animation='zoom-in'].is-visible { --scale: 1; }
|
|
48
|
+
[data-animation='zoom-out'].is-visible { --scale: 1; }
|
|
49
|
+
[data-animation='zoom-in-up'].is-visible { --scale: 1; --ty: 0; }
|
|
50
|
+
[data-animation='zoom-in-left'].is-visible { --scale: 1; --tx: 0; }
|
|
51
|
+
[data-animation='zoom-in-right'].is-visible { --scale: 1; --tx: 0; }
|
|
132
52
|
|
|
133
|
-
|
|
134
|
-
[data-animation='
|
|
135
|
-
transform: translateX(-300px) rotate(-45deg);
|
|
136
|
-
}
|
|
53
|
+
[data-animation='flip'].is-visible { --ry: 0deg; }
|
|
54
|
+
[data-animation='flip-x'].is-visible { --rx: 0deg; }
|
|
137
55
|
|
|
138
|
-
[data-animation='slide-rotate'].is-visible {
|
|
139
|
-
transform: translateX(0) rotate(0deg);
|
|
140
|
-
}
|
|
56
|
+
[data-animation='slide-rotate'].is-visible { --tx: 0; --rotate: 0deg; }
|
|
141
57
|
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
transform: scale(
|
|
58
|
+
@keyframes bounce {
|
|
59
|
+
0% { transform: scale(0); }
|
|
60
|
+
50% { transform: scale(1.1); }
|
|
61
|
+
100% { transform: scale(1); }
|
|
145
62
|
}
|
|
146
63
|
|
|
64
|
+
/* Bounce animation - special case with keyframes */
|
|
147
65
|
[data-animation='bounce-in'].is-visible {
|
|
148
|
-
|
|
149
|
-
animation: bounce var(--duration, 800ms) cubic-bezier(0.68, -0.55, 0.265, 1.55);
|
|
66
|
+
animation: bounce var(--duration, 1500ms) cubic-bezier(0.68, -0.55, 0.265, 1.55);
|
|
150
67
|
animation-delay: var(--delay, 0ms);
|
|
151
68
|
}
|
|
152
69
|
|
|
153
|
-
@keyframes bounce {
|
|
154
|
-
0% {
|
|
155
|
-
transform: scale(0);
|
|
156
|
-
}
|
|
157
|
-
50% {
|
|
158
|
-
transform: scale(1.1);
|
|
159
|
-
}
|
|
160
|
-
100% {
|
|
161
|
-
transform: scale(1);
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
|
|
165
70
|
/* Accessibility: Respect user's motion preferences */
|
|
166
71
|
@media (prefers-reduced-motion: reduce) {
|
|
167
72
|
.scroll-animate,
|
|
168
73
|
.animated-element {
|
|
169
|
-
/* Disable animations for users who prefer reduced motion */
|
|
170
74
|
transition: none;
|
|
171
75
|
animation: none !important;
|
|
172
76
|
}
|
|
173
77
|
|
|
174
|
-
/* Still show final state without animation */
|
|
175
78
|
.scroll-animate.is-visible,
|
|
176
79
|
.animated-element.is-visible {
|
|
177
80
|
opacity: 1;
|
package/dist/animations.d.ts
CHANGED
|
@@ -1,15 +1,18 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Calculate rootMargin for IntersectionObserver from offset or custom rootMargin
|
|
3
|
+
*
|
|
4
|
+
* @param {number} [offset] - Viewport offset (0-100). 0 = bottom of viewport touches top of element, 100 = top of viewport touches top of element
|
|
5
|
+
* @param {string} [rootMargin] - Custom rootMargin string (takes precedence over offset)
|
|
6
|
+
* @returns {string} rootMargin string for IntersectionObserver
|
|
3
7
|
*/
|
|
8
|
+
export function calculateRootMargin(offset?: number, rootMargin?: string): string;
|
|
4
9
|
/**
|
|
5
|
-
*
|
|
6
|
-
*
|
|
10
|
+
* Animation utilities
|
|
11
|
+
* Type definitions have been moved to types.js for single source of truth
|
|
7
12
|
*/
|
|
8
|
-
export type AnimationType = 'fade-in' | 'fade-in-up' | 'fade-in-down' | 'fade-in-left' | 'fade-in-right' | 'zoom-in' | 'zoom-out' | 'zoom-in-up' | 'zoom-in-left' | 'zoom-in-right' | 'flip' | 'flip-x' | 'slide-rotate' | 'bounce-in';
|
|
9
13
|
/**
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
* @
|
|
13
|
-
* @returns rootMargin string for IntersectionObserver
|
|
14
|
+
* All available animation types in the library
|
|
15
|
+
* Useful for programmatic access and validation
|
|
16
|
+
* @type {readonly string[]}
|
|
14
17
|
*/
|
|
15
|
-
export
|
|
18
|
+
export const ANIMATION_TYPES: readonly string[];
|
package/dist/animations.js
CHANGED
|
@@ -1,13 +1,38 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Animation
|
|
2
|
+
* Animation utilities
|
|
3
|
+
* Type definitions have been moved to types.js for single source of truth
|
|
3
4
|
*/
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* All available animation types in the library
|
|
8
|
+
* Useful for programmatic access and validation
|
|
9
|
+
* @type {readonly string[]}
|
|
10
|
+
*/
|
|
11
|
+
export const ANIMATION_TYPES = [
|
|
12
|
+
'fade-in',
|
|
13
|
+
'fade-in-up',
|
|
14
|
+
'fade-in-down',
|
|
15
|
+
'fade-in-left',
|
|
16
|
+
'fade-in-right',
|
|
17
|
+
'zoom-in',
|
|
18
|
+
'zoom-out',
|
|
19
|
+
'zoom-in-up',
|
|
20
|
+
'zoom-in-left',
|
|
21
|
+
'zoom-in-right',
|
|
22
|
+
'flip',
|
|
23
|
+
'flip-x',
|
|
24
|
+
'slide-rotate',
|
|
25
|
+
'bounce-in'
|
|
26
|
+
];
|
|
27
|
+
|
|
4
28
|
/**
|
|
5
29
|
* Calculate rootMargin for IntersectionObserver from offset or custom rootMargin
|
|
6
|
-
*
|
|
7
|
-
* @param
|
|
8
|
-
* @
|
|
30
|
+
*
|
|
31
|
+
* @param {number} [offset] - Viewport offset (0-100). 0 = bottom of viewport touches top of element, 100 = top of viewport touches top of element
|
|
32
|
+
* @param {string} [rootMargin] - Custom rootMargin string (takes precedence over offset)
|
|
33
|
+
* @returns {string} rootMargin string for IntersectionObserver
|
|
9
34
|
*/
|
|
10
35
|
export function calculateRootMargin(offset, rootMargin) {
|
|
11
|
-
|
|
12
|
-
|
|
36
|
+
return rootMargin ??
|
|
37
|
+
(offset !== undefined ? `-${100 - offset}% 0px -${offset}% 0px` : '-10% 0px -10% 0px');
|
|
13
38
|
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @param {HTMLElement} element
|
|
3
|
+
* @param {number} [duration]
|
|
4
|
+
* @param {number} [delay=0]
|
|
5
|
+
*/
|
|
6
|
+
export function setCSSVariables(element: HTMLElement, duration?: number, delay?: number): void;
|
|
7
|
+
/**
|
|
8
|
+
* @param {HTMLElement} element
|
|
9
|
+
* @param {import('./types.js').AnimationType} animation
|
|
10
|
+
*/
|
|
11
|
+
export function setupAnimationElement(element: HTMLElement, animation: import("./types.js").AnimationType): void;
|
|
12
|
+
/**
|
|
13
|
+
* @param {HTMLElement} element
|
|
14
|
+
* @param {boolean} [debug=false]
|
|
15
|
+
* @param {number} [offset=0]
|
|
16
|
+
* @param {string} [sentinelColor='#00e0ff']
|
|
17
|
+
* @param {string} [debugLabel]
|
|
18
|
+
* @param {string} [sentinelId]
|
|
19
|
+
* @returns {{ element: HTMLElement, id: string }}
|
|
20
|
+
*/
|
|
21
|
+
export function createSentinel(element: HTMLElement, debug?: boolean, offset?: number, sentinelColor?: string, debugLabel?: string, sentinelId?: string): {
|
|
22
|
+
element: HTMLElement;
|
|
23
|
+
id: string;
|
|
24
|
+
};
|
|
25
|
+
/**
|
|
26
|
+
* Check if CSS animations are loaded and warn if not (dev only)
|
|
27
|
+
* @returns {boolean} True if CSS appears to be loaded
|
|
28
|
+
*/
|
|
29
|
+
export function checkAndWarnIfCSSNotLoaded(): boolean;
|