use-scroll-animate 5.8.0 → 6.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. package/CHANGELOG.md +31 -0
  2. package/README.md +3 -2
  3. package/README_ja.md +1 -0
  4. package/README_zh.md +2 -1
  5. package/bin/usa-codemod-6.mjs +146 -0
  6. package/dist/components/angular.d.cts +5 -3
  7. package/dist/components/angular.d.ts +5 -3
  8. package/dist/components/click.cjs +2 -4
  9. package/dist/components/click.cjs.map +1 -1
  10. package/dist/components/click.d.cts +3 -8
  11. package/dist/components/click.d.ts +3 -8
  12. package/dist/components/click.js +3 -2
  13. package/dist/components/click.js.map +1 -1
  14. package/dist/components/effects.cjs +306 -1
  15. package/dist/components/effects.cjs.map +1 -1
  16. package/dist/components/effects.d.cts +66 -2
  17. package/dist/components/effects.d.ts +66 -2
  18. package/dist/components/effects.js +303 -2
  19. package/dist/components/effects.js.map +1 -1
  20. package/dist/components/fx.cjs +2 -2
  21. package/dist/components/fx.cjs.map +1 -1
  22. package/dist/components/fx.js +2 -2
  23. package/dist/components/fx.js.map +1 -1
  24. package/dist/components/lite.cjs +9 -10
  25. package/dist/components/lite.cjs.map +1 -1
  26. package/dist/components/lite.js +10 -8
  27. package/dist/components/lite.js.map +1 -1
  28. package/dist/components/page.cjs +6 -5
  29. package/dist/components/page.cjs.map +1 -1
  30. package/dist/components/page.css +1 -1
  31. package/dist/components/page.d.cts +4 -3
  32. package/dist/components/page.d.ts +4 -3
  33. package/dist/components/page.js +6 -5
  34. package/dist/components/page.js.map +1 -1
  35. package/dist/components/solid.d.cts +5 -3
  36. package/dist/components/solid.d.ts +5 -3
  37. package/dist/components/svelte.d.cts +5 -3
  38. package/dist/components/svelte.d.ts +5 -3
  39. package/dist/components/vue.d.cts +5 -3
  40. package/dist/components/vue.d.ts +5 -3
  41. package/dist/components.cjs +2 -5
  42. package/dist/components.cjs.map +1 -1
  43. package/dist/components.css +1 -1
  44. package/dist/components.d.cts +7 -11
  45. package/dist/components.d.ts +7 -11
  46. package/dist/components.js +1 -1
  47. package/dist/components.umd.js +2 -2
  48. package/dist/components.umd.js.map +1 -1
  49. package/docs/ROADMAP.md +12 -12
  50. package/docs/components.md +29 -3
  51. package/docs/deprecations.md +4 -0
  52. package/docs/performance.md +1 -1
  53. package/docs/upgrading-6.md +32 -0
  54. package/package.json +3 -2
@@ -414,6 +414,70 @@ interface UsaThemeElement extends UsaElement {
414
414
  /** `<usa-theme name="neon | paper | glass | retro | brutalist">` — a themed subtree. */
415
415
  declare function defineTheme(tag?: string): CustomElementConstructor | undefined;
416
416
 
417
+ interface AnimationTrack {
418
+ /** Selector inside the player (`:scope` = the player). */
419
+ target?: string;
420
+ /** Start time in ms. */
421
+ start?: number;
422
+ duration?: number;
423
+ /** A timeline preset (`fade-up`, `scale`, `blur`…). */
424
+ preset?: string;
425
+ keyframes?: Keyframe[];
426
+ easing?: string;
427
+ /** A registered effect fired at `start` (instead of keyframes). */
428
+ effect?: string;
429
+ options?: Record<string, unknown>;
430
+ label?: string;
431
+ }
432
+ interface AnimationJSON {
433
+ format?: string;
434
+ version?: number;
435
+ name?: string;
436
+ loop?: boolean;
437
+ /** Total length; defaults to the end of the last track. */
438
+ duration?: number;
439
+ tracks: AnimationTrack[];
440
+ }
441
+ declare const ANIMATION_FORMAT = "use-scroll-animate/animation";
442
+ /** Validate / normalise an animation (object or JSON text). Throws on anything unusable. */
443
+ declare function normalizeAnimation(input: string | AnimationJSON | Record<string, any>): Required<Pick<AnimationJSON, 'tracks' | 'duration' | 'loop' | 'name'>>;
444
+ interface Player {
445
+ readonly duration: number;
446
+ readonly currentTime: number;
447
+ readonly playing: boolean;
448
+ play(): void;
449
+ pause(): void;
450
+ /** Jump to `ms` (effects between are not replayed). */
451
+ seek(ms: number): void;
452
+ /** Playback rate (default 1). */
453
+ rate: number;
454
+ /** Resolves each time the animation reaches its end (not when looping). */
455
+ readonly finished: Promise<void>;
456
+ destroy(): void;
457
+ }
458
+ /** Bind an animation to `root` and return its controller (paused at 0 unless `autoplay`). */
459
+ declare function createPlayer(root: HTMLElement, animation: string | AnimationJSON, o?: {
460
+ autoplay?: boolean;
461
+ loop?: boolean;
462
+ rate?: number;
463
+ onFinish?: () => void;
464
+ }): Player;
465
+ interface UsaPlayerElement extends UsaElement {
466
+ readonly player: Player | null;
467
+ /** Load an animation (object or JSON text) and restart. */
468
+ load(animation: string | AnimationJSON): void;
469
+ play(): void;
470
+ pause(): void;
471
+ seek(ms: number): void;
472
+ }
473
+ /**
474
+ * `<usa-player src="hero.json" | <script type="application/json"> child
475
+ * trigger="load | view | scroll | click | manual" loop rate controls>`.
476
+ * Emits `usa-player-ready` and `usa-player-finish`; sets `data-error` when the
477
+ * animation cannot be loaded.
478
+ */
479
+ declare function definePlayer(tag?: string): CustomElementConstructor | undefined;
480
+
417
481
  /**
418
482
  * 5.4 — `<usa-story template="…">` scroll-storytelling templates.
419
483
  *
@@ -477,5 +541,5 @@ declare function defineEffectElements(): void;
477
541
  /** Register the built-ins and every pack (idempotent). */
478
542
  declare function registerAllEffects(): void;
479
543
 
480
- export { AUDIO_FX, CARD_FX, CLICK_FX, CURSOR_FX, EFFECT_PACKS, GENERATIVE_FX, GESTURES, MICRO_FX, PAGE_FX, PHYSICS_FX, STORY_TEMPLATES, THEMES, THEME_FX, THEME_NAMES, THEME_ROLES, angleDelta, applyTheme, bindBeat, bindGesture, bounceKeyframes, bumpCount, canvasBackground, createBeatDetector, defineAudio, defineEffectElements, defineGestureFx, defineStory, defineTheme, disableAudio, enableAudio, flingVelocity, formatCount, fxLayer, getAudio, hexRgb, noise2, onBeat, playThemeEffect, registerAllEffects, registerAudioEffects, registerCardClickEffects, registerCursorEffects, registerGenerativeEffects, registerMicroEffects, registerPageEffects, registerPhysicsEffects, solveSpring, springKeyframes, storyProgress, swapLabel, themeCss, themePreset, themeVars, togglePressed };
481
- export type { AudioInput, AudioReactive, AudioSample, BeatOptions, GenFrame, GenerativeSpec, GestureDetail, GestureFxOptions, GestureName, SpringOptions, StoryTemplate, ThemePack, ThemeRole, UsaAudioElement, UsaGestureFxElement, UsaStoryElement, UsaThemeElement };
544
+ export { ANIMATION_FORMAT, AUDIO_FX, CARD_FX, CLICK_FX, CURSOR_FX, EFFECT_PACKS, GENERATIVE_FX, GESTURES, MICRO_FX, PAGE_FX, PHYSICS_FX, STORY_TEMPLATES, THEMES, THEME_FX, THEME_NAMES, THEME_ROLES, angleDelta, applyTheme, bindBeat, bindGesture, bounceKeyframes, bumpCount, canvasBackground, createBeatDetector, createPlayer, defineAudio, defineEffectElements, defineGestureFx, definePlayer, defineStory, defineTheme, disableAudio, enableAudio, flingVelocity, formatCount, fxLayer, getAudio, hexRgb, noise2, normalizeAnimation, onBeat, playThemeEffect, registerAllEffects, registerAudioEffects, registerCardClickEffects, registerCursorEffects, registerGenerativeEffects, registerMicroEffects, registerPageEffects, registerPhysicsEffects, solveSpring, springKeyframes, storyProgress, swapLabel, themeCss, themePreset, themeVars, togglePressed };
545
+ export type { AnimationJSON, AnimationTrack, AudioInput, AudioReactive, AudioSample, BeatOptions, GenFrame, GenerativeSpec, GestureDetail, GestureFxOptions, GestureName, Player, SpringOptions, StoryTemplate, ThemePack, ThemeRole, UsaAudioElement, UsaGestureFxElement, UsaPlayerElement, UsaStoryElement, UsaThemeElement };
@@ -414,6 +414,70 @@ interface UsaThemeElement extends UsaElement {
414
414
  /** `<usa-theme name="neon | paper | glass | retro | brutalist">` — a themed subtree. */
415
415
  declare function defineTheme(tag?: string): CustomElementConstructor | undefined;
416
416
 
417
+ interface AnimationTrack {
418
+ /** Selector inside the player (`:scope` = the player). */
419
+ target?: string;
420
+ /** Start time in ms. */
421
+ start?: number;
422
+ duration?: number;
423
+ /** A timeline preset (`fade-up`, `scale`, `blur`…). */
424
+ preset?: string;
425
+ keyframes?: Keyframe[];
426
+ easing?: string;
427
+ /** A registered effect fired at `start` (instead of keyframes). */
428
+ effect?: string;
429
+ options?: Record<string, unknown>;
430
+ label?: string;
431
+ }
432
+ interface AnimationJSON {
433
+ format?: string;
434
+ version?: number;
435
+ name?: string;
436
+ loop?: boolean;
437
+ /** Total length; defaults to the end of the last track. */
438
+ duration?: number;
439
+ tracks: AnimationTrack[];
440
+ }
441
+ declare const ANIMATION_FORMAT = "use-scroll-animate/animation";
442
+ /** Validate / normalise an animation (object or JSON text). Throws on anything unusable. */
443
+ declare function normalizeAnimation(input: string | AnimationJSON | Record<string, any>): Required<Pick<AnimationJSON, 'tracks' | 'duration' | 'loop' | 'name'>>;
444
+ interface Player {
445
+ readonly duration: number;
446
+ readonly currentTime: number;
447
+ readonly playing: boolean;
448
+ play(): void;
449
+ pause(): void;
450
+ /** Jump to `ms` (effects between are not replayed). */
451
+ seek(ms: number): void;
452
+ /** Playback rate (default 1). */
453
+ rate: number;
454
+ /** Resolves each time the animation reaches its end (not when looping). */
455
+ readonly finished: Promise<void>;
456
+ destroy(): void;
457
+ }
458
+ /** Bind an animation to `root` and return its controller (paused at 0 unless `autoplay`). */
459
+ declare function createPlayer(root: HTMLElement, animation: string | AnimationJSON, o?: {
460
+ autoplay?: boolean;
461
+ loop?: boolean;
462
+ rate?: number;
463
+ onFinish?: () => void;
464
+ }): Player;
465
+ interface UsaPlayerElement extends UsaElement {
466
+ readonly player: Player | null;
467
+ /** Load an animation (object or JSON text) and restart. */
468
+ load(animation: string | AnimationJSON): void;
469
+ play(): void;
470
+ pause(): void;
471
+ seek(ms: number): void;
472
+ }
473
+ /**
474
+ * `<usa-player src="hero.json" | <script type="application/json"> child
475
+ * trigger="load | view | scroll | click | manual" loop rate controls>`.
476
+ * Emits `usa-player-ready` and `usa-player-finish`; sets `data-error` when the
477
+ * animation cannot be loaded.
478
+ */
479
+ declare function definePlayer(tag?: string): CustomElementConstructor | undefined;
480
+
417
481
  /**
418
482
  * 5.4 — `<usa-story template="…">` scroll-storytelling templates.
419
483
  *
@@ -477,5 +541,5 @@ declare function defineEffectElements(): void;
477
541
  /** Register the built-ins and every pack (idempotent). */
478
542
  declare function registerAllEffects(): void;
479
543
 
480
- export { AUDIO_FX, CARD_FX, CLICK_FX, CURSOR_FX, EFFECT_PACKS, GENERATIVE_FX, GESTURES, MICRO_FX, PAGE_FX, PHYSICS_FX, STORY_TEMPLATES, THEMES, THEME_FX, THEME_NAMES, THEME_ROLES, angleDelta, applyTheme, bindBeat, bindGesture, bounceKeyframes, bumpCount, canvasBackground, createBeatDetector, defineAudio, defineEffectElements, defineGestureFx, defineStory, defineTheme, disableAudio, enableAudio, flingVelocity, formatCount, fxLayer, getAudio, hexRgb, noise2, onBeat, playThemeEffect, registerAllEffects, registerAudioEffects, registerCardClickEffects, registerCursorEffects, registerGenerativeEffects, registerMicroEffects, registerPageEffects, registerPhysicsEffects, solveSpring, springKeyframes, storyProgress, swapLabel, themeCss, themePreset, themeVars, togglePressed };
481
- export type { AudioInput, AudioReactive, AudioSample, BeatOptions, GenFrame, GenerativeSpec, GestureDetail, GestureFxOptions, GestureName, SpringOptions, StoryTemplate, ThemePack, ThemeRole, UsaAudioElement, UsaGestureFxElement, UsaStoryElement, UsaThemeElement };
544
+ export { ANIMATION_FORMAT, AUDIO_FX, CARD_FX, CLICK_FX, CURSOR_FX, EFFECT_PACKS, GENERATIVE_FX, GESTURES, MICRO_FX, PAGE_FX, PHYSICS_FX, STORY_TEMPLATES, THEMES, THEME_FX, THEME_NAMES, THEME_ROLES, angleDelta, applyTheme, bindBeat, bindGesture, bounceKeyframes, bumpCount, canvasBackground, createBeatDetector, createPlayer, defineAudio, defineEffectElements, defineGestureFx, definePlayer, defineStory, defineTheme, disableAudio, enableAudio, flingVelocity, formatCount, fxLayer, getAudio, hexRgb, noise2, normalizeAnimation, onBeat, playThemeEffect, registerAllEffects, registerAudioEffects, registerCardClickEffects, registerCursorEffects, registerGenerativeEffects, registerMicroEffects, registerPageEffects, registerPhysicsEffects, solveSpring, springKeyframes, storyProgress, swapLabel, themeCss, themePreset, themeVars, togglePressed };
545
+ export type { AnimationJSON, AnimationTrack, AudioInput, AudioReactive, AudioSample, BeatOptions, GenFrame, GenerativeSpec, GestureDetail, GestureFxOptions, GestureName, Player, SpringOptions, StoryTemplate, ThemePack, ThemeRole, UsaAudioElement, UsaGestureFxElement, UsaPlayerElement, UsaStoryElement, UsaThemeElement };
@@ -1,7 +1,7 @@
1
1
  import { playEffect, bindEffect, registerBuiltinEffects, registerEffects } from './fx.js';
2
2
  import { x as defineElement, p as prefersReducedMotion, u as adoptStyles } from '../chunks/base-C3Sw9sAO.js';
3
3
  import { applyMotionTokens, motionTokensToVars, mergeMotionTokens } from './tokens.js';
4
- import '../chunks/core-DN3hHbHh.js';
4
+ import { T as TIMELINE_PRESETS } from '../chunks/core-DN3hHbHh.js';
5
5
  import '../chunks/fx-ChjxrMBo.js';
6
6
 
7
7
  let layer = null;
@@ -2160,6 +2160,306 @@ function defineTheme(tag = 'usa-theme') {
2160
2160
  }, { id: 'usa-theme-el', text: 'usa-theme{display:block}' });
2161
2161
  }
2162
2162
 
2163
+ /**
2164
+ * 5.9 — `<usa-player>`: plays JSON animations.
2165
+ *
2166
+ * Format `use-scroll-animate/animation` v1:
2167
+ *
2168
+ * ```json
2169
+ * { "format": "use-scroll-animate/animation", "version": 1, "name": "Hero",
2170
+ * "loop": false,
2171
+ * "tracks": [
2172
+ * { "target": "h1", "start": 0, "duration": 600, "preset": "fade-up" },
2173
+ * { "target": ".cta", "start": 500, "duration": 500, "keyframes": [{ "opacity": 0 }, { "opacity": 1 }], "easing": "ease-out" },
2174
+ * { "target": ".cta", "start": 1100, "effect": "jelly", "options": {} }
2175
+ * ] }
2176
+ * ```
2177
+ *
2178
+ * A track animates `target` (a selector inside the player; `:scope` for the
2179
+ * player itself) with a timeline preset, its own keyframes, or fires any
2180
+ * registered effect at `start`. Playground presets (format
2181
+ * `use-scroll-animate/playground`) are accepted too — their tracks map to the
2182
+ * player's children in order.
2183
+ *
2184
+ * Keyframe tracks are WAAPI animations driven by one clock, so the player can
2185
+ * play, pause, seek, change rate and be scrubbed by scroll
2186
+ * (`trigger="scroll"`). Reduced motion: jumps to the end state, effects skipped.
2187
+ */
2188
+ const ANIMATION_FORMAT = 'use-scroll-animate/animation';
2189
+ function decodePlayground(state) {
2190
+ try {
2191
+ const b64 = state.replace(/-/g, '+').replace(/_/g, '/');
2192
+ const json = decodeURIComponent(escape(atob(b64)));
2193
+ const t = (JSON.parse(json).t || []);
2194
+ return t.map(([preset, start, duration, label], i) => ({ target: `:scope > :nth-child(${i + 1})`, preset, start, duration, label }));
2195
+ }
2196
+ catch {
2197
+ return [];
2198
+ }
2199
+ }
2200
+ /** Validate / normalise an animation (object or JSON text). Throws on anything unusable. */
2201
+ function normalizeAnimation(input) {
2202
+ const d = typeof input === 'string' ? JSON.parse(input) : input;
2203
+ if (!d || typeof d !== 'object')
2204
+ throw new Error('[use-scroll-animate] animation: expected an object');
2205
+ let tracks;
2206
+ if (d.format === 'use-scroll-animate/playground')
2207
+ tracks = decodePlayground(String(d.state || ''));
2208
+ else if (Array.isArray(d.tracks))
2209
+ tracks = d.tracks;
2210
+ else
2211
+ throw new Error('[use-scroll-animate] animation: missing "tracks"');
2212
+ if (d.format && d.format !== ANIMATION_FORMAT && d.format !== 'use-scroll-animate/playground')
2213
+ throw new Error(`[use-scroll-animate] animation: unknown format "${d.format}"`);
2214
+ if (d.version && d.version > 1 && d.format === ANIMATION_FORMAT)
2215
+ throw new Error(`[use-scroll-animate] animation: version ${d.version} needs a newer use-scroll-animate`);
2216
+ const out = tracks
2217
+ .filter((t) => t && (t.effect || t.preset || Array.isArray(t.keyframes)))
2218
+ .map((t) => ({ ...t, target: t.target || ':scope', start: Math.max(0, Number(t.start) || 0), duration: t.effect ? 0 : Math.max(1, Number(t.duration) || 600) }));
2219
+ const end = out.reduce((m, t) => Math.max(m, t.start + t.duration), 0);
2220
+ return { tracks: out, duration: Math.max(end, Number(d.duration) || 0), loop: !!d.loop, name: String(d.name || 'animation') };
2221
+ }
2222
+ const pick = (root, sel) => {
2223
+ if (sel === ':scope')
2224
+ return [root];
2225
+ try {
2226
+ return Array.from(root.querySelectorAll(sel));
2227
+ }
2228
+ catch {
2229
+ return [];
2230
+ }
2231
+ };
2232
+ /** Bind an animation to `root` and return its controller (paused at 0 unless `autoplay`). */
2233
+ function createPlayer(root, animation, o = {}) {
2234
+ const a = normalizeAnimation(animation);
2235
+ const loop = o.loop ?? a.loop;
2236
+ const anims = [];
2237
+ const effects = [];
2238
+ for (const t of a.tracks) {
2239
+ const els = pick(root, t.target);
2240
+ if (t.effect)
2241
+ effects.push({ els, t, fired: false });
2242
+ else {
2243
+ const kf = t.keyframes || TIMELINE_PRESETS[t.preset] || TIMELINE_PRESETS.fade;
2244
+ for (const el of els) {
2245
+ if (typeof el.animate !== 'function')
2246
+ continue;
2247
+ const an = el.animate(kf, { duration: t.duration, delay: t.start, easing: t.easing || 'cubic-bezier(0.22, 1, 0.36, 1)', fill: 'both' });
2248
+ an.pause();
2249
+ an.currentTime = 0;
2250
+ anims.push(an);
2251
+ }
2252
+ }
2253
+ }
2254
+ let time = 0;
2255
+ let playing = false;
2256
+ let raf = 0;
2257
+ let last = 0;
2258
+ let resolve;
2259
+ let finished = new Promise((r) => (resolve = r));
2260
+ const set = (ms) => {
2261
+ time = Math.min(a.duration, Math.max(0, ms));
2262
+ for (const an of anims)
2263
+ an.currentTime = time;
2264
+ };
2265
+ const fireDue = () => {
2266
+ if (prefersReducedMotion())
2267
+ return;
2268
+ for (const e of effects)
2269
+ if (!e.fired && time >= e.t.start) {
2270
+ e.fired = true;
2271
+ for (const el of e.els)
2272
+ playEffect(el, e.t.effect, e.t.options || {}).catch(() => undefined);
2273
+ }
2274
+ };
2275
+ const done = () => {
2276
+ resolve();
2277
+ o.onFinish?.();
2278
+ finished = new Promise((r) => (resolve = r));
2279
+ };
2280
+ const frame = (now) => {
2281
+ raf = 0;
2282
+ if (!playing)
2283
+ return;
2284
+ set(time + (now - last) * player.rate);
2285
+ last = now;
2286
+ fireDue();
2287
+ if (time >= a.duration) {
2288
+ if (loop) {
2289
+ effects.forEach((e) => (e.fired = false));
2290
+ set(0);
2291
+ }
2292
+ else {
2293
+ playing = false;
2294
+ done();
2295
+ return;
2296
+ }
2297
+ }
2298
+ raf = requestAnimationFrame(frame);
2299
+ };
2300
+ const player = {
2301
+ get duration() {
2302
+ return a.duration;
2303
+ },
2304
+ get currentTime() {
2305
+ return time;
2306
+ },
2307
+ get playing() {
2308
+ return playing;
2309
+ },
2310
+ rate: o.rate ?? 1,
2311
+ get finished() {
2312
+ return finished;
2313
+ },
2314
+ play() {
2315
+ if (prefersReducedMotion()) {
2316
+ set(a.duration);
2317
+ effects.forEach((e) => (e.fired = true));
2318
+ done();
2319
+ return;
2320
+ }
2321
+ if (playing)
2322
+ return;
2323
+ if (time >= a.duration) {
2324
+ effects.forEach((e) => (e.fired = false));
2325
+ set(0);
2326
+ }
2327
+ playing = true;
2328
+ last = performance.now();
2329
+ fireDue();
2330
+ raf = requestAnimationFrame(frame);
2331
+ },
2332
+ pause() {
2333
+ playing = false;
2334
+ cancelAnimationFrame(raf);
2335
+ raf = 0;
2336
+ },
2337
+ seek(ms) {
2338
+ set(ms);
2339
+ effects.forEach((e) => (e.fired = e.t.start < time));
2340
+ },
2341
+ destroy() {
2342
+ player.pause();
2343
+ anims.forEach((an) => an.cancel());
2344
+ },
2345
+ };
2346
+ if (o.autoplay)
2347
+ player.play();
2348
+ return player;
2349
+ }
2350
+ /**
2351
+ * `<usa-player src="hero.json" | <script type="application/json"> child
2352
+ * trigger="load | view | scroll | click | manual" loop rate controls>`.
2353
+ * Emits `usa-player-ready` and `usa-player-finish`; sets `data-error` when the
2354
+ * animation cannot be loaded.
2355
+ */
2356
+ function definePlayer(tag = 'usa-player') {
2357
+ return defineElement(tag, (Base) => class UsaPlayer extends Base {
2358
+ constructor() {
2359
+ super(...arguments);
2360
+ this.player = null;
2361
+ this.json = null;
2362
+ }
2363
+ static get observedAttributes() {
2364
+ return ['src', 'trigger', 'loop', 'rate'];
2365
+ }
2366
+ load(animation) {
2367
+ this.json = animation;
2368
+ this.start();
2369
+ }
2370
+ play() {
2371
+ this.player?.play();
2372
+ }
2373
+ pause() {
2374
+ this.player?.pause();
2375
+ }
2376
+ seek(ms) {
2377
+ this.player?.seek(ms);
2378
+ }
2379
+ start() {
2380
+ this.player?.destroy();
2381
+ this.player = null;
2382
+ if (!this.json)
2383
+ return;
2384
+ try {
2385
+ this.player = createPlayer(this, this.json, {
2386
+ loop: this.hasAttribute('loop') ? true : undefined,
2387
+ rate: this.num('rate', 1),
2388
+ onFinish: () => this.dispatchEvent(new CustomEvent('usa-player-finish', { bubbles: true })),
2389
+ });
2390
+ this.removeAttribute('data-error');
2391
+ }
2392
+ catch (err) {
2393
+ this.setAttribute('data-error', String(err.message || err));
2394
+ return;
2395
+ }
2396
+ const p = this.player;
2397
+ this.dispatchEvent(new CustomEvent('usa-player-ready', { detail: { duration: p.duration }, bubbles: true }));
2398
+ const trig = this.str('trigger', 'view');
2399
+ if (trig === 'load')
2400
+ p.play();
2401
+ else if (trig === 'view')
2402
+ this.inView((v) => v && p.play(), { threshold: 0.25 });
2403
+ else if (trig === 'click')
2404
+ this.listen(this, 'click', () => (p.playing ? p.pause() : p.play()));
2405
+ else if (trig === 'scroll') {
2406
+ let f = 0;
2407
+ const upd = () => {
2408
+ f = 0;
2409
+ p.seek(storyProgress(this) * p.duration);
2410
+ };
2411
+ const kick = () => void (f || (f = requestAnimationFrame(upd)));
2412
+ this.listen(window, 'scroll', kick, { passive: true });
2413
+ this.listen(window, 'resize', kick);
2414
+ this.onCleanup(() => cancelAnimationFrame(f));
2415
+ upd();
2416
+ }
2417
+ if (this.flag('controls'))
2418
+ this.mountControls(p);
2419
+ }
2420
+ mountControls(p) {
2421
+ const b = document.createElement('button');
2422
+ b.type = 'button';
2423
+ b.setAttribute('data-player-toggle', '');
2424
+ b.textContent = '▶︎ / ❚❚';
2425
+ b.setAttribute('aria-label', 'Play / pause animation');
2426
+ this.listen(b, 'click', (e) => {
2427
+ e.stopPropagation();
2428
+ if (p.playing)
2429
+ p.pause();
2430
+ else
2431
+ p.play();
2432
+ b.setAttribute('aria-pressed', String(p.playing));
2433
+ });
2434
+ this.append(b);
2435
+ this.onCleanup(() => b.remove());
2436
+ }
2437
+ changed(name) {
2438
+ if (name === 'src')
2439
+ this.json = null;
2440
+ super.changed(name);
2441
+ }
2442
+ mount() {
2443
+ this.onCleanup(() => {
2444
+ this.player?.destroy();
2445
+ this.player = null;
2446
+ });
2447
+ const inline = this.querySelector('script[type="application/json"]');
2448
+ const src = this.str('src');
2449
+ if (inline && !this.json)
2450
+ this.json = inline.textContent || '';
2451
+ if (src && !this.json) {
2452
+ fetch(src)
2453
+ .then((r) => (r.ok ? r.text() : Promise.reject(new Error(`HTTP ${r.status}`))))
2454
+ .then((t) => this.isConnected && this.load(t))
2455
+ .catch((err) => this.setAttribute('data-error', String(err.message || err)));
2456
+ return;
2457
+ }
2458
+ this.start();
2459
+ }
2460
+ }, { id: 'usa-player', text: 'usa-player{display:block;position:relative}usa-player>script{display:none}usa-player [data-player-toggle]{position:absolute;right:8px;bottom:8px}' });
2461
+ }
2462
+
2163
2463
  /**
2164
2464
  * use-scroll-animate/components/effects — the 5.x effect packs, all
2165
2465
  * registered through `registerEffect()` (5.0) and playable with
@@ -2218,6 +2518,7 @@ function defineEffectElements() {
2218
2518
  defineAudio();
2219
2519
  defineGestureFx();
2220
2520
  defineTheme();
2521
+ definePlayer();
2221
2522
  }
2222
2523
  /** Register the built-ins and every pack (idempotent). */
2223
2524
  function registerAllEffects() {
@@ -2226,5 +2527,5 @@ function registerAllEffects() {
2226
2527
  registerEffects(defs);
2227
2528
  }
2228
2529
 
2229
- export { AUDIO_FX, CARD_FX, CLICK_FX, CURSOR_FX, EFFECT_PACKS, GENERATIVE_FX, GESTURES, MICRO_FX, PAGE_FX, PHYSICS_FX, STORY_TEMPLATES, THEMES, THEME_FX, THEME_NAMES, THEME_ROLES, angleDelta, applyTheme, bindBeat, bindGesture, bounceKeyframes, bumpCount, canvasBackground, createBeatDetector, defineAudio, defineEffectElements, defineGestureFx, defineStory, defineTheme, disableAudio, enableAudio, flingVelocity, formatCount, fxLayer, getAudio, hexRgb, noise2, onBeat, playThemeEffect, registerAllEffects, registerAudioEffects, registerCardClickEffects, registerCursorEffects, registerGenerativeEffects, registerMicroEffects, registerPageEffects, registerPhysicsEffects, solveSpring, springKeyframes, storyProgress, swapLabel, themeCss, themePreset, themeVars, togglePressed };
2530
+ export { ANIMATION_FORMAT, AUDIO_FX, CARD_FX, CLICK_FX, CURSOR_FX, EFFECT_PACKS, GENERATIVE_FX, GESTURES, MICRO_FX, PAGE_FX, PHYSICS_FX, STORY_TEMPLATES, THEMES, THEME_FX, THEME_NAMES, THEME_ROLES, angleDelta, applyTheme, bindBeat, bindGesture, bounceKeyframes, bumpCount, canvasBackground, createBeatDetector, createPlayer, defineAudio, defineEffectElements, defineGestureFx, definePlayer, defineStory, defineTheme, disableAudio, enableAudio, flingVelocity, formatCount, fxLayer, getAudio, hexRgb, noise2, normalizeAnimation, onBeat, playThemeEffect, registerAllEffects, registerAudioEffects, registerCardClickEffects, registerCursorEffects, registerGenerativeEffects, registerMicroEffects, registerPageEffects, registerPhysicsEffects, solveSpring, springKeyframes, storyProgress, swapLabel, themeCss, themePreset, themeVars, togglePressed };
2230
2531
  //# sourceMappingURL=effects.js.map