use-scroll-animate 1.5.0 → 2.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 (66) hide show
  1. package/CHANGELOG.md +67 -0
  2. package/README.md +145 -10
  3. package/README_ja.md +57 -1
  4. package/README_zh.md +68 -1
  5. package/dist/{index.esm.js → chunks/core-BP-a1iNc.js} +297 -404
  6. package/dist/chunks/core-BP-a1iNc.js.map +1 -0
  7. package/dist/{index.mjs → chunks/core-B_J4rXbC.cjs} +312 -404
  8. package/dist/chunks/core-B_J4rXbC.cjs.map +1 -0
  9. package/dist/chunks/stagger-DHw0ExuR.cjs +86 -0
  10. package/dist/chunks/stagger-DHw0ExuR.cjs.map +1 -0
  11. package/dist/chunks/stagger-DTv_WiUQ.js +84 -0
  12. package/dist/chunks/stagger-DTv_WiUQ.js.map +1 -0
  13. package/dist/element.cjs +97 -0
  14. package/dist/element.cjs.map +1 -0
  15. package/dist/{types/types.d.ts → element.d.cts} +73 -10
  16. package/dist/element.d.ts +216 -0
  17. package/dist/element.js +95 -0
  18. package/dist/element.js.map +1 -0
  19. package/dist/element.umd.js +2 -0
  20. package/dist/element.umd.js.map +1 -0
  21. package/dist/index.cjs +241 -0
  22. package/dist/index.cjs.map +1 -0
  23. package/dist/{index.d.mts → index.d.cts} +84 -51
  24. package/dist/index.d.ts +84 -51
  25. package/dist/index.js +98 -1133
  26. package/dist/index.js.map +1 -1
  27. package/dist/index.umd.js +5 -3
  28. package/dist/index.umd.js.map +1 -1
  29. package/dist/react.cjs +67 -0
  30. package/dist/react.cjs.map +1 -0
  31. package/dist/react.d.cts +159 -0
  32. package/dist/react.d.ts +159 -0
  33. package/dist/react.js +64 -0
  34. package/dist/react.js.map +1 -0
  35. package/dist/solid.cjs +62 -0
  36. package/dist/solid.cjs.map +1 -0
  37. package/dist/solid.d.cts +245 -0
  38. package/dist/solid.d.ts +245 -0
  39. package/dist/solid.js +58 -0
  40. package/dist/solid.js.map +1 -0
  41. package/dist/svelte.cjs +65 -0
  42. package/dist/svelte.cjs.map +1 -0
  43. package/dist/svelte.d.cts +244 -0
  44. package/dist/svelte.d.ts +244 -0
  45. package/dist/svelte.js +62 -0
  46. package/dist/svelte.js.map +1 -0
  47. package/dist/vue.cjs +61 -0
  48. package/dist/vue.cjs.map +1 -0
  49. package/dist/vue.d.cts +159 -0
  50. package/dist/vue.d.ts +159 -0
  51. package/dist/vue.js +59 -0
  52. package/dist/vue.js.map +1 -0
  53. package/docs/API.md +139 -0
  54. package/docs/deprecations.md +20 -0
  55. package/docs/migration-from-aos.md +67 -0
  56. package/docs/migration-from-gsap-scrolltrigger.md +80 -0
  57. package/package.json +86 -15
  58. package/dist/index.esm.js.map +0 -1
  59. package/dist/index.mjs.map +0 -1
  60. package/dist/types/core.d.ts +0 -41
  61. package/dist/types/index.d.ts +0 -36
  62. package/dist/types/presets.d.ts +0 -15
  63. package/dist/types/react.d.ts +0 -29
  64. package/dist/types/sequence.d.ts +0 -38
  65. package/dist/types/stagger.d.ts +0 -25
  66. package/dist/types/vue.d.ts +0 -28
@@ -3,27 +3,38 @@
3
3
  * A lightweight, high-performance scroll animation library
4
4
  */
5
5
  /** Built-in animation presets */
6
- export type AnimationPreset = 'fade-in' | 'fade-in-up' | 'fade-in-down' | 'fade-in-left' | 'fade-in-right' | 'zoom-in' | 'zoom-out' | 'flip-x' | 'flip-y' | 'slide-up' | 'slide-down' | 'slide-left' | 'slide-right' | 'bounce' | 'rotate-in' | 'blur-in' | 'skew-in' | 'scale-x' | 'scale-y' | 'shimmer' | 'pulse' | 'swing' | 'scale-up' | 'blur-in-up' | 'flip-up' | 'flip-down' | 'rotate-left' | 'rotate-right' | 'clip-up' | 'clip-down' | 'clip-left' | 'clip-right' | 'clip-circle';
6
+ type AnimationPreset = 'fade-in' | 'fade-in-up' | 'fade-in-down' | 'fade-in-left' | 'fade-in-right' | 'zoom-in' | 'zoom-out' | 'flip-x' | 'flip-y' | 'slide-up' | 'slide-down' | 'slide-left' | 'slide-right' | 'bounce' | 'rotate-in' | 'blur-in' | 'skew-in' | 'scale-x' | 'scale-y' | 'shimmer' | 'pulse' | 'swing' | 'scale-up' | 'blur-in-up' | 'flip-up' | 'flip-down' | 'rotate-left' | 'rotate-right' | 'clip-up' | 'clip-down' | 'clip-left' | 'clip-right' | 'clip-circle';
7
7
  /**
8
8
  * How `onProgress` (and parallax) progress is measured.
9
9
  * - `'ratio'` (default): the element's visible ratio (IntersectionObserver `intersectionRatio`).
10
10
  * - `'scroll'`: true scroll progress, 0 when the element's top touches the bottom of the
11
11
  * viewport and 1 when its bottom leaves the top. Works for elements taller than the screen.
12
12
  */
13
- export type ProgressMode = 'ratio' | 'scroll';
13
+ type ProgressMode = 'ratio' | 'scroll';
14
+ /**
15
+ * Which engine runs the entrance animation.
16
+ * - `'js'`: IntersectionObserver triggers a time-based Web Animation.
17
+ * - `'css'`: the preset runs on the browser's native scroll-driven timeline
18
+ * (`animation-timeline: view()`), so its progress follows the scroll position
19
+ * off the main thread. Falls back to `'js'` where unsupported.
20
+ * - `'auto'` (default since 2.0): native when supported, JS otherwise — and JS
21
+ * whenever the element sets `duration`, `delay`, `offset` or `stagger` itself,
22
+ * since those only mean something for a time-based animation.
23
+ */
24
+ type ScrollEngine = 'auto' | 'js' | 'css';
14
25
  /** Easing function types */
15
- export type EasingType = 'linear' | 'ease' | 'ease-in' | 'ease-out' | 'ease-in-out' | 'spring' | 'soft-spring' | 'heavy-bounce' | [number, number, number, number] | ((t: number) => number) | string;
26
+ type EasingType = 'linear' | 'ease' | 'ease-in' | 'ease-out' | 'ease-in-out' | 'spring' | 'soft-spring' | 'heavy-bounce' | [number, number, number, number] | ((t: number) => number) | string;
16
27
  /** Keyframe definition for custom animations */
17
- export interface AnimationKeyframe {
28
+ interface AnimationKeyframe {
18
29
  [property: string]: string | number;
19
30
  }
20
31
  /** Custom animation definition */
21
- export interface CustomAnimation {
32
+ interface CustomAnimation {
22
33
  from: AnimationKeyframe;
23
34
  to: AnimationKeyframe;
24
35
  }
25
36
  /** Parallax configuration */
26
- export interface ParallaxOptions {
37
+ interface ParallaxOptions {
27
38
  /** Movement on X axis (e.g., '100px', '20%') */
28
39
  x?: string | number;
29
40
  /** Movement on Y axis (e.g., '100px', '20%') */
@@ -36,7 +47,7 @@ export interface ParallaxOptions {
36
47
  speed?: number;
37
48
  }
38
49
  /** Per-element animation options */
39
- export interface AnimateOptions {
50
+ interface AnimateOptions {
40
51
  /** Animation preset name, array of presets, or custom animation object */
41
52
  animation?: AnimationPreset | AnimationPreset[] | CustomAnimation;
42
53
  /** Duration in milliseconds (default: 600) */
@@ -77,9 +88,29 @@ export interface AnimateOptions {
77
88
  * style, for scroll-driven effects written in plain CSS. Off by default.
78
89
  */
79
90
  progressVar?: string;
91
+ /**
92
+ * Animation engine (default: `'auto'`, see `ScrollEngine`). With the native
93
+ * engine the animation is linked to scroll position: `duration`, `delay`,
94
+ * `threshold`, `offset` and `stagger` do not apply; `viewRange` does.
95
+ */
96
+ engine?: ScrollEngine;
97
+ /**
98
+ * Native engine only: the view-timeline range the entrance animation spans,
99
+ * as `[rangeStart, rangeEnd]` (default: `['entry 0%', 'entry 100%']`).
100
+ */
101
+ viewRange?: [string, string];
102
+ /**
103
+ * Animate out when the element leaves the viewport, and back in when it
104
+ * re-enters (implies `repeat: true` unless `repeat` is set).
105
+ * - `true`: play the entrance animation in reverse.
106
+ * - a preset / presets / `{ from, to }`: play that animation in reverse
107
+ * (e.g. `exit: 'fade-in-down'` leaves upwards).
108
+ * Skipped under reduced motion. (default: `false`)
109
+ */
110
+ exit?: boolean | AnimationPreset | AnimationPreset[] | CustomAnimation;
80
111
  }
81
112
  /** Global configuration for ScrollAnimate instance */
82
- export interface ScrollAnimateConfig {
113
+ interface ScrollAnimateConfig {
83
114
  /** Default animation preset (default: 'fade-in-up') */
84
115
  defaultAnimation?: AnimationPreset | AnimationPreset[] | CustomAnimation;
85
116
  /** Default duration in ms (default: 600) */
@@ -115,17 +146,21 @@ export interface ScrollAnimateConfig {
115
146
  * never re-hide or replay them. (default: true)
116
147
  */
117
148
  autoUnregister?: boolean;
149
+ /** Default animation engine (default: `'auto'`; `'js'` restores the 1.x behaviour) */
150
+ defaultEngine?: ScrollEngine;
118
151
  }
119
152
  /** Registered element entry */
120
- export interface AnimatedElement {
153
+ interface AnimatedElement {
121
154
  element: Element;
122
155
  options: Required<AnimateOptions>;
123
156
  observer: IntersectionObserver;
124
157
  animated: boolean;
125
158
  progressObserver?: IntersectionObserver;
159
+ /** Engine actually used for this element (`'css'` = native scroll-driven timeline) */
160
+ engine?: 'js' | 'css';
126
161
  }
127
162
  /** ScrollAnimate public API */
128
- export interface ScrollAnimateInstance {
163
+ interface ScrollAnimateInstance {
129
164
  /** Observe a single element or CSS selector */
130
165
  observe(target: string | Element | NodeList | Element[], options?: AnimateOptions): void;
131
166
  /** Stop observing a single element or CSS selector */
@@ -151,3 +186,31 @@ export interface ScrollAnimateInstance {
151
186
  /** Update global configuration */
152
187
  configure(config: Partial<ScrollAnimateConfig>): void;
153
188
  }
189
+
190
+ /**
191
+ * use-scroll-animate - `<scroll-animate>` Web Component
192
+ *
193
+ * ```html
194
+ * <script type="module">
195
+ * import { defineScrollAnimate } from 'use-scroll-animate/element';
196
+ * defineScrollAnimate(); // registers <scroll-animate>
197
+ * </script>
198
+ * <scroll-animate animation="fade-in-up" duration="800">…</scroll-animate>
199
+ * ```
200
+ *
201
+ * Attributes mirror the `data-sa-*` attributes without the prefix
202
+ * (`animation`, `duration`, `delay`, `easing`, `threshold`, `root-margin`,
203
+ * `offset`, `once`, `repeat`, `engine`, `view-range`, `progress`,
204
+ * `progress-var`, `exit`, `parallax-*`). The element dispatches `sa:enter`,
205
+ * `sa:leave`, `sa:start`, `sa:complete` and `sa:progress` (`detail.progress`)
206
+ * events. It renders as `display: block` unless styled otherwise.
207
+ */
208
+
209
+ /**
210
+ * Register the custom element (default tag `scroll-animate`) and return its
211
+ * class. Safe to call more than once and on the server (returns `undefined`
212
+ * without `customElements`).
213
+ */
214
+ declare function defineScrollAnimate(tagName?: string, instance?: ScrollAnimateInstance): CustomElementConstructor | undefined;
215
+
216
+ export { defineScrollAnimate };
@@ -0,0 +1,216 @@
1
+ /**
2
+ * use-scroll-animate - Core Type Definitions
3
+ * A lightweight, high-performance scroll animation library
4
+ */
5
+ /** Built-in animation presets */
6
+ type AnimationPreset = 'fade-in' | 'fade-in-up' | 'fade-in-down' | 'fade-in-left' | 'fade-in-right' | 'zoom-in' | 'zoom-out' | 'flip-x' | 'flip-y' | 'slide-up' | 'slide-down' | 'slide-left' | 'slide-right' | 'bounce' | 'rotate-in' | 'blur-in' | 'skew-in' | 'scale-x' | 'scale-y' | 'shimmer' | 'pulse' | 'swing' | 'scale-up' | 'blur-in-up' | 'flip-up' | 'flip-down' | 'rotate-left' | 'rotate-right' | 'clip-up' | 'clip-down' | 'clip-left' | 'clip-right' | 'clip-circle';
7
+ /**
8
+ * How `onProgress` (and parallax) progress is measured.
9
+ * - `'ratio'` (default): the element's visible ratio (IntersectionObserver `intersectionRatio`).
10
+ * - `'scroll'`: true scroll progress, 0 when the element's top touches the bottom of the
11
+ * viewport and 1 when its bottom leaves the top. Works for elements taller than the screen.
12
+ */
13
+ type ProgressMode = 'ratio' | 'scroll';
14
+ /**
15
+ * Which engine runs the entrance animation.
16
+ * - `'js'`: IntersectionObserver triggers a time-based Web Animation.
17
+ * - `'css'`: the preset runs on the browser's native scroll-driven timeline
18
+ * (`animation-timeline: view()`), so its progress follows the scroll position
19
+ * off the main thread. Falls back to `'js'` where unsupported.
20
+ * - `'auto'` (default since 2.0): native when supported, JS otherwise — and JS
21
+ * whenever the element sets `duration`, `delay`, `offset` or `stagger` itself,
22
+ * since those only mean something for a time-based animation.
23
+ */
24
+ type ScrollEngine = 'auto' | 'js' | 'css';
25
+ /** Easing function types */
26
+ type EasingType = 'linear' | 'ease' | 'ease-in' | 'ease-out' | 'ease-in-out' | 'spring' | 'soft-spring' | 'heavy-bounce' | [number, number, number, number] | ((t: number) => number) | string;
27
+ /** Keyframe definition for custom animations */
28
+ interface AnimationKeyframe {
29
+ [property: string]: string | number;
30
+ }
31
+ /** Custom animation definition */
32
+ interface CustomAnimation {
33
+ from: AnimationKeyframe;
34
+ to: AnimationKeyframe;
35
+ }
36
+ /** Parallax configuration */
37
+ interface ParallaxOptions {
38
+ /** Movement on X axis (e.g., '100px', '20%') */
39
+ x?: string | number;
40
+ /** Movement on Y axis (e.g., '100px', '20%') */
41
+ y?: string | number;
42
+ /** Rotation in degrees */
43
+ rotate?: number;
44
+ /** Scale factor */
45
+ scale?: number;
46
+ /** Speed multiplier (default: 1) */
47
+ speed?: number;
48
+ }
49
+ /** Per-element animation options */
50
+ interface AnimateOptions {
51
+ /** Animation preset name, array of presets, or custom animation object */
52
+ animation?: AnimationPreset | AnimationPreset[] | CustomAnimation;
53
+ /** Duration in milliseconds (default: 600) */
54
+ duration?: number;
55
+ /** Delay in milliseconds (default: 0) */
56
+ delay?: number;
57
+ /** Easing function (default: 'ease') */
58
+ easing?: EasingType;
59
+ /** Intersection threshold 0-1 (default: 0.1) */
60
+ threshold?: number | number[];
61
+ /** Root margin for IntersectionObserver (default: '0px') */
62
+ rootMargin?: string;
63
+ /** Whether to replay animation each time element enters viewport (default: false) */
64
+ repeat?: boolean;
65
+ /** Whether to trigger animation only once (default: true if repeat is false) */
66
+ once?: boolean;
67
+ /** Offset in pixels from the viewport edge to trigger animation (default: 0) */
68
+ offset?: number;
69
+ /** Stagger delay for child elements in ms (default: 0) */
70
+ stagger?: number;
71
+ /** Parallax effect configuration */
72
+ parallax?: ParallaxOptions;
73
+ /** Callback fired when animation starts */
74
+ onStart?: (element: Element) => void;
75
+ /** Callback fired when animation completes */
76
+ onComplete?: (element: Element) => void;
77
+ /** Callback fired when element enters viewport */
78
+ onEnter?: (element: Element) => void;
79
+ /** Callback fired when element leaves viewport */
80
+ onLeave?: (element: Element) => void;
81
+ /** Callback fired with scroll progress (0 to 1) */
82
+ onProgress?: (element: Element, progress: number) => void;
83
+ /** How progress for `onProgress`/parallax is measured (default: 'ratio') */
84
+ progressMode?: ProgressMode;
85
+ /**
86
+ * Name of a CSS custom property (e.g. `'--sa-progress'`) that receives the
87
+ * element's progress (0 to 1, same value as `onProgress`) as an inline
88
+ * style, for scroll-driven effects written in plain CSS. Off by default.
89
+ */
90
+ progressVar?: string;
91
+ /**
92
+ * Animation engine (default: `'auto'`, see `ScrollEngine`). With the native
93
+ * engine the animation is linked to scroll position: `duration`, `delay`,
94
+ * `threshold`, `offset` and `stagger` do not apply; `viewRange` does.
95
+ */
96
+ engine?: ScrollEngine;
97
+ /**
98
+ * Native engine only: the view-timeline range the entrance animation spans,
99
+ * as `[rangeStart, rangeEnd]` (default: `['entry 0%', 'entry 100%']`).
100
+ */
101
+ viewRange?: [string, string];
102
+ /**
103
+ * Animate out when the element leaves the viewport, and back in when it
104
+ * re-enters (implies `repeat: true` unless `repeat` is set).
105
+ * - `true`: play the entrance animation in reverse.
106
+ * - a preset / presets / `{ from, to }`: play that animation in reverse
107
+ * (e.g. `exit: 'fade-in-down'` leaves upwards).
108
+ * Skipped under reduced motion. (default: `false`)
109
+ */
110
+ exit?: boolean | AnimationPreset | AnimationPreset[] | CustomAnimation;
111
+ }
112
+ /** Global configuration for ScrollAnimate instance */
113
+ interface ScrollAnimateConfig {
114
+ /** Default animation preset (default: 'fade-in-up') */
115
+ defaultAnimation?: AnimationPreset | AnimationPreset[] | CustomAnimation;
116
+ /** Default duration in ms (default: 600) */
117
+ defaultDuration?: number;
118
+ /** Default delay in ms (default: 0) */
119
+ defaultDelay?: number;
120
+ /** Default easing (default: 'ease') */
121
+ defaultEasing?: EasingType;
122
+ /** Default threshold (default: 0.1) */
123
+ defaultThreshold?: number | number[];
124
+ /** Default root margin (default: '0px') */
125
+ defaultRootMargin?: string;
126
+ /** Whether animations replay by default (default: false) */
127
+ defaultRepeat?: boolean;
128
+ /** Default once setting (default: true) */
129
+ defaultOnce?: boolean;
130
+ /** Default offset in pixels (default: 0) */
131
+ defaultOffset?: number;
132
+ /** CSS class added before animation (default: 'sa-hidden') */
133
+ hiddenClass?: string;
134
+ /** CSS class added when element is visible (default: 'sa-visible') */
135
+ visibleClass?: string;
136
+ /** Whether to use CSS class-based animation instead of Web Animations API */
137
+ useClassNames?: boolean;
138
+ /** Disable all animations (useful for reduced-motion preference) */
139
+ disabled?: boolean;
140
+ /** Custom IntersectionObserver root element */
141
+ root?: Element | null;
142
+ /**
143
+ * Drop `once` elements from the registry as soon as their entrance animation
144
+ * has been triggered (unless they still need parallax/onProgress), so they can
145
+ * be garbage-collected. They are remembered in a WeakSet, so `init()`/`observe()`
146
+ * never re-hide or replay them. (default: true)
147
+ */
148
+ autoUnregister?: boolean;
149
+ /** Default animation engine (default: `'auto'`; `'js'` restores the 1.x behaviour) */
150
+ defaultEngine?: ScrollEngine;
151
+ }
152
+ /** Registered element entry */
153
+ interface AnimatedElement {
154
+ element: Element;
155
+ options: Required<AnimateOptions>;
156
+ observer: IntersectionObserver;
157
+ animated: boolean;
158
+ progressObserver?: IntersectionObserver;
159
+ /** Engine actually used for this element (`'css'` = native scroll-driven timeline) */
160
+ engine?: 'js' | 'css';
161
+ }
162
+ /** ScrollAnimate public API */
163
+ interface ScrollAnimateInstance {
164
+ /** Observe a single element or CSS selector */
165
+ observe(target: string | Element | NodeList | Element[], options?: AnimateOptions): void;
166
+ /** Stop observing a single element or CSS selector */
167
+ unobserve(target: string | Element | NodeList | Element[]): void;
168
+ /** Observe all elements matching the data-sa attribute */
169
+ init(rootElement?: Element | Document): void;
170
+ /**
171
+ * Like `init()`, then keep watching `rootElement` (default: `document`) with a
172
+ * MutationObserver: `[data-sa]` elements added later (or that gain the
173
+ * attribute) are observed automatically, and removed ones are released.
174
+ * Returns a function that stops watching. `destroy()` stops every watcher.
175
+ * SSR-safe: a no-op without a DOM / MutationObserver.
176
+ */
177
+ watch(rootElement?: Element | Document): () => void;
178
+ /** Destroy the instance and clean up all observers */
179
+ destroy(): void;
180
+ /** Refresh all observers (useful after DOM changes) */
181
+ refresh(): void;
182
+ /** Manually trigger animation on an element */
183
+ animate(target: string | Element, options?: AnimateOptions): void;
184
+ /** Get all currently observed elements */
185
+ getObservedElements(): AnimatedElement[];
186
+ /** Update global configuration */
187
+ configure(config: Partial<ScrollAnimateConfig>): void;
188
+ }
189
+
190
+ /**
191
+ * use-scroll-animate - `<scroll-animate>` Web Component
192
+ *
193
+ * ```html
194
+ * <script type="module">
195
+ * import { defineScrollAnimate } from 'use-scroll-animate/element';
196
+ * defineScrollAnimate(); // registers <scroll-animate>
197
+ * </script>
198
+ * <scroll-animate animation="fade-in-up" duration="800">…</scroll-animate>
199
+ * ```
200
+ *
201
+ * Attributes mirror the `data-sa-*` attributes without the prefix
202
+ * (`animation`, `duration`, `delay`, `easing`, `threshold`, `root-margin`,
203
+ * `offset`, `once`, `repeat`, `engine`, `view-range`, `progress`,
204
+ * `progress-var`, `exit`, `parallax-*`). The element dispatches `sa:enter`,
205
+ * `sa:leave`, `sa:start`, `sa:complete` and `sa:progress` (`detail.progress`)
206
+ * events. It renders as `display: block` unless styled otherwise.
207
+ */
208
+
209
+ /**
210
+ * Register the custom element (default tag `scroll-animate`) and return its
211
+ * class. Safe to call more than once and on the server (returns `undefined`
212
+ * without `customElements`).
213
+ */
214
+ declare function defineScrollAnimate(tagName?: string, instance?: ScrollAnimateInstance): CustomElementConstructor | undefined;
215
+
216
+ export { defineScrollAnimate };
@@ -0,0 +1,95 @@
1
+ import { i as readOptions, c as createScrollAnimate } from './chunks/core-BP-a1iNc.js';
2
+
3
+ /**
4
+ * use-scroll-animate - `<scroll-animate>` Web Component
5
+ *
6
+ * ```html
7
+ * <script type="module">
8
+ * import { defineScrollAnimate } from 'use-scroll-animate/element';
9
+ * defineScrollAnimate(); // registers <scroll-animate>
10
+ * </script>
11
+ * <scroll-animate animation="fade-in-up" duration="800">…</scroll-animate>
12
+ * ```
13
+ *
14
+ * Attributes mirror the `data-sa-*` attributes without the prefix
15
+ * (`animation`, `duration`, `delay`, `easing`, `threshold`, `root-margin`,
16
+ * `offset`, `once`, `repeat`, `engine`, `view-range`, `progress`,
17
+ * `progress-var`, `exit`, `parallax-*`). The element dispatches `sa:enter`,
18
+ * `sa:leave`, `sa:start`, `sa:complete` and `sa:progress` (`detail.progress`)
19
+ * events. It renders as `display: block` unless styled otherwise.
20
+ */
21
+ const ATTRIBUTES = [
22
+ 'animation', 'duration', 'delay', 'easing', 'threshold', 'root-margin', 'offset', 'once', 'repeat',
23
+ 'stagger', 'engine', 'view-range', 'progress', 'progress-var', 'exit',
24
+ 'parallax-x', 'parallax-y', 'parallax-rotate', 'parallax-scale', 'parallax-speed',
25
+ ];
26
+ let shared = null;
27
+ /**
28
+ * Register the custom element (default tag `scroll-animate`) and return its
29
+ * class. Safe to call more than once and on the server (returns `undefined`
30
+ * without `customElements`).
31
+ */
32
+ function defineScrollAnimate(tagName = 'scroll-animate', instance) {
33
+ if (typeof customElements === 'undefined' || typeof HTMLElement === 'undefined')
34
+ return undefined;
35
+ const existing = customElements.get(tagName);
36
+ if (existing)
37
+ return existing;
38
+ const getInstance = () => instance || shared || (shared = createScrollAnimate());
39
+ class ScrollAnimateElement extends HTMLElement {
40
+ constructor() {
41
+ super(...arguments);
42
+ this.observing = false;
43
+ }
44
+ static get observedAttributes() {
45
+ return ATTRIBUTES;
46
+ }
47
+ emit(type, detail) {
48
+ this.dispatchEvent(new CustomEvent(`sa:${type}`, { detail }));
49
+ }
50
+ /** Options parsed from the element's attributes (plus event callbacks). */
51
+ get options() {
52
+ const opts = readOptions((name) => {
53
+ const v = this.getAttribute(name);
54
+ return v === null ? undefined : v;
55
+ });
56
+ return {
57
+ ...opts,
58
+ onEnter: () => this.emit('enter'),
59
+ onLeave: () => this.emit('leave'),
60
+ onStart: () => this.emit('start'),
61
+ onComplete: () => this.emit('complete'),
62
+ ...(this.hasAttribute('progress') || this.hasAttribute('progress-var')
63
+ ? { onProgress: (_el, progress) => this.emit('progress', { progress }) }
64
+ : {}),
65
+ };
66
+ }
67
+ connectedCallback() {
68
+ if (!this.style.display && typeof getComputedStyle === 'function' && (getComputedStyle(this).display || 'inline') === 'inline') {
69
+ this.style.display = 'block';
70
+ }
71
+ getInstance().observe(this, this.options);
72
+ this.observing = true;
73
+ }
74
+ disconnectedCallback() {
75
+ getInstance().unobserve(this);
76
+ this.observing = false;
77
+ }
78
+ attributeChangedCallback() {
79
+ if (!this.observing)
80
+ return;
81
+ const sa = getInstance();
82
+ const record = sa.getObservedElements().find((r) => r.element === this);
83
+ // Already animated: keep it as it is (avoids re-hiding visible content).
84
+ if (record && !record.animated) {
85
+ sa.unobserve(this);
86
+ sa.observe(this, this.options);
87
+ }
88
+ }
89
+ }
90
+ customElements.define(tagName, ScrollAnimateElement);
91
+ return ScrollAnimateElement;
92
+ }
93
+
94
+ export { defineScrollAnimate };
95
+ //# sourceMappingURL=element.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"element.js","sources":["../src/element.ts"],"sourcesContent":["/**\n * use-scroll-animate - `<scroll-animate>` Web Component\n *\n * ```html\n * <script type=\"module\">\n * import { defineScrollAnimate } from 'use-scroll-animate/element';\n * defineScrollAnimate(); // registers <scroll-animate>\n * </script>\n * <scroll-animate animation=\"fade-in-up\" duration=\"800\">…</scroll-animate>\n * ```\n *\n * Attributes mirror the `data-sa-*` attributes without the prefix\n * (`animation`, `duration`, `delay`, `easing`, `threshold`, `root-margin`,\n * `offset`, `once`, `repeat`, `engine`, `view-range`, `progress`,\n * `progress-var`, `exit`, `parallax-*`). The element dispatches `sa:enter`,\n * `sa:leave`, `sa:start`, `sa:complete` and `sa:progress` (`detail.progress`)\n * events. It renders as `display: block` unless styled otherwise.\n */\n\nimport type { AnimateOptions, ScrollAnimateInstance } from './types';\nimport { createScrollAnimate, readOptions } from './core';\n\nconst ATTRIBUTES = [\n 'animation', 'duration', 'delay', 'easing', 'threshold', 'root-margin', 'offset', 'once', 'repeat',\n 'stagger', 'engine', 'view-range', 'progress', 'progress-var', 'exit',\n 'parallax-x', 'parallax-y', 'parallax-rotate', 'parallax-scale', 'parallax-speed',\n];\n\nlet shared: ScrollAnimateInstance | null = null;\n\n/**\n * Register the custom element (default tag `scroll-animate`) and return its\n * class. Safe to call more than once and on the server (returns `undefined`\n * without `customElements`).\n */\nexport function defineScrollAnimate(\n tagName = 'scroll-animate',\n instance?: ScrollAnimateInstance\n): CustomElementConstructor | undefined {\n if (typeof customElements === 'undefined' || typeof HTMLElement === 'undefined') return undefined;\n const existing = customElements.get(tagName);\n if (existing) return existing;\n const getInstance = () => instance || shared || (shared = createScrollAnimate());\n\n class ScrollAnimateElement extends HTMLElement {\n static get observedAttributes(): string[] {\n return ATTRIBUTES;\n }\n\n private observing = false;\n\n private emit(type: string, detail?: unknown): void {\n this.dispatchEvent(new CustomEvent(`sa:${type}`, { detail }));\n }\n\n /** Options parsed from the element's attributes (plus event callbacks). */\n get options(): AnimateOptions {\n const opts = readOptions((name) => {\n const v = this.getAttribute(name);\n return v === null ? undefined : v;\n });\n return {\n ...opts,\n onEnter: () => this.emit('enter'),\n onLeave: () => this.emit('leave'),\n onStart: () => this.emit('start'),\n onComplete: () => this.emit('complete'),\n ...(this.hasAttribute('progress') || this.hasAttribute('progress-var')\n ? { onProgress: (_el: Element, progress: number) => this.emit('progress', { progress }) }\n : {}),\n };\n }\n\n connectedCallback(): void {\n if (!this.style.display && typeof getComputedStyle === 'function' && (getComputedStyle(this).display || 'inline') === 'inline') {\n this.style.display = 'block';\n }\n getInstance().observe(this, this.options);\n this.observing = true;\n }\n\n disconnectedCallback(): void {\n getInstance().unobserve(this);\n this.observing = false;\n }\n\n attributeChangedCallback(): void {\n if (!this.observing) return;\n const sa = getInstance();\n const record = sa.getObservedElements().find((r) => r.element === this);\n // Already animated: keep it as it is (avoids re-hiding visible content).\n if (record && !record.animated) {\n sa.unobserve(this);\n sa.observe(this, this.options);\n }\n }\n }\n\n customElements.define(tagName, ScrollAnimateElement);\n return ScrollAnimateElement;\n}\n"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ !function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).ScrollAnimateElement={})}(this,function(e){"use strict";const t={"fade-in":{from:{opacity:0},to:{opacity:1}},"fade-in-up":{from:{opacity:0,transform:"translateY(40px)"},to:{opacity:1,transform:"translateY(0px)"}},"fade-in-down":{from:{opacity:0,transform:"translateY(-40px)"},to:{opacity:1,transform:"translateY(0px)"}},"fade-in-left":{from:{opacity:0,transform:"translateX(-40px)"},to:{opacity:1,transform:"translateX(0px)"}},"fade-in-right":{from:{opacity:0,transform:"translateX(40px)"},to:{opacity:1,transform:"translateX(0px)"}},"zoom-in":{from:{opacity:0,transform:"scale(0.8)"},to:{opacity:1,transform:"scale(1)"}},"zoom-out":{from:{opacity:0,transform:"scale(1.2)"},to:{opacity:1,transform:"scale(1)"}},"flip-x":{from:{opacity:0,transform:"rotateX(-90deg)"},to:{opacity:1,transform:"rotateX(0deg)"}},"flip-y":{from:{opacity:0,transform:"rotateY(-90deg)"},to:{opacity:1,transform:"rotateY(0deg)"}},"slide-up":{from:{transform:"translateY(100%)"},to:{transform:"translateY(0px)"}},"slide-down":{from:{transform:"translateY(-100%)"},to:{transform:"translateY(0px)"}},"slide-left":{from:{transform:"translateX(-100%)"},to:{transform:"translateX(0px)"}},"slide-right":{from:{transform:"translateX(100%)"},to:{transform:"translateX(0px)"}},bounce:{from:{opacity:0,transform:"translateY(-60px)"},to:{opacity:1,transform:"translateY(0px)"}},"rotate-in":{from:{opacity:0,transform:"rotate(-180deg) scale(0.5)"},to:{opacity:1,transform:"rotate(0deg) scale(1)"}},"blur-in":{from:{opacity:0,filter:"blur(12px)"},to:{opacity:1,filter:"blur(0px)"}},"skew-in":{from:{opacity:0,transform:"skewX(20deg) translateX(30px)"},to:{opacity:1,transform:"skewX(0deg) translateX(0px)"}},"scale-x":{from:{transform:"scaleX(0)"},to:{transform:"scaleX(1)"}},"scale-y":{from:{transform:"scaleY(0)"},to:{transform:"scaleY(1)"}},shimmer:{from:{opacity:.5,filter:"brightness(1)"},to:{opacity:1,filter:"brightness(1.5)"}},pulse:{from:{transform:"scale(1)"},to:{transform:"scale(1.05)"}},swing:{from:{transform:"rotate(-10deg)"},to:{transform:"rotate(10deg)"}},"scale-up":{from:{opacity:0,transform:"scale(0.5)"},to:{opacity:1,transform:"scale(1)"}},"blur-in-up":{from:{opacity:0,filter:"blur(12px)",transform:"translateY(40px)"},to:{opacity:1,filter:"blur(0px)",transform:"translateY(0px)"}},"flip-up":{from:{opacity:0,transform:"perspective(800px) rotateX(60deg)"},to:{opacity:1,transform:"perspective(800px) rotateX(0deg)"}},"flip-down":{from:{opacity:0,transform:"perspective(800px) rotateX(-60deg)"},to:{opacity:1,transform:"perspective(800px) rotateX(0deg)"}},"rotate-left":{from:{opacity:0,transform:"rotate(-15deg) translateX(-40px)"},to:{opacity:1,transform:"rotate(0deg) translateX(0px)"}},"rotate-right":{from:{opacity:0,transform:"rotate(15deg) translateX(40px)"},to:{opacity:1,transform:"rotate(0deg) translateX(0px)"}},"clip-up":{from:{clipPath:"inset(100% 0% 0% 0%)"},to:{clipPath:"inset(0% 0% 0% 0%)"}},"clip-down":{from:{clipPath:"inset(0% 0% 100% 0%)"},to:{clipPath:"inset(0% 0% 0% 0%)"}},"clip-left":{from:{clipPath:"inset(0% 0% 0% 100%)"},to:{clipPath:"inset(0% 0% 0% 0%)"}},"clip-right":{from:{clipPath:"inset(0% 100% 0% 0%)"},to:{clipPath:"inset(0% 0% 0% 0%)"}},"clip-circle":{from:{clipPath:"circle(0% at 50% 50%)"},to:{clipPath:"circle(75% at 50% 50%)"}}};function n(e){if("string"==typeof e)return t[e.trim()]??t["fade-in-up"];if(Array.isArray(e)){const n={from:{},to:{}};return e.forEach(e=>{const r=t[e.trim()];r&&(Object.entries(r.from).forEach(([e,t])=>{"transform"===e&&n.from[e]?n.from[e]=`${n.from[e]} ${t}`:n.from[e]=t}),Object.entries(r.to).forEach(([e,t])=>{"transform"===e&&n.to[e]?n.to[e]=`${n.to[e]} ${t}`:n.to[e]=t}))}),n}return e}const r={linear:"linear",ease:"ease","ease-in":"ease-in","ease-out":"ease-out","ease-in-out":"ease-in-out",spring:"cubic-bezier(0.34, 1.56, 0.64, 1)","soft-spring":"cubic-bezier(0.175, 0.885, 0.32, 1.275)","heavy-bounce":"cubic-bezier(0.68, -0.55, 0.265, 1.55)"};function o(e){return"string"==typeof e?r[e]??e:Array.isArray(e)?`cubic-bezier(${e.join(", ")})`:"function"==typeof e?"linear":"ease"}const a={defaultAnimation:"fade-in-up",defaultDuration:600,defaultDelay:0,defaultEasing:"ease",defaultThreshold:.1,defaultRootMargin:"0px",defaultRepeat:!1,defaultOnce:!0,defaultOffset:0,hiddenClass:"sa-hidden",visibleClass:"sa-visible",useClassNames:!1,disabled:!1,root:null,autoUnregister:!0,defaultEngine:"auto"},s=()=>{},i=()=>"undefined"!=typeof window&&"undefined"!=typeof document,c=()=>i()&&"undefined"!=typeof IntersectionObserver;let l,f;function d(e){if(void 0===e||""===e.trim())return;const t=parseFloat(e);return Number.isFinite(t)?t:void 0}function p(e){if(void 0===e||""===e.trim())return;const t=e.trim();return/^-?(\d+\.?\d*|\.\d+)$/.test(t)?parseFloat(t):t}const u=["entry 0%","entry 100%"];function m(e){const t=e.trim();return t?t.startsWith("--")?t:`--${t}`:"--sa-progress"}function g(e){const t={},n=e("animation");n&&(t.animation=n.includes(",")?n.split(",").map(e=>e.trim()):n.trim()),t.duration=d(e("duration")),t.delay=d(e("delay"));const r=e("easing");if(r){const e=r.trim();if(e.startsWith("["))try{t.easing=JSON.parse(e)}catch{}else t.easing=e}const o=e("threshold");o&&(t.threshold=o.includes(",")?o.split(",").map(parseFloat).filter(Number.isFinite):d(o));const a=e("root-margin");a&&(t.rootMargin=a);const s=e("repeat");void 0!==s&&(t.repeat="false"!==s);const i=e("once");void 0!==i&&(t.once="false"!==i),t.offset=d(e("offset")),t.stagger=d(e("stagger"));const c=e("progress-var");void 0!==c&&(t.progressVar=m(c));const l=e("engine")?.trim();"auto"!==l&&"js"!==l&&"css"!==l||(t.engine=l);const f=e("view-range");if(f){const[e,n]=f.split(",").map(e=>e.trim());e&&n&&(t.viewRange=[e,n])}const u=e("exit");if(void 0!==u){const e=u.trim();t.exit=""===e||"true"===e||"false"!==e&&(e.includes(",")?e.split(",").map(e=>e.trim()):e)}const g=e("progress");g&&(t.progressMode="scroll"===g.trim()?"scroll":"ratio");const h=e("parallax-x"),y=e("parallax-y"),v=e("parallax-rotate"),b=e("parallax-scale");return(h||y||v||b)&&(t.parallax={x:p(h),y:p(y),rotate:d(v),scale:d(b),speed:d(e("parallax-speed"))??1}),Object.keys(t).forEach(e=>void 0===t[e]&&delete t[e]),t}function h(e,t){let n=e.engine??t.defaultEngine;"auto"===n&&(void 0!==e.duration||void 0!==e.delay||void 0!==e.offset||(e.stagger??0)>0)&&(n="js");const r=e.exit??!1,o=e.repeat??(!!r||t.defaultRepeat),a=e.threshold??t.defaultThreshold;return{animation:e.animation??t.defaultAnimation,duration:e.duration??t.defaultDuration,delay:e.delay??t.defaultDelay,easing:e.easing??t.defaultEasing,threshold:Array.isArray(a)&&0===a.length?t.defaultThreshold:a,rootMargin:e.rootMargin??t.defaultRootMargin,repeat:o,once:e.once??(!o&&t.defaultOnce),offset:e.offset??t.defaultOffset,stagger:e.stagger??0,parallax:e.parallax??{},onStart:e.onStart??s,onComplete:e.onComplete??s,onEnter:e.onEnter??s,onLeave:e.onLeave??s,onProgress:e.onProgress??s,progressMode:e.progressMode??"ratio",progressVar:e.progressVar?m(e.progressVar):"",engine:n,viewRange:e.viewRange??u,exit:r}}function y(e){return e&&i()?"string"==typeof e?Array.from(document.querySelectorAll(e)):e instanceof Element?[e]:Array.isArray(e)||e instanceof NodeList?Array.from(e).filter(e=>e instanceof Element):[]:[]}function v(e){return!!e&&Object.keys(e).some(t=>void 0!==e[t])}function b(e,t){if(!i())return 0;const n=e.getBoundingClientRect();let r=0,o=window.innerHeight||document.documentElement.clientHeight||0;if(t){const e=t.getBoundingClientRect();r=e.top,o=e.height}const a=o+n.height;if(a<=0)return 0;const s=(r+o-n.top)/a;return s<0?0:s>1?1:s}const x=new WeakMap,E=new WeakMap,w=new WeakMap;function S(e){const t=E.get(e);t&&(E.delete(e),t.cancel());const n=x.get(e);n&&(x.delete(e),n.onfinish=null,n.cancel());const r=w.get(e);void 0!==r&&(clearTimeout(r),w.delete(e))}function C(e,t){const n=e.style;n&&Object.keys(t).forEach(e=>{n[e]=String(t[e])})}const M=/(-?(?:\d+\.?\d*|\.\d+)(?:e[+-]?\d+)?)([a-z%]*)/gi;let k,$;function A(e,t){if("function"!=typeof t)return{keyframes:[e.from,e.to],easing:o(t)};const n=function(e){const t=[];for(let n=0;n<=30;n++){const r=e(n/30);t.push(Number.isFinite(r)?+r.toFixed(4):n/30)}return t}(t);if(void 0===k&&(k="undefined"!=typeof CSS&&"function"==typeof CSS.supports&&CSS.supports("animation-timing-function","linear(0, 1)")),k)return{keyframes:[e.from,e.to],easing:`linear(${n.join(", ")})`};const r=Object.keys(e.from).filter(t=>t in e.to);return{keyframes:n.map((t,n)=>{const o={offset:n/30};return r.forEach(n=>{o[n]=function(e,t,n){if("number"==typeof e&&"number"==typeof t)return e+(t-e)*n;const r=String(e),o=String(t),a=[],s=[],i=r.replace(M,(e,t,n)=>(a.push([parseFloat(t),n]),"#"));if(i!==o.replace(M,(e,t,n)=>(s.push([parseFloat(t),n]),"#"))||a.length!==s.length)return n<.5?e:t;let c=0,l=!0;const f=i.replace(/#/g,()=>{const[e,t]=a[c],[r,o]=s[c++];t!==o&&0!==e&&0!==r&&t&&o&&(l=!1);const i=t||o;return`${+(e+(r-e)*n).toFixed(4)}${i}`});return l?f:n<.5?e:t}(e.from[n],e.to[n],t)}),o}),easing:"linear"}}function X(e,t){if(S(e),t.useClassNames)e.classList.remove(t.hiddenClass),e.classList.add(t.visibleClass);else{const t=e.style;t&&(t.opacity="")}}function O(e){return!!e.disabled||!!i()&&(void 0===l&&(l="function"==typeof window.matchMedia?window.matchMedia("(prefers-reduced-motion: reduce)"):null),!!l&&l.matches)}function L(e,t,r,o=0,a){const{duration:s,delay:i,stagger:c,onStart:l,onComplete:f}=t,d=Math.max(0,i+o*c);if(S(e),O(r))return X(e,r),l(e),void f(e);if(r.useClassNames)return d>0&&(e.style.animationDelay=`${d}ms`),e.classList.remove(r.hiddenClass),e.classList.add(r.visibleClass),l(e),a?.add(e),void w.set(e,setTimeout(()=>{w.delete(e),a?.delete(e),f(e)},s+d));const p=n(t.animation);if(!("opacity"in p.to)){const t=e.style;t&&"0"===t.opacity&&(t.opacity="")}if("function"!=typeof e.animate)return C(e,p.to),l(e),void f(e);const u=A(p,t.easing),m={duration:s,delay:d,easing:u.easing,fill:"both"};let g;try{g=e.animate(u.keyframes,m)}catch{g=e.animate(u.keyframes,{...m,easing:"ease"})}x.set(e,g),l(e),g.onfinish=()=>{if(x.get(e)===g){x.delete(e);try{g.commitStyles()}catch{C(e,p.to)}g.cancel()}f(e)}}function Y(e,t){if(S(e),t.useClassNames)e.classList.add(t.hiddenClass),e.classList.remove(t.visibleClass);else{const t=e.style;t&&(t.opacity="0")}}function j(){if(!$){$=[];for(let e=0;e<=100;e++)$.push(e/100)}return $}const P={passive:!0};function T(e={}){let t={...a,...e};const r=new Map;let o=new WeakSet;const l=new Set;let d=0,p=null;const u=new Set,m=new Set,M=new Set,k=new Map;function $(e,t,n){let r=k.get(e);r||k.set(e,r=new Map);let o=r.get(t);return o||r.set(t,o=n()),o}function T(e,n){const o=r.get(e);o&&(o.observer.unobserve(e),o.progressObserver?.unobserve(e),r.delete(e),I(e),"css"!==o.engine?n&&!o.animated&&X(e,t):n?(M.delete(e),X(e,t)):!1===e.isConnected&&M.delete(e))}function F(){r.forEach((e,t)=>{!1===t.isConnected&&T(t,!1)})}function N(e,n,r){const o=n.options;if(o.onProgress(e,r),o.progressVar){const t=e.style;t&&t.setProperty(o.progressVar,String(+r.toFixed(4)))}v(o.parallax)&&!O(t)&&function(e,t,n){const{x:r=0,y:o=0,rotate:a=0,scale:s=1,speed:i=1}=n,c=2*(t-.5)*i,l=e=>"number"==typeof e?e*c+"px":`calc(${e} * ${c})`;let f="";r&&(f+=` translateX(${l(r)})`),o&&(f+=` translateY(${l(o)})`),a&&(f+=` rotate(${a*c}deg)`),1!==s&&(f+=` scale(${1+(s-1)*c})`),e.style.transform=f.trim()}(e,r,o.parallax)}function R(){d=0,l.forEach(e=>{const n=r.get(e);n&&N(e,n,b(e,t.root))})}const V=()=>d||(d=requestAnimationFrame(R));function z(e){if(e===!!p)return;const n=e?"addEventListener":"removeEventListener",r=p||t.root||window;r[n]("scroll",V,P),window[n]("resize",V,P),p=e?r:null,!e&&d&&(cancelAnimationFrame(d),d=0)}function I(e){l.delete(e)&&!l.size&&z(!1)}function W(e){e.forEach(e=>{const n=e.target,o=r.get(n);o&&(e.isIntersecting?(l.add(n),z(!0)):I(n),N(n,o,b(n,t.root)))})}function D(e){const a=new Map;e.forEach(e=>{const s=e.target,i=r.get(s);if(!i)return;const c=i.options;if(!1!==s.isConnected)if("css"!==i.engine)if(e.isIntersecting){if(c.onEnter(s),i.animated&&!c.repeat)return;let e=0;if(c.stagger>0){const t=s.parentElement;e=a.get(t)??0,a.set(t,e+1)}L(s,c,t,e,m),i.animated=!0,c.once&&!c.repeat&&(i.observer.unobserve(s),t.autoUnregister&&!i.progressObserver&&(o.add(s),T(s,!1)))}else c.onLeave(s),c.repeat&&i.animated&&(O(t)||(c.exit?function(e,t,r){if(r.useClassNames||"function"!=typeof e.animate)return void Y(e,r);S(e);const o=n(!0===t.exit?t.animation:t.exit),a=A({from:o.to,to:o.from},t.easing),s={duration:t.duration,easing:a.easing,fill:"forwards"};let i;try{i=e.animate(a.keyframes,s)}catch{i=e.animate(a.keyframes,{...s,easing:"ease"})}x.set(e,i)}(s,c,t):Y(s,t)),i.animated=!1);else e.isIntersecting?(c.onEnter(s),i.animated||(i.animated=!0,c.onStart(s)),c.once&&!c.repeat&&(i.observer.unobserve(s),t.autoUnregister&&!i.progressObserver&&(o.add(s),T(s,!1)))):(c.onLeave(s),c.repeat&&(i.animated=!1));else T(s,!1)})}function q(e){e.forEach(e=>{const t=r.get(e.target);t&&N(e.target,t,e.intersectionRatio)})}function H(e){const n=function(e,t){if(!t)return e;const n=(e||"0px").trim().split(/\s+/),r=n[0],o=n[1]??r,a=n[2]??r,s=n[3]??o,i=/^(-?\d*\.?\d+)(px)?$/.exec(a);return`${r} ${o} ${(i?parseFloat(i[1]):0)-t}px ${s}`}(e.rootMargin,e.offset),r=e.threshold,o=t.root;return $(o,`m|${n}|${String(r)}`,()=>new IntersectionObserver(D,{threshold:r,rootMargin:n,root:o}))}function U(e,n,r){var o;n.observer=H(n.options),r&&n.observer.observe(e),n.progressObserver=v((o=n.options).parallax)||o.onProgress!==s||o.progressVar?function(e){const n=t.root;return"scroll"===e.progressMode?$(n,`s|${e.rootMargin}`,()=>new IntersectionObserver(W,{threshold:0,rootMargin:e.rootMargin,root:n})):$(n,`p|${e.rootMargin}`,()=>new IntersectionObserver(q,{threshold:j(),rootMargin:e.rootMargin,root:n}))}(n.options):void 0,n.progressObserver?.observe(e)}function B(e){return"js"!==e.engine&&!t.useClassNames&&!O(t)&&(void 0===f&&(f=i()&&"undefined"!=typeof CSS&&"function"==typeof CSS.supports&&CSS.supports("animation-timeline: view()")&&"function"==typeof globalThis.ViewTimeline),f)}function J(e,a){if(r.has(e)||o.has(e))return;if(!c())return void X(e,t);const s={element:e,options:a,animated:!1,engine:"js"};B(a)&&(S(e),function(e,t,r){if("function"!=typeof e.animate)return null;const o=n(t.animation),a=A(o,t.easing);let s;try{const n=new globalThis.ViewTimeline({subject:e,axis:"block"}),r={fill:"both",easing:a.easing,timeline:n,rangeStart:t.viewRange[0],rangeEnd:t.viewRange[1]};try{s=e.animate(a.keyframes,r)}catch{s=e.animate(a.keyframes,{...r,easing:"linear"})}}catch{return null}if(x.set(e,s),t.exit){const r=n(!0===t.exit?t.animation:t.exit),o=A({from:r.to,to:r.from},t.easing);try{const t=new globalThis.ViewTimeline({subject:e,axis:"block"}),n={fill:"forwards",easing:o.easing,timeline:t,rangeStart:"exit 0%",rangeEnd:"exit 100%"};E.set(e,e.animate(o.keyframes,n))}catch{}}const i=t.repeat||!t.once;return s.onfinish=()=>{if(!i&&x.get(e)===s){x.delete(e);try{s.commitStyles()}catch{C(e,o.to)}s.cancel(),r?.()}t.onComplete(e)},s}(e,a,()=>M.delete(e))&&(s.engine="css",M.add(e))),"js"!==s.engine||O(t)||Y(e,t),r.set(e,s),U(e,s,!0)}function G(e){r.has(e)||J(e,function(e,t){return h(g(t=>{const n=e.getAttribute(`data-sa-${t}`);return null===n?void 0:n}),t)}(e,t))}const K={observe(e,n={}){F();const r=h(n,t);y(e).forEach(e=>J(e,r))},unobserve(e){y(e).forEach(e=>{o.delete(e),T(e,!0)})},init(e){const t=e??(i()?document:null);t&&(F(),t.querySelectorAll("[data-sa]").forEach(G))},watch(e){const t=e??(i()?document:null);if(!t||"undefined"==typeof MutationObserver)return s;K.init(t);const n=e=>{e.hasAttribute("data-sa")&&G(e),e.querySelectorAll("[data-sa]").forEach(G)},r=new MutationObserver(e=>{let t=!1;e.forEach(e=>{if("attributes"===e.type){const t=e.target;return void(!1!==t.isConnected&&n(t))}e.addedNodes.forEach(e=>{e instanceof Element&&!1!==e.isConnected&&n(e)}),e.removedNodes.length&&(t=!0)}),t&&F()});return r.observe(t,{childList:!0,subtree:!0,attributes:!0,attributeFilter:["data-sa"]}),u.add(r),()=>{r.disconnect(),u.delete(r)}},destroy(){u.forEach(e=>e.disconnect()),u.clear(),m.forEach(e=>{const t=w.get(e);void 0!==t&&(clearTimeout(t),w.delete(e))}),m.clear(),r.forEach((e,n)=>{e.animated||"css"===e.engine||X(n,t)}),M.forEach(e=>{x.has(e)&&X(e,t)}),M.clear(),k.forEach(e=>e.forEach(e=>e.disconnect())),k.clear(),r.clear(),l.clear(),z(!1),o=new WeakSet},refresh(){k.forEach(e=>e.forEach(e=>e.disconnect())),k.clear(),l.clear(),z(!1),F(),c()&&r.forEach((e,t)=>{U(t,e,!(e.animated&&e.options.once&&!e.options.repeat))})},animate(e,n={}){const r=h(n,t);y(e).forEach(e=>L(e,r,t,0,m))},getObservedElements:()=>Array.from(r.values()),configure(e){t={...t,...e}}};return K}const F=["animation","duration","delay","easing","threshold","root-margin","offset","once","repeat","stagger","engine","view-range","progress","progress-var","exit","parallax-x","parallax-y","parallax-rotate","parallax-scale","parallax-speed"];let N=null;function R(e="scroll-animate",t){if("undefined"==typeof customElements||"undefined"==typeof HTMLElement)return;const n=customElements.get(e);if(n)return n;const r=()=>t||N||(N=T());class o extends HTMLElement{constructor(){super(...arguments),this.observing=!1}static get observedAttributes(){return F}emit(e,t){this.dispatchEvent(new CustomEvent(`sa:${e}`,{detail:t}))}get options(){return{...g(e=>{const t=this.getAttribute(e);return null===t?void 0:t}),onEnter:()=>this.emit("enter"),onLeave:()=>this.emit("leave"),onStart:()=>this.emit("start"),onComplete:()=>this.emit("complete"),...this.hasAttribute("progress")||this.hasAttribute("progress-var")?{onProgress:(e,t)=>this.emit("progress",{progress:t})}:{}}}connectedCallback(){this.style.display||"function"!=typeof getComputedStyle||"inline"!==(getComputedStyle(this).display||"inline")||(this.style.display="block"),r().observe(this,this.options),this.observing=!0}disconnectedCallback(){r().unobserve(this),this.observing=!1}attributeChangedCallback(){if(!this.observing)return;const e=r(),t=e.getObservedElements().find(e=>e.element===this);t&&!t.animated&&(e.unobserve(this),e.observe(this,this.options))}}return customElements.define(e,o),o}R(),e.defineScrollAnimate=R});
2
+ //# sourceMappingURL=element.umd.js.map