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.
- package/CHANGELOG.md +31 -0
- package/README.md +3 -2
- package/README_ja.md +1 -0
- package/README_zh.md +2 -1
- package/bin/usa-codemod-6.mjs +146 -0
- package/dist/components/angular.d.cts +5 -3
- package/dist/components/angular.d.ts +5 -3
- package/dist/components/click.cjs +2 -4
- package/dist/components/click.cjs.map +1 -1
- package/dist/components/click.d.cts +3 -8
- package/dist/components/click.d.ts +3 -8
- package/dist/components/click.js +3 -2
- package/dist/components/click.js.map +1 -1
- package/dist/components/effects.cjs +306 -1
- package/dist/components/effects.cjs.map +1 -1
- package/dist/components/effects.d.cts +66 -2
- package/dist/components/effects.d.ts +66 -2
- package/dist/components/effects.js +303 -2
- package/dist/components/effects.js.map +1 -1
- package/dist/components/fx.cjs +2 -2
- package/dist/components/fx.cjs.map +1 -1
- package/dist/components/fx.js +2 -2
- package/dist/components/fx.js.map +1 -1
- package/dist/components/lite.cjs +9 -10
- package/dist/components/lite.cjs.map +1 -1
- package/dist/components/lite.js +10 -8
- package/dist/components/lite.js.map +1 -1
- package/dist/components/page.cjs +6 -5
- package/dist/components/page.cjs.map +1 -1
- package/dist/components/page.css +1 -1
- package/dist/components/page.d.cts +4 -3
- package/dist/components/page.d.ts +4 -3
- package/dist/components/page.js +6 -5
- package/dist/components/page.js.map +1 -1
- package/dist/components/solid.d.cts +5 -3
- package/dist/components/solid.d.ts +5 -3
- package/dist/components/svelte.d.cts +5 -3
- package/dist/components/svelte.d.ts +5 -3
- package/dist/components/vue.d.cts +5 -3
- package/dist/components/vue.d.ts +5 -3
- package/dist/components.cjs +2 -5
- package/dist/components.cjs.map +1 -1
- package/dist/components.css +1 -1
- package/dist/components.d.cts +7 -11
- package/dist/components.d.ts +7 -11
- package/dist/components.js +1 -1
- package/dist/components.umd.js +2 -2
- package/dist/components.umd.js.map +1 -1
- package/docs/ROADMAP.md +12 -12
- package/docs/components.md +29 -3
- package/docs/deprecations.md +4 -0
- package/docs/performance.md +1 -1
- package/docs/upgrading-6.md +32 -0
- package/package.json +3 -2
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
var components_fx = require('./fx.cjs');
|
|
4
4
|
var base = require('../chunks/base-B5i8qQPR.cjs');
|
|
5
5
|
var components_tokens = require('./tokens.cjs');
|
|
6
|
-
require('../chunks/core-BDcszY4L.cjs');
|
|
6
|
+
var core = require('../chunks/core-BDcszY4L.cjs');
|
|
7
7
|
require('../chunks/fx-B8hk1Fby.cjs');
|
|
8
8
|
|
|
9
9
|
let layer = null;
|
|
@@ -2162,6 +2162,306 @@ function defineTheme(tag = 'usa-theme') {
|
|
|
2162
2162
|
}, { id: 'usa-theme-el', text: 'usa-theme{display:block}' });
|
|
2163
2163
|
}
|
|
2164
2164
|
|
|
2165
|
+
/**
|
|
2166
|
+
* 5.9 — `<usa-player>`: plays JSON animations.
|
|
2167
|
+
*
|
|
2168
|
+
* Format `use-scroll-animate/animation` v1:
|
|
2169
|
+
*
|
|
2170
|
+
* ```json
|
|
2171
|
+
* { "format": "use-scroll-animate/animation", "version": 1, "name": "Hero",
|
|
2172
|
+
* "loop": false,
|
|
2173
|
+
* "tracks": [
|
|
2174
|
+
* { "target": "h1", "start": 0, "duration": 600, "preset": "fade-up" },
|
|
2175
|
+
* { "target": ".cta", "start": 500, "duration": 500, "keyframes": [{ "opacity": 0 }, { "opacity": 1 }], "easing": "ease-out" },
|
|
2176
|
+
* { "target": ".cta", "start": 1100, "effect": "jelly", "options": {} }
|
|
2177
|
+
* ] }
|
|
2178
|
+
* ```
|
|
2179
|
+
*
|
|
2180
|
+
* A track animates `target` (a selector inside the player; `:scope` for the
|
|
2181
|
+
* player itself) with a timeline preset, its own keyframes, or fires any
|
|
2182
|
+
* registered effect at `start`. Playground presets (format
|
|
2183
|
+
* `use-scroll-animate/playground`) are accepted too — their tracks map to the
|
|
2184
|
+
* player's children in order.
|
|
2185
|
+
*
|
|
2186
|
+
* Keyframe tracks are WAAPI animations driven by one clock, so the player can
|
|
2187
|
+
* play, pause, seek, change rate and be scrubbed by scroll
|
|
2188
|
+
* (`trigger="scroll"`). Reduced motion: jumps to the end state, effects skipped.
|
|
2189
|
+
*/
|
|
2190
|
+
const ANIMATION_FORMAT = 'use-scroll-animate/animation';
|
|
2191
|
+
function decodePlayground(state) {
|
|
2192
|
+
try {
|
|
2193
|
+
const b64 = state.replace(/-/g, '+').replace(/_/g, '/');
|
|
2194
|
+
const json = decodeURIComponent(escape(atob(b64)));
|
|
2195
|
+
const t = (JSON.parse(json).t || []);
|
|
2196
|
+
return t.map(([preset, start, duration, label], i) => ({ target: `:scope > :nth-child(${i + 1})`, preset, start, duration, label }));
|
|
2197
|
+
}
|
|
2198
|
+
catch {
|
|
2199
|
+
return [];
|
|
2200
|
+
}
|
|
2201
|
+
}
|
|
2202
|
+
/** Validate / normalise an animation (object or JSON text). Throws on anything unusable. */
|
|
2203
|
+
function normalizeAnimation(input) {
|
|
2204
|
+
const d = typeof input === 'string' ? JSON.parse(input) : input;
|
|
2205
|
+
if (!d || typeof d !== 'object')
|
|
2206
|
+
throw new Error('[use-scroll-animate] animation: expected an object');
|
|
2207
|
+
let tracks;
|
|
2208
|
+
if (d.format === 'use-scroll-animate/playground')
|
|
2209
|
+
tracks = decodePlayground(String(d.state || ''));
|
|
2210
|
+
else if (Array.isArray(d.tracks))
|
|
2211
|
+
tracks = d.tracks;
|
|
2212
|
+
else
|
|
2213
|
+
throw new Error('[use-scroll-animate] animation: missing "tracks"');
|
|
2214
|
+
if (d.format && d.format !== ANIMATION_FORMAT && d.format !== 'use-scroll-animate/playground')
|
|
2215
|
+
throw new Error(`[use-scroll-animate] animation: unknown format "${d.format}"`);
|
|
2216
|
+
if (d.version && d.version > 1 && d.format === ANIMATION_FORMAT)
|
|
2217
|
+
throw new Error(`[use-scroll-animate] animation: version ${d.version} needs a newer use-scroll-animate`);
|
|
2218
|
+
const out = tracks
|
|
2219
|
+
.filter((t) => t && (t.effect || t.preset || Array.isArray(t.keyframes)))
|
|
2220
|
+
.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) }));
|
|
2221
|
+
const end = out.reduce((m, t) => Math.max(m, t.start + t.duration), 0);
|
|
2222
|
+
return { tracks: out, duration: Math.max(end, Number(d.duration) || 0), loop: !!d.loop, name: String(d.name || 'animation') };
|
|
2223
|
+
}
|
|
2224
|
+
const pick = (root, sel) => {
|
|
2225
|
+
if (sel === ':scope')
|
|
2226
|
+
return [root];
|
|
2227
|
+
try {
|
|
2228
|
+
return Array.from(root.querySelectorAll(sel));
|
|
2229
|
+
}
|
|
2230
|
+
catch {
|
|
2231
|
+
return [];
|
|
2232
|
+
}
|
|
2233
|
+
};
|
|
2234
|
+
/** Bind an animation to `root` and return its controller (paused at 0 unless `autoplay`). */
|
|
2235
|
+
function createPlayer(root, animation, o = {}) {
|
|
2236
|
+
const a = normalizeAnimation(animation);
|
|
2237
|
+
const loop = o.loop ?? a.loop;
|
|
2238
|
+
const anims = [];
|
|
2239
|
+
const effects = [];
|
|
2240
|
+
for (const t of a.tracks) {
|
|
2241
|
+
const els = pick(root, t.target);
|
|
2242
|
+
if (t.effect)
|
|
2243
|
+
effects.push({ els, t, fired: false });
|
|
2244
|
+
else {
|
|
2245
|
+
const kf = t.keyframes || core.TIMELINE_PRESETS[t.preset] || core.TIMELINE_PRESETS.fade;
|
|
2246
|
+
for (const el of els) {
|
|
2247
|
+
if (typeof el.animate !== 'function')
|
|
2248
|
+
continue;
|
|
2249
|
+
const an = el.animate(kf, { duration: t.duration, delay: t.start, easing: t.easing || 'cubic-bezier(0.22, 1, 0.36, 1)', fill: 'both' });
|
|
2250
|
+
an.pause();
|
|
2251
|
+
an.currentTime = 0;
|
|
2252
|
+
anims.push(an);
|
|
2253
|
+
}
|
|
2254
|
+
}
|
|
2255
|
+
}
|
|
2256
|
+
let time = 0;
|
|
2257
|
+
let playing = false;
|
|
2258
|
+
let raf = 0;
|
|
2259
|
+
let last = 0;
|
|
2260
|
+
let resolve;
|
|
2261
|
+
let finished = new Promise((r) => (resolve = r));
|
|
2262
|
+
const set = (ms) => {
|
|
2263
|
+
time = Math.min(a.duration, Math.max(0, ms));
|
|
2264
|
+
for (const an of anims)
|
|
2265
|
+
an.currentTime = time;
|
|
2266
|
+
};
|
|
2267
|
+
const fireDue = () => {
|
|
2268
|
+
if (base.prefersReducedMotion())
|
|
2269
|
+
return;
|
|
2270
|
+
for (const e of effects)
|
|
2271
|
+
if (!e.fired && time >= e.t.start) {
|
|
2272
|
+
e.fired = true;
|
|
2273
|
+
for (const el of e.els)
|
|
2274
|
+
components_fx.playEffect(el, e.t.effect, e.t.options || {}).catch(() => undefined);
|
|
2275
|
+
}
|
|
2276
|
+
};
|
|
2277
|
+
const done = () => {
|
|
2278
|
+
resolve();
|
|
2279
|
+
o.onFinish?.();
|
|
2280
|
+
finished = new Promise((r) => (resolve = r));
|
|
2281
|
+
};
|
|
2282
|
+
const frame = (now) => {
|
|
2283
|
+
raf = 0;
|
|
2284
|
+
if (!playing)
|
|
2285
|
+
return;
|
|
2286
|
+
set(time + (now - last) * player.rate);
|
|
2287
|
+
last = now;
|
|
2288
|
+
fireDue();
|
|
2289
|
+
if (time >= a.duration) {
|
|
2290
|
+
if (loop) {
|
|
2291
|
+
effects.forEach((e) => (e.fired = false));
|
|
2292
|
+
set(0);
|
|
2293
|
+
}
|
|
2294
|
+
else {
|
|
2295
|
+
playing = false;
|
|
2296
|
+
done();
|
|
2297
|
+
return;
|
|
2298
|
+
}
|
|
2299
|
+
}
|
|
2300
|
+
raf = requestAnimationFrame(frame);
|
|
2301
|
+
};
|
|
2302
|
+
const player = {
|
|
2303
|
+
get duration() {
|
|
2304
|
+
return a.duration;
|
|
2305
|
+
},
|
|
2306
|
+
get currentTime() {
|
|
2307
|
+
return time;
|
|
2308
|
+
},
|
|
2309
|
+
get playing() {
|
|
2310
|
+
return playing;
|
|
2311
|
+
},
|
|
2312
|
+
rate: o.rate ?? 1,
|
|
2313
|
+
get finished() {
|
|
2314
|
+
return finished;
|
|
2315
|
+
},
|
|
2316
|
+
play() {
|
|
2317
|
+
if (base.prefersReducedMotion()) {
|
|
2318
|
+
set(a.duration);
|
|
2319
|
+
effects.forEach((e) => (e.fired = true));
|
|
2320
|
+
done();
|
|
2321
|
+
return;
|
|
2322
|
+
}
|
|
2323
|
+
if (playing)
|
|
2324
|
+
return;
|
|
2325
|
+
if (time >= a.duration) {
|
|
2326
|
+
effects.forEach((e) => (e.fired = false));
|
|
2327
|
+
set(0);
|
|
2328
|
+
}
|
|
2329
|
+
playing = true;
|
|
2330
|
+
last = performance.now();
|
|
2331
|
+
fireDue();
|
|
2332
|
+
raf = requestAnimationFrame(frame);
|
|
2333
|
+
},
|
|
2334
|
+
pause() {
|
|
2335
|
+
playing = false;
|
|
2336
|
+
cancelAnimationFrame(raf);
|
|
2337
|
+
raf = 0;
|
|
2338
|
+
},
|
|
2339
|
+
seek(ms) {
|
|
2340
|
+
set(ms);
|
|
2341
|
+
effects.forEach((e) => (e.fired = e.t.start < time));
|
|
2342
|
+
},
|
|
2343
|
+
destroy() {
|
|
2344
|
+
player.pause();
|
|
2345
|
+
anims.forEach((an) => an.cancel());
|
|
2346
|
+
},
|
|
2347
|
+
};
|
|
2348
|
+
if (o.autoplay)
|
|
2349
|
+
player.play();
|
|
2350
|
+
return player;
|
|
2351
|
+
}
|
|
2352
|
+
/**
|
|
2353
|
+
* `<usa-player src="hero.json" | <script type="application/json"> child
|
|
2354
|
+
* trigger="load | view | scroll | click | manual" loop rate controls>`.
|
|
2355
|
+
* Emits `usa-player-ready` and `usa-player-finish`; sets `data-error` when the
|
|
2356
|
+
* animation cannot be loaded.
|
|
2357
|
+
*/
|
|
2358
|
+
function definePlayer(tag = 'usa-player') {
|
|
2359
|
+
return base.defineElement(tag, (Base) => class UsaPlayer extends Base {
|
|
2360
|
+
constructor() {
|
|
2361
|
+
super(...arguments);
|
|
2362
|
+
this.player = null;
|
|
2363
|
+
this.json = null;
|
|
2364
|
+
}
|
|
2365
|
+
static get observedAttributes() {
|
|
2366
|
+
return ['src', 'trigger', 'loop', 'rate'];
|
|
2367
|
+
}
|
|
2368
|
+
load(animation) {
|
|
2369
|
+
this.json = animation;
|
|
2370
|
+
this.start();
|
|
2371
|
+
}
|
|
2372
|
+
play() {
|
|
2373
|
+
this.player?.play();
|
|
2374
|
+
}
|
|
2375
|
+
pause() {
|
|
2376
|
+
this.player?.pause();
|
|
2377
|
+
}
|
|
2378
|
+
seek(ms) {
|
|
2379
|
+
this.player?.seek(ms);
|
|
2380
|
+
}
|
|
2381
|
+
start() {
|
|
2382
|
+
this.player?.destroy();
|
|
2383
|
+
this.player = null;
|
|
2384
|
+
if (!this.json)
|
|
2385
|
+
return;
|
|
2386
|
+
try {
|
|
2387
|
+
this.player = createPlayer(this, this.json, {
|
|
2388
|
+
loop: this.hasAttribute('loop') ? true : undefined,
|
|
2389
|
+
rate: this.num('rate', 1),
|
|
2390
|
+
onFinish: () => this.dispatchEvent(new CustomEvent('usa-player-finish', { bubbles: true })),
|
|
2391
|
+
});
|
|
2392
|
+
this.removeAttribute('data-error');
|
|
2393
|
+
}
|
|
2394
|
+
catch (err) {
|
|
2395
|
+
this.setAttribute('data-error', String(err.message || err));
|
|
2396
|
+
return;
|
|
2397
|
+
}
|
|
2398
|
+
const p = this.player;
|
|
2399
|
+
this.dispatchEvent(new CustomEvent('usa-player-ready', { detail: { duration: p.duration }, bubbles: true }));
|
|
2400
|
+
const trig = this.str('trigger', 'view');
|
|
2401
|
+
if (trig === 'load')
|
|
2402
|
+
p.play();
|
|
2403
|
+
else if (trig === 'view')
|
|
2404
|
+
this.inView((v) => v && p.play(), { threshold: 0.25 });
|
|
2405
|
+
else if (trig === 'click')
|
|
2406
|
+
this.listen(this, 'click', () => (p.playing ? p.pause() : p.play()));
|
|
2407
|
+
else if (trig === 'scroll') {
|
|
2408
|
+
let f = 0;
|
|
2409
|
+
const upd = () => {
|
|
2410
|
+
f = 0;
|
|
2411
|
+
p.seek(storyProgress(this) * p.duration);
|
|
2412
|
+
};
|
|
2413
|
+
const kick = () => void (f || (f = requestAnimationFrame(upd)));
|
|
2414
|
+
this.listen(window, 'scroll', kick, { passive: true });
|
|
2415
|
+
this.listen(window, 'resize', kick);
|
|
2416
|
+
this.onCleanup(() => cancelAnimationFrame(f));
|
|
2417
|
+
upd();
|
|
2418
|
+
}
|
|
2419
|
+
if (this.flag('controls'))
|
|
2420
|
+
this.mountControls(p);
|
|
2421
|
+
}
|
|
2422
|
+
mountControls(p) {
|
|
2423
|
+
const b = document.createElement('button');
|
|
2424
|
+
b.type = 'button';
|
|
2425
|
+
b.setAttribute('data-player-toggle', '');
|
|
2426
|
+
b.textContent = '▶︎ / ❚❚';
|
|
2427
|
+
b.setAttribute('aria-label', 'Play / pause animation');
|
|
2428
|
+
this.listen(b, 'click', (e) => {
|
|
2429
|
+
e.stopPropagation();
|
|
2430
|
+
if (p.playing)
|
|
2431
|
+
p.pause();
|
|
2432
|
+
else
|
|
2433
|
+
p.play();
|
|
2434
|
+
b.setAttribute('aria-pressed', String(p.playing));
|
|
2435
|
+
});
|
|
2436
|
+
this.append(b);
|
|
2437
|
+
this.onCleanup(() => b.remove());
|
|
2438
|
+
}
|
|
2439
|
+
changed(name) {
|
|
2440
|
+
if (name === 'src')
|
|
2441
|
+
this.json = null;
|
|
2442
|
+
super.changed(name);
|
|
2443
|
+
}
|
|
2444
|
+
mount() {
|
|
2445
|
+
this.onCleanup(() => {
|
|
2446
|
+
this.player?.destroy();
|
|
2447
|
+
this.player = null;
|
|
2448
|
+
});
|
|
2449
|
+
const inline = this.querySelector('script[type="application/json"]');
|
|
2450
|
+
const src = this.str('src');
|
|
2451
|
+
if (inline && !this.json)
|
|
2452
|
+
this.json = inline.textContent || '';
|
|
2453
|
+
if (src && !this.json) {
|
|
2454
|
+
fetch(src)
|
|
2455
|
+
.then((r) => (r.ok ? r.text() : Promise.reject(new Error(`HTTP ${r.status}`))))
|
|
2456
|
+
.then((t) => this.isConnected && this.load(t))
|
|
2457
|
+
.catch((err) => this.setAttribute('data-error', String(err.message || err)));
|
|
2458
|
+
return;
|
|
2459
|
+
}
|
|
2460
|
+
this.start();
|
|
2461
|
+
}
|
|
2462
|
+
}, { 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}' });
|
|
2463
|
+
}
|
|
2464
|
+
|
|
2165
2465
|
/**
|
|
2166
2466
|
* use-scroll-animate/components/effects — the 5.x effect packs, all
|
|
2167
2467
|
* registered through `registerEffect()` (5.0) and playable with
|
|
@@ -2220,6 +2520,7 @@ function defineEffectElements() {
|
|
|
2220
2520
|
defineAudio();
|
|
2221
2521
|
defineGestureFx();
|
|
2222
2522
|
defineTheme();
|
|
2523
|
+
definePlayer();
|
|
2223
2524
|
}
|
|
2224
2525
|
/** Register the built-ins and every pack (idempotent). */
|
|
2225
2526
|
function registerAllEffects() {
|
|
@@ -2228,6 +2529,7 @@ function registerAllEffects() {
|
|
|
2228
2529
|
components_fx.registerEffects(defs);
|
|
2229
2530
|
}
|
|
2230
2531
|
|
|
2532
|
+
exports.ANIMATION_FORMAT = ANIMATION_FORMAT;
|
|
2231
2533
|
exports.AUDIO_FX = AUDIO_FX;
|
|
2232
2534
|
exports.CARD_FX = CARD_FX;
|
|
2233
2535
|
exports.CLICK_FX = CLICK_FX;
|
|
@@ -2251,9 +2553,11 @@ exports.bounceKeyframes = bounceKeyframes;
|
|
|
2251
2553
|
exports.bumpCount = bumpCount;
|
|
2252
2554
|
exports.canvasBackground = canvasBackground;
|
|
2253
2555
|
exports.createBeatDetector = createBeatDetector;
|
|
2556
|
+
exports.createPlayer = createPlayer;
|
|
2254
2557
|
exports.defineAudio = defineAudio;
|
|
2255
2558
|
exports.defineEffectElements = defineEffectElements;
|
|
2256
2559
|
exports.defineGestureFx = defineGestureFx;
|
|
2560
|
+
exports.definePlayer = definePlayer;
|
|
2257
2561
|
exports.defineStory = defineStory;
|
|
2258
2562
|
exports.defineTheme = defineTheme;
|
|
2259
2563
|
exports.disableAudio = disableAudio;
|
|
@@ -2264,6 +2568,7 @@ exports.fxLayer = fxLayer;
|
|
|
2264
2568
|
exports.getAudio = getAudio;
|
|
2265
2569
|
exports.hexRgb = hexRgb;
|
|
2266
2570
|
exports.noise2 = noise2;
|
|
2571
|
+
exports.normalizeAnimation = normalizeAnimation;
|
|
2267
2572
|
exports.onBeat = onBeat;
|
|
2268
2573
|
exports.playThemeEffect = playThemeEffect;
|
|
2269
2574
|
exports.registerAllEffects = registerAllEffects;
|