use-scroll-animate 6.1.1 → 6.2.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 +19 -0
- package/dist/chunks/{base-Yno7N2eC.js → base-C_3cAoRz.js} +2 -2
- package/dist/chunks/{base-Yno7N2eC.js.map → base-C_3cAoRz.js.map} +1 -1
- package/dist/chunks/{core-CUGCsMsJ.js → core-VEBw36QK.js} +2 -2
- package/dist/chunks/{core-CUGCsMsJ.js.map → core-VEBw36QK.js.map} +1 -1
- package/dist/chunks/{core-BIOaYdgN.js → core-ps8uvYuN.js} +2 -2
- package/dist/chunks/{core-BIOaYdgN.js.map → core-ps8uvYuN.js.map} +1 -1
- package/dist/chunks/{fx-BqDeFywO.js → fx-Bs6IrV4Q.js} +2 -2
- package/dist/chunks/{fx-BqDeFywO.js.map → fx-Bs6IrV4Q.js.map} +1 -1
- package/dist/chunks/generative-D2YyhaeO.js +336 -0
- package/dist/chunks/generative-D2YyhaeO.js.map +1 -0
- package/dist/chunks/generative-DzIZq-_g.cjs +348 -0
- package/dist/chunks/generative-DzIZq-_g.cjs.map +1 -0
- package/dist/chunks/gpu-Bu7qwugO.js +380 -0
- package/dist/chunks/gpu-Bu7qwugO.js.map +1 -0
- package/dist/chunks/gpu-CenK2l6b.cjs +387 -0
- package/dist/chunks/gpu-CenK2l6b.cjs.map +1 -0
- package/dist/chunks/registry-CKNLQpwd.js +121 -0
- package/dist/chunks/registry-CKNLQpwd.js.map +1 -0
- package/dist/chunks/registry-DehBVRDV.cjs +131 -0
- package/dist/chunks/registry-DehBVRDV.cjs.map +1 -0
- package/dist/chunks/{spring-DfQO5USi.js → spring-CbvHfVtP.js} +2 -2
- package/dist/chunks/{spring-DfQO5USi.js.map → spring-CbvHfVtP.js.map} +1 -1
- package/dist/chunks/{variants-VNscPeTD.js → variants-DRlKVHvu.js} +2 -2
- package/dist/chunks/{variants-VNscPeTD.js.map → variants-DRlKVHvu.js.map} +1 -1
- package/dist/components/a11y.js +2 -2
- package/dist/components/angular.cjs +1 -0
- package/dist/components/angular.cjs.map +1 -1
- package/dist/components/angular.js +7 -6
- package/dist/components/angular.js.map +1 -1
- package/dist/components/background.js +2 -2
- package/dist/components/bridge.js +1 -1
- package/dist/components/cards.js +2 -2
- package/dist/components/click.js +3 -3
- package/dist/components/depth.js +3 -3
- package/dist/components/effects.cjs +71 -403
- package/dist/components/effects.cjs.map +1 -1
- package/dist/components/effects.js +8 -339
- package/dist/components/effects.js.map +1 -1
- package/dist/components/feedback.js +1 -1
- package/dist/components/fx-gpu.cjs +12 -0
- package/dist/components/fx-gpu.cjs.map +1 -0
- package/dist/components/fx-gpu.d.cts +63 -0
- package/dist/components/fx-gpu.d.ts +63 -0
- package/dist/components/fx-gpu.js +5 -0
- package/dist/components/fx-gpu.js.map +1 -0
- package/dist/components/fx.cjs +13 -133
- package/dist/components/fx.cjs.map +1 -1
- package/dist/components/fx.js +6 -125
- package/dist/components/fx.js.map +1 -1
- package/dist/components/fx2.cjs +25 -0
- package/dist/components/fx2.cjs.map +1 -0
- package/dist/components/fx2.d.cts +136 -0
- package/dist/components/fx2.d.ts +136 -0
- package/dist/components/fx2.js +17 -0
- package/dist/components/fx2.js.map +1 -0
- package/dist/components/gesture.js +4 -4
- package/dist/components/interaction.js +1 -1
- package/dist/components/layout.js +1 -1
- package/dist/components/lite.cjs +5 -9
- package/dist/components/lite.cjs.map +1 -1
- package/dist/components/lite.js +5 -9
- package/dist/components/lite.js.map +1 -1
- package/dist/components/packs.js +1 -1
- package/dist/components/page.js +3 -3
- package/dist/components/perf.js +2 -2
- package/dist/components/physics.js +3 -3
- package/dist/components/reveal.js +1 -1
- package/dist/components/solid.cjs +1 -0
- package/dist/components/solid.cjs.map +1 -1
- package/dist/components/solid.js +7 -6
- package/dist/components/solid.js.map +1 -1
- package/dist/components/svelte.cjs +1 -0
- package/dist/components/svelte.cjs.map +1 -1
- package/dist/components/svelte.js +7 -6
- package/dist/components/svelte.js.map +1 -1
- package/dist/components/svg.js +1 -1
- package/dist/components/text.js +2 -2
- package/dist/components/timeline.js +3 -3
- package/dist/components/transitions.js +1 -1
- package/dist/components/ui.js +4 -4
- package/dist/components/vue.cjs +1 -0
- package/dist/components/vue.cjs.map +1 -1
- package/dist/components/vue.js +7 -6
- package/dist/components/vue.js.map +1 -1
- package/dist/components/webgl.js +1 -1
- package/dist/components/widgets.cjs +591 -0
- package/dist/components/widgets.cjs.map +1 -0
- package/dist/components/widgets.d.cts +110 -0
- package/dist/components/widgets.d.ts +110 -0
- package/dist/components/widgets.js +581 -0
- package/dist/components/widgets.js.map +1 -0
- package/dist/components.cjs +10 -9
- package/dist/components.cjs.map +1 -1
- package/dist/components.js +10 -9
- package/dist/components.js.map +1 -1
- package/dist/components.umd.js +2 -2
- package/dist/components.umd.js.map +1 -1
- package/dist/index.js +2 -2
- package/dist/widgets.umd.js +2 -0
- package/dist/widgets.umd.js.map +1 -0
- package/docs/ROADMAP.md +10 -11
- package/docs/components.md +29 -0
- package/package.json +31 -1
|
@@ -1,59 +1,13 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
var components_fx = require('./fx.cjs');
|
|
4
|
+
var generative = require('../chunks/generative-DzIZq-_g.cjs');
|
|
4
5
|
var base = require('../chunks/base-BaQV-2ha.cjs');
|
|
6
|
+
var registry = require('../chunks/registry-DehBVRDV.cjs');
|
|
5
7
|
var components_tokens = require('./tokens.cjs');
|
|
6
8
|
var core = require('../chunks/core-BGAyaY6L.cjs');
|
|
7
9
|
require('../chunks/fx-lBGVtQO1.cjs');
|
|
8
10
|
|
|
9
|
-
let layer = null;
|
|
10
|
-
/** A fixed, pointer-transparent, aria-hidden layer for transient particles. */
|
|
11
|
-
function fxLayer() {
|
|
12
|
-
if (layer?.isConnected)
|
|
13
|
-
return layer;
|
|
14
|
-
layer = document.createElement('div');
|
|
15
|
-
layer.setAttribute('aria-hidden', 'true');
|
|
16
|
-
layer.setAttribute('data-usa-fx-layer', '');
|
|
17
|
-
layer.style.cssText = 'position:fixed;inset:0;pointer-events:none;overflow:hidden;z-index:2147483000;contain:strict';
|
|
18
|
-
document.body.appendChild(layer);
|
|
19
|
-
return layer;
|
|
20
|
-
}
|
|
21
|
-
/** Client point of the triggering pointer event, or the element's center. */
|
|
22
|
-
function origin(el, ctx) {
|
|
23
|
-
const e = ctx.event;
|
|
24
|
-
const r = el.getBoundingClientRect();
|
|
25
|
-
return e && typeof e.clientX === 'number' && (e.clientX || e.clientY) ? { x: e.clientX, y: e.clientY } : { x: r.left + r.width / 2, y: r.top + r.height / 2 };
|
|
26
|
-
}
|
|
27
|
-
/** Spawn an absolutely positioned node in the fx layer at (x, y); removed when its animation ends. */
|
|
28
|
-
function spawn(x, y, css, ctx, frames, opts, text = '') {
|
|
29
|
-
const n = document.createElement('span');
|
|
30
|
-
n.style.cssText = `position:absolute;left:${x}px;top:${y}px;${css}`;
|
|
31
|
-
if (text)
|
|
32
|
-
n.textContent = text;
|
|
33
|
-
fxLayer().appendChild(n);
|
|
34
|
-
const a = ctx.animate(n, frames, { fill: 'forwards', ...opts });
|
|
35
|
-
const rm = () => n.remove();
|
|
36
|
-
if (a)
|
|
37
|
-
a.finished.then(rm, rm);
|
|
38
|
-
else
|
|
39
|
-
rm();
|
|
40
|
-
return a;
|
|
41
|
-
}
|
|
42
|
-
/** An overlay child covering `el` (makes `el` a positioning context). Returns it and a remover. */
|
|
43
|
-
function overlay(el, css) {
|
|
44
|
-
const o = document.createElement('span');
|
|
45
|
-
o.setAttribute('aria-hidden', 'true');
|
|
46
|
-
o.style.cssText = `position:absolute;inset:0;pointer-events:none;border-radius:inherit;${css}`;
|
|
47
|
-
if (getComputedStyle(el).position === 'static')
|
|
48
|
-
el.style.position = 'relative';
|
|
49
|
-
el.appendChild(o);
|
|
50
|
-
return [o, () => o.remove()];
|
|
51
|
-
}
|
|
52
|
-
const PALETTE = ['#7c5cff', '#ff5c8a', '#22d3ee', '#facc15', '#34d399'];
|
|
53
|
-
const rand = (a, b) => a + Math.random() * (b - a);
|
|
54
|
-
/** Wait for all animations (ignoring nulls). */
|
|
55
|
-
const all = (anims) => Promise.all(anims.filter(Boolean).map((a) => a.finished.catch(() => undefined)));
|
|
56
|
-
|
|
57
11
|
const CARD_FX = [
|
|
58
12
|
{
|
|
59
13
|
name: 'holo',
|
|
@@ -62,7 +16,7 @@ const CARD_FX = [
|
|
|
62
16
|
reduced: 'run',
|
|
63
17
|
defaults: { strength: 0.55 },
|
|
64
18
|
run: (el, o, ctx) => {
|
|
65
|
-
const [layer, remove] = overlay(el, `mix-blend-mode:color-dodge;opacity:${o.strength};background:linear-gradient(115deg,transparent 20%,#ff8bd855 35%,#8bf3ff55 45%,#fff58b55 55%,transparent 70%),repeating-linear-gradient(55deg,#ffffff10 0 2px,transparent 2px 6px);background-size:250% 250%,100% 100%;background-position:50% 50%;transition:background-position .2s ease-out`);
|
|
19
|
+
const [layer, remove] = generative.overlay(el, `mix-blend-mode:color-dodge;opacity:${o.strength};background:linear-gradient(115deg,transparent 20%,#ff8bd855 35%,#8bf3ff55 45%,#fff58b55 55%,transparent 70%),repeating-linear-gradient(55deg,#ffffff10 0 2px,transparent 2px 6px);background-size:250% 250%,100% 100%;background-position:50% 50%;transition:background-position .2s ease-out`);
|
|
66
20
|
if (ctx.reduced)
|
|
67
21
|
return remove;
|
|
68
22
|
const move = (e) => {
|
|
@@ -94,7 +48,7 @@ const CARD_FX = [
|
|
|
94
48
|
run: (el, o, ctx) => {
|
|
95
49
|
if (ctx.reduced)
|
|
96
50
|
return;
|
|
97
|
-
const [g, remove] = overlay(el, `overflow:hidden;background:linear-gradient(105deg,transparent 35%,${o.color} 50%,transparent 65%);background-size:250% 100%`);
|
|
51
|
+
const [g, remove] = generative.overlay(el, `overflow:hidden;background:linear-gradient(105deg,transparent 35%,${o.color} 50%,transparent 65%);background-size:250% 100%`);
|
|
98
52
|
const a = ctx.animate(g, [{ backgroundPosition: '150% 0' }, { backgroundPosition: '-50% 0' }], { duration: o.duration, easing: 'ease-in-out' });
|
|
99
53
|
if (a)
|
|
100
54
|
a.finished.then(remove, remove);
|
|
@@ -121,7 +75,7 @@ const CARD_FX = [
|
|
|
121
75
|
run: (el, o, ctx) => {
|
|
122
76
|
const kids = Array.from(el.children);
|
|
123
77
|
const mid = (kids.length - 1) / 2;
|
|
124
|
-
return all(kids.map((k, i) => ctx.animate(k, [{ transform: 'none' }, { transform: `translateX(${(i - mid) * o.spread}px) rotate(${(i - mid) * (o.spread / 2)}deg)`, offset: 0.5 }, { transform: 'none' }], { duration: o.duration, easing: 'cubic-bezier(0.34, 1.56, 0.64, 1)' })));
|
|
78
|
+
return generative.all(kids.map((k, i) => ctx.animate(k, [{ transform: 'none' }, { transform: `translateX(${(i - mid) * o.spread}px) rotate(${(i - mid) * (o.spread / 2)}deg)`, offset: 0.5 }, { transform: 'none' }], { duration: o.duration, easing: 'cubic-bezier(0.34, 1.56, 0.64, 1)' })));
|
|
125
79
|
},
|
|
126
80
|
},
|
|
127
81
|
{
|
|
@@ -154,24 +108,24 @@ const CLICK_FX = [
|
|
|
154
108
|
run: (el, o, ctx) => {
|
|
155
109
|
if (ctx.reduced)
|
|
156
110
|
return;
|
|
157
|
-
const { x, y } = origin(el, ctx);
|
|
158
|
-
return all([0, 120].map((delay) => spawn(x, y, `width:${o.size}px;height:${o.size}px;margin:${-o.size / 2}px 0 0 ${-o.size / 2}px;border:3px solid ${o.color};border-radius:50%`, ctx, [{ transform: 'scale(0)', opacity: 0.9 }, { transform: 'scale(1)', opacity: 0 }], { duration: o.duration, delay, easing: 'cubic-bezier(0.22, 1, 0.36, 1)' })));
|
|
111
|
+
const { x, y } = generative.origin(el, ctx);
|
|
112
|
+
return generative.all([0, 120].map((delay) => generative.spawn(x, y, `width:${o.size}px;height:${o.size}px;margin:${-o.size / 2}px 0 0 ${-o.size / 2}px;border:3px solid ${o.color};border-radius:50%`, ctx, [{ transform: 'scale(0)', opacity: 0.9 }, { transform: 'scale(1)', opacity: 0 }], { duration: o.duration, delay, easing: 'cubic-bezier(0.22, 1, 0.36, 1)' })));
|
|
159
113
|
},
|
|
160
114
|
},
|
|
161
115
|
{
|
|
162
116
|
name: 'ink-splash',
|
|
163
117
|
kind: 'click',
|
|
164
118
|
description: 'Ink blobs splatter from the click point and fade.',
|
|
165
|
-
defaults: { count: 9, colors: PALETTE, duration: 800 },
|
|
119
|
+
defaults: { count: 9, colors: generative.PALETTE, duration: 800 },
|
|
166
120
|
run: (el, o, ctx) => {
|
|
167
121
|
if (ctx.reduced)
|
|
168
122
|
return;
|
|
169
|
-
const { x, y } = origin(el, ctx);
|
|
170
|
-
return all(Array.from({ length: o.count }, (_, i) => {
|
|
171
|
-
const s = rand(6, 22);
|
|
172
|
-
const ang = rand(0, Math.PI * 2);
|
|
173
|
-
const d = rand(20, 70);
|
|
174
|
-
return spawn(x, y, `width:${s}px;height:${s}px;margin:${-s / 2}px 0 0 ${-s / 2}px;background:${o.colors[i % o.colors.length]};border-radius:${rand(40, 50)}% ${rand(50, 60)}% ${rand(40, 60)}% ${rand(45, 55)}%`, ctx, [{ transform: 'translate(0,0) scale(.3)', opacity: 1 }, { transform: `translate(${Math.cos(ang) * d}px,${Math.sin(ang) * d}px) scale(1)`, opacity: 0.9, offset: 0.5 }, { transform: `translate(${Math.cos(ang) * d}px,${Math.sin(ang) * d + 8}px) scale(1.1)`, opacity: 0 }], { duration: o.duration, easing: 'cubic-bezier(0.22, 1, 0.36, 1)' });
|
|
123
|
+
const { x, y } = generative.origin(el, ctx);
|
|
124
|
+
return generative.all(Array.from({ length: o.count }, (_, i) => {
|
|
125
|
+
const s = generative.rand(6, 22);
|
|
126
|
+
const ang = generative.rand(0, Math.PI * 2);
|
|
127
|
+
const d = generative.rand(20, 70);
|
|
128
|
+
return generative.spawn(x, y, `width:${s}px;height:${s}px;margin:${-s / 2}px 0 0 ${-s / 2}px;background:${o.colors[i % o.colors.length]};border-radius:${generative.rand(40, 50)}% ${generative.rand(50, 60)}% ${generative.rand(40, 60)}% ${generative.rand(45, 55)}%`, ctx, [{ transform: 'translate(0,0) scale(.3)', opacity: 1 }, { transform: `translate(${Math.cos(ang) * d}px,${Math.sin(ang) * d}px) scale(1)`, opacity: 0.9, offset: 0.5 }, { transform: `translate(${Math.cos(ang) * d}px,${Math.sin(ang) * d + 8}px) scale(1.1)`, opacity: 0 }], { duration: o.duration, easing: 'cubic-bezier(0.22, 1, 0.36, 1)' });
|
|
175
129
|
}));
|
|
176
130
|
},
|
|
177
131
|
},
|
|
@@ -183,12 +137,12 @@ const CLICK_FX = [
|
|
|
183
137
|
run: (el, o, ctx) => {
|
|
184
138
|
if (ctx.reduced)
|
|
185
139
|
return;
|
|
186
|
-
const { x, y } = origin(el, ctx);
|
|
187
|
-
return all(Array.from({ length: o.count }, (_, i) => {
|
|
140
|
+
const { x, y } = generative.origin(el, ctx);
|
|
141
|
+
return generative.all(Array.from({ length: o.count }, (_, i) => {
|
|
188
142
|
const ang = (i / o.count) * Math.PI * 2;
|
|
189
|
-
const d = rand(40, 80);
|
|
190
|
-
const s = rand(12, 22);
|
|
191
|
-
return spawn(x, y, `font-size:${s}px;line-height:1;margin:${-s / 2}px 0 0 ${-s / 2}px;color:${o.colors[i % o.colors.length]}`, ctx, [{ transform: 'translate(0,0) rotate(0) scale(.2)', opacity: 1 }, { transform: `translate(${Math.cos(ang) * d}px,${Math.sin(ang) * d}px) rotate(${rand(180, 360)}deg) scale(1)`, opacity: 0 }], { duration: o.duration, easing: 'cubic-bezier(0.22, 1, 0.36, 1)' }, '★');
|
|
143
|
+
const d = generative.rand(40, 80);
|
|
144
|
+
const s = generative.rand(12, 22);
|
|
145
|
+
return generative.spawn(x, y, `font-size:${s}px;line-height:1;margin:${-s / 2}px 0 0 ${-s / 2}px;color:${o.colors[i % o.colors.length]}`, ctx, [{ transform: 'translate(0,0) rotate(0) scale(.2)', opacity: 1 }, { transform: `translate(${Math.cos(ang) * d}px,${Math.sin(ang) * d}px) rotate(${generative.rand(180, 360)}deg) scale(1)`, opacity: 0 }], { duration: o.duration, easing: 'cubic-bezier(0.22, 1, 0.36, 1)' }, '★');
|
|
192
146
|
}));
|
|
193
147
|
},
|
|
194
148
|
},
|
|
@@ -207,8 +161,8 @@ const CLICK_FX = [
|
|
|
207
161
|
run: (el, o, ctx) => {
|
|
208
162
|
if (ctx.reduced)
|
|
209
163
|
return;
|
|
210
|
-
const { x, y } = origin(el, ctx);
|
|
211
|
-
return all(Array.from({ length: o.rings }, (_, i) => spawn(x, y, `width:${o.size}px;height:${o.size}px;margin:${-o.size / 2}px 0 0 ${-o.size / 2}px;border:2px solid ${o.color};border-radius:50%`, ctx, [{ transform: 'scale(.1)', opacity: 0.8 }, { transform: 'scale(1)', opacity: 0 }], { duration: o.duration, delay: i * 160, easing: 'ease-out' })));
|
|
164
|
+
const { x, y } = generative.origin(el, ctx);
|
|
165
|
+
return generative.all(Array.from({ length: o.rings }, (_, i) => generative.spawn(x, y, `width:${o.size}px;height:${o.size}px;margin:${-o.size / 2}px 0 0 ${-o.size / 2}px;border:2px solid ${o.color};border-radius:50%`, ctx, [{ transform: 'scale(.1)', opacity: 0.8 }, { transform: 'scale(1)', opacity: 0 }], { duration: o.duration, delay: i * 160, easing: 'ease-out' })));
|
|
212
166
|
},
|
|
213
167
|
},
|
|
214
168
|
{
|
|
@@ -219,11 +173,11 @@ const CLICK_FX = [
|
|
|
219
173
|
run: (el, o, ctx) => {
|
|
220
174
|
if (ctx.reduced)
|
|
221
175
|
return;
|
|
222
|
-
const { x, y } = origin(el, ctx);
|
|
176
|
+
const { x, y } = generative.origin(el, ctx);
|
|
223
177
|
const chars = Array.from(String(o.emoji));
|
|
224
|
-
return all(Array.from({ length: o.count }, (_, i) => {
|
|
225
|
-
const dx = rand(-90, 90);
|
|
226
|
-
return spawn(x, y, `font-size:${rand(16, 28)}px;line-height:1`, ctx, [{ transform: 'translate(0,0) scale(.4)', opacity: 0 }, { transform: `translate(${dx * 0.6}px,${rand(-90, -50)}px) scale(1)`, opacity: 1, offset: 0.35 }, { transform: `translate(${dx}px,${rand(40, 90)}px) rotate(${rand(-60, 60)}deg) scale(.9)`, opacity: 0 }], { duration: o.duration, delay: i * 25, easing: 'cubic-bezier(0.33, 0, 0.67, 1)' }, chars[i % chars.length]);
|
|
178
|
+
return generative.all(Array.from({ length: o.count }, (_, i) => {
|
|
179
|
+
const dx = generative.rand(-90, 90);
|
|
180
|
+
return generative.spawn(x, y, `font-size:${generative.rand(16, 28)}px;line-height:1`, ctx, [{ transform: 'translate(0,0) scale(.4)', opacity: 0 }, { transform: `translate(${dx * 0.6}px,${generative.rand(-90, -50)}px) scale(1)`, opacity: 1, offset: 0.35 }, { transform: `translate(${dx}px,${generative.rand(40, 90)}px) rotate(${generative.rand(-60, 60)}deg) scale(.9)`, opacity: 0 }], { duration: o.duration, delay: i * 25, easing: 'cubic-bezier(0.33, 0, 0.67, 1)' }, chars[i % chars.length]);
|
|
227
181
|
}));
|
|
228
182
|
},
|
|
229
183
|
},
|
|
@@ -370,7 +324,7 @@ const PHYSICS_FX = [
|
|
|
370
324
|
el.dataset.usaSplit = '1';
|
|
371
325
|
}
|
|
372
326
|
const hs = bounceKeyframes(o.bounce, 32);
|
|
373
|
-
return all(Array.from(el.children).map((s, i) => ctx.animate(s, hs.map((h) => ({ transform: `translateY(${(-h * o.height).toFixed(1)}px)` })), { duration: o.duration, delay: i * o.stagger, easing: 'linear', fill: 'backwards' })));
|
|
327
|
+
return generative.all(Array.from(el.children).map((s, i) => ctx.animate(s, hs.map((h) => ({ transform: `translateY(${(-h * o.height).toFixed(1)}px)` })), { duration: o.duration, delay: i * o.stagger, easing: 'linear', fill: 'backwards' })));
|
|
374
328
|
},
|
|
375
329
|
},
|
|
376
330
|
{
|
|
@@ -504,7 +458,7 @@ const PAGE_FX = [
|
|
|
504
458
|
reduced: 'run',
|
|
505
459
|
defaults: TRANSITION_DEFAULTS,
|
|
506
460
|
run: transition((s, o, ctx, el) => {
|
|
507
|
-
const { x, y } = origin(el, ctx);
|
|
461
|
+
const { x, y } = generative.origin(el, ctx);
|
|
508
462
|
const r = Math.hypot(Math.max(x, innerWidth - x), Math.max(y, innerHeight - y)) + 2;
|
|
509
463
|
// a huge box-shadow ring around a hole: the hole shrinks to 0 to cover
|
|
510
464
|
const p = part(s, `left:${x}px;top:${y}px;width:0;height:0;border-radius:50%;box-shadow:0 0 0 ${Math.ceil(r)}px ${o.color}`);
|
|
@@ -829,292 +783,6 @@ function defineStory(tag = 'usa-story') {
|
|
|
829
783
|
}, { id: 'usa-story', text: CSS });
|
|
830
784
|
}
|
|
831
785
|
|
|
832
|
-
/** Deterministic smooth pseudo-noise in [-1, 1] (sum of sines — cheap, no tables). */
|
|
833
|
-
function noise2(x, y, t = 0) {
|
|
834
|
-
return (Math.sin(x * 1.7 + t) * Math.cos(y * 1.3 - t * 0.7) + Math.sin((x + y) * 0.9 + t * 0.5) * 0.5 + Math.cos(x * 0.6 - y * 1.1 - t * 0.3) * 0.5) / 2;
|
|
835
|
-
}
|
|
836
|
-
/** Hex `#rrggbb` → [r, g, b]. */
|
|
837
|
-
function hexRgb(hex) {
|
|
838
|
-
const m = /^#?([\da-f]{2})([\da-f]{2})([\da-f]{2})$/i.exec(hex.trim());
|
|
839
|
-
return m ? [parseInt(m[1], 16), parseInt(m[2], 16), parseInt(m[3], 16)] : [124, 92, 255];
|
|
840
|
-
}
|
|
841
|
-
/**
|
|
842
|
-
* Mount a generative canvas behind `el` and run `spec` on it. Returns the
|
|
843
|
-
* cleanup. Exposed for custom generative effects.
|
|
844
|
-
*/
|
|
845
|
-
function canvasBackground(el, fx, spec, o) {
|
|
846
|
-
const canvas = document.createElement('canvas');
|
|
847
|
-
canvas.setAttribute('aria-hidden', 'true');
|
|
848
|
-
canvas.setAttribute('data-usa-fx-canvas', '');
|
|
849
|
-
canvas.style.cssText = 'position:absolute;inset:0;width:100%;height:100%;pointer-events:none;z-index:-1;border-radius:inherit';
|
|
850
|
-
const restore = [];
|
|
851
|
-
const set = (k, v) => {
|
|
852
|
-
restore.push([k, el.style[k]]);
|
|
853
|
-
el.style[k] = v;
|
|
854
|
-
};
|
|
855
|
-
if (getComputedStyle(el).position === 'static')
|
|
856
|
-
set('position', 'relative');
|
|
857
|
-
set('isolation', 'isolate');
|
|
858
|
-
el.prepend(canvas);
|
|
859
|
-
const ctx = canvas.getContext('2d');
|
|
860
|
-
let raf = 0;
|
|
861
|
-
let visible = true;
|
|
862
|
-
let quality = Math.min(1, Math.max(0.35, Number(o.quality) || 1));
|
|
863
|
-
let w = 0;
|
|
864
|
-
let h = 0;
|
|
865
|
-
let state = null;
|
|
866
|
-
const t0 = performance.now();
|
|
867
|
-
let last = t0;
|
|
868
|
-
let slow = 0;
|
|
869
|
-
let fast = 0;
|
|
870
|
-
const resize = () => {
|
|
871
|
-
const r = el.getBoundingClientRect();
|
|
872
|
-
w = Math.max(1, Math.round(r.width));
|
|
873
|
-
h = Math.max(1, Math.round(r.height));
|
|
874
|
-
const dpr = Math.min(2, (typeof devicePixelRatio === 'number' ? devicePixelRatio : 1) || 1) * quality;
|
|
875
|
-
canvas.width = Math.max(1, Math.round(w * dpr));
|
|
876
|
-
canvas.height = Math.max(1, Math.round(h * dpr));
|
|
877
|
-
if (ctx)
|
|
878
|
-
ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
|
|
879
|
-
state = spec.init ? spec.init(w, h, o) : {};
|
|
880
|
-
};
|
|
881
|
-
const frame = (now) => {
|
|
882
|
-
raf = 0;
|
|
883
|
-
if (!ctx)
|
|
884
|
-
return;
|
|
885
|
-
const dt = now - last;
|
|
886
|
-
last = now;
|
|
887
|
-
// adaptive quality: sustained slow frames lower the render scale, fast ones raise it
|
|
888
|
-
if (dt > 28)
|
|
889
|
-
slow++;
|
|
890
|
-
else if (dt < 18)
|
|
891
|
-
fast++;
|
|
892
|
-
if (slow > 20 && quality > 0.35) {
|
|
893
|
-
quality = Math.max(0.35, quality - 0.15);
|
|
894
|
-
slow = fast = 0;
|
|
895
|
-
resize();
|
|
896
|
-
}
|
|
897
|
-
else if (fast > 240 && quality < 1) {
|
|
898
|
-
quality = Math.min(1, quality + 0.15);
|
|
899
|
-
slow = fast = 0;
|
|
900
|
-
resize();
|
|
901
|
-
}
|
|
902
|
-
spec.draw({ ctx, w, h, t: (now - t0) / 1000, quality, state, o });
|
|
903
|
-
if (visible && !document.hidden)
|
|
904
|
-
raf = requestAnimationFrame(frame);
|
|
905
|
-
};
|
|
906
|
-
const start = () => {
|
|
907
|
-
if (!raf && ctx && !fx.reduced) {
|
|
908
|
-
last = performance.now();
|
|
909
|
-
raf = requestAnimationFrame(frame);
|
|
910
|
-
}
|
|
911
|
-
};
|
|
912
|
-
resize();
|
|
913
|
-
if (ctx)
|
|
914
|
-
spec.draw({ ctx, w, h, t: 0, quality, state, o }); // first / static frame
|
|
915
|
-
const io = typeof IntersectionObserver === 'function' ? new IntersectionObserver((es) => {
|
|
916
|
-
visible = es.some((e) => e.isIntersecting);
|
|
917
|
-
if (visible)
|
|
918
|
-
start();
|
|
919
|
-
}) : null;
|
|
920
|
-
io?.observe(el);
|
|
921
|
-
const ro = typeof ResizeObserver === 'function' ? new ResizeObserver(() => {
|
|
922
|
-
resize();
|
|
923
|
-
if (ctx && fx.reduced)
|
|
924
|
-
spec.draw({ ctx, w, h, t: 0, quality, state, o });
|
|
925
|
-
}) : null;
|
|
926
|
-
ro?.observe(el);
|
|
927
|
-
const vis = () => !document.hidden && visible && start();
|
|
928
|
-
document.addEventListener('visibilitychange', vis);
|
|
929
|
-
start();
|
|
930
|
-
return () => {
|
|
931
|
-
cancelAnimationFrame(raf);
|
|
932
|
-
raf = 0;
|
|
933
|
-
io?.disconnect();
|
|
934
|
-
ro?.disconnect();
|
|
935
|
-
document.removeEventListener('visibilitychange', vis);
|
|
936
|
-
canvas.remove();
|
|
937
|
-
for (const [k, v] of restore)
|
|
938
|
-
el.style[k] = v;
|
|
939
|
-
};
|
|
940
|
-
}
|
|
941
|
-
const gen = (name, description, defaults, spec) => ({
|
|
942
|
-
name,
|
|
943
|
-
kind: 'background',
|
|
944
|
-
description,
|
|
945
|
-
reduced: 'run',
|
|
946
|
-
defaults: { colors: PALETTE, background: '#0b0d12', speed: 1, quality: 1, ...defaults },
|
|
947
|
-
run: (el, o, ctx) => canvasBackground(el, ctx, spec, o),
|
|
948
|
-
});
|
|
949
|
-
/** Low-resolution scalar field → ImageData, drawn scaled up (used by voronoi / metaballs / contours). */
|
|
950
|
-
function field(f, cell, px) {
|
|
951
|
-
const step = Math.max(2, Math.round(cell / f.quality));
|
|
952
|
-
const cw = Math.max(1, Math.ceil(f.w / step));
|
|
953
|
-
const ch = Math.max(1, Math.ceil(f.h / step));
|
|
954
|
-
const st = f.state;
|
|
955
|
-
if (!st.buf || st.buf.width !== cw || st.buf.height !== ch) {
|
|
956
|
-
st.buf = document.createElement('canvas');
|
|
957
|
-
st.buf.width = cw;
|
|
958
|
-
st.buf.height = ch;
|
|
959
|
-
st.bctx = st.buf.getContext('2d');
|
|
960
|
-
st.img = st.bctx?.createImageData(cw, ch);
|
|
961
|
-
}
|
|
962
|
-
if (!st.bctx || !st.img)
|
|
963
|
-
return;
|
|
964
|
-
const d = st.img.data;
|
|
965
|
-
for (let y = 0; y < ch; y++)
|
|
966
|
-
for (let x = 0; x < cw; x++)
|
|
967
|
-
px(x * step, y * step, d, (y * cw + x) * 4);
|
|
968
|
-
st.bctx.putImageData(st.img, 0, 0);
|
|
969
|
-
f.ctx.imageSmoothingEnabled = true;
|
|
970
|
-
f.ctx.drawImage(st.buf, 0, 0, f.w, f.h);
|
|
971
|
-
}
|
|
972
|
-
const GENERATIVE_FX = [
|
|
973
|
-
gen('flow-field', 'Particles drift along a slowly changing flow field, leaving silky trails.', { count: 600, fade: 0.06 }, {
|
|
974
|
-
init: (w, h, o) => ({ p: Array.from({ length: o.count }, () => [Math.random() * w, Math.random() * h]) }),
|
|
975
|
-
draw: ({ ctx, w, h, t, state, o, quality }) => {
|
|
976
|
-
ctx.fillStyle = o.background;
|
|
977
|
-
ctx.globalAlpha = t === 0 ? 1 : o.fade;
|
|
978
|
-
ctx.fillRect(0, 0, w, h);
|
|
979
|
-
ctx.globalAlpha = 0.8;
|
|
980
|
-
const n = Math.round(state.p.length * quality);
|
|
981
|
-
const steps = t === 0 ? 40 : 1;
|
|
982
|
-
for (let i = 0; i < n; i++) {
|
|
983
|
-
const q = state.p[i];
|
|
984
|
-
ctx.fillStyle = o.colors[i % o.colors.length];
|
|
985
|
-
for (let s = 0; s < steps; s++) {
|
|
986
|
-
const a = noise2(q[0] * 0.004, q[1] * 0.004, t * 0.15 * o.speed) * Math.PI * 2;
|
|
987
|
-
q[0] += Math.cos(a) * 1.2 * o.speed;
|
|
988
|
-
q[1] += Math.sin(a) * 1.2 * o.speed;
|
|
989
|
-
if (q[0] < 0 || q[0] > w || q[1] < 0 || q[1] > h)
|
|
990
|
-
(q[0] = Math.random() * w), (q[1] = Math.random() * h);
|
|
991
|
-
ctx.fillRect(q[0], q[1], 1.4, 1.4);
|
|
992
|
-
}
|
|
993
|
-
}
|
|
994
|
-
ctx.globalAlpha = 1;
|
|
995
|
-
},
|
|
996
|
-
}),
|
|
997
|
-
gen('voronoi', 'Drifting Voronoi cells in the palette, with darker borders.', { seeds: 14, cell: 6 }, {
|
|
998
|
-
init: (w, h, o) => ({ s: Array.from({ length: o.seeds }, (_, i) => ({ x: Math.random() * w, y: Math.random() * h, vx: Math.random() - 0.5, vy: Math.random() - 0.5, c: hexRgb(o.colors[i % o.colors.length]) })) }),
|
|
999
|
-
draw: (f) => {
|
|
1000
|
-
const { w, h, state, o } = f;
|
|
1001
|
-
for (const s of state.s) {
|
|
1002
|
-
s.x += s.vx * o.speed;
|
|
1003
|
-
s.y += s.vy * o.speed;
|
|
1004
|
-
if (s.x < 0 || s.x > w)
|
|
1005
|
-
s.vx *= -1;
|
|
1006
|
-
if (s.y < 0 || s.y > h)
|
|
1007
|
-
s.vy *= -1;
|
|
1008
|
-
}
|
|
1009
|
-
field(f, o.cell, (x, y, d, i) => {
|
|
1010
|
-
let b = Infinity;
|
|
1011
|
-
let b2 = Infinity;
|
|
1012
|
-
let c = state.s[0].c;
|
|
1013
|
-
for (const s of state.s) {
|
|
1014
|
-
const dd = (s.x - x) ** 2 + (s.y - y) ** 2;
|
|
1015
|
-
if (dd < b)
|
|
1016
|
-
(b2 = b), (b = dd), (c = s.c);
|
|
1017
|
-
else if (dd < b2)
|
|
1018
|
-
b2 = dd;
|
|
1019
|
-
}
|
|
1020
|
-
const edge = Math.sqrt(b2) - Math.sqrt(b) < 6 ? 0.55 : 1;
|
|
1021
|
-
d[i] = c[0] * edge;
|
|
1022
|
-
d[i + 1] = c[1] * edge;
|
|
1023
|
-
d[i + 2] = c[2] * edge;
|
|
1024
|
-
d[i + 3] = 255;
|
|
1025
|
-
});
|
|
1026
|
-
},
|
|
1027
|
-
}),
|
|
1028
|
-
gen('mesh-gradient', 'Soft blobs of colour orbit and blend into a living mesh gradient.', { blobs: 4, blur: 0.65 }, {
|
|
1029
|
-
draw: ({ ctx, w, h, t, o }) => {
|
|
1030
|
-
ctx.fillStyle = o.background;
|
|
1031
|
-
ctx.fillRect(0, 0, w, h);
|
|
1032
|
-
const r = Math.max(w, h) * o.blur;
|
|
1033
|
-
for (let i = 0; i < o.blobs; i++) {
|
|
1034
|
-
const a = t * 0.25 * o.speed + (i * Math.PI * 2) / o.blobs;
|
|
1035
|
-
const x = w / 2 + Math.cos(a * (1 + i * 0.13)) * w * 0.32;
|
|
1036
|
-
const y = h / 2 + Math.sin(a * (0.8 + i * 0.11)) * h * 0.32;
|
|
1037
|
-
const [cr, cg, cb] = hexRgb(o.colors[i % o.colors.length]);
|
|
1038
|
-
const g = ctx.createRadialGradient(x, y, 0, x, y, r);
|
|
1039
|
-
g.addColorStop(0, `rgba(${cr},${cg},${cb},0.7)`);
|
|
1040
|
-
g.addColorStop(1, `rgba(${cr},${cg},${cb},0)`);
|
|
1041
|
-
ctx.fillStyle = g;
|
|
1042
|
-
ctx.fillRect(0, 0, w, h);
|
|
1043
|
-
}
|
|
1044
|
-
},
|
|
1045
|
-
}),
|
|
1046
|
-
gen('starfield', 'Stars stream toward the viewer (warp speed with `speed: 4`).', { stars: 400, color: '#ffffff' }, {
|
|
1047
|
-
init: (_w, _h, o) => ({ s: Array.from({ length: o.stars }, () => [Math.random() * 2 - 1, Math.random() * 2 - 1, Math.random()]) }),
|
|
1048
|
-
draw: ({ ctx, w, h, t, state, o, quality }) => {
|
|
1049
|
-
ctx.fillStyle = o.background;
|
|
1050
|
-
ctx.fillRect(0, 0, w, h);
|
|
1051
|
-
ctx.fillStyle = o.color;
|
|
1052
|
-
const n = Math.round(state.s.length * quality);
|
|
1053
|
-
for (let i = 0; i < n; i++) {
|
|
1054
|
-
const s = state.s[i];
|
|
1055
|
-
if (t > 0)
|
|
1056
|
-
s[2] -= 0.004 * o.speed;
|
|
1057
|
-
if (s[2] <= 0.01)
|
|
1058
|
-
(s[0] = Math.random() * 2 - 1), (s[1] = Math.random() * 2 - 1), (s[2] = 1);
|
|
1059
|
-
const x = w / 2 + (s[0] / s[2]) * w * 0.5;
|
|
1060
|
-
const y = h / 2 + (s[1] / s[2]) * h * 0.5;
|
|
1061
|
-
if (x < 0 || x > w || y < 0 || y > h)
|
|
1062
|
-
continue;
|
|
1063
|
-
const size = (1 - s[2]) * 2.6;
|
|
1064
|
-
ctx.globalAlpha = Math.min(1, (1 - s[2]) * 1.6);
|
|
1065
|
-
ctx.fillRect(x, y, size, size);
|
|
1066
|
-
}
|
|
1067
|
-
ctx.globalAlpha = 1;
|
|
1068
|
-
},
|
|
1069
|
-
}),
|
|
1070
|
-
gen('metaballs', 'Gooey blobs that merge and split (low-resolution field, scaled up).', { balls: 6, cell: 5, threshold: 1 }, {
|
|
1071
|
-
init: (w, h, o) => ({ b: Array.from({ length: o.balls }, () => ({ x: Math.random() * w, y: Math.random() * h, vx: (Math.random() - 0.5) * 1.6, vy: (Math.random() - 0.5) * 1.6, r: Math.min(w, h) * (0.08 + Math.random() * 0.08) })) }),
|
|
1072
|
-
draw: (f) => {
|
|
1073
|
-
const { w, h, state, o } = f;
|
|
1074
|
-
for (const b of state.b) {
|
|
1075
|
-
b.x += b.vx * o.speed;
|
|
1076
|
-
b.y += b.vy * o.speed;
|
|
1077
|
-
if (b.x < 0 || b.x > w)
|
|
1078
|
-
b.vx *= -1;
|
|
1079
|
-
if (b.y < 0 || b.y > h)
|
|
1080
|
-
b.vy *= -1;
|
|
1081
|
-
}
|
|
1082
|
-
const [ar, ag, ab] = hexRgb(o.colors[0]);
|
|
1083
|
-
const [br, bg, bb] = hexRgb(o.colors[1] || o.colors[0]);
|
|
1084
|
-
const [kr, kg, kb] = hexRgb(o.background);
|
|
1085
|
-
field(f, o.cell, (x, y, d, i) => {
|
|
1086
|
-
let v = 0;
|
|
1087
|
-
for (const b of state.b)
|
|
1088
|
-
v += (b.r * b.r) / ((b.x - x) ** 2 + (b.y - y) ** 2 + 1);
|
|
1089
|
-
const inside = v >= o.threshold;
|
|
1090
|
-
const k = Math.min(1, (v - o.threshold) * 0.8);
|
|
1091
|
-
d[i] = inside ? ar + (br - ar) * k : kr;
|
|
1092
|
-
d[i + 1] = inside ? ag + (bg - ag) * k : kg;
|
|
1093
|
-
d[i + 2] = inside ? ab + (bb - ab) * k : kb;
|
|
1094
|
-
d[i + 3] = 255;
|
|
1095
|
-
});
|
|
1096
|
-
},
|
|
1097
|
-
}),
|
|
1098
|
-
gen('contours', 'Topographic contour lines over a slowly shifting height field.', { levels: 9, cell: 4, color: '#7c5cff' }, {
|
|
1099
|
-
draw: (f) => {
|
|
1100
|
-
const { t, o } = f;
|
|
1101
|
-
const [lr, lg, lb] = hexRgb(o.color);
|
|
1102
|
-
const [kr, kg, kb] = hexRgb(o.background);
|
|
1103
|
-
const z = (x, y) => (noise2(x * 0.006, y * 0.006, t * 0.2 * o.speed) + 1) / 2;
|
|
1104
|
-
const step = Math.max(2, Math.round(o.cell / f.quality));
|
|
1105
|
-
field(f, o.cell, (x, y, d, i) => {
|
|
1106
|
-
const a = Math.floor(z(x, y) * o.levels);
|
|
1107
|
-
const line = a !== Math.floor(z(x + step, y) * o.levels) || a !== Math.floor(z(x, y + step) * o.levels);
|
|
1108
|
-
const shade = 0.12 + (a / o.levels) * 0.18;
|
|
1109
|
-
d[i] = line ? lr : kr + (lr - kr) * shade;
|
|
1110
|
-
d[i + 1] = line ? lg : kg + (lg - kg) * shade;
|
|
1111
|
-
d[i + 2] = line ? lb : kb + (lb - kb) * shade;
|
|
1112
|
-
d[i + 3] = 255;
|
|
1113
|
-
});
|
|
1114
|
-
},
|
|
1115
|
-
}),
|
|
1116
|
-
];
|
|
1117
|
-
|
|
1118
786
|
let actx = null;
|
|
1119
787
|
let current = null;
|
|
1120
788
|
const mediaSources = new WeakMap();
|
|
@@ -1279,7 +947,7 @@ function bindBeat(el, name, options = {}) {
|
|
|
1279
947
|
const { threshold, cooldown, history, floor, ...fx } = options;
|
|
1280
948
|
return onBeat(() => {
|
|
1281
949
|
if (!base.prefersReducedMotion())
|
|
1282
|
-
|
|
950
|
+
registry.playEffect(el, name, fx).catch(() => undefined);
|
|
1283
951
|
}, { threshold, cooldown, history, floor });
|
|
1284
952
|
}
|
|
1285
953
|
// --- visual effects ----------------------------------------------------------
|
|
@@ -1300,8 +968,8 @@ const audioFx = (name, description, defaults, spec) => ({
|
|
|
1300
968
|
kind: 'background',
|
|
1301
969
|
description,
|
|
1302
970
|
reduced: 'skip',
|
|
1303
|
-
defaults: { colors: PALETTE, background: '#0b0d12', speed: 1, quality: 1, ...defaults },
|
|
1304
|
-
run: (el, o, ctx) => canvasBackground(el, ctx, spec, o),
|
|
971
|
+
defaults: { colors: generative.PALETTE, background: '#0b0d12', speed: 1, quality: 1, ...defaults },
|
|
972
|
+
run: (el, o, ctx) => generative.canvasBackground(el, ctx, spec, o),
|
|
1305
973
|
});
|
|
1306
974
|
const AUDIO_FX = [
|
|
1307
975
|
audioFx('spectrum-bars', 'Frequency bars dance to the audio (mirror them with `mirror: true`).', { bars: 48, gap: 2, mirror: false }, {
|
|
@@ -1326,7 +994,7 @@ const AUDIO_FX = [
|
|
|
1326
994
|
ctx.fillStyle = o.background;
|
|
1327
995
|
ctx.fillRect(0, 0, w, h);
|
|
1328
996
|
const s = read(t, 32);
|
|
1329
|
-
const [r, g, b] = hexRgb(o.color);
|
|
997
|
+
const [r, g, b] = generative.hexRgb(o.color);
|
|
1330
998
|
const base = Math.min(w, h) * 0.18;
|
|
1331
999
|
for (let i = 0; i < o.rings; i++) {
|
|
1332
1000
|
const rad = base * (1 + i * 0.55) * (1 + s.bass * 0.6);
|
|
@@ -1414,7 +1082,7 @@ function defineAudio(tag = 'usa-audio') {
|
|
|
1414
1082
|
catch {
|
|
1415
1083
|
/* ignore bad JSON */
|
|
1416
1084
|
}
|
|
1417
|
-
|
|
1085
|
+
registry.playEffect(el, el.dataset.usaBeat || 'pulse', opts).catch(() => undefined);
|
|
1418
1086
|
});
|
|
1419
1087
|
}, { threshold: this.num('threshold', 1.35), cooldown: this.num('cooldown', 250) });
|
|
1420
1088
|
}
|
|
@@ -1446,7 +1114,7 @@ const touchOk = (e, o) => o.touch || e.pointerType !== 'touch';
|
|
|
1446
1114
|
function overlayCanvas(draw) {
|
|
1447
1115
|
const c = document.createElement('canvas');
|
|
1448
1116
|
c.style.cssText = 'position:absolute;inset:0;width:100%;height:100%';
|
|
1449
|
-
fxLayer().appendChild(c);
|
|
1117
|
+
generative.fxLayer().appendChild(c);
|
|
1450
1118
|
const g = c.getContext('2d');
|
|
1451
1119
|
let raf = 0;
|
|
1452
1120
|
const frame = (now) => {
|
|
@@ -1508,7 +1176,7 @@ const CURSOR_FX = [
|
|
|
1508
1176
|
description: 'A glowing comet tail follows the pointer over the element (persistent; `color`, `width`, `life`).',
|
|
1509
1177
|
defaults: { color: '#7c5cff', width: 10, life: 320, touch: false },
|
|
1510
1178
|
run: (el, o) => trail(el, o, (g, pts, now) => {
|
|
1511
|
-
const [r, gg, b] = hexRgb(o.color);
|
|
1179
|
+
const [r, gg, b] = generative.hexRgb(o.color);
|
|
1512
1180
|
g.lineCap = 'round';
|
|
1513
1181
|
for (let i = 1; i < pts.length; i++) {
|
|
1514
1182
|
const k = 1 - (now - pts[i].t) / o.life;
|
|
@@ -1546,7 +1214,7 @@ const CURSOR_FX = [
|
|
|
1546
1214
|
name: 'sparkle-trail',
|
|
1547
1215
|
kind: 'cursor',
|
|
1548
1216
|
description: 'Little stars twinkle off the pointer as it moves (persistent; `colors`, `spacing` px between stars).',
|
|
1549
|
-
defaults: { colors: PALETTE, spacing: 14, size: 14, touch: false },
|
|
1217
|
+
defaults: { colors: generative.PALETTE, spacing: 14, size: 14, touch: false },
|
|
1550
1218
|
run: (el, o, ctx) => {
|
|
1551
1219
|
let lx = -1e4;
|
|
1552
1220
|
let ly = -1e4;
|
|
@@ -1555,8 +1223,8 @@ const CURSOR_FX = [
|
|
|
1555
1223
|
return;
|
|
1556
1224
|
lx = e.clientX;
|
|
1557
1225
|
ly = e.clientY;
|
|
1558
|
-
const s = o.size * rand(0.6, 1.2);
|
|
1559
|
-
spawn(e.clientX - s / 2, e.clientY - s / 2, `font-size:${s}px;line-height:1;color:${o.colors[Math.floor(rand(0, o.colors.length))]}`, ctx, [{ transform: 'translate(0,0) scale(0) rotate(0deg)', opacity: 1 }, { transform: `translate(${rand(-14, 14)}px,${rand(4, 26)}px) scale(1) rotate(${rand(-90, 90)}deg)`, opacity: 0 }], { duration: rand(500, 800), easing: 'ease-out' }, '✦');
|
|
1226
|
+
const s = o.size * generative.rand(0.6, 1.2);
|
|
1227
|
+
generative.spawn(e.clientX - s / 2, e.clientY - s / 2, `font-size:${s}px;line-height:1;color:${o.colors[Math.floor(generative.rand(0, o.colors.length))]}`, ctx, [{ transform: 'translate(0,0) scale(0) rotate(0deg)', opacity: 1 }, { transform: `translate(${generative.rand(-14, 14)}px,${generative.rand(4, 26)}px) scale(1) rotate(${generative.rand(-90, 90)}deg)`, opacity: 0 }], { duration: generative.rand(500, 800), easing: 'ease-out' }, '✦');
|
|
1560
1228
|
};
|
|
1561
1229
|
el.addEventListener('pointermove', move, { passive: true });
|
|
1562
1230
|
return () => el.removeEventListener('pointermove', move);
|
|
@@ -1580,7 +1248,7 @@ const CURSOR_FX = [
|
|
|
1580
1248
|
const leave = () => ((p.x = -1e4), (p.y = -1e4));
|
|
1581
1249
|
el.addEventListener('pointermove', move, { passive: true });
|
|
1582
1250
|
el.addEventListener('pointerleave', leave);
|
|
1583
|
-
const off = canvasBackground(el, ctx, {
|
|
1251
|
+
const off = generative.canvasBackground(el, ctx, {
|
|
1584
1252
|
draw: ({ ctx: g, w, h }) => {
|
|
1585
1253
|
g.clearRect(0, 0, w, h);
|
|
1586
1254
|
if (o.background !== 'transparent') {
|
|
@@ -1614,7 +1282,7 @@ const CURSOR_FX = [
|
|
|
1614
1282
|
description: 'A soft light follows the pointer across the element, easing behind it (persistent; `color`, `size`).',
|
|
1615
1283
|
defaults: { color: '#ffffff', size: 260, ease: 0.18, touch: false },
|
|
1616
1284
|
run: (el, o) => {
|
|
1617
|
-
const [r, g, b] = hexRgb(o.color);
|
|
1285
|
+
const [r, g, b] = generative.hexRgb(o.color);
|
|
1618
1286
|
const s = document.createElement('span');
|
|
1619
1287
|
s.setAttribute('aria-hidden', 'true');
|
|
1620
1288
|
s.style.cssText = `position:absolute;inset:0;pointer-events:none;border-radius:inherit;opacity:0;transition:opacity .25s;mix-blend-mode:soft-light;background:radial-gradient(circle ${o.size / 2}px at var(--sx,50%) var(--sy,50%),rgba(${r},${g},${b},.75),transparent)`;
|
|
@@ -1700,7 +1368,7 @@ function bindGesture(el, gesture, effect, o = {}) {
|
|
|
1700
1368
|
if (typeof effect === 'function')
|
|
1701
1369
|
effect(d, e);
|
|
1702
1370
|
else
|
|
1703
|
-
|
|
1371
|
+
registry.playEffect(el, effect, effectOptions, e).catch(() => undefined);
|
|
1704
1372
|
};
|
|
1705
1373
|
const twistAngle = () => {
|
|
1706
1374
|
const [a, b] = [...pts.values()];
|
|
@@ -1814,7 +1482,7 @@ function defineGestureFx(tag = 'usa-gesture-fx') {
|
|
|
1814
1482
|
}
|
|
1815
1483
|
const target = this.flag('self') ? this : this.firstElementChild || this;
|
|
1816
1484
|
const effect = this.str('effect', 'pulse');
|
|
1817
|
-
this.onCleanup(bindGesture(this, this.gesture, (_d, e) => void
|
|
1485
|
+
this.onCleanup(bindGesture(this, this.gesture, (_d, e) => void registry.playEffect(target, effect, effectOptions, e).catch(() => undefined), {
|
|
1818
1486
|
velocity: this.num('velocity', 0.8),
|
|
1819
1487
|
angle: this.num('angle', 30),
|
|
1820
1488
|
duration: this.num('duration', 650),
|
|
@@ -1855,11 +1523,11 @@ function bumpCount(el, delta, ctx) {
|
|
|
1855
1523
|
const pop = (el, ctx, s = 1.25, d = 380) => ctx.animate(el, [{ transform: 'scale(1)' }, { transform: `scale(${s})`, offset: 0.4 }, { transform: 'scale(1)' }], { duration: d, easing: 'cubic-bezier(.2,1.4,.4,1)' });
|
|
1856
1524
|
/** A few glyphs flying out of the pointer / centre. */
|
|
1857
1525
|
function burst(el, ctx, glyph, color, n = 6, rise = false) {
|
|
1858
|
-
const { x, y } = origin(el, ctx);
|
|
1859
|
-
return all(Array.from({ length: n }, (_, i) => {
|
|
1860
|
-
const a = rise ? -Math.PI / 2 + rand(-0.6, 0.6) : (i / n) * Math.PI * 2;
|
|
1861
|
-
const d = rand(28, 56);
|
|
1862
|
-
return spawn(x - 7, y - 7, `font-size:14px;line-height:1;color:${color}`, ctx, [{ transform: 'translate(0,0) scale(.4)', opacity: 1 }, { transform: `translate(${Math.cos(a) * d}px,${Math.sin(a) * d}px) scale(1)`, opacity: 0 }], { duration: rand(500, 750), easing: 'cubic-bezier(.2,.8,.3,1)' }, glyph);
|
|
1526
|
+
const { x, y } = generative.origin(el, ctx);
|
|
1527
|
+
return generative.all(Array.from({ length: n }, (_, i) => {
|
|
1528
|
+
const a = rise ? -Math.PI / 2 + generative.rand(-0.6, 0.6) : (i / n) * Math.PI * 2;
|
|
1529
|
+
const d = generative.rand(28, 56);
|
|
1530
|
+
return generative.spawn(x - 7, y - 7, `font-size:14px;line-height:1;color:${color}`, ctx, [{ transform: 'translate(0,0) scale(.4)', opacity: 1 }, { transform: `translate(${Math.cos(a) * d}px,${Math.sin(a) * d}px) scale(1)`, opacity: 0 }], { duration: generative.rand(500, 750), easing: 'cubic-bezier(.2,.8,.3,1)' }, glyph);
|
|
1863
1531
|
}));
|
|
1864
1532
|
}
|
|
1865
1533
|
const click = (name, description, defaults, run) => ({ name, kind: 'click', description, defaults, run });
|
|
@@ -1902,7 +1570,7 @@ const MICRO_FX = [
|
|
|
1902
1570
|
if (el.getAttribute('aria-busy') === 'true')
|
|
1903
1571
|
return;
|
|
1904
1572
|
el.setAttribute('aria-busy', 'true');
|
|
1905
|
-
const [bar, remove] = overlay(el, `background:${o.color}55;transform-origin:left;transform:scaleX(0)`);
|
|
1573
|
+
const [bar, remove] = generative.overlay(el, `background:${o.color}55;transform-origin:left;transform:scaleX(0)`);
|
|
1906
1574
|
const a = ctx.animate(bar, [{ transform: 'scaleX(0)' }, { transform: 'scaleX(1)' }], { duration: o.duration, easing: 'linear', fill: 'forwards' });
|
|
1907
1575
|
const done = a ? a.finished.catch(() => undefined) : Promise.resolve();
|
|
1908
1576
|
return done.then(() => {
|
|
@@ -1928,12 +1596,12 @@ const MICRO_FX = [
|
|
|
1928
1596
|
click('send-plane', 'A paper plane ✈ takes off from the button.', { color: '#22d3ee' }, (el, o, ctx) => {
|
|
1929
1597
|
const r = el.getBoundingClientRect();
|
|
1930
1598
|
pop(el, ctx, 0.92, 200);
|
|
1931
|
-
return spawn(r.left + r.width / 2 - 8, r.top + r.height / 2 - 8, `font-size:16px;color:${o.color}`, ctx, [{ transform: 'translate(0,0) rotate(0)', opacity: 1 }, { transform: 'translate(40px,-10px) rotate(-10deg)', opacity: 1, offset: 0.4 }, { transform: 'translate(160px,-90px) rotate(-25deg)', opacity: 0 }], { duration: 800, easing: 'ease-in' }, '✈')?.finished;
|
|
1599
|
+
return generative.spawn(r.left + r.width / 2 - 8, r.top + r.height / 2 - 8, `font-size:16px;color:${o.color}`, ctx, [{ transform: 'translate(0,0) rotate(0)', opacity: 1 }, { transform: 'translate(40px,-10px) rotate(-10deg)', opacity: 1, offset: 0.4 }, { transform: 'translate(160px,-90px) rotate(-25deg)', opacity: 0 }], { duration: 800, easing: 'ease-in' }, '✈')?.finished;
|
|
1932
1600
|
}),
|
|
1933
1601
|
click('add-to-cart', 'Bumps `[data-count]` by one and floats a "+1" (`text`).', { text: '+1', color: '#34d399' }, (el, o, ctx) => {
|
|
1934
1602
|
bumpCount(el, 1, ctx);
|
|
1935
|
-
const { x, y } = origin(el, ctx);
|
|
1936
|
-
return spawn(x - 10, y - 10, `font:700 14px system-ui;color:${o.color}`, ctx, [{ transform: 'translateY(0)', opacity: 1 }, { transform: 'translateY(-40px)', opacity: 0 }], { duration: 700, easing: 'ease-out' }, o.text)?.finished;
|
|
1603
|
+
const { x, y } = generative.origin(el, ctx);
|
|
1604
|
+
return generative.spawn(x - 10, y - 10, `font:700 14px system-ui;color:${o.color}`, ctx, [{ transform: 'translateY(0)', opacity: 1 }, { transform: 'translateY(-40px)', opacity: 0 }], { duration: 700, easing: 'ease-out' }, o.text)?.finished;
|
|
1937
1605
|
}),
|
|
1938
1606
|
click('counter-bump', 'Adds `step` to `[data-count]` with a rolling number.', { step: 1 }, (el, o, ctx) => void bumpCount(el, Number(o.step) || 1, ctx)),
|
|
1939
1607
|
click('upvote', 'Toggles an upvote: the arrow nudges up and `[data-count]` ±1.', {}, (el, _o, ctx) => {
|
|
@@ -1967,7 +1635,7 @@ const MICRO_FX = [
|
|
|
1967
1635
|
}),
|
|
1968
1636
|
attn('error-flash', 'Flashes the element red once (no more than one flash per call).', { color: '#ff5c8a' }, (el, o, ctx) => ctx.animate(el, [{ boxShadow: `0 0 0 0 ${o.color}00` }, { boxShadow: `0 0 0 4px ${o.color}`, offset: 0.3 }, { boxShadow: `0 0 0 0 ${o.color}00` }], { duration: 700 })),
|
|
1969
1637
|
attn('success-check', 'A green ✓ badge pops over the element and fades.', { color: '#34d399' }, (el, o, ctx) => {
|
|
1970
|
-
const [b, remove] = overlay(el, `display:grid;place-items:center;font:700 22px system-ui;color:#fff;background:${o.color}d0`);
|
|
1638
|
+
const [b, remove] = generative.overlay(el, `display:grid;place-items:center;font:700 22px system-ui;color:#fff;background:${o.color}d0`);
|
|
1971
1639
|
b.textContent = '✓';
|
|
1972
1640
|
const a = ctx.animate(b, [{ opacity: 0, transform: 'scale(.6)' }, { opacity: 1, transform: 'scale(1)', offset: 0.3 }, { opacity: 1, offset: 0.75 }, { opacity: 0 }], { duration: 1100, easing: 'ease-out' });
|
|
1973
1641
|
return a ? a.finished.then(remove, remove) : void setTimeout(remove, 900);
|
|
@@ -2082,7 +1750,7 @@ function themePreset(t, role) {
|
|
|
2082
1750
|
function playThemeEffect(el, role, theme) {
|
|
2083
1751
|
const name = theme || el.closest('[data-usa-theme]')?.getAttribute('data-usa-theme') || 'neon';
|
|
2084
1752
|
const pr = themePreset(name, role);
|
|
2085
|
-
return
|
|
1753
|
+
return registry.playEffect(el, pr.effect, pr.options);
|
|
2086
1754
|
}
|
|
2087
1755
|
const THEME_FX = [
|
|
2088
1756
|
{
|
|
@@ -2105,7 +1773,7 @@ const THEME_FX = [
|
|
|
2105
1773
|
description: 'A bright diagonal shine sweeps across frosted glass.',
|
|
2106
1774
|
defaults: { duration: 700 },
|
|
2107
1775
|
run: (el, o, ctx) => {
|
|
2108
|
-
const [s, remove] = overlay(el, 'overflow:hidden;background:linear-gradient(105deg,transparent 35%,#ffffff8c 50%,transparent 65%);background-size:250% 100%;background-position:120% 0');
|
|
1776
|
+
const [s, remove] = generative.overlay(el, 'overflow:hidden;background:linear-gradient(105deg,transparent 35%,#ffffff8c 50%,transparent 65%);background-size:250% 100%;background-position:120% 0');
|
|
2109
1777
|
const a = ctx.animate(s, [{ backgroundPosition: '120% 0' }, { backgroundPosition: '-20% 0' }], { duration: o.duration, easing: 'ease-in-out' });
|
|
2110
1778
|
return a ? a.finished.then(remove, remove) : void remove();
|
|
2111
1779
|
},
|
|
@@ -2117,7 +1785,7 @@ const THEME_FX = [
|
|
|
2117
1785
|
reduced: 'run',
|
|
2118
1786
|
defaults: { opacity: 0.18 },
|
|
2119
1787
|
run: (el, o, ctx) => {
|
|
2120
|
-
const [s, remove] = overlay(el, `opacity:${o.opacity};background:repeating-linear-gradient(0deg,#000 0 1px,transparent 1px 3px);mix-blend-mode:multiply`);
|
|
1788
|
+
const [s, remove] = generative.overlay(el, `opacity:${o.opacity};background:repeating-linear-gradient(0deg,#000 0 1px,transparent 1px 3px);mix-blend-mode:multiply`);
|
|
2121
1789
|
const a = ctx.reduced ? null : ctx.animate(s, [{ backgroundPosition: '0 0' }, { backgroundPosition: '0 30px' }], { duration: 2400, iterations: Infinity, easing: 'linear' });
|
|
2122
1790
|
return () => {
|
|
2123
1791
|
a?.cancel();
|
|
@@ -2152,7 +1820,7 @@ function defineTheme(tag = 'usa-theme') {
|
|
|
2152
1820
|
const pr = themePreset(this.theme, role);
|
|
2153
1821
|
const trigger = role === 'attention' ? 'click' : role === 'background' ? 'load' : role;
|
|
2154
1822
|
try {
|
|
2155
|
-
this.onCleanup(
|
|
1823
|
+
this.onCleanup(registry.bindEffect(el, pr.effect, { ...(pr.options || {}), trigger }));
|
|
2156
1824
|
}
|
|
2157
1825
|
catch {
|
|
2158
1826
|
/* effect not registered: call registerAllEffects() */
|
|
@@ -2271,7 +1939,7 @@ function createPlayer(root, animation, o = {}) {
|
|
|
2271
1939
|
if (!e.fired && time >= e.t.start) {
|
|
2272
1940
|
e.fired = true;
|
|
2273
1941
|
for (const el of e.els)
|
|
2274
|
-
|
|
1942
|
+
registry.playEffect(el, e.t.effect, e.t.options || {}).catch(() => undefined);
|
|
2275
1943
|
}
|
|
2276
1944
|
};
|
|
2277
1945
|
const done = () => {
|
|
@@ -2479,7 +2147,7 @@ const EFFECT_PACKS = {
|
|
|
2479
2147
|
'cards-click': [...CARD_FX, ...CLICK_FX],
|
|
2480
2148
|
physics: PHYSICS_FX,
|
|
2481
2149
|
page: PAGE_FX,
|
|
2482
|
-
generative: GENERATIVE_FX,
|
|
2150
|
+
generative: generative.GENERATIVE_FX,
|
|
2483
2151
|
audio: AUDIO_FX,
|
|
2484
2152
|
cursor: CURSOR_FX,
|
|
2485
2153
|
micro: MICRO_FX,
|
|
@@ -2487,32 +2155,32 @@ const EFFECT_PACKS = {
|
|
|
2487
2155
|
};
|
|
2488
2156
|
/** 5.1: card & click effects 2.0. */
|
|
2489
2157
|
function registerCardClickEffects() {
|
|
2490
|
-
|
|
2158
|
+
registry.registerEffects(EFFECT_PACKS['cards-click']);
|
|
2491
2159
|
}
|
|
2492
2160
|
/** 5.2: bounce & physics micro-interactions. */
|
|
2493
2161
|
function registerPhysicsEffects() {
|
|
2494
|
-
|
|
2162
|
+
registry.registerEffects(EFFECT_PACKS.physics);
|
|
2495
2163
|
}
|
|
2496
2164
|
/** 5.3: page-wide transitions and effects. */
|
|
2497
2165
|
function registerPageEffects() {
|
|
2498
|
-
|
|
2166
|
+
registry.registerEffects(EFFECT_PACKS.page);
|
|
2499
2167
|
}
|
|
2500
2168
|
/** 5.5: generative Canvas 2D backgrounds. */
|
|
2501
2169
|
function registerGenerativeEffects() {
|
|
2502
|
-
|
|
2170
|
+
registry.registerEffects(EFFECT_PACKS.generative);
|
|
2503
2171
|
}
|
|
2504
2172
|
/** 5.6: sound-reactive (Web Audio) backgrounds. */
|
|
2505
2173
|
function registerAudioEffects() {
|
|
2506
|
-
|
|
2174
|
+
registry.registerEffects(EFFECT_PACKS.audio);
|
|
2507
2175
|
}
|
|
2508
2176
|
/** 5.7: cursor trails, magnetic dots, spotlight cursor. */
|
|
2509
2177
|
function registerCursorEffects() {
|
|
2510
|
-
|
|
2178
|
+
registry.registerEffects(EFFECT_PACKS.cursor);
|
|
2511
2179
|
}
|
|
2512
2180
|
/** 5.8: micro-interactions + theme-pack effects. */
|
|
2513
2181
|
function registerMicroEffects() {
|
|
2514
|
-
|
|
2515
|
-
|
|
2182
|
+
registry.registerEffects(EFFECT_PACKS.micro);
|
|
2183
|
+
registry.registerEffects(EFFECT_PACKS.themes);
|
|
2516
2184
|
}
|
|
2517
2185
|
/** Define the 5.x elements of this entry (`<usa-story>`, …) under their default tags. */
|
|
2518
2186
|
function defineEffectElements() {
|
|
@@ -2526,16 +2194,20 @@ function defineEffectElements() {
|
|
|
2526
2194
|
function registerAllEffects() {
|
|
2527
2195
|
components_fx.registerBuiltinEffects();
|
|
2528
2196
|
for (const defs of Object.values(EFFECT_PACKS))
|
|
2529
|
-
|
|
2197
|
+
registry.registerEffects(defs);
|
|
2530
2198
|
}
|
|
2531
2199
|
|
|
2200
|
+
exports.GENERATIVE_FX = generative.GENERATIVE_FX;
|
|
2201
|
+
exports.canvasBackground = generative.canvasBackground;
|
|
2202
|
+
exports.fxLayer = generative.fxLayer;
|
|
2203
|
+
exports.hexRgb = generative.hexRgb;
|
|
2204
|
+
exports.noise2 = generative.noise2;
|
|
2532
2205
|
exports.ANIMATION_FORMAT = ANIMATION_FORMAT;
|
|
2533
2206
|
exports.AUDIO_FX = AUDIO_FX;
|
|
2534
2207
|
exports.CARD_FX = CARD_FX;
|
|
2535
2208
|
exports.CLICK_FX = CLICK_FX;
|
|
2536
2209
|
exports.CURSOR_FX = CURSOR_FX;
|
|
2537
2210
|
exports.EFFECT_PACKS = EFFECT_PACKS;
|
|
2538
|
-
exports.GENERATIVE_FX = GENERATIVE_FX;
|
|
2539
2211
|
exports.GESTURES = GESTURES;
|
|
2540
2212
|
exports.MICRO_FX = MICRO_FX;
|
|
2541
2213
|
exports.PAGE_FX = PAGE_FX;
|
|
@@ -2551,7 +2223,6 @@ exports.bindBeat = bindBeat;
|
|
|
2551
2223
|
exports.bindGesture = bindGesture;
|
|
2552
2224
|
exports.bounceKeyframes = bounceKeyframes;
|
|
2553
2225
|
exports.bumpCount = bumpCount;
|
|
2554
|
-
exports.canvasBackground = canvasBackground;
|
|
2555
2226
|
exports.createBeatDetector = createBeatDetector;
|
|
2556
2227
|
exports.createPlayer = createPlayer;
|
|
2557
2228
|
exports.defineAudio = defineAudio;
|
|
@@ -2564,10 +2235,7 @@ exports.disableAudio = disableAudio;
|
|
|
2564
2235
|
exports.enableAudio = enableAudio;
|
|
2565
2236
|
exports.flingVelocity = flingVelocity;
|
|
2566
2237
|
exports.formatCount = formatCount;
|
|
2567
|
-
exports.fxLayer = fxLayer;
|
|
2568
2238
|
exports.getAudio = getAudio;
|
|
2569
|
-
exports.hexRgb = hexRgb;
|
|
2570
|
-
exports.noise2 = noise2;
|
|
2571
2239
|
exports.normalizeAnimation = normalizeAnimation;
|
|
2572
2240
|
exports.onBeat = onBeat;
|
|
2573
2241
|
exports.playThemeEffect = playThemeEffect;
|