@pixerate/flair 0.31.0 → 0.33.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 +22 -0
- package/README.md +5 -1
- package/dist/catalog/index.d.ts +2 -2
- package/dist/catalog/index.js +2 -2
- package/dist/{chunk-7F4ITXGK.js → chunk-4FFBIVPI.js} +3253 -33
- package/dist/chunk-4FFBIVPI.js.map +1 -0
- package/dist/{chunk-PIWJOW76.js → chunk-6ZG2RIWA.js} +6 -6
- package/dist/{chunk-45YUAQNZ.js → chunk-ARYM5VEJ.js} +3 -3
- package/dist/{chunk-DA4UUSDO.js → chunk-JETRHCP3.js} +9 -277
- package/dist/chunk-JETRHCP3.js.map +1 -0
- package/dist/{chunk-AUH3PEFW.js → chunk-MOLJPWME.js} +5 -3
- package/dist/{chunk-AUH3PEFW.js.map → chunk-MOLJPWME.js.map} +1 -1
- package/dist/{chunk-X3C2VFTM.js → chunk-N5HXMQOK.js} +7 -7
- package/dist/{chunk-ORAFCHX3.js → chunk-VKIDRHBG.js} +278 -2
- package/dist/chunk-VKIDRHBG.js.map +1 -0
- package/dist/cli.js +2 -2
- package/dist/editor/index.d.ts +1 -1
- package/dist/editor/index.js +5 -4
- package/dist/editor/index.js.map +1 -1
- package/dist/{effect-DHYWPXD3.js → effect-6PRVCU55.js} +8 -8
- package/dist/embed/index.d.ts +1 -1
- package/dist/embed/index.js +4 -4
- package/dist/index.js +19 -19
- package/dist/node.d.ts +1 -1
- package/dist/node.js +7 -7
- package/dist/react/index.d.ts +1 -1
- package/dist/react/index.js +5 -5
- package/dist/svelte/index.d.ts +1 -1
- package/dist/svelte/index.js +5 -5
- package/dist/{types-BDIzGWIS.d.ts → types-c8cnPzuE.d.ts} +1 -1
- package/dist/vector/index.js +14 -14
- package/package.json +1 -1
- package/dist/chunk-7F4ITXGK.js.map +0 -1
- package/dist/chunk-DA4UUSDO.js.map +0 -1
- package/dist/chunk-ORAFCHX3.js.map +0 -1
- /package/dist/{chunk-PIWJOW76.js.map → chunk-6ZG2RIWA.js.map} +0 -0
- /package/dist/{chunk-45YUAQNZ.js.map → chunk-ARYM5VEJ.js.map} +0 -0
- /package/dist/{chunk-X3C2VFTM.js.map → chunk-N5HXMQOK.js.map} +0 -0
- /package/dist/{effect-DHYWPXD3.js.map → effect-6PRVCU55.js.map} +0 -0
|
@@ -8,14 +8,14 @@ import {
|
|
|
8
8
|
} from "./chunk-7KYQ4R76.js";
|
|
9
9
|
import {
|
|
10
10
|
svgAnimatedMarkup
|
|
11
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-N5HXMQOK.js";
|
|
12
12
|
import {
|
|
13
13
|
svgFrameMarkup,
|
|
14
14
|
svgViewBox
|
|
15
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-MOLJPWME.js";
|
|
16
16
|
import {
|
|
17
17
|
subjectValues
|
|
18
|
-
} from "./chunk-
|
|
18
|
+
} from "./chunk-ARYM5VEJ.js";
|
|
19
19
|
import {
|
|
20
20
|
wrapSource
|
|
21
21
|
} from "./chunk-HDYBY3PQ.js";
|
|
@@ -28,14 +28,14 @@ import {
|
|
|
28
28
|
effectInputs,
|
|
29
29
|
getEffect,
|
|
30
30
|
renderVectorEffect
|
|
31
|
-
} from "./chunk-
|
|
31
|
+
} from "./chunk-4FFBIVPI.js";
|
|
32
32
|
import {
|
|
33
33
|
checkControlValue,
|
|
34
34
|
parseColor
|
|
35
35
|
} from "./chunk-VYOMXE6I.js";
|
|
36
36
|
import {
|
|
37
37
|
svgDuration
|
|
38
|
-
} from "./chunk-
|
|
38
|
+
} from "./chunk-JETRHCP3.js";
|
|
39
39
|
|
|
40
40
|
// src/export/effect.ts
|
|
41
41
|
import { spawn } from "child_process";
|
|
@@ -411,4 +411,4 @@ export {
|
|
|
411
411
|
effectQueryFromFlags,
|
|
412
412
|
describeEffect
|
|
413
413
|
};
|
|
414
|
-
//# sourceMappingURL=chunk-
|
|
414
|
+
//# sourceMappingURL=chunk-6ZG2RIWA.js.map
|
|
@@ -12,7 +12,7 @@ import {
|
|
|
12
12
|
getEffect,
|
|
13
13
|
renderVectorEffect,
|
|
14
14
|
textImage
|
|
15
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-4FFBIVPI.js";
|
|
16
16
|
import {
|
|
17
17
|
parseColor
|
|
18
18
|
} from "./chunk-VYOMXE6I.js";
|
|
@@ -20,7 +20,7 @@ import {
|
|
|
20
20
|
SvgPlayer,
|
|
21
21
|
svgDuration,
|
|
22
22
|
svgView
|
|
23
|
-
} from "./chunk-
|
|
23
|
+
} from "./chunk-JETRHCP3.js";
|
|
24
24
|
|
|
25
25
|
// src/export/mp4mux.ts
|
|
26
26
|
var AAC_RATES = [96e3, 88200, 64e3, 48e3, 44100, 32e3, 24e3, 22050, 16e3, 12e3, 11025, 8e3, 7350];
|
|
@@ -1126,4 +1126,4 @@ export {
|
|
|
1126
1126
|
subjectValues,
|
|
1127
1127
|
mountEffect
|
|
1128
1128
|
};
|
|
1129
|
-
//# sourceMappingURL=chunk-
|
|
1129
|
+
//# sourceMappingURL=chunk-ARYM5VEJ.js.map
|
|
@@ -1,267 +1,14 @@
|
|
|
1
1
|
import {
|
|
2
2
|
realClock
|
|
3
3
|
} from "./chunk-WMZQCUFJ.js";
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
var clamp01 = (t) => t < 0 ? 0 : t > 1 ? 1 : t;
|
|
13
|
-
function stepStart(steps, step) {
|
|
14
|
-
if (step <= 0) return 0;
|
|
15
|
-
return steps[step - 1];
|
|
16
|
-
}
|
|
17
|
-
function stepProgress(steps, timeMs, step, options = {}) {
|
|
18
|
-
const start = stepStart(steps, step);
|
|
19
|
-
if (start === void 0) return 0;
|
|
20
|
-
const from = start + (options.delay ?? 0);
|
|
21
|
-
const ms = options.ms ?? STEP_MS;
|
|
22
|
-
const t = ms > 0 ? clamp01((timeMs - from) / ms) : timeMs >= from ? 1 : 0;
|
|
23
|
-
return EASES[options.ease ?? "in-out"](t);
|
|
24
|
-
}
|
|
25
|
-
var defaultDataKey = (item, index) => {
|
|
26
|
-
if (item && typeof item === "object") {
|
|
27
|
-
const o = item;
|
|
28
|
-
if (o.source !== void 0 && o.target !== void 0)
|
|
29
|
-
return `${String(o.source)}\u2192${String(o.target)}`;
|
|
30
|
-
for (const k of ["id", "key", "name", "label"])
|
|
31
|
-
if (typeof o[k] === "string" || typeof o[k] === "number") return String(o[k]);
|
|
32
|
-
}
|
|
33
|
-
return String(index);
|
|
34
|
-
};
|
|
35
|
-
function scaled(value, k) {
|
|
36
|
-
if (typeof value === "number") return value * k;
|
|
37
|
-
if (Array.isArray(value)) return value.map((v) => scaled(v, k));
|
|
38
|
-
if (value && typeof value === "object")
|
|
39
|
-
return Object.fromEntries(Object.entries(value).map(([key, v]) => [key, scaled(v, k)]));
|
|
40
|
-
return value;
|
|
41
|
-
}
|
|
42
|
-
function lerpData(a, b, u, keyOf = defaultDataKey) {
|
|
43
|
-
if (u <= 0) return a;
|
|
44
|
-
if (u >= 1) return b;
|
|
45
|
-
return blend(a, b, u, keyOf);
|
|
46
|
-
}
|
|
47
|
-
function blend(a, b, u, keyOf) {
|
|
48
|
-
if (typeof a === "number" && typeof b === "number") return a + (b - a) * u;
|
|
49
|
-
if (Array.isArray(a) && Array.isArray(b)) {
|
|
50
|
-
const before = new Map(a.map((item, i) => [keyOf(item, i), item]));
|
|
51
|
-
const after = new Set(b.map((item, i) => keyOf(item, i)));
|
|
52
|
-
const out = b.map((item, i) => {
|
|
53
|
-
const key = keyOf(item, i);
|
|
54
|
-
return before.has(key) ? blend(before.get(key), item, u, keyOf) : scaled(item, u);
|
|
55
|
-
});
|
|
56
|
-
a.forEach((item, i) => {
|
|
57
|
-
if (!after.has(keyOf(item, i))) out.push(scaled(item, 1 - u));
|
|
58
|
-
});
|
|
59
|
-
return out;
|
|
60
|
-
}
|
|
61
|
-
if (a && b && typeof a === "object" && typeof b === "object") {
|
|
62
|
-
const ao = a;
|
|
63
|
-
const bo = b;
|
|
64
|
-
return Object.fromEntries(
|
|
65
|
-
[.../* @__PURE__ */ new Set([...Object.keys(ao), ...Object.keys(bo)])].map((k) => [
|
|
66
|
-
k,
|
|
67
|
-
k in ao && k in bo ? blend(ao[k], bo[k], u, keyOf) : k in bo ? scaled(bo[k], u) : scaled(ao[k], 1 - u)
|
|
68
|
-
])
|
|
69
|
-
);
|
|
70
|
-
}
|
|
71
|
-
return u < 0.5 ? a : b;
|
|
72
|
-
}
|
|
73
|
-
function stepData(states, steps, timeMs, options = {}) {
|
|
74
|
-
const last = Math.min(steps.length, states.length - 1);
|
|
75
|
-
const to = states[last];
|
|
76
|
-
if (last === 0) return to;
|
|
77
|
-
const u = stepProgress(steps, timeMs, last, options);
|
|
78
|
-
return lerpData(states[last - 1], to, u, options.keyOf);
|
|
79
|
-
}
|
|
80
|
-
var SCRAMBLE_MS = 50;
|
|
81
|
-
function writtenText(text, u, options = {}) {
|
|
82
|
-
const letters = Array.from(text);
|
|
83
|
-
const shown = Math.round(clamp01(u) * letters.length);
|
|
84
|
-
let out = letters.slice(0, shown).join("");
|
|
85
|
-
const pool = Array.from(options.scramble ?? "");
|
|
86
|
-
if (pool.length) {
|
|
87
|
-
const tick = Math.floor((options.timeMs ?? 0) / SCRAMBLE_MS);
|
|
88
|
-
for (let i = shown; i < letters.length; i++)
|
|
89
|
-
out += /\s/.test(letters[i]) ? letters[i] : pool[Math.floor(noise(i, tick) * pool.length)];
|
|
90
|
-
}
|
|
91
|
-
return shown < letters.length && options.caret ? out + options.caret : out;
|
|
92
|
-
}
|
|
93
|
-
function noise(i, tick) {
|
|
94
|
-
const x = Math.sin(i * 127.1 + tick * 311.7) * 43758.5453;
|
|
95
|
-
return x - Math.floor(x);
|
|
96
|
-
}
|
|
97
|
-
var SVG_PICTURE = "";
|
|
98
|
-
var NUMBER = /-?(?:\d+\.?\d*|\.\d+)(?:e[-+]?\d+)?/gi;
|
|
99
|
-
var HEX = /^#([0-9a-f]{3}|[0-9a-f]{6})$/i;
|
|
100
|
-
function rgb(hex) {
|
|
101
|
-
let h = hex.slice(1);
|
|
102
|
-
if (h.length === 3) h = [...h].map((c) => c + c).join("");
|
|
103
|
-
return [0, 2, 4].map((i) => parseInt(h.slice(i, i + 2), 16));
|
|
104
|
-
}
|
|
105
|
-
var round = (n) => Math.round(n * 1e3) / 1e3;
|
|
106
|
-
function lerpAttr(a, b, u) {
|
|
107
|
-
if (typeof a === "number" && typeof b === "number") return String(round(a + (b - a) * u));
|
|
108
|
-
const sa = String(a);
|
|
109
|
-
const sb = String(b);
|
|
110
|
-
if (HEX.test(sa) && HEX.test(sb)) {
|
|
111
|
-
const [ca, cb] = [rgb(sa), rgb(sb)];
|
|
112
|
-
return `#${ca.map(
|
|
113
|
-
(c, i) => Math.round(c + (cb[i] - c) * u).toString(16).padStart(2, "0")
|
|
114
|
-
).join("")}`;
|
|
115
|
-
}
|
|
116
|
-
const na = sa.match(NUMBER) ?? [];
|
|
117
|
-
const nb = sb.match(NUMBER) ?? [];
|
|
118
|
-
if (na.length && na.length === nb.length && sa.replace(NUMBER, "#") === sb.replace(NUMBER, "#")) {
|
|
119
|
-
let i = 0;
|
|
120
|
-
return sa.replace(NUMBER, () => {
|
|
121
|
-
const v = Number(na[i]) + (Number(nb[i]) - Number(na[i])) * u;
|
|
122
|
-
i++;
|
|
123
|
-
return String(round(v));
|
|
124
|
-
});
|
|
125
|
-
}
|
|
126
|
-
return u < 0.5 ? sa : sb;
|
|
127
|
-
}
|
|
128
|
-
function formatCount(value, options = {}) {
|
|
129
|
-
const n = value.toFixed(options.decimals ?? 0);
|
|
130
|
-
return (options.format ?? "{n}").replace("{n}", n);
|
|
131
|
-
}
|
|
132
|
-
var startOf = (a, steps) => {
|
|
133
|
-
const s = stepStart(steps, a.step ?? 0);
|
|
134
|
-
return s === void 0 ? Infinity : s + (a.delay ?? 0);
|
|
135
|
-
};
|
|
136
|
-
function svgFrame(animations, steps, timeMs) {
|
|
137
|
-
const tracks = /* @__PURE__ */ new Map();
|
|
138
|
-
const add = (prop, anim, index) => {
|
|
139
|
-
const key = `${prop === "view" ? SVG_PICTURE : anim.id}\0${prop}`;
|
|
140
|
-
const list = tracks.get(key) ?? [];
|
|
141
|
-
list.push({ anim, start: startOf(anim, steps), index });
|
|
142
|
-
tracks.set(key, list);
|
|
143
|
-
};
|
|
144
|
-
animations.forEach((a, i) => {
|
|
145
|
-
for (const name of Object.keys(a.attrs ?? {})) add(`@${name}`, a, i);
|
|
146
|
-
if (a.draw) add("draw", a, i);
|
|
147
|
-
if (a.count) add("count", a, i);
|
|
148
|
-
if (a.write !== void 0) add("count", a, i);
|
|
149
|
-
if (a.solo !== void 0) add("solo", a, i);
|
|
150
|
-
if (a.view !== void 0) add("view", a, i);
|
|
151
|
-
if (a.along) add("along", a, i);
|
|
152
|
-
});
|
|
153
|
-
const frames = /* @__PURE__ */ new Map();
|
|
154
|
-
for (const [key, list] of tracks) {
|
|
155
|
-
list.sort((x, y) => x.start - y.start || x.index - y.index);
|
|
156
|
-
const [id, prop] = key.split("\0");
|
|
157
|
-
const started = list.filter((c) => c.start <= timeMs);
|
|
158
|
-
const current = started[started.length - 1];
|
|
159
|
-
const anim = (current ?? list[0]).anim;
|
|
160
|
-
const u = current ? stepProgress(steps, timeMs, anim.step ?? 0, {
|
|
161
|
-
delay: anim.delay,
|
|
162
|
-
ms: anim.ms,
|
|
163
|
-
ease: anim.ease
|
|
164
|
-
}) : 0;
|
|
165
|
-
const frame = frames.get(id) ?? { attrs: {} };
|
|
166
|
-
if (prop === "view") {
|
|
167
|
-
const before = current ? list[list.indexOf(current) - 1] : void 0;
|
|
168
|
-
frame.view = { from: before?.anim.view ?? null, to: anim.view, u };
|
|
169
|
-
} else if (prop === "along") {
|
|
170
|
-
const [a, b] = anim.at ?? [0, 1];
|
|
171
|
-
frame.along = { path: anim.along, at: a + (b - a) * u };
|
|
172
|
-
} else if (prop === "solo") {
|
|
173
|
-
frame.solo = anim.solo;
|
|
174
|
-
} else if (prop.startsWith("@")) {
|
|
175
|
-
const [a, b] = anim.attrs[prop.slice(1)];
|
|
176
|
-
frame.attrs[prop.slice(1)] = lerpAttr(a, b, u);
|
|
177
|
-
} else if (prop === "draw") {
|
|
178
|
-
const [a, b] = anim.draw === true ? [0, 1] : anim.draw;
|
|
179
|
-
frame.draw = a + (b - a) * u;
|
|
180
|
-
} else if (anim.write !== void 0) {
|
|
181
|
-
frame.text = writtenText(anim.write, u, { scramble: anim.scramble, caret: anim.caret, timeMs });
|
|
182
|
-
} else {
|
|
183
|
-
const [a, b] = anim.count;
|
|
184
|
-
frame.text = formatCount(a + (b - a) * u, anim);
|
|
185
|
-
}
|
|
186
|
-
frames.set(id, frame);
|
|
187
|
-
}
|
|
188
|
-
return frames;
|
|
189
|
-
}
|
|
190
|
-
function svgOpeningTag(markup) {
|
|
191
|
-
const end = markup.indexOf(">");
|
|
192
|
-
return end < 0 ? markup : markup.slice(0, end + 1);
|
|
193
|
-
}
|
|
194
|
-
function svgMotionIssues(motion) {
|
|
195
|
-
const issues = [];
|
|
196
|
-
const m = motion ?? {};
|
|
197
|
-
const svg = typeof m.svg === "string" ? m.svg.trim() : "";
|
|
198
|
-
if (!svg) return ["`svg` needs the picture's markup: one <svg> element"];
|
|
199
|
-
if (!/^<svg[\s>]/i.test(svg) || !svg.toLowerCase().endsWith("</svg>"))
|
|
200
|
-
issues.push("`svg` must be one <svg>\u2026</svg> element");
|
|
201
|
-
if (!/\sviewBox\s*=/i.test(svgOpeningTag(svg)))
|
|
202
|
-
issues.push("the <svg> needs a `viewBox`, so it scales to where it is placed");
|
|
203
|
-
if (/<script|<foreignObject|\son\w+\s*=/i.test(svg))
|
|
204
|
-
issues.push("`svg` cannot have scripts, event handlers or <foreignObject>");
|
|
205
|
-
if (/<(animate|animateTransform|animateMotion|set)[\s/>]/i.test(svg))
|
|
206
|
-
issues.push(
|
|
207
|
-
"`svg` cannot animate itself (<animate>, <animateTransform>, <animateMotion>, <set>): give changes in `animate`, so every frame is a function of time"
|
|
208
|
-
);
|
|
209
|
-
if (/(?:href|src)\s*=\s*["'](?!#|data:image\/)/i.test(svg))
|
|
210
|
-
issues.push("`svg` cannot link outside itself (only #ids and data:image URLs)");
|
|
211
|
-
const ids = new Set([...svg.matchAll(/\sid\s*=\s*["']([^"']+)["']/g)].map((x) => x[1]));
|
|
212
|
-
if (m.animate !== void 0 && !Array.isArray(m.animate)) issues.push("`animate` must be a list");
|
|
213
|
-
(Array.isArray(m.animate) ? m.animate : []).forEach((a, i) => {
|
|
214
|
-
const where = `animate[${i}]`;
|
|
215
|
-
if (!a || typeof a !== "object") return void issues.push(`${where} is not an animation`);
|
|
216
|
-
if (typeof a.id !== "string") {
|
|
217
|
-
if (a.view === void 0) return void issues.push(`${where} needs an \`id\``);
|
|
218
|
-
} else if (!ids.has(a.id))
|
|
219
|
-
issues.push(`${where}: there is no element with id "${a.id}" in the SVG`);
|
|
220
|
-
if (a.step !== void 0 && !(Number.isInteger(a.step) && a.step >= 0))
|
|
221
|
-
issues.push(`${where}: \`step\` is a whole number, 0 or more`);
|
|
222
|
-
if (a.ms !== void 0 && !(a.ms >= 0)) issues.push(`${where}: \`ms\` must be 0 or more`);
|
|
223
|
-
if (a.delay !== void 0 && !(a.delay >= 0))
|
|
224
|
-
issues.push(`${where}: \`delay\` must be 0 or more`);
|
|
225
|
-
if (a.ease !== void 0 && !(a.ease in EASES))
|
|
226
|
-
issues.push(`${where}: \`ease\` is one of ${Object.keys(EASES).join(", ")}`);
|
|
227
|
-
if (!a.attrs && !a.draw && !a.count && a.write === void 0 && a.view === void 0 && !a.along && a.solo === void 0)
|
|
228
|
-
issues.push(
|
|
229
|
-
`${where} changes nothing: give \`attrs\`, \`draw\`, \`count\`, \`write\`, \`view\`, \`along\` or \`solo\``
|
|
230
|
-
);
|
|
231
|
-
if (a.solo !== void 0 && !(Number.isInteger(a.solo) && a.solo >= 0))
|
|
232
|
-
issues.push(`${where}: \`solo\` is an index (whole number, 0 or more)`);
|
|
233
|
-
if (a.view !== void 0) {
|
|
234
|
-
const box = a.view;
|
|
235
|
-
if (typeof box === "string") {
|
|
236
|
-
if (!ids.has(box))
|
|
237
|
-
issues.push(`${where}: \`view\` frames "${box}", which is not an id in the SVG`);
|
|
238
|
-
} else if (!Array.isArray(box) || box.length !== 4 || !box.every((n) => Number.isFinite(n)) || !(box[2] > 0 && box[3] > 0))
|
|
239
|
-
issues.push(
|
|
240
|
-
`${where}: \`view\` is [x, y, width, height] (width and height above 0) or an element's id`
|
|
241
|
-
);
|
|
242
|
-
}
|
|
243
|
-
if (a.along !== void 0) {
|
|
244
|
-
if (typeof a.along !== "string" || !ids.has(a.along))
|
|
245
|
-
issues.push(`${where}: \`along\` is the id of a path in the SVG`);
|
|
246
|
-
if (typeof a.id !== "string")
|
|
247
|
-
issues.push(`${where}: \`along\` moves an element: give its \`id\``);
|
|
248
|
-
}
|
|
249
|
-
if (a.at !== void 0 && !(Array.isArray(a.at) && a.at.length === 2 && a.at.every((v) => v >= 0 && v <= 1)))
|
|
250
|
-
issues.push(`${where}: \`at\` is [from, to] between 0 and 1`);
|
|
251
|
-
for (const [name, pair] of Object.entries(a.attrs ?? {}))
|
|
252
|
-
if (!Array.isArray(pair) || pair.length !== 2)
|
|
253
|
-
issues.push(`${where}: \`attrs.${name}\` is [from, to]`);
|
|
254
|
-
if (Array.isArray(a.draw) && a.draw.some((v) => !(v >= 0 && v <= 1)))
|
|
255
|
-
issues.push(`${where}: \`draw\` is true or [from, to] between 0 and 1`);
|
|
256
|
-
if (a.count !== void 0 && !(Array.isArray(a.count) && a.count.length === 2))
|
|
257
|
-
issues.push(`${where}: \`count\` is [from, to]`);
|
|
258
|
-
if (a.write !== void 0 && typeof a.write !== "string")
|
|
259
|
-
issues.push(`${where}: \`write\` is the text to write`);
|
|
260
|
-
if ((a.scramble !== void 0 || a.caret !== void 0) && a.write === void 0)
|
|
261
|
-
issues.push(`${where}: \`scramble\` and \`caret\` go with \`write\``);
|
|
262
|
-
});
|
|
263
|
-
return issues;
|
|
264
|
-
}
|
|
4
|
+
import {
|
|
5
|
+
EASES,
|
|
6
|
+
STEP_MS,
|
|
7
|
+
SVG_PICTURE,
|
|
8
|
+
formatCount,
|
|
9
|
+
svgFrame,
|
|
10
|
+
svgOpeningTag
|
|
11
|
+
} from "./chunk-VKIDRHBG.js";
|
|
265
12
|
|
|
266
13
|
// src/vector/svg.ts
|
|
267
14
|
var SVG_NS = "http://www.w3.org/2000/svg";
|
|
@@ -810,21 +557,6 @@ function animateCount(options) {
|
|
|
810
557
|
}
|
|
811
558
|
|
|
812
559
|
export {
|
|
813
|
-
EASES,
|
|
814
|
-
STEP_MS,
|
|
815
|
-
stepStart,
|
|
816
|
-
stepProgress,
|
|
817
|
-
defaultDataKey,
|
|
818
|
-
lerpData,
|
|
819
|
-
stepData,
|
|
820
|
-
SCRAMBLE_MS,
|
|
821
|
-
writtenText,
|
|
822
|
-
SVG_PICTURE,
|
|
823
|
-
lerpAttr,
|
|
824
|
-
formatCount,
|
|
825
|
-
svgFrame,
|
|
826
|
-
svgOpeningTag,
|
|
827
|
-
svgMotionIssues,
|
|
828
560
|
parseSvg,
|
|
829
561
|
svgView,
|
|
830
562
|
svgDuration,
|
|
@@ -832,4 +564,4 @@ export {
|
|
|
832
564
|
SvgPlayer,
|
|
833
565
|
animateCount
|
|
834
566
|
};
|
|
835
|
-
//# sourceMappingURL=chunk-
|
|
567
|
+
//# sourceMappingURL=chunk-JETRHCP3.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/vector/svg.ts"],"sourcesContent":["/**\n * Drawing animated SVG pictures (see `SvgMotion`) in the DOM: a sanitising\n * parser, a view that applies frames, and a player that runs on a `Clock`.\n */\n\nimport { realClock, type Clock } from \"../time/clock.js\";\nimport {\n EASES,\n formatCount,\n STEP_MS,\n SVG_PICTURE,\n svgFrame,\n svgOpeningTag,\n type EaseName,\n type SvgBox,\n type SvgMotion,\n} from \"./motion.js\";\n\nconst SVG_NS = \"http://www.w3.org/2000/svg\";\n\n/**\n * The `<svg>` in `markup`, parsed and made safe to show: no scripts, event\n * handlers, `<foreignObject>` or links outside itself. Undefined when it is\n * not an SVG.\n */\nexport function parseSvg(markup: string): SVGSVGElement | undefined {\n // Parsed as XML, an <svg> without its namespace is not an SVG at all.\n let source = markup.trim();\n const open = svgOpeningTag(source);\n if (!/\\sxmlns\\s*=/i.test(open)) source = source.replace(/^<svg/i, `<svg xmlns=\"${SVG_NS}\"`);\n if (/xlink:/.test(source) && !/\\sxmlns:xlink\\s*=/i.test(open))\n source = source.replace(/^<svg/i, '<svg xmlns:xlink=\"http://www.w3.org/1999/xlink\"');\n const doc = new DOMParser().parseFromString(source, \"image/svg+xml\");\n const svg = doc.documentElement;\n if (svg.namespaceURI !== SVG_NS || svg.localName !== \"svg\" || doc.querySelector(\"parsererror\"))\n return undefined;\n for (const bad of svg.querySelectorAll(\"script, foreignObject\")) bad.remove();\n for (const el of [svg, ...svg.querySelectorAll(\"*\")]) {\n for (const attr of [...el.attributes]) {\n const name = attr.name.toLowerCase();\n const link = name === \"href\" || name === \"xlink:href\" || name === \"src\";\n if (name.startsWith(\"on\") || (link && !/^(#|data:image\\/)/i.test(attr.value.trim())))\n el.removeAttribute(attr.name);\n }\n }\n return document.importNode(svg, true) as unknown as SVGSVGElement;\n}\n\n/** Live views made so far, for ids unique on the page (see {@link scopeIds}). */\nlet views = 0;\n\nconst URL_REF = /url\\(\\s*(['\"]?)#([^'\")\\s]+)\\1\\s*\\)/g;\n\n/**\n * Gives the ids a live picture refers to (by `url(#…)` in attributes and\n * styles, or `href=\"#…\"`: its gradients, filters, clips, masks, `<use>`d\n * shapes) `suffix`, and points the references at the new ids, so two\n * pictures on one page that both define `id=\"glow\"` each use their own.\n * Other ids stay as written. The original id is kept in `data-flair-id`;\n * returns how ids map.\n */\nfunction scopeIds(svg: Element, suffix: string): Map<string, string> {\n const all = [svg, ...svg.querySelectorAll(\"*\")];\n const referred = new Set<string>();\n for (const el of all) {\n const texts = [...el.attributes].map((a) => [a.name.toLowerCase(), a.value] as const);\n if (el.localName === \"style\") texts.push([\"\", el.textContent ?? \"\"]);\n for (const [name, value] of texts) {\n if ((name === \"href\" || name === \"xlink:href\") && value.startsWith(\"#\")) referred.add(value.slice(1));\n for (const m of value.matchAll(URL_REF)) referred.add(m[2]!);\n }\n }\n const ids = new Map<string, string>();\n for (const el of all) {\n if (!el.id || !referred.has(el.id) || ids.has(el.id)) continue;\n ids.set(el.id, `${el.id}-${suffix}`);\n el.setAttribute(\"data-flair-id\", el.id);\n el.id = `${el.id}-${suffix}`;\n }\n if (ids.size)\n for (const el of [svg, ...svg.querySelectorAll(\"*\")]) {\n for (const attr of [...el.attributes]) {\n const scoped = scopedRefs(attr.value, ids, attr.name);\n if (scoped !== attr.value) attr.value = scoped;\n }\n if (el.localName === \"style\" && el.textContent) el.textContent = scopedRefs(el.textContent, ids);\n }\n return ids;\n}\n\n/** `value` with its references to ids in `ids` pointed at their scoped ids. */\nfunction scopedRefs(value: string, ids: ReadonlyMap<string, string>, attr = \"\"): string {\n const name = attr.toLowerCase();\n if (name === \"href\" || name === \"xlink:href\") {\n const to = value.startsWith(\"#\") ? ids.get(value.slice(1)) : undefined;\n return to ? `#${to}` : value;\n }\n if (!value.includes(\"url(\")) return value;\n return value.replace(URL_REF, (whole, quote: string, id: string) => {\n const to = ids.get(id);\n return to ? `url(${quote}#${to}${quote})` : whole;\n });\n}\n\n/** Share of an element's size left round it when a `view` frames it. */\nconst VIEW_MARGIN = 0.12;\n\n/**\n * The box a `view` frames, in viewBox units: a box as given, an element's\n * box with a margin, widened to the picture's shape so the zoom does not\n * skew, or the whole picture for `null`.\n */\nfunction viewBox(view: SvgBox | string | null, whole: SvgBox, byId: Map<string, Element>): SvgBox {\n if (view === null) return whole;\n if (typeof view !== \"string\") return view;\n const el = byId.get(view) as SVGGraphicsElement | undefined;\n let box: DOMRect | undefined;\n try {\n box = el?.getBBox?.();\n } catch {\n // Not laid out (or not drawable): keep the whole picture.\n }\n if (!box || !(box.width > 0 || box.height > 0)) return whole;\n // In the picture's own units: through the element's transforms (a point\n // placed with one), which its box leaves out.\n try {\n const svg = el!.ownerSVGElement;\n const m = svg?.getScreenCTM()?.inverse().multiply(el!.getScreenCTM()!);\n if (m) {\n const corners = [\n [box.x, box.y],\n [box.x + box.width, box.y],\n [box.x, box.y + box.height],\n [box.x + box.width, box.y + box.height],\n ].map(([x, y]) => new DOMPoint(x, y).matrixTransform(m));\n const xs = corners.map((p) => p.x);\n const ys = corners.map((p) => p.y);\n box = new DOMRect(\n Math.min(...xs),\n Math.min(...ys),\n Math.max(...xs) - Math.min(...xs),\n Math.max(...ys) - Math.min(...ys),\n );\n }\n } catch {\n // Not laid out: keep its own box.\n }\n const aspect = whole[2] / whole[3];\n let w = box.width * (1 + 2 * VIEW_MARGIN);\n let h = box.height * (1 + 2 * VIEW_MARGIN);\n if (w / h > aspect) h = w / aspect;\n else w = h * aspect;\n return [box.x + box.width / 2 - w / 2, box.y + box.height / 2 - h / 2, w, h];\n}\n\n/**\n * Keeps the picture's `data-nonscaling` elements (a map's pins and labels)\n * the same size on screen as a `view` zooms: each is scaled round its own\n * origin by how far the picture is zoomed (so draw it round (0, 0) inside a\n * group that places it).\n */\nfunction keepSize(svg: SVGSVGElement, whole: SvgBox, nonscaling: Element[]) {\n if (!nonscaling.length) return;\n const width = Number((svg.getAttribute(\"viewBox\") ?? \"\").trim().split(/[\\s,]+/)[2]);\n const zoom = width > 0 ? width / whole[2] : 1;\n const transform = Math.abs(zoom - 1) < 1e-4 ? \"\" : `scale(${+zoom.toFixed(4)})`;\n for (const el of nonscaling)\n if ((el.getAttribute(\"transform\") ?? \"\") !== transform)\n if (transform) el.setAttribute(\"transform\", transform);\n else el.removeAttribute(\"transform\");\n}\n\n/** Applies a frame of the media's animations to `svg`. */\nfunction apply(\n svg: SVGSVGElement,\n whole: SvgBox,\n byId: Map<string, Element>,\n motion: SvgMotion,\n timeMs: number,\n steps: readonly number[],\n ids: ReadonlyMap<string, string> = new Map(),\n) {\n for (const [id, frame] of svgFrame(motion.animate ?? [], steps, timeMs)) {\n if (id === SVG_PICTURE && frame.view) {\n const a = viewBox(frame.view.from, whole, byId);\n const b = viewBox(frame.view.to, whole, byId);\n const u = frame.view.u;\n svg.setAttribute(\"viewBox\", a.map((n, i) => +(n + (b[i]! - n) * u).toFixed(2)).join(\" \"));\n continue;\n }\n const el = byId.get(id);\n if (!el) continue;\n for (const [name, value] of Object.entries(frame.attrs)) el.setAttribute(name, scopedRefs(value, ids, name));\n if (frame.draw !== undefined) {\n // A path length of 1 lets the dash be a fraction, with nothing measured.\n el.setAttribute(\"pathLength\", \"1\");\n el.setAttribute(\"stroke-dasharray\", \"1 1\");\n el.setAttribute(\"stroke-dashoffset\", String(1 - frame.draw));\n }\n if (frame.text !== undefined) el.textContent = frame.text;\n if (frame.solo !== undefined) {\n const children = Array.from(el.children);\n children.forEach((child, idx) => {\n if (child instanceof SVGElement || child instanceof HTMLElement) {\n if (idx === frame.solo) {\n child.removeAttribute(\"display\");\n } else {\n child.setAttribute(\"display\", \"none\");\n }\n }\n });\n }\n if (frame.along) {\n const path = byId.get(frame.along.path) as SVGGeometryElement | undefined;\n try {\n const length = path?.getTotalLength?.();\n if (path && length !== undefined) {\n const p = path.getPointAtLength(frame.along.at * length);\n el.setAttribute(\"transform\", `translate(${+p.x.toFixed(2)} ${+p.y.toFixed(2)})`);\n }\n } catch {\n // Not laid out: leave it where it is.\n }\n }\n }\n}\n\nconst VAR = /var\\(\\s*(--[\\w-]+)\\s*(?:,\\s*([^()]*(?:\\([^()]*\\)[^()]*)*))?\\)/g;\n\n/**\n * `copy` with the CSS variables in its attributes and styles replaced by\n * their values on `live` (its original, in the page), or their fallbacks:\n * an image of the SVG is drawn on its own, where the page's variables (a\n * host's palette) mean nothing.\n */\nfunction resolveVariables(live: SVGSVGElement, copy: SVGSVGElement) {\n const originals = [live, ...live.querySelectorAll(\"*\")];\n const copies = [copy, ...copy.querySelectorAll(\"*\")];\n copies.forEach((el, i) => {\n let style: CSSStyleDeclaration | undefined;\n for (const attr of [...el.attributes]) {\n if (!attr.value.includes(\"var(\")) continue;\n style ??= getComputedStyle(originals[i] ?? live);\n el.setAttribute(\n attr.name,\n attr.value.replace(\n VAR,\n (_, name: string, fallback?: string) =>\n style!.getPropertyValue(name).trim() || fallback?.trim() || \"\",\n ),\n );\n }\n });\n}\n\n/** An image of the picture as drawn now (its layers in order), at `pixels`. */\nasync function rasterize(layers: SVGSVGElement[], pixels: { width: number; height: number }): Promise<HTMLCanvasElement> {\n const canvas = document.createElement(\"canvas\");\n canvas.width = pixels.width;\n canvas.height = pixels.height;\n const defs = layers[0]?.querySelector(\"defs\");\n for (const [i, svg] of layers.entries()) {\n const copy = svg.cloneNode(true) as SVGSVGElement;\n resolveVariables(svg, copy);\n // A fixed layer's gradients and markers live in the picture's <defs>.\n if (i > 0 && defs) copy.prepend(defs.cloneNode(true));\n copy.setAttribute(\"xmlns\", SVG_NS);\n copy.setAttribute(\"width\", String(pixels.width));\n copy.setAttribute(\"height\", String(pixels.height));\n const url = URL.createObjectURL(\n new Blob([new XMLSerializer().serializeToString(copy)], { type: \"image/svg+xml\" }),\n );\n try {\n const image = new Image();\n image.src = url;\n await image.decode();\n canvas.getContext(\"2d\")!.drawImage(image, 0, 0, pixels.width, pixels.height);\n } finally {\n URL.revokeObjectURL(url);\n }\n }\n return canvas;\n}\n\n/**\n * Moves the picture's `data-fixed` elements (titles, legends, notes) into a\n * layer over it that keeps the original `viewBox`, so a `view` zooms the\n * picture under them while they stay put. Undefined when it has none.\n */\nfunction fixedLayer(svg: SVGSVGElement): SVGSVGElement | undefined {\n const fixed = [...svg.querySelectorAll(\"[data-fixed]\")].filter(\n (el) => !el.parentElement?.closest(\"[data-fixed]\"),\n );\n if (!fixed.length) return undefined;\n // The picture's own attributes (viewBox, fonts, fills) without its id.\n const layer = svg.cloneNode(false) as SVGSVGElement;\n layer.removeAttribute(\"id\");\n layer.setAttribute(\"aria-hidden\", \"true\");\n layer.style.cssText = \"position:absolute;inset:0;overflow:visible\";\n for (const el of fixed) layer.appendChild(el);\n return layer;\n}\n\nexport interface SvgViewOptions {\n /**\n * Called with the id of the innermost element with an id that is clicked:\n * its id in the motion's markup (in the page, ids carry a suffix that keeps\n * them unique, so several pictures can use the same ones).\n */\n onPick?: (id: string) => void;\n}\n\n/** An {@link SvgMotion} drawn into an element; call `draw` with a time to show it then. */\nexport interface SvgView {\n /** A `div` filled by the picture; put it anywhere. */\n readonly element: HTMLDivElement;\n /** Shows the picture at `timeMs`, with steps starting at `steps` (ms). */\n draw(timeMs: number, steps?: readonly number[]): void;\n /** An image of the picture as drawn now. */\n capture(pixels: { width: number; height: number }): Promise<HTMLCanvasElement | undefined>;\n}\n\n/** Shows an {@link SvgMotion} as a live view; with `onPick`, clicks report the id of the element clicked. */\nexport function svgView(motion: SvgMotion, options: SvgViewOptions = {}): SvgView {\n const element = document.createElement(\"div\");\n element.style.width = \"100%\";\n element.style.height = \"100%\";\n const { onPick } = options;\n if (onPick)\n element.addEventListener(\"click\", (event) => {\n // The innermost element with an id, up to the picture itself.\n for (let el = event.target as Element | null; el && el !== element; el = el.parentElement)\n if (el.id && el.localName !== \"svg\") return onPick(el.getAttribute(\"data-flair-id\") ?? el.id);\n });\n let svg: SVGSVGElement | undefined | null = null;\n let fixed: SVGSVGElement | undefined;\n let nonscaling: Element[] = [];\n let whole: SvgBox = [0, 0, 100, 100];\n const byId = new Map<string, Element>();\n let ids: ReadonlyMap<string, string> = new Map();\n const suffix = `f${++views}`;\n return {\n element,\n draw(timeMs, steps = []) {\n if (svg === null) {\n // Parsed on first use, so a view made and dropped costs nothing.\n svg = parseSvg(motion.svg);\n if (svg) {\n ids = scopeIds(svg, suffix);\n svg.setAttribute(\"width\", \"100%\");\n svg.setAttribute(\"height\", \"100%\");\n element.appendChild(svg);\n fixed = fixedLayer(svg);\n if (fixed) {\n element.style.position = \"relative\";\n element.appendChild(fixed);\n }\n for (const layer of [svg, fixed])\n for (const el of layer?.querySelectorAll(\"[id]\") ?? [])\n byId.set(el.getAttribute(\"data-flair-id\") ?? el.id, el);\n nonscaling = [...svg.querySelectorAll(\"[data-nonscaling]\")];\n const box = (svg.getAttribute(\"viewBox\") ?? \"\")\n .trim()\n .split(/[\\s,]+/)\n .map(Number);\n if (box.length === 4 && box.every(Number.isFinite)) whole = box as unknown as SvgBox;\n }\n }\n if (svg) {\n apply(svg, whole, byId, motion, timeMs, steps, ids);\n keepSize(svg, whole, nonscaling);\n }\n },\n capture: async (pixels) => (svg ? rasterize(fixed ? [svg, fixed] : [svg], pixels) : undefined),\n };\n}\n\n/** How long an {@link SvgMotion} runs: its last change's end, in ms (at least 1). */\nexport function svgDuration(motion: SvgMotion, steps: readonly number[] = []): number {\n let end = 0;\n for (const a of motion.animate ?? []) {\n const step = a.step ?? 0;\n const start = step <= 0 ? 0 : steps[step - 1];\n if (start === undefined) continue;\n end = Math.max(end, start + (a.delay ?? 0) + (a.ms ?? 800));\n }\n return Math.max(1, end);\n}\n\nexport interface SvgPlayerOptions extends SvgViewOptions {\n /** Time source (default: animation frames). Pass a `ManualClock` to step frames yourself. */\n clock?: Clock;\n /** Start playing at once (default `true`). */\n autoplay?: boolean;\n /**\n * Start again at the end (default `true`). When `false` it plays once and\n * stays on the last frame (hidden instead with `hideWhenPaused`).\n */\n loop?: boolean;\n /** Loop length in ms (default: the picture's own, see {@link svgDuration}, plus `hold`). */\n duration?: number;\n /** Ms to rest on the last frame before looping (default 600). */\n hold?: number;\n /** Playback speed (default 1). */\n speed?: number;\n /** Step start times in ms, for pictures that change on steps (default none). */\n steps?: readonly number[];\n /** Pause the animation loop when scrolled out of view via IntersectionObserver (default false). */\n pauseWhenHidden?: boolean;\n /** Wait to start playing until the element enters the viewport via IntersectionObserver (default false). When true, autoplay is deferred until first visible. */\n playOnEnter?: boolean;\n /** Hide the view (`visibility: hidden`) whenever playback is paused or reset (default false). */\n hideWhenPaused?: boolean;\n /**\n * Plays in answer to the pointer or the page instead of on its own (see\n * {@link SvgTrigger}): `\"hover\"` and `\"press\"` play to the end while\n * hovered or held and back to the start after; `\"click\"` plays the next\n * step on each click, starting over after the last; `\"scroll\"` follows\n * the trigger target's progress through the viewport (see `scrollRange`),\n * scrubbing back and forth as the page scrolls. `autoplay`, `loop` and\n * `steps` are ignored.\n */\n trigger?: SvgTrigger;\n /** The element whose pointer events trigger it, or whose scrolling a scroll trigger follows (default the container). */\n triggerTarget?: HTMLElement;\n /**\n * For a scroll trigger: the stretch of the target's progress through the\n * viewport (0 as its top comes in at the bottom, 1 as its bottom leaves at\n * the top) over which the picture plays from start to end (default\n * `[0.1, 0.6]`: from just after it comes into view to a little past the\n * middle).\n */\n scrollRange?: readonly [number, number];\n}\n\n/** What makes a triggered {@link SvgPlayer} play. */\nexport type SvgTrigger = \"hover\" | \"press\" | \"click\" | \"scroll\";\n\n/** The stretch of the viewport a scroll-triggered picture plays over (see `SvgPlayerOptions.scrollRange`). */\nexport const SCROLL_RANGE: readonly [number, number] = [0.1, 0.6];\n\n/**\n * Plays an {@link SvgMotion} inside `container`: loops by default, and can be\n * paused, sought and sped up. Dispose it to stop and remove the picture.\n */\nexport class SvgPlayer {\n readonly view: SvgView;\n private readonly clock: Clock;\n private steps: readonly number[];\n private readonly loop: boolean;\n private length: number;\n private readonly motion: SvgMotion;\n private readonly trigger?: SvgTrigger;\n private readonly triggerTarget?: HTMLElement;\n /** With a hover or press trigger: 1 playing to the end, -1 back to the start. */\n private direction = 1;\n /** With a click trigger: when each click came, in ms of playback. */\n private clicks: number[] = [];\n private readonly hideWhenPaused: boolean;\n private speed: number;\n private time = 0;\n private last = 0;\n private playing = false;\n private disposed = false;\n private wasPlayingBeforeHidden = false;\n private hasEnteredViewport = false;\n private intersectionObserver?: IntersectionObserver;\n private readonly scrollRange: readonly [number, number];\n /** A scroll trigger's pending update (one per frame). */\n private scrollFrame = 0;\n private followingScroll = false;\n\n constructor(\n private readonly container: HTMLElement,\n motion: SvgMotion,\n options: SvgPlayerOptions = {},\n ) {\n this.clock = options.clock ?? realClock;\n this.motion = motion;\n this.trigger = options.trigger;\n this.steps = this.trigger ? [] : (options.steps ?? []);\n this.loop = this.trigger ? false : (options.loop ?? true);\n this.speed = options.speed ?? 1;\n this.hideWhenPaused = Boolean(options.hideWhenPaused);\n this.scrollRange = options.scrollRange ?? SCROLL_RANGE;\n this.length = options.duration ?? svgDuration(motion, this.steps) + (options.hold ?? 600);\n this.view = svgView(motion, options);\n container.appendChild(this.view.element);\n this.view.draw(0, this.steps);\n\n const needsObserver =\n (options.pauseWhenHidden || options.playOnEnter) && typeof IntersectionObserver !== \"undefined\";\n\n if (needsObserver) {\n this.intersectionObserver = new IntersectionObserver(([entry]) => {\n if (!entry) return;\n if (entry.isIntersecting) {\n if (options.playOnEnter && !this.hasEnteredViewport) {\n this.hasEnteredViewport = true;\n if (options.autoplay ?? true) {\n this.play();\n }\n } else if (this.wasPlayingBeforeHidden) {\n this.wasPlayingBeforeHidden = false;\n this.play();\n }\n } else {\n if (this.playing && options.pauseWhenHidden) {\n this.wasPlayingBeforeHidden = true;\n this.pause();\n }\n }\n });\n this.intersectionObserver.observe(this.view.element);\n }\n\n if (this.trigger === \"scroll\") {\n this.length = options.duration ?? svgDuration(motion);\n this.triggerTarget = options.triggerTarget ?? container;\n this.followingScroll = true;\n this.listen(true);\n this.followScroll();\n return;\n }\n\n if (this.trigger) {\n // A click-triggered picture runs until its latest step is over, however many clicks.\n // Hovers and presses run to the last change and back, with no rest to wait through.\n this.length = this.trigger === \"click\" ? Infinity : (options.duration ?? svgDuration(motion));\n this.triggerTarget = options.triggerTarget ?? container;\n this.listen(true);\n return;\n }\n\n // Without IntersectionObserver (a server, a test), nothing would say it entered: play at once.\n const shouldAutoplay = (options.autoplay ?? true) && !(options.playOnEnter && needsObserver);\n if (shouldAutoplay) {\n this.play();\n } else if (this.hideWhenPaused) {\n this.view.element.style.visibility = \"hidden\";\n }\n }\n\n /** The loop length in ms. */\n get duration(): number {\n return this.length;\n }\n\n /** The time shown, in ms. */\n get currentTime(): number {\n return this.time;\n }\n\n get isPlaying(): boolean {\n return this.playing;\n }\n\n play(): void {\n if (this.playing || this.disposed) return;\n this.playing = true;\n if (this.hideWhenPaused) this.view.element.style.visibility = \"visible\";\n this.last = this.clock.now();\n void this.run();\n }\n\n pause(): void {\n this.playing = false;\n if (this.hideWhenPaused) this.view.element.style.visibility = \"hidden\";\n }\n\n /** Stops playback, rewinds to `0 ms`, and hides the view if `hideWhenPaused` is enabled. */\n reset(): void {\n this.playing = false;\n this.wasPlayingBeforeHidden = false;\n this.seek(0);\n if (this.hideWhenPaused) this.view.element.style.visibility = \"hidden\";\n }\n\n /** Shows the picture at `ms` (wrapped into the loop when looping). */\n seek(ms: number): void {\n this.time = this.loop\n ? ((ms % this.length) + this.length) % this.length\n : Math.min(Math.max(ms, 0), this.length);\n this.view.draw(this.time, this.steps);\n }\n\n /**\n * Shows the picture at `progress` of the way through it, 0–1: how hosts\n * scrub it themselves (a slider, a scroll position of their own). A scroll\n * trigger stops following the page once a host calls this.\n */\n setProgress(progress: number): void {\n if (this.followingScroll) {\n this.followingScroll = false;\n this.listen(false);\n }\n this.playing = false;\n this.seek(Math.min(1, Math.max(0, progress)) * (Number.isFinite(this.length) ? this.length : svgDuration(this.motion, this.steps)));\n }\n\n /**\n * A scroll trigger's playback position, 0–1, for the trigger target's\n * `progress` through the viewport (see {@link SvgPlayerOptions.scrollRange}).\n */\n scrollPosition(progress: number): number {\n const [from, to] = this.scrollRange;\n return to > from ? Math.min(1, Math.max(0, (progress - from) / (to - from))) : progress >= to ? 1 : 0;\n }\n\n setSpeed(speed: number): void {\n this.speed = speed;\n }\n\n /**\n * Plays to the end (`true`) or back to the start (`false`) from wherever it\n * is: what a hover or press trigger does, for hosts that drive it themselves\n * (a hovered card, a focused button).\n */\n setActive(active: boolean): void {\n this.direction = active ? 1 : -1;\n this.play();\n }\n\n /**\n * Plays the picture's next step (what a click trigger does): steps 1, 2, …\n * in turn, then step 1 again, so a picture whose last step leads back to\n * how it started toggles; one with no steps plays again from the start.\n */\n advance(): void {\n const last = Math.max(0, ...(this.motion.animate ?? []).map((a) => a.step ?? 0));\n if (last === 0) {\n this.clicks = [];\n this.time = 0;\n } else {\n this.clicks.push(this.time);\n // Only the clicks of the current round count, so each round starts from how the last ended.\n this.steps = this.clicks.slice(Math.floor((this.clicks.length - 1) / last) * last);\n }\n this.view.draw(this.time, this.steps);\n this.play();\n }\n\n dispose(): void {\n this.disposed = true;\n this.playing = false;\n if (this.trigger && (this.trigger !== \"scroll\" || this.followingScroll)) this.listen(false);\n if (this.scrollFrame && typeof cancelAnimationFrame !== \"undefined\") cancelAnimationFrame(this.scrollFrame);\n this.intersectionObserver?.disconnect();\n this.view.element.remove();\n }\n\n private onEnter = () => this.setActive(true);\n private onLeave = () => this.setActive(false);\n private onClick = () => this.advance();\n\n /** Seeks to where the trigger target is in the viewport. */\n private followScroll = () => {\n this.scrollFrame = 0;\n if (!this.followingScroll || this.disposed) return;\n const rect = this.triggerTarget!.getBoundingClientRect();\n const viewport = window.innerHeight || document.documentElement.clientHeight || 1;\n const travel = viewport + rect.height;\n const progress = travel > 0 ? Math.min(1, Math.max(0, (viewport - rect.top) / travel)) : 0;\n this.seek(this.scrollPosition(progress) * this.length);\n };\n\n /** A scroll or resize: follow it on the next frame (or now, without frames). */\n private onScroll = () => {\n if (typeof requestAnimationFrame === \"undefined\") return this.followScroll();\n if (!this.scrollFrame) this.scrollFrame = requestAnimationFrame(this.followScroll);\n };\n\n private listen(on: boolean) {\n const target = this.triggerTarget!;\n const method = on ? \"addEventListener\" : \"removeEventListener\";\n if (this.trigger === \"scroll\") {\n // Any scroll that may move the target: the page's or a scrolling element's.\n window[method](\"scroll\", this.onScroll, { capture: true, passive: true } as AddEventListenerOptions);\n window[method](\"resize\", this.onScroll, { passive: true } as AddEventListenerOptions);\n } else if (this.trigger === \"hover\") {\n target[method](\"pointerenter\", this.onEnter);\n target[method](\"pointerleave\", this.onLeave);\n } else if (this.trigger === \"press\") {\n target[method](\"pointerdown\", this.onEnter);\n window[method](\"pointerup\", this.onLeave);\n window[method](\"pointercancel\", this.onLeave);\n } else target[method](\"click\", this.onClick);\n }\n\n private async run(): Promise<void> {\n while (this.playing && !this.disposed) {\n await this.clock.frame();\n if (!this.playing || this.disposed) return;\n const now = this.clock.now();\n const dt = (now - this.last) * this.speed;\n this.last = now;\n if (this.trigger === \"click\") {\n // Rest once the latest step is over, until the next click.\n const end = svgDuration(this.motion, this.steps);\n this.time = Math.min(this.time + dt, Math.max(end, this.time));\n this.view.draw(this.time, this.steps);\n if (this.time >= end) this.playing = false;\n continue;\n }\n if (this.trigger) {\n const next = Math.min(Math.max(this.time + dt * this.direction, 0), this.length);\n this.seek(next);\n if (next === 0 || next === this.length) this.playing = false;\n continue;\n }\n const next = this.time + dt;\n if (!this.loop && next >= this.length) {\n this.seek(this.length);\n this.playing = false;\n if (this.hideWhenPaused) this.view.element.style.visibility = \"hidden\";\n return;\n }\n this.seek(next);\n }\n }\n}\n\nexport interface AnimateCountOptions {\n /** Starting number. */\n from: number;\n /** Ending number. */\n to: number;\n /** Duration in ms (default {@link STEP_MS}). */\n ms?: number;\n /** Delay before starting in ms (default 0). */\n delay?: number;\n /** Easing curve (default `\"out\"`). */\n ease?: EaseName;\n /** Number of decimal places (default 0). */\n decimals?: number;\n /** Format template where `{n}` is replaced by the formatted number (default `\"{n}\"`). */\n format?: string;\n /** Time source (default: animation frames). */\n clock?: Clock;\n /** Called with each formatted text frame (and raw numeric value). */\n onUpdate: (text: string, value: number) => void;\n}\n\n/**\n * Counts a number from `from` to `to` over `ms` using Flair's easing curves\n * and `Clock`, calling `onUpdate` with the formatted string on each frame.\n * Returns a cancel function that stops the animation.\n */\nexport function animateCount(options: AnimateCountOptions): () => void {\n const clock = options.clock ?? realClock;\n const ms = options.ms ?? STEP_MS;\n const delay = options.delay ?? 0;\n const ease = EASES[options.ease ?? \"out\"];\n const start = clock.now();\n let stopped = false;\n\n const emit = (u: number) => {\n const v = options.from + (options.to - options.from) * ease(u);\n options.onUpdate(formatCount(v, options), v);\n };\n\n emit(ms <= 0 && delay <= 0 ? 1 : 0);\n if (ms <= 0 && delay <= 0) return () => {};\n\n void (async () => {\n while (!stopped) {\n await clock.frame();\n if (stopped) return;\n const elapsed = clock.now() - start - delay;\n if (elapsed < 0) continue;\n if (ms <= 0 || elapsed >= ms) {\n emit(1);\n return;\n }\n emit(elapsed / ms);\n }\n })();\n\n return () => {\n stopped = true;\n };\n}\n"],"mappings":"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","names":["next"]}
|
|
@@ -1,8 +1,10 @@
|
|
|
1
|
+
import {
|
|
2
|
+
svgDuration
|
|
3
|
+
} from "./chunk-JETRHCP3.js";
|
|
1
4
|
import {
|
|
2
5
|
SVG_PICTURE,
|
|
3
|
-
svgDuration,
|
|
4
6
|
svgFrame
|
|
5
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-VKIDRHBG.js";
|
|
6
8
|
|
|
7
9
|
// src/vector/static.ts
|
|
8
10
|
var DROPPED = /* @__PURE__ */ new Set(["script", "foreignobject", "iframe", "object", "embed"]);
|
|
@@ -153,4 +155,4 @@ export {
|
|
|
153
155
|
svgFrameMarkup,
|
|
154
156
|
svgFrameTree
|
|
155
157
|
};
|
|
156
|
-
//# sourceMappingURL=chunk-
|
|
158
|
+
//# sourceMappingURL=chunk-MOLJPWME.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/vector/static.ts"],"sourcesContent":["/**\n * An {@link SvgMotion}'s frame as markup, with no DOM: for server rendering\n * (a still that hydrates without a blank flash), reduced motion and other\n * places a live player is not wanted.\n */\n\nimport { SVG_PICTURE, svgFrame, type SvgBox, type SvgMotion } from \"./motion.js\";\nimport { svgDuration } from \"./svg.js\";\n\n/** @internal An element of a parsed picture (see `svgFrameTree`). */\nexport interface SvgNode {\n tag: string;\n attrs: [string, string][];\n children: (SvgNode | string)[];\n}\n\ntype Node = SvgNode;\n\n/** Elements dropped with all they hold: they run code or show outside markup. */\nconst DROPPED = new Set([\"script\", \"foreignobject\", \"iframe\", \"object\", \"embed\"]);\nconst ATTR = /([^\\s=/>\"']+)(?:\\s*=\\s*(?:\"([^\"]*)\"|'([^']*)'|([^\\s>\"']+)))?/g;\nconst TOKEN = /<!--[\\s\\S]*?(?:-->|$)|<!\\[CDATA\\[([\\s\\S]*?)(?:\\]\\]>|$)|<[!?][^>]*>|<\\/\\s*([\\w:.-]+)\\s*>|<([\\w:.-]+)((?:\\s+[^\\s=/>\"']+(?:\\s*=\\s*(?:\"[^\"]*\"|'[^']*'|[^\\s>\"']+))?)*)\\s*(\\/?)>/g;\n\nconst ENTITIES: Record<string, string> = { amp: \"&\", lt: \"<\", gt: \">\", quot: '\"', apos: \"'\", nbsp: \" \" };\nconst decode = (s: string) =>\n s.replace(/&(#x[0-9a-f]+|#\\d+|\\w+);/gi, (match, e: string) => {\n if (e[0] !== \"#\") return ENTITIES[e.toLowerCase()] ?? match;\n const code = e[1] === \"x\" || e[1] === \"X\" ? parseInt(e.slice(2), 16) : parseInt(e.slice(1), 10);\n return code > 0 && code <= 0x10ffff ? String.fromCodePoint(code) : \"\";\n });\nconst escapeText = (s: string) => s.replace(/&/g, \"&\").replace(/</g, \"<\").replace(/>/g, \">\");\nconst escapeAttr = (s: string) => escapeText(s).replace(/\"/g, \""\");\n\n/** Safe as `parseSvg` keeps it: no handlers, and links only within the picture or to data images. */\nfunction safeAttr(name: string, value: string): boolean {\n const n = name.toLowerCase();\n if (n.startsWith(\"on\") || !/^[a-z_][\\w:.-]*$/i.test(n)) return false;\n if (n === \"href\" || n === \"xlink:href\" || n === \"src\") return /^(#|data:image\\/)/i.test(value.trim());\n return true;\n}\n\n/** The `<svg>` in `markup` as a tree, without anything `parseSvg` would remove. */\nfunction parse(markup: string): Node | undefined {\n const root: Node = { tag: \"\", attrs: [], children: [] };\n const stack = [root];\n let dropping = 0;\n let at = 0;\n const text = (s: string) => {\n if (!dropping && s) stack[stack.length - 1]!.children.push(decode(s));\n };\n for (const m of markup.matchAll(TOKEN)) {\n text(markup.slice(at, m.index));\n at = m.index + m[0].length;\n const [, cdata, close, open, attrs = \"\", selfClosing] = m;\n if (cdata !== undefined) {\n if (!dropping) stack[stack.length - 1]!.children.push(cdata);\n } else if (close) {\n const tag = close.toLowerCase();\n if (dropping) {\n if (DROPPED.has(tag)) dropping--;\n continue;\n }\n // Close up to the matching element; a stray closing tag is ignored.\n let i = stack.length - 1;\n while (i > 0 && stack[i]!.tag.toLowerCase() !== tag) i--;\n if (i > 0) stack.length = i;\n } else if (open) {\n if (DROPPED.has(open.toLowerCase())) {\n if (!selfClosing) dropping++;\n continue;\n }\n if (dropping) continue;\n const node: Node = { tag: open, attrs: [], children: [] };\n for (const a of attrs.matchAll(ATTR)) {\n const value = decode(a[2] ?? a[3] ?? a[4] ?? \"\");\n if (safeAttr(a[1]!, value)) node.attrs.push([a[1]!, value]);\n }\n stack[stack.length - 1]!.children.push(node);\n if (!selfClosing) stack.push(node);\n }\n }\n text(markup.slice(at));\n const svg = root.children.find((c): c is Node => typeof c !== \"string\");\n return svg && svg.tag.toLowerCase() === \"svg\" ? svg : undefined;\n}\n\n/** @internal A parsed element as markup. */\nexport function serialize(node: Node | string): string {\n if (typeof node === \"string\") return escapeText(node);\n const attrs = node.attrs.map(([k, v]) => ` ${k}=\"${escapeAttr(v)}\"`).join(\"\");\n return `<${node.tag}${attrs}>${node.children.map(serialize).join(\"\")}</${node.tag}>`;\n}\n\n/** @internal Sets (or with `undefined` removes) an attribute, unless it is unsafe. */\nexport function setAttr(node: Node, name: string, value: string | undefined) {\n const i = node.attrs.findIndex(([k]) => k === name);\n if (value === undefined) {\n if (i >= 0) node.attrs.splice(i, 1);\n } else if (!safeAttr(name, value)) return;\n else if (i >= 0) node.attrs[i]![1] = value;\n else node.attrs.push([name, value]);\n}\n\n/** @internal */\nexport const getAttr = (node: Node, name: string) => node.attrs.find(([k]) => k === name)?.[1];\n\n/** A `viewBox` attribute's box, or undefined when it has none or it is malformed. */\nexport function parseViewBox(value: string | null | undefined): SvgBox | undefined {\n const box = (value ?? \"\").trim().split(/[\\s,]+/).map(Number);\n return box.length === 4 && box.every(Number.isFinite) && box[2]! > 0 && box[3]! > 0\n ? (box as unknown as SvgBox)\n : undefined;\n}\n\n/** The `viewBox` on an SVG's opening tag (see {@link parseViewBox}). */\nexport function svgViewBox(markup: string): SvgBox | undefined {\n const open = /<svg\\b[^>]*>/i.exec(markup)?.[0] ?? \"\";\n return parseViewBox(/\\sviewBox\\s*=\\s*(?:\"([^\"]*)\"|'([^']*)')/i.exec(open)?.slice(1).find((v) => v !== undefined));\n}\n\n/** How a picture framed for a line of text (a text effect's `inline`) sits in it; see {@link svgInlineLayout}. */\nexport interface SvgInlineLayout {\n /** Its viewBox: x, y, width, height. */\n viewBox: SvgBox;\n /** Width over height, for its host's CSS `aspect-ratio`. */\n aspectRatio: number;\n /**\n * How far it reaches past its box at rest (descenders, outlines, glows,\n * drips), in ems of the font size it is set at; 0 where the picture says\n * nothing (no `data-overhang`).\n */\n overhang: { top: number; right: number; bottom: number; left: number };\n}\n\n/**\n * The box and overhang on an SVG's opening tag: its `viewBox`, and\n * `data-overhang=\"top right bottom left\"` (ems), which text effects write.\n * Set inline (`height: 0.72em`, the cap height they frame), give the host a\n * margin of the overhang to keep room for what spills out:\n * `margin: ${top}em ${right}em ${bottom}em ${left}em`.\n */\nexport function svgInlineLayout(markup: string): SvgInlineLayout | undefined {\n const viewBox = svgViewBox(markup);\n if (!viewBox || !(viewBox[3] > 0)) return undefined;\n const open = /<svg\\b[^>]*>/i.exec(markup)?.[0] ?? \"\";\n const spill = (/\\sdata-overhang\\s*=\\s*\"([^\"]*)\"/i.exec(open)?.[1] ?? \"\").trim().split(/\\s+/).map(Number);\n const [top, right, bottom, left] = [0, 1, 2, 3].map((i) => (Number.isFinite(spill[i]) ? Math.max(0, spill[i]!) : 0));\n return { viewBox, aspectRatio: viewBox[2] / viewBox[3], overhang: { top: top!, right: right!, bottom: bottom!, left: left! } };\n}\n\nexport interface SvgFrameMarkupOptions {\n /**\n * The time shown, in ms: a number, `\"first\"` (0) or `\"last\"` (the end of\n * its last change, see `svgDuration`; the default).\n */\n at?: number | \"first\" | \"last\";\n /** Step start times in ms, as a player's `steps` (default none). */\n steps?: readonly number[];\n}\n\n/**\n * The picture of an {@link SvgMotion} at a moment, as `<svg>` markup made safe\n * the way `parseSvg` makes it (no scripts, handlers, `<foreignObject>` or\n * outside links), with no DOM needed: render it on a server, show it under\n * `prefers-reduced-motion`, or anywhere a still will do. It is sized to fill\n * its container, as `SvgPlayer` draws it.\n *\n * It matches the player's frame except where that needs layout: a change\n * `along` a path, and a `view` that frames an element by id, stay where the\n * markup puts them. Undefined when `motion.svg` is not an `<svg>`.\n */\nexport function svgFrameMarkup(motion: SvgMotion, options: SvgFrameMarkupOptions = {}): string | undefined {\n const steps = options.steps ?? [];\n const at = options.at ?? \"last\";\n const time = at === \"first\" ? 0 : at === \"last\" ? svgDuration(motion, steps) : at;\n const tree = svgFrameTree(motion, steps, time);\n if (!tree) return undefined;\n const { svg } = tree;\n setAttr(svg, \"width\", \"100%\");\n setAttr(svg, \"height\", \"100%\");\n if (!getAttr(svg, \"xmlns\")) setAttr(svg, \"xmlns\", \"http://www.w3.org/2000/svg\");\n return serialize(svg);\n}\n\n/**\n * @internal The safe tree of `motion.svg` showing its frame at `time`, with\n * its elements by id (the first of each) and its own `viewBox`; undefined\n * when it is not an `<svg>`. What {@link svgFrameMarkup} and\n * `svgAnimatedMarkup` build on.\n */\nexport function svgFrameTree(\n motion: SvgMotion,\n steps: readonly number[],\n time: number,\n): { svg: Node; byId: Map<string, Node>; whole: SvgBox | undefined } | undefined {\n const svg = parse(motion.svg.trim());\n if (!svg) return undefined;\n const byId = new Map<string, Node>();\n const index = (node: Node) => {\n const id = getAttr(node, \"id\");\n if (id && !byId.has(id)) byId.set(id, node);\n for (const c of node.children) if (typeof c !== \"string\") index(c);\n };\n index(svg);\n const whole = parseViewBox(getAttr(svg, \"viewBox\"));\n for (const [id, frame] of svgFrame(motion.animate ?? [], steps, time)) {\n if (id === SVG_PICTURE && frame.view) {\n const { from, to, u } = frame.view;\n const a = from === null ? whole : typeof from === \"string\" ? undefined : from;\n const b = typeof to === \"string\" ? undefined : to;\n if (a && b) setAttr(svg, \"viewBox\", a.map((n, i) => +(n + (b[i]! - n) * u).toFixed(2)).join(\" \"));\n continue;\n }\n const el = byId.get(id);\n if (!el) continue;\n for (const [name, value] of Object.entries(frame.attrs)) setAttr(el, name, value);\n if (frame.draw !== undefined) {\n setAttr(el, \"pathLength\", \"1\");\n setAttr(el, \"stroke-dasharray\", \"1 1\");\n setAttr(el, \"stroke-dashoffset\", String(1 - frame.draw));\n }\n if (frame.text !== undefined) el.children = [frame.text];\n if (frame.solo !== undefined) {\n let i = 0;\n for (const child of el.children)\n if (typeof child !== \"string\") setAttr(child, \"display\", i++ === frame.solo ? undefined : \"none\");\n }\n }\n return { svg, byId, whole };\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/vector/static.ts"],"sourcesContent":["/**\n * An {@link SvgMotion}'s frame as markup, with no DOM: for server rendering\n * (a still that hydrates without a blank flash), reduced motion and other\n * places a live player is not wanted.\n */\n\nimport { SVG_PICTURE, svgFrame, type SvgBox, type SvgMotion } from \"./motion.js\";\nimport { svgDuration } from \"./svg.js\";\n\n/** @internal An element of a parsed picture (see `svgFrameTree`). */\nexport interface SvgNode {\n tag: string;\n attrs: [string, string][];\n children: (SvgNode | string)[];\n}\n\ntype Node = SvgNode;\n\n/** Elements dropped with all they hold: they run code or show outside markup. */\nconst DROPPED = new Set([\"script\", \"foreignobject\", \"iframe\", \"object\", \"embed\"]);\nconst ATTR = /([^\\s=/>\"']+)(?:\\s*=\\s*(?:\"([^\"]*)\"|'([^']*)'|([^\\s>\"']+)))?/g;\nconst TOKEN = /<!--[\\s\\S]*?(?:-->|$)|<!\\[CDATA\\[([\\s\\S]*?)(?:\\]\\]>|$)|<[!?][^>]*>|<\\/\\s*([\\w:.-]+)\\s*>|<([\\w:.-]+)((?:\\s+[^\\s=/>\"']+(?:\\s*=\\s*(?:\"[^\"]*\"|'[^']*'|[^\\s>\"']+))?)*)\\s*(\\/?)>/g;\n\nconst ENTITIES: Record<string, string> = { amp: \"&\", lt: \"<\", gt: \">\", quot: '\"', apos: \"'\", nbsp: \" \" };\nconst decode = (s: string) =>\n s.replace(/&(#x[0-9a-f]+|#\\d+|\\w+);/gi, (match, e: string) => {\n if (e[0] !== \"#\") return ENTITIES[e.toLowerCase()] ?? match;\n const code = e[1] === \"x\" || e[1] === \"X\" ? parseInt(e.slice(2), 16) : parseInt(e.slice(1), 10);\n return code > 0 && code <= 0x10ffff ? String.fromCodePoint(code) : \"\";\n });\nconst escapeText = (s: string) => s.replace(/&/g, \"&\").replace(/</g, \"<\").replace(/>/g, \">\");\nconst escapeAttr = (s: string) => escapeText(s).replace(/\"/g, \""\");\n\n/** Safe as `parseSvg` keeps it: no handlers, and links only within the picture or to data images. */\nfunction safeAttr(name: string, value: string): boolean {\n const n = name.toLowerCase();\n if (n.startsWith(\"on\") || !/^[a-z_][\\w:.-]*$/i.test(n)) return false;\n if (n === \"href\" || n === \"xlink:href\" || n === \"src\") return /^(#|data:image\\/)/i.test(value.trim());\n return true;\n}\n\n/** The `<svg>` in `markup` as a tree, without anything `parseSvg` would remove. */\nfunction parse(markup: string): Node | undefined {\n const root: Node = { tag: \"\", attrs: [], children: [] };\n const stack = [root];\n let dropping = 0;\n let at = 0;\n const text = (s: string) => {\n if (!dropping && s) stack[stack.length - 1]!.children.push(decode(s));\n };\n for (const m of markup.matchAll(TOKEN)) {\n text(markup.slice(at, m.index));\n at = m.index + m[0].length;\n const [, cdata, close, open, attrs = \"\", selfClosing] = m;\n if (cdata !== undefined) {\n if (!dropping) stack[stack.length - 1]!.children.push(cdata);\n } else if (close) {\n const tag = close.toLowerCase();\n if (dropping) {\n if (DROPPED.has(tag)) dropping--;\n continue;\n }\n // Close up to the matching element; a stray closing tag is ignored.\n let i = stack.length - 1;\n while (i > 0 && stack[i]!.tag.toLowerCase() !== tag) i--;\n if (i > 0) stack.length = i;\n } else if (open) {\n if (DROPPED.has(open.toLowerCase())) {\n if (!selfClosing) dropping++;\n continue;\n }\n if (dropping) continue;\n const node: Node = { tag: open, attrs: [], children: [] };\n for (const a of attrs.matchAll(ATTR)) {\n const value = decode(a[2] ?? a[3] ?? a[4] ?? \"\");\n if (safeAttr(a[1]!, value)) node.attrs.push([a[1]!, value]);\n }\n stack[stack.length - 1]!.children.push(node);\n if (!selfClosing) stack.push(node);\n }\n }\n text(markup.slice(at));\n const svg = root.children.find((c): c is Node => typeof c !== \"string\");\n return svg && svg.tag.toLowerCase() === \"svg\" ? svg : undefined;\n}\n\n/** @internal A parsed element as markup. */\nexport function serialize(node: Node | string): string {\n if (typeof node === \"string\") return escapeText(node);\n const attrs = node.attrs.map(([k, v]) => ` ${k}=\"${escapeAttr(v)}\"`).join(\"\");\n return `<${node.tag}${attrs}>${node.children.map(serialize).join(\"\")}</${node.tag}>`;\n}\n\n/** @internal Sets (or with `undefined` removes) an attribute, unless it is unsafe. */\nexport function setAttr(node: Node, name: string, value: string | undefined) {\n const i = node.attrs.findIndex(([k]) => k === name);\n if (value === undefined) {\n if (i >= 0) node.attrs.splice(i, 1);\n } else if (!safeAttr(name, value)) return;\n else if (i >= 0) node.attrs[i]![1] = value;\n else node.attrs.push([name, value]);\n}\n\n/** @internal */\nexport const getAttr = (node: Node, name: string) => node.attrs.find(([k]) => k === name)?.[1];\n\n/** A `viewBox` attribute's box, or undefined when it has none or it is malformed. */\nexport function parseViewBox(value: string | null | undefined): SvgBox | undefined {\n const box = (value ?? \"\").trim().split(/[\\s,]+/).map(Number);\n return box.length === 4 && box.every(Number.isFinite) && box[2]! > 0 && box[3]! > 0\n ? (box as unknown as SvgBox)\n : undefined;\n}\n\n/** The `viewBox` on an SVG's opening tag (see {@link parseViewBox}). */\nexport function svgViewBox(markup: string): SvgBox | undefined {\n const open = /<svg\\b[^>]*>/i.exec(markup)?.[0] ?? \"\";\n return parseViewBox(/\\sviewBox\\s*=\\s*(?:\"([^\"]*)\"|'([^']*)')/i.exec(open)?.slice(1).find((v) => v !== undefined));\n}\n\n/** How a picture framed for a line of text (a text effect's `inline`) sits in it; see {@link svgInlineLayout}. */\nexport interface SvgInlineLayout {\n /** Its viewBox: x, y, width, height. */\n viewBox: SvgBox;\n /** Width over height, for its host's CSS `aspect-ratio`. */\n aspectRatio: number;\n /**\n * How far it reaches past its box at rest (descenders, outlines, glows,\n * drips), in ems of the font size it is set at; 0 where the picture says\n * nothing (no `data-overhang`).\n */\n overhang: { top: number; right: number; bottom: number; left: number };\n}\n\n/**\n * The box and overhang on an SVG's opening tag: its `viewBox`, and\n * `data-overhang=\"top right bottom left\"` (ems), which text effects write.\n * Set inline (`height: 0.72em`, the cap height they frame), give the host a\n * margin of the overhang to keep room for what spills out:\n * `margin: ${top}em ${right}em ${bottom}em ${left}em`.\n */\nexport function svgInlineLayout(markup: string): SvgInlineLayout | undefined {\n const viewBox = svgViewBox(markup);\n if (!viewBox || !(viewBox[3] > 0)) return undefined;\n const open = /<svg\\b[^>]*>/i.exec(markup)?.[0] ?? \"\";\n const spill = (/\\sdata-overhang\\s*=\\s*\"([^\"]*)\"/i.exec(open)?.[1] ?? \"\").trim().split(/\\s+/).map(Number);\n const [top, right, bottom, left] = [0, 1, 2, 3].map((i) => (Number.isFinite(spill[i]) ? Math.max(0, spill[i]!) : 0));\n return { viewBox, aspectRatio: viewBox[2] / viewBox[3], overhang: { top: top!, right: right!, bottom: bottom!, left: left! } };\n}\n\nexport interface SvgFrameMarkupOptions {\n /**\n * The time shown, in ms: a number, `\"first\"` (0) or `\"last\"` (the end of\n * its last change, see `svgDuration`; the default).\n */\n at?: number | \"first\" | \"last\";\n /** Step start times in ms, as a player's `steps` (default none). */\n steps?: readonly number[];\n}\n\n/**\n * The picture of an {@link SvgMotion} at a moment, as `<svg>` markup made safe\n * the way `parseSvg` makes it (no scripts, handlers, `<foreignObject>` or\n * outside links), with no DOM needed: render it on a server, show it under\n * `prefers-reduced-motion`, or anywhere a still will do. It is sized to fill\n * its container, as `SvgPlayer` draws it.\n *\n * It matches the player's frame except where that needs layout: a change\n * `along` a path, and a `view` that frames an element by id, stay where the\n * markup puts them. Undefined when `motion.svg` is not an `<svg>`.\n */\nexport function svgFrameMarkup(motion: SvgMotion, options: SvgFrameMarkupOptions = {}): string | undefined {\n const steps = options.steps ?? [];\n const at = options.at ?? \"last\";\n const time = at === \"first\" ? 0 : at === \"last\" ? svgDuration(motion, steps) : at;\n const tree = svgFrameTree(motion, steps, time);\n if (!tree) return undefined;\n const { svg } = tree;\n setAttr(svg, \"width\", \"100%\");\n setAttr(svg, \"height\", \"100%\");\n if (!getAttr(svg, \"xmlns\")) setAttr(svg, \"xmlns\", \"http://www.w3.org/2000/svg\");\n return serialize(svg);\n}\n\n/**\n * @internal The safe tree of `motion.svg` showing its frame at `time`, with\n * its elements by id (the first of each) and its own `viewBox`; undefined\n * when it is not an `<svg>`. What {@link svgFrameMarkup} and\n * `svgAnimatedMarkup` build on.\n */\nexport function svgFrameTree(\n motion: SvgMotion,\n steps: readonly number[],\n time: number,\n): { svg: Node; byId: Map<string, Node>; whole: SvgBox | undefined } | undefined {\n const svg = parse(motion.svg.trim());\n if (!svg) return undefined;\n const byId = new Map<string, Node>();\n const index = (node: Node) => {\n const id = getAttr(node, \"id\");\n if (id && !byId.has(id)) byId.set(id, node);\n for (const c of node.children) if (typeof c !== \"string\") index(c);\n };\n index(svg);\n const whole = parseViewBox(getAttr(svg, \"viewBox\"));\n for (const [id, frame] of svgFrame(motion.animate ?? [], steps, time)) {\n if (id === SVG_PICTURE && frame.view) {\n const { from, to, u } = frame.view;\n const a = from === null ? whole : typeof from === \"string\" ? undefined : from;\n const b = typeof to === \"string\" ? undefined : to;\n if (a && b) setAttr(svg, \"viewBox\", a.map((n, i) => +(n + (b[i]! - n) * u).toFixed(2)).join(\" \"));\n continue;\n }\n const el = byId.get(id);\n if (!el) continue;\n for (const [name, value] of Object.entries(frame.attrs)) setAttr(el, name, value);\n if (frame.draw !== undefined) {\n setAttr(el, \"pathLength\", \"1\");\n setAttr(el, \"stroke-dasharray\", \"1 1\");\n setAttr(el, \"stroke-dashoffset\", String(1 - frame.draw));\n }\n if (frame.text !== undefined) el.children = [frame.text];\n if (frame.solo !== undefined) {\n let i = 0;\n for (const child of el.children)\n if (typeof child !== \"string\") setAttr(child, \"display\", i++ === frame.solo ? undefined : \"none\");\n }\n }\n return { svg, byId, whole };\n}\n"],"mappings":"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","names":[]}
|
|
@@ -3,17 +3,17 @@ import {
|
|
|
3
3
|
serialize,
|
|
4
4
|
setAttr,
|
|
5
5
|
svgFrameTree
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-MOLJPWME.js";
|
|
7
|
+
import {
|
|
8
|
+
svgDuration
|
|
9
|
+
} from "./chunk-JETRHCP3.js";
|
|
7
10
|
import {
|
|
8
11
|
STEP_MS,
|
|
9
12
|
SVG_PICTURE,
|
|
13
|
+
pathPointAt,
|
|
10
14
|
stepStart,
|
|
11
|
-
svgDuration,
|
|
12
15
|
svgFrame
|
|
13
|
-
} from "./chunk-
|
|
14
|
-
import {
|
|
15
|
-
pathPointAt
|
|
16
|
-
} from "./chunk-ORAFCHX3.js";
|
|
16
|
+
} from "./chunk-VKIDRHBG.js";
|
|
17
17
|
|
|
18
18
|
// src/vector/animated.ts
|
|
19
19
|
var SPLINES = { linear: "0 0 1 1", in: "0.3333 0 0.6667 0", out: "0.3333 1 0.6667 1" };
|
|
@@ -404,4 +404,4 @@ function scopeIds(svg, suffix) {
|
|
|
404
404
|
export {
|
|
405
405
|
svgAnimatedMarkup
|
|
406
406
|
};
|
|
407
|
-
//# sourceMappingURL=chunk-
|
|
407
|
+
//# sourceMappingURL=chunk-N5HXMQOK.js.map
|