pixi-effects 0.1.0 → 0.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/README.md +107 -7
- package/dist/Base-B1Tdn0Cv.d.cts +92 -0
- package/dist/Base-Bh_VSusi.d.ts +92 -0
- package/dist/Composition-BI5HZJFL.cjs +13 -0
- package/dist/Composition-BI5HZJFL.cjs.map +1 -0
- package/dist/Composition-IO7ZN32J.js +4 -0
- package/dist/Composition-IO7ZN32J.js.map +1 -0
- package/dist/Controller.d.cts +2 -1
- package/dist/Controller.d.ts +2 -1
- package/dist/Movie-CcR6h2jO.d.cts +82 -0
- package/dist/Movie-D-n8glA6.d.ts +82 -0
- package/dist/chunk-64IHCYYN.cjs +355 -0
- package/dist/chunk-64IHCYYN.cjs.map +1 -0
- package/dist/chunk-7OIWYXGV.cjs +1860 -0
- package/dist/chunk-7OIWYXGV.cjs.map +1 -0
- package/dist/chunk-H55V3U56.js +353 -0
- package/dist/chunk-H55V3U56.js.map +1 -0
- package/dist/chunk-VJCDG6YG.js +1847 -0
- package/dist/chunk-VJCDG6YG.js.map +1 -0
- package/dist/index.cjs +57 -1784
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +44 -3
- package/dist/index.d.ts +44 -3
- package/dist/index.js +51 -1782
- package/dist/index.js.map +1 -1
- package/dist/three.cjs +161 -0
- package/dist/three.cjs.map +1 -0
- package/dist/three.d.cts +79 -0
- package/dist/three.d.ts +79 -0
- package/dist/three.js +157 -0
- package/dist/three.js.map +1 -0
- package/dist/{Movie-Bp66rkaC.d.cts → types-CNBilhpz.d.cts} +32 -76
- package/dist/{Movie-Bp66rkaC.d.ts → types-CNBilhpz.d.ts} +32 -76
- package/package.json +16 -5
|
@@ -0,0 +1,1847 @@
|
|
|
1
|
+
import { Rectangle, Container, Graphics, Assets, Texture, Sprite, Text, Filter, GlProgram, defaultFilterVert, GpuProgram, UniformGroup, Color, GraphicsPath } from 'pixi.js';
|
|
2
|
+
import { gsap } from 'gsap';
|
|
3
|
+
|
|
4
|
+
// src/sequences/Base.ts
|
|
5
|
+
|
|
6
|
+
// src/expr/Parser.ts
|
|
7
|
+
var FUNCS = {
|
|
8
|
+
min: Math.min,
|
|
9
|
+
max: Math.max,
|
|
10
|
+
abs: Math.abs,
|
|
11
|
+
floor: Math.floor,
|
|
12
|
+
ceil: Math.ceil,
|
|
13
|
+
round: Math.round,
|
|
14
|
+
sqrt: Math.sqrt,
|
|
15
|
+
pow: Math.pow,
|
|
16
|
+
sin: Math.sin,
|
|
17
|
+
cos: Math.cos,
|
|
18
|
+
tan: Math.tan
|
|
19
|
+
};
|
|
20
|
+
function isDigit(c) {
|
|
21
|
+
return c >= "0" && c <= "9";
|
|
22
|
+
}
|
|
23
|
+
function isIdStart(c) {
|
|
24
|
+
return c >= "a" && c <= "z" || c >= "A" && c <= "Z" || c === "_";
|
|
25
|
+
}
|
|
26
|
+
function isIdCont(c) {
|
|
27
|
+
return isIdStart(c) || isDigit(c);
|
|
28
|
+
}
|
|
29
|
+
function tokenize(src) {
|
|
30
|
+
const out = [];
|
|
31
|
+
for (let i = 0; i < src.length; ) {
|
|
32
|
+
const c = src[i];
|
|
33
|
+
if (c === " " || c === " " || c === "\n" || c === "\r") {
|
|
34
|
+
i++;
|
|
35
|
+
continue;
|
|
36
|
+
}
|
|
37
|
+
if (isDigit(c) || c === "." && isDigit(src[i + 1] ?? "")) {
|
|
38
|
+
let j = i + 1;
|
|
39
|
+
while (j < src.length && (isDigit(src[j]) || src[j] === ".")) j++;
|
|
40
|
+
const value = parseFloat(src.slice(i, j));
|
|
41
|
+
if (Number.isNaN(value)) throw new Error(`bad number at ${i}`);
|
|
42
|
+
out.push({ type: "num", value });
|
|
43
|
+
i = j;
|
|
44
|
+
continue;
|
|
45
|
+
}
|
|
46
|
+
if (isIdStart(c)) {
|
|
47
|
+
let j = i + 1;
|
|
48
|
+
while (j < src.length && isIdCont(src[j])) j++;
|
|
49
|
+
out.push({ type: "id", name: src.slice(i, j) });
|
|
50
|
+
i = j;
|
|
51
|
+
continue;
|
|
52
|
+
}
|
|
53
|
+
if (c === "+" || c === "-" || c === "*" || c === "/") {
|
|
54
|
+
out.push({ type: "op", op: c });
|
|
55
|
+
i++;
|
|
56
|
+
continue;
|
|
57
|
+
}
|
|
58
|
+
if (c === "(") {
|
|
59
|
+
out.push({ type: "lparen" });
|
|
60
|
+
i++;
|
|
61
|
+
continue;
|
|
62
|
+
}
|
|
63
|
+
if (c === ")") {
|
|
64
|
+
out.push({ type: "rparen" });
|
|
65
|
+
i++;
|
|
66
|
+
continue;
|
|
67
|
+
}
|
|
68
|
+
if (c === ",") {
|
|
69
|
+
out.push({ type: "comma" });
|
|
70
|
+
i++;
|
|
71
|
+
continue;
|
|
72
|
+
}
|
|
73
|
+
throw new Error(`unexpected character "${c}" at ${i}`);
|
|
74
|
+
}
|
|
75
|
+
return out;
|
|
76
|
+
}
|
|
77
|
+
var Cursor = class {
|
|
78
|
+
constructor(tokens) {
|
|
79
|
+
this.tokens = tokens;
|
|
80
|
+
}
|
|
81
|
+
tokens;
|
|
82
|
+
i = 0;
|
|
83
|
+
peek() {
|
|
84
|
+
return this.tokens[this.i];
|
|
85
|
+
}
|
|
86
|
+
next() {
|
|
87
|
+
return this.tokens[this.i++];
|
|
88
|
+
}
|
|
89
|
+
};
|
|
90
|
+
function parseExpr(c) {
|
|
91
|
+
return parseAdd(c);
|
|
92
|
+
}
|
|
93
|
+
function parseAdd(c) {
|
|
94
|
+
let node = parseMul(c);
|
|
95
|
+
for (; ; ) {
|
|
96
|
+
const t = c.peek();
|
|
97
|
+
if (t?.type !== "op" || t.op !== "+" && t.op !== "-") break;
|
|
98
|
+
c.next();
|
|
99
|
+
node = { kind: "bin", op: t.op, left: node, right: parseMul(c) };
|
|
100
|
+
}
|
|
101
|
+
return node;
|
|
102
|
+
}
|
|
103
|
+
function parseMul(c) {
|
|
104
|
+
let node = parseUnary(c);
|
|
105
|
+
for (; ; ) {
|
|
106
|
+
const t = c.peek();
|
|
107
|
+
if (t?.type !== "op" || t.op !== "*" && t.op !== "/") break;
|
|
108
|
+
c.next();
|
|
109
|
+
node = { kind: "bin", op: t.op, left: node, right: parseUnary(c) };
|
|
110
|
+
}
|
|
111
|
+
return node;
|
|
112
|
+
}
|
|
113
|
+
function parseUnary(c) {
|
|
114
|
+
const t = c.peek();
|
|
115
|
+
if (t?.type === "op" && t.op === "-") {
|
|
116
|
+
c.next();
|
|
117
|
+
return { kind: "neg", child: parseUnary(c) };
|
|
118
|
+
}
|
|
119
|
+
if (t?.type === "op" && t.op === "+") {
|
|
120
|
+
c.next();
|
|
121
|
+
return parseUnary(c);
|
|
122
|
+
}
|
|
123
|
+
return parsePrimary(c);
|
|
124
|
+
}
|
|
125
|
+
function parsePrimary(c) {
|
|
126
|
+
const t = c.next();
|
|
127
|
+
if (!t) throw new Error("unexpected end of expression");
|
|
128
|
+
if (t.type === "num") return { kind: "num", value: t.value };
|
|
129
|
+
if (t.type === "lparen") {
|
|
130
|
+
const node = parseExpr(c);
|
|
131
|
+
const close = c.next();
|
|
132
|
+
if (close?.type !== "rparen") throw new Error('expected ")"');
|
|
133
|
+
return node;
|
|
134
|
+
}
|
|
135
|
+
if (t.type === "id") {
|
|
136
|
+
if (c.peek()?.type === "lparen") {
|
|
137
|
+
c.next();
|
|
138
|
+
const args = [];
|
|
139
|
+
if (c.peek()?.type !== "rparen") {
|
|
140
|
+
args.push(parseExpr(c));
|
|
141
|
+
while (c.peek()?.type === "comma") {
|
|
142
|
+
c.next();
|
|
143
|
+
args.push(parseExpr(c));
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
const close = c.next();
|
|
147
|
+
if (close?.type !== "rparen") throw new Error('expected ")"');
|
|
148
|
+
return { kind: "call", name: t.name, args };
|
|
149
|
+
}
|
|
150
|
+
return { kind: "var", name: t.name };
|
|
151
|
+
}
|
|
152
|
+
throw new Error(`unexpected token "${t.type}"`);
|
|
153
|
+
}
|
|
154
|
+
function parse(src) {
|
|
155
|
+
const c = new Cursor(tokenize(src));
|
|
156
|
+
const node = parseExpr(c);
|
|
157
|
+
if (c.peek()) throw new Error("trailing input");
|
|
158
|
+
return node;
|
|
159
|
+
}
|
|
160
|
+
function evalNode(node, scope) {
|
|
161
|
+
switch (node.kind) {
|
|
162
|
+
case "num":
|
|
163
|
+
return node.value;
|
|
164
|
+
case "var": {
|
|
165
|
+
const v = scope[node.name];
|
|
166
|
+
if (typeof v !== "number") throw new Error(`undefined variable: ${node.name}`);
|
|
167
|
+
return v;
|
|
168
|
+
}
|
|
169
|
+
case "neg":
|
|
170
|
+
return -evalNode(node.child, scope);
|
|
171
|
+
case "bin": {
|
|
172
|
+
const l = evalNode(node.left, scope);
|
|
173
|
+
const r = evalNode(node.right, scope);
|
|
174
|
+
switch (node.op) {
|
|
175
|
+
case "+":
|
|
176
|
+
return l + r;
|
|
177
|
+
case "-":
|
|
178
|
+
return l - r;
|
|
179
|
+
case "*":
|
|
180
|
+
return l * r;
|
|
181
|
+
case "/":
|
|
182
|
+
return l / r;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
/* c8 ignore next 5 */
|
|
186
|
+
// eslint-disable-next-line no-fallthrough
|
|
187
|
+
case "call": {
|
|
188
|
+
const fn = FUNCS[node.name];
|
|
189
|
+
if (!fn) throw new Error(`unknown function: ${node.name}`);
|
|
190
|
+
return fn(...node.args.map((a) => evalNode(a, scope)));
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
var ZERO_NODE = { kind: "num", value: 0 };
|
|
195
|
+
var astCache = /* @__PURE__ */ new Map();
|
|
196
|
+
function isExpr(v) {
|
|
197
|
+
return typeof v === "string" && v.length > 0;
|
|
198
|
+
}
|
|
199
|
+
function evaluateExpr(source, scope) {
|
|
200
|
+
let ast = astCache.get(source);
|
|
201
|
+
if (ast === void 0) {
|
|
202
|
+
try {
|
|
203
|
+
ast = parse(source);
|
|
204
|
+
} catch (e) {
|
|
205
|
+
console.warn(`pixi-effects: expression failed: ${source}`, e);
|
|
206
|
+
ast = ZERO_NODE;
|
|
207
|
+
}
|
|
208
|
+
astCache.set(source, ast);
|
|
209
|
+
}
|
|
210
|
+
try {
|
|
211
|
+
return evalNode(ast, scope);
|
|
212
|
+
} catch (e) {
|
|
213
|
+
console.warn(`pixi-effects: expression failed: ${source}`, e);
|
|
214
|
+
return 0;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
// src/expr/normalizeProps.ts
|
|
219
|
+
function normalizeProps(input, scope, options = {}) {
|
|
220
|
+
const skip = new Set(options.skipKeys ?? []);
|
|
221
|
+
return walk(input, scope, skip, null);
|
|
222
|
+
}
|
|
223
|
+
function walk(value, scope, skip, currentKey) {
|
|
224
|
+
if (Array.isArray(value)) {
|
|
225
|
+
return value.map((v) => walk(v, scope, skip, currentKey));
|
|
226
|
+
}
|
|
227
|
+
if (value !== null && typeof value === "object") {
|
|
228
|
+
const out = {};
|
|
229
|
+
for (const k of Object.keys(value)) {
|
|
230
|
+
out[k] = walk(value[k], scope, skip, k);
|
|
231
|
+
}
|
|
232
|
+
return out;
|
|
233
|
+
}
|
|
234
|
+
if (isExpr(value) && !(currentKey !== null && skip.has(currentKey))) {
|
|
235
|
+
if (looksLikeColorString(value)) return value;
|
|
236
|
+
return evaluateExpr(value, scope);
|
|
237
|
+
}
|
|
238
|
+
return value;
|
|
239
|
+
}
|
|
240
|
+
function looksLikeColorString(s) {
|
|
241
|
+
if (s.length === 0) return false;
|
|
242
|
+
if (s.charCodeAt(0) === 35) return true;
|
|
243
|
+
return /^(rgb|hsl|oklab|oklch|lab|lch)\b/i.test(s);
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
// src/core/Timeline.ts
|
|
247
|
+
function resolveAt(at, duration) {
|
|
248
|
+
if (at === void 0 || at === null) return 0;
|
|
249
|
+
return at < 0 ? duration + at : at;
|
|
250
|
+
}
|
|
251
|
+
function normalizeKeyframe(kf, parentDuration) {
|
|
252
|
+
const at = resolveAt(kf.at, parentDuration);
|
|
253
|
+
const duration = kf.duration ?? 0;
|
|
254
|
+
const ease = kf.ease ?? "none";
|
|
255
|
+
const hasSet = !!kf.set;
|
|
256
|
+
const hasFrom = !!kf.from;
|
|
257
|
+
const hasTo = !!kf.to;
|
|
258
|
+
let kind;
|
|
259
|
+
if (hasSet) kind = "set";
|
|
260
|
+
else if (hasFrom && hasTo) kind = "fromTo";
|
|
261
|
+
else if (hasFrom) kind = "from";
|
|
262
|
+
else if (hasTo) kind = "to";
|
|
263
|
+
else kind = "to";
|
|
264
|
+
return { at, duration, ease, kind, set: kf.set, from: kf.from, to: kf.to };
|
|
265
|
+
}
|
|
266
|
+
function partitionProps(props) {
|
|
267
|
+
const ownProps = {};
|
|
268
|
+
const filterProps = {};
|
|
269
|
+
for (const k of Object.keys(props)) {
|
|
270
|
+
const m = k.match(/^filters\.([^.]+)\.(.+)$/);
|
|
271
|
+
if (m) {
|
|
272
|
+
const [, name, sub] = m;
|
|
273
|
+
(filterProps[name] ??= {})[sub] = props[k];
|
|
274
|
+
} else {
|
|
275
|
+
ownProps[k] = props[k];
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
return { ownProps, filterProps };
|
|
279
|
+
}
|
|
280
|
+
function splitRouted(props, routers) {
|
|
281
|
+
if (!routers) return { rest: props, routed: [] };
|
|
282
|
+
const rest = {};
|
|
283
|
+
const routed = [];
|
|
284
|
+
for (const k of Object.keys(props)) {
|
|
285
|
+
const dot = k.indexOf(".");
|
|
286
|
+
const router = dot > 0 ? routers[k.slice(0, dot)] : void 0;
|
|
287
|
+
if (router) {
|
|
288
|
+
const hit = router(k.slice(dot + 1));
|
|
289
|
+
if (hit) routed.push({ target: hit.target, prop: hit.prop, key: k, value: props[k] });
|
|
290
|
+
} else {
|
|
291
|
+
rest[k] = props[k];
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
return { rest, routed };
|
|
295
|
+
}
|
|
296
|
+
function findFilter(target, name) {
|
|
297
|
+
if (!target?.filters) return null;
|
|
298
|
+
for (const f of target.filters) {
|
|
299
|
+
if (f?._name === name) return f;
|
|
300
|
+
}
|
|
301
|
+
return null;
|
|
302
|
+
}
|
|
303
|
+
var PIXI_SHORTHANDS = /* @__PURE__ */ new Set([
|
|
304
|
+
"scale",
|
|
305
|
+
"scaleX",
|
|
306
|
+
"scaleY",
|
|
307
|
+
"anchor",
|
|
308
|
+
"anchorX",
|
|
309
|
+
"anchorY",
|
|
310
|
+
"pivot",
|
|
311
|
+
"pivotX",
|
|
312
|
+
"pivotY",
|
|
313
|
+
"skew",
|
|
314
|
+
"skewX",
|
|
315
|
+
"skewY",
|
|
316
|
+
"rotation",
|
|
317
|
+
"position",
|
|
318
|
+
"positionX",
|
|
319
|
+
"positionY",
|
|
320
|
+
"tilePosition",
|
|
321
|
+
"tilePositionX",
|
|
322
|
+
"tilePositionY",
|
|
323
|
+
"tileScale",
|
|
324
|
+
"tileScaleX",
|
|
325
|
+
"tileScaleY",
|
|
326
|
+
"tint",
|
|
327
|
+
"colorize",
|
|
328
|
+
"colorizeAmount",
|
|
329
|
+
"colorMatrixFilter",
|
|
330
|
+
"blur",
|
|
331
|
+
"blurX",
|
|
332
|
+
"blurY",
|
|
333
|
+
"blurPadding",
|
|
334
|
+
"autoAlpha",
|
|
335
|
+
"lineColor",
|
|
336
|
+
"lineAlpha",
|
|
337
|
+
"fillColor",
|
|
338
|
+
"fillAlpha"
|
|
339
|
+
]);
|
|
340
|
+
function pixiwrap(props) {
|
|
341
|
+
const out = {};
|
|
342
|
+
const pixi = {};
|
|
343
|
+
let usedPixi = false;
|
|
344
|
+
for (const [k, v] of Object.entries(props)) {
|
|
345
|
+
if (PIXI_SHORTHANDS.has(k)) {
|
|
346
|
+
pixi[k] = v;
|
|
347
|
+
usedPixi = true;
|
|
348
|
+
} else {
|
|
349
|
+
out[k] = v;
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
if (usedPixi) out.pixi = pixi;
|
|
353
|
+
return out;
|
|
354
|
+
}
|
|
355
|
+
function applyKeyframes(timeline, target, keyframes, parentDuration, scope, skipKeys = [], offset = 0, routers) {
|
|
356
|
+
for (const raw of keyframes ?? []) {
|
|
357
|
+
const kf = normalizeKeyframe(raw, parentDuration);
|
|
358
|
+
const at = offset + kf.at;
|
|
359
|
+
if (kf.kind === "set") {
|
|
360
|
+
const resolved = normalizeProps(kf.set, scope, { skipKeys });
|
|
361
|
+
const { rest, routed } = splitRouted(resolved, routers);
|
|
362
|
+
for (const r of routed) timeline.set(r.target, { [r.prop]: r.value }, at);
|
|
363
|
+
const { ownProps, filterProps } = partitionProps(rest);
|
|
364
|
+
if (Object.keys(ownProps).length > 0) timeline.set(target, pixiwrap(ownProps), at);
|
|
365
|
+
for (const [name, props] of Object.entries(filterProps)) {
|
|
366
|
+
const f = findFilter(target, name);
|
|
367
|
+
if (!f) continue;
|
|
368
|
+
timeline.set(f, props, at);
|
|
369
|
+
}
|
|
370
|
+
} else if (kf.kind === "to") {
|
|
371
|
+
const resolved = normalizeProps(kf.to, scope, { skipKeys });
|
|
372
|
+
const { rest, routed } = splitRouted(resolved, routers);
|
|
373
|
+
for (const r of routed)
|
|
374
|
+
timeline.to(r.target, { [r.prop]: r.value, duration: kf.duration, ease: kf.ease }, at);
|
|
375
|
+
const { ownProps, filterProps } = partitionProps(rest);
|
|
376
|
+
if (Object.keys(ownProps).length > 0)
|
|
377
|
+
timeline.to(target, { ...pixiwrap(ownProps), duration: kf.duration, ease: kf.ease }, at);
|
|
378
|
+
for (const [name, props] of Object.entries(filterProps)) {
|
|
379
|
+
const f = findFilter(target, name);
|
|
380
|
+
if (!f) continue;
|
|
381
|
+
timeline.to(f, { ...props, duration: kf.duration, ease: kf.ease }, at);
|
|
382
|
+
}
|
|
383
|
+
} else if (kf.kind === "from") {
|
|
384
|
+
const resolved = normalizeProps(kf.from, scope, { skipKeys });
|
|
385
|
+
const { rest, routed } = splitRouted(resolved, routers);
|
|
386
|
+
for (const r of routed)
|
|
387
|
+
timeline.from(r.target, { [r.prop]: r.value, duration: kf.duration, ease: kf.ease }, at);
|
|
388
|
+
const { ownProps, filterProps } = partitionProps(rest);
|
|
389
|
+
if (Object.keys(ownProps).length > 0)
|
|
390
|
+
timeline.from(target, { ...pixiwrap(ownProps), duration: kf.duration, ease: kf.ease }, at);
|
|
391
|
+
for (const [name, props] of Object.entries(filterProps)) {
|
|
392
|
+
const f = findFilter(target, name);
|
|
393
|
+
if (!f) continue;
|
|
394
|
+
timeline.from(f, { ...props, duration: kf.duration, ease: kf.ease }, at);
|
|
395
|
+
}
|
|
396
|
+
} else {
|
|
397
|
+
const fromResolved = normalizeProps(kf.from, scope, { skipKeys });
|
|
398
|
+
const toResolved = normalizeProps(kf.to, scope, { skipKeys });
|
|
399
|
+
const fromRouted = splitRouted(fromResolved, routers);
|
|
400
|
+
const toRouted = splitRouted(toResolved, routers);
|
|
401
|
+
const fromByKey = new Map(fromRouted.routed.map((r) => [r.key, r]));
|
|
402
|
+
for (const r of toRouted.routed) {
|
|
403
|
+
const f = fromByKey.get(r.key);
|
|
404
|
+
fromByKey.delete(r.key);
|
|
405
|
+
if (f) {
|
|
406
|
+
timeline.fromTo(
|
|
407
|
+
r.target,
|
|
408
|
+
{ [r.prop]: f.value },
|
|
409
|
+
{ [r.prop]: r.value, duration: kf.duration, ease: kf.ease },
|
|
410
|
+
at
|
|
411
|
+
);
|
|
412
|
+
} else {
|
|
413
|
+
timeline.to(r.target, { [r.prop]: r.value, duration: kf.duration, ease: kf.ease }, at);
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
for (const f of fromByKey.values())
|
|
417
|
+
timeline.from(f.target, { [f.prop]: f.value, duration: kf.duration, ease: kf.ease }, at);
|
|
418
|
+
const fromSplit = partitionProps(fromRouted.rest);
|
|
419
|
+
const toSplit = partitionProps(toRouted.rest);
|
|
420
|
+
const ownKeys = /* @__PURE__ */ new Set([...Object.keys(fromSplit.ownProps), ...Object.keys(toSplit.ownProps)]);
|
|
421
|
+
if (ownKeys.size > 0) {
|
|
422
|
+
timeline.fromTo(
|
|
423
|
+
target,
|
|
424
|
+
{ ...pixiwrap(fromSplit.ownProps) },
|
|
425
|
+
{ ...pixiwrap(toSplit.ownProps), duration: kf.duration, ease: kf.ease },
|
|
426
|
+
at
|
|
427
|
+
);
|
|
428
|
+
}
|
|
429
|
+
const filterNames = /* @__PURE__ */ new Set([
|
|
430
|
+
...Object.keys(fromSplit.filterProps),
|
|
431
|
+
...Object.keys(toSplit.filterProps)
|
|
432
|
+
]);
|
|
433
|
+
for (const name of filterNames) {
|
|
434
|
+
const f = findFilter(target, name);
|
|
435
|
+
if (!f) continue;
|
|
436
|
+
timeline.fromTo(
|
|
437
|
+
f,
|
|
438
|
+
{ ...fromSplit.filterProps[name] ?? {} },
|
|
439
|
+
{ ...toSplit.filterProps[name] ?? {}, duration: kf.duration, ease: kf.ease },
|
|
440
|
+
at
|
|
441
|
+
);
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
function applyInitial(target, initial, scope, skipKeys = [], routers) {
|
|
447
|
+
if (!initial) return;
|
|
448
|
+
const resolved = normalizeProps(initial, scope, { skipKeys });
|
|
449
|
+
const { rest, routed } = splitRouted(resolved, routers);
|
|
450
|
+
for (const r of routed) gsap.set(r.target, { [r.prop]: r.value });
|
|
451
|
+
const { ownProps, filterProps } = partitionProps(rest);
|
|
452
|
+
if (Object.keys(ownProps).length > 0) gsap.set(target, pixiwrap(ownProps));
|
|
453
|
+
for (const [name, props] of Object.entries(filterProps)) {
|
|
454
|
+
const f = findFilter(target, name);
|
|
455
|
+
if (!f) continue;
|
|
456
|
+
gsap.set(f, props);
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
// src/expr/Scope.ts
|
|
461
|
+
function buildScope(sequence, parent, root) {
|
|
462
|
+
const w = sequence.intrinsicWidth || 0;
|
|
463
|
+
const h = sequence.intrinsicHeight || 0;
|
|
464
|
+
const W = parent?.width ?? root.width;
|
|
465
|
+
const H = parent?.height ?? root.height;
|
|
466
|
+
const GW = root.width;
|
|
467
|
+
const GH = root.height;
|
|
468
|
+
const contain = w === 0 || h === 0 ? 1 : Math.min(W / w, H / h);
|
|
469
|
+
const cover = w === 0 || h === 0 ? 1 : Math.max(W / w, H / h);
|
|
470
|
+
return {
|
|
471
|
+
w,
|
|
472
|
+
h,
|
|
473
|
+
W,
|
|
474
|
+
H,
|
|
475
|
+
GW,
|
|
476
|
+
GH,
|
|
477
|
+
contain,
|
|
478
|
+
cover,
|
|
479
|
+
t: sequence.at ?? 0,
|
|
480
|
+
d: sequence.duration ?? parent?.duration ?? root.duration,
|
|
481
|
+
T: parent?.duration ?? root.duration
|
|
482
|
+
};
|
|
483
|
+
}
|
|
484
|
+
var GL_FRAGMENT = `
|
|
485
|
+
in vec2 vTextureCoord;
|
|
486
|
+
uniform sampler2D uTexture;
|
|
487
|
+
uniform vec3 uKeyColor;
|
|
488
|
+
uniform float uThreshold;
|
|
489
|
+
uniform float uSmoothing;
|
|
490
|
+
uniform float uSpill;
|
|
491
|
+
|
|
492
|
+
out vec4 finalColor;
|
|
493
|
+
|
|
494
|
+
void main(void) {
|
|
495
|
+
vec4 raw = texture(uTexture, vTextureCoord);
|
|
496
|
+
|
|
497
|
+
// Pixi passes pre-multiplied alpha into filters. Un-premultiply so the
|
|
498
|
+
// chromakey distance is computed against the source color, not a darkened
|
|
499
|
+
// version that drifts away from the key as sprite.alpha drops.
|
|
500
|
+
vec3 rgb = raw.a > 0.0 ? raw.rgb / raw.a : vec3(0.0);
|
|
501
|
+
|
|
502
|
+
vec3 ycbcr = vec3(
|
|
503
|
+
0.299 * rgb.r + 0.587 * rgb.g + 0.114 * rgb.b,
|
|
504
|
+
-0.169 * rgb.r - 0.331 * rgb.g + 0.5 * rgb.b + 0.5,
|
|
505
|
+
0.5 * rgb.r - 0.419 * rgb.g - 0.081 * rgb.b + 0.5
|
|
506
|
+
);
|
|
507
|
+
vec3 keyYcbcr = vec3(
|
|
508
|
+
0.299 * uKeyColor.r + 0.587 * uKeyColor.g + 0.114 * uKeyColor.b,
|
|
509
|
+
-0.169 * uKeyColor.r - 0.331 * uKeyColor.g + 0.5 * uKeyColor.b + 0.5,
|
|
510
|
+
0.5 * uKeyColor.r - 0.419 * uKeyColor.g - 0.081 * uKeyColor.b + 0.5
|
|
511
|
+
);
|
|
512
|
+
|
|
513
|
+
float dist = length(ycbcr - keyYcbcr);
|
|
514
|
+
float alpha = smoothstep(uThreshold - uSmoothing, uThreshold + uSmoothing, dist);
|
|
515
|
+
|
|
516
|
+
vec3 despilled = rgb;
|
|
517
|
+
if (uSpill > 0.0) {
|
|
518
|
+
float spillAmount = max(0.0, rgb.g - max(rgb.r, rgb.b));
|
|
519
|
+
despilled.g -= spillAmount * uSpill;
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
float outA = raw.a * alpha;
|
|
523
|
+
finalColor = vec4(despilled * outA, outA);
|
|
524
|
+
}
|
|
525
|
+
`;
|
|
526
|
+
var WGSL_SOURCE = `
|
|
527
|
+
struct GlobalFilterUniforms {
|
|
528
|
+
uInputSize: vec4<f32>,
|
|
529
|
+
uInputPixel: vec4<f32>,
|
|
530
|
+
uInputClamp: vec4<f32>,
|
|
531
|
+
uOutputFrame: vec4<f32>,
|
|
532
|
+
uGlobalFrame: vec4<f32>,
|
|
533
|
+
uOutputTexture: vec4<f32>,
|
|
534
|
+
};
|
|
535
|
+
|
|
536
|
+
struct ChromaUniforms {
|
|
537
|
+
uKeyColor: vec3<f32>,
|
|
538
|
+
uThreshold: f32,
|
|
539
|
+
uSmoothing: f32,
|
|
540
|
+
uSpill: f32,
|
|
541
|
+
};
|
|
542
|
+
|
|
543
|
+
@group(0) @binding(0) var<uniform> gfu: GlobalFilterUniforms;
|
|
544
|
+
@group(0) @binding(1) var uTexture: texture_2d<f32>;
|
|
545
|
+
@group(0) @binding(2) var uSampler : sampler;
|
|
546
|
+
@group(1) @binding(0) var<uniform> chromaUniforms : ChromaUniforms;
|
|
547
|
+
|
|
548
|
+
struct VSOutput {
|
|
549
|
+
@builtin(position) position: vec4<f32>,
|
|
550
|
+
@location(0) uv : vec2<f32>,
|
|
551
|
+
};
|
|
552
|
+
|
|
553
|
+
fn filterVertexPosition(aPosition : vec2<f32>) -> vec4<f32> {
|
|
554
|
+
var position = aPosition * gfu.uOutputFrame.zw + gfu.uOutputFrame.xy;
|
|
555
|
+
position.x = position.x * (2.0 / gfu.uOutputTexture.x) - 1.0;
|
|
556
|
+
position.y = position.y * (2.0 * gfu.uOutputTexture.z / gfu.uOutputTexture.y) - gfu.uOutputTexture.z;
|
|
557
|
+
return vec4<f32>(position, 0.0, 1.0);
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
fn filterTextureCoord(aPosition : vec2<f32>) -> vec2<f32> {
|
|
561
|
+
return aPosition * (gfu.uOutputFrame.zw * gfu.uInputSize.zw);
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
// NOTE on whitespace: PIXI v8's WGSL attribute extractor is regex-based and
|
|
565
|
+
// expects the noise/blur reference format \u2014 multi-line param list + space
|
|
566
|
+
// before the colon. A compact one-liner with no space before the colon parses
|
|
567
|
+
// as having NO vertex attributes, which then causes a "Vertex attribute slot 0
|
|
568
|
+
// not present in the VertexState" pipeline validation error.
|
|
569
|
+
@vertex
|
|
570
|
+
fn mainVertex(
|
|
571
|
+
@location(0) aPosition : vec2<f32>,
|
|
572
|
+
) -> VSOutput {
|
|
573
|
+
return VSOutput(
|
|
574
|
+
filterVertexPosition(aPosition),
|
|
575
|
+
filterTextureCoord(aPosition),
|
|
576
|
+
);
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
@fragment
|
|
580
|
+
fn mainFragment(@location(0) uv : vec2<f32>) -> @location(0) vec4<f32> {
|
|
581
|
+
let raw = textureSample(uTexture, uSampler, uv);
|
|
582
|
+
|
|
583
|
+
// Un-premultiply (see comment in the GLSL version above).
|
|
584
|
+
var rgb: vec3<f32>;
|
|
585
|
+
if (raw.a > 0.0) {
|
|
586
|
+
rgb = raw.rgb / raw.a;
|
|
587
|
+
} else {
|
|
588
|
+
rgb = vec3<f32>(0.0);
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
let key = chromaUniforms.uKeyColor;
|
|
592
|
+
|
|
593
|
+
let ycbcr = vec3<f32>(
|
|
594
|
+
0.299 * rgb.r + 0.587 * rgb.g + 0.114 * rgb.b,
|
|
595
|
+
-0.169 * rgb.r - 0.331 * rgb.g + 0.5 * rgb.b + 0.5,
|
|
596
|
+
0.5 * rgb.r - 0.419 * rgb.g - 0.081 * rgb.b + 0.5,
|
|
597
|
+
);
|
|
598
|
+
let keyYcbcr = vec3<f32>(
|
|
599
|
+
0.299 * key.r + 0.587 * key.g + 0.114 * key.b,
|
|
600
|
+
-0.169 * key.r - 0.331 * key.g + 0.5 * key.b + 0.5,
|
|
601
|
+
0.5 * key.r - 0.419 * key.g - 0.081 * key.b + 0.5,
|
|
602
|
+
);
|
|
603
|
+
|
|
604
|
+
let dist = length(ycbcr - keyYcbcr);
|
|
605
|
+
let alpha = smoothstep(
|
|
606
|
+
chromaUniforms.uThreshold - chromaUniforms.uSmoothing,
|
|
607
|
+
chromaUniforms.uThreshold + chromaUniforms.uSmoothing,
|
|
608
|
+
dist,
|
|
609
|
+
);
|
|
610
|
+
|
|
611
|
+
var despilled = rgb;
|
|
612
|
+
if (chromaUniforms.uSpill > 0.0) {
|
|
613
|
+
let spillAmount = max(0.0, rgb.g - max(rgb.r, rgb.b));
|
|
614
|
+
despilled.g = despilled.g - spillAmount * chromaUniforms.uSpill;
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
let outA = raw.a * alpha;
|
|
618
|
+
return vec4<f32>(despilled * outA, outA);
|
|
619
|
+
}
|
|
620
|
+
`;
|
|
621
|
+
function toRgb01(input) {
|
|
622
|
+
if (Array.isArray(input)) return [input[0], input[1], input[2]];
|
|
623
|
+
const c = new Color(input);
|
|
624
|
+
return [c.red, c.green, c.blue];
|
|
625
|
+
}
|
|
626
|
+
var ChromaKeyFilter = class extends Filter {
|
|
627
|
+
constructor(options = {}) {
|
|
628
|
+
const {
|
|
629
|
+
keyColor = "#00ff00",
|
|
630
|
+
threshold = 0.4,
|
|
631
|
+
smoothing = 0.1,
|
|
632
|
+
spill = 0.2
|
|
633
|
+
} = options;
|
|
634
|
+
const glProgram = GlProgram.from({
|
|
635
|
+
vertex: defaultFilterVert,
|
|
636
|
+
fragment: GL_FRAGMENT,
|
|
637
|
+
name: "chroma-key-filter"
|
|
638
|
+
});
|
|
639
|
+
const gpuProgram = GpuProgram.from({
|
|
640
|
+
vertex: { source: WGSL_SOURCE, entryPoint: "mainVertex" },
|
|
641
|
+
fragment: { source: WGSL_SOURCE, entryPoint: "mainFragment" }
|
|
642
|
+
});
|
|
643
|
+
super({
|
|
644
|
+
glProgram,
|
|
645
|
+
gpuProgram,
|
|
646
|
+
resources: {
|
|
647
|
+
chromaUniforms: new UniformGroup({
|
|
648
|
+
uKeyColor: { value: toRgb01(keyColor), type: "vec3<f32>" },
|
|
649
|
+
uThreshold: { value: threshold, type: "f32" },
|
|
650
|
+
uSmoothing: { value: smoothing, type: "f32" },
|
|
651
|
+
uSpill: { value: spill, type: "f32" }
|
|
652
|
+
})
|
|
653
|
+
}
|
|
654
|
+
});
|
|
655
|
+
}
|
|
656
|
+
get threshold() {
|
|
657
|
+
return this.resources.chromaUniforms.uniforms.uThreshold;
|
|
658
|
+
}
|
|
659
|
+
set threshold(v) {
|
|
660
|
+
this.resources.chromaUniforms.uniforms.uThreshold = v;
|
|
661
|
+
}
|
|
662
|
+
get smoothing() {
|
|
663
|
+
return this.resources.chromaUniforms.uniforms.uSmoothing;
|
|
664
|
+
}
|
|
665
|
+
set smoothing(v) {
|
|
666
|
+
this.resources.chromaUniforms.uniforms.uSmoothing = v;
|
|
667
|
+
}
|
|
668
|
+
get spill() {
|
|
669
|
+
return this.resources.chromaUniforms.uniforms.uSpill;
|
|
670
|
+
}
|
|
671
|
+
set spill(v) {
|
|
672
|
+
this.resources.chromaUniforms.uniforms.uSpill = v;
|
|
673
|
+
}
|
|
674
|
+
setKeyColor(input) {
|
|
675
|
+
const rgb = toRgb01(input);
|
|
676
|
+
const u = this.resources.chromaUniforms.uniforms.uKeyColor;
|
|
677
|
+
u[0] = rgb[0];
|
|
678
|
+
u[1] = rgb[1];
|
|
679
|
+
u[2] = rgb[2];
|
|
680
|
+
}
|
|
681
|
+
};
|
|
682
|
+
|
|
683
|
+
// src/filters/index.ts
|
|
684
|
+
var registry = {
|
|
685
|
+
chromaKey: ChromaKeyFilter
|
|
686
|
+
};
|
|
687
|
+
function looksLikePixiFilter(x) {
|
|
688
|
+
return !!x && typeof x === "object" && typeof x.apply === "function";
|
|
689
|
+
}
|
|
690
|
+
function createFilter(spec) {
|
|
691
|
+
if (spec.type === "custom") {
|
|
692
|
+
const inst2 = spec.filter;
|
|
693
|
+
if (!looksLikePixiFilter(inst2)) {
|
|
694
|
+
throw new Error('pixi-effects: { type: "custom" } requires a `filter` that is a PIXI Filter instance (duck-typed: must have an `apply` method).');
|
|
695
|
+
}
|
|
696
|
+
const named = inst2;
|
|
697
|
+
named._name = spec.name;
|
|
698
|
+
return named;
|
|
699
|
+
}
|
|
700
|
+
const Cls = registry[spec.type];
|
|
701
|
+
if (!Cls) throw new Error(`pixi-effects: unknown filter type "${spec.type}"`);
|
|
702
|
+
const { type: _t, name, ...params } = spec;
|
|
703
|
+
const inst = new Cls(params);
|
|
704
|
+
inst._name = name;
|
|
705
|
+
return inst;
|
|
706
|
+
}
|
|
707
|
+
|
|
708
|
+
// src/sequences/Base.ts
|
|
709
|
+
var Sequence = class {
|
|
710
|
+
spec;
|
|
711
|
+
parent;
|
|
712
|
+
root;
|
|
713
|
+
target = null;
|
|
714
|
+
intrinsicWidth = 0;
|
|
715
|
+
intrinsicHeight = 0;
|
|
716
|
+
at;
|
|
717
|
+
duration;
|
|
718
|
+
filters = [];
|
|
719
|
+
/**
|
|
720
|
+
* Mask sequence built from `spec.mask`, if any. Built and added to the
|
|
721
|
+
* scene graph by the parent CompositionSequence; rendered as a mask
|
|
722
|
+
* (not a normal child) by PIXI when `target.mask = maskSequence.target`.
|
|
723
|
+
*/
|
|
724
|
+
maskSequence = null;
|
|
725
|
+
/**
|
|
726
|
+
* Absolute start time on the global timeline (offset + at), recorded by
|
|
727
|
+
* bindTimeline. Movie._awaitVideoFrames uses it to determine the correct
|
|
728
|
+
* time to pass to awaitFrameAt. Subclasses that override bindTimeline
|
|
729
|
+
* without calling super must set this field themselves. Null until bound.
|
|
730
|
+
*/
|
|
731
|
+
absoluteStart = null;
|
|
732
|
+
constructor(spec, parent, root) {
|
|
733
|
+
this.spec = spec;
|
|
734
|
+
this.parent = parent;
|
|
735
|
+
this.root = root;
|
|
736
|
+
this.at = spec.at ?? 0;
|
|
737
|
+
this.duration = spec.duration;
|
|
738
|
+
}
|
|
739
|
+
buildFilters() {
|
|
740
|
+
const specs = this.spec.filters ?? [];
|
|
741
|
+
this.filters = specs.map(createFilter);
|
|
742
|
+
if (this.target && "filters" in this.target) {
|
|
743
|
+
this.target.filters = this.filters;
|
|
744
|
+
}
|
|
745
|
+
const fa = this.spec.filterArea;
|
|
746
|
+
if (fa && this.target) {
|
|
747
|
+
this.target.filterArea = new Rectangle(fa.x, fa.y, fa.width, fa.height);
|
|
748
|
+
}
|
|
749
|
+
}
|
|
750
|
+
scope() {
|
|
751
|
+
return buildScope(this, this.parent, this.root);
|
|
752
|
+
}
|
|
753
|
+
/**
|
|
754
|
+
* Prefix routers contributed to the keyframe pipeline (e.g. `three.` in
|
|
755
|
+
* pixi-effects/three). Base sequences route nothing extra.
|
|
756
|
+
*/
|
|
757
|
+
pathRouters() {
|
|
758
|
+
return {};
|
|
759
|
+
}
|
|
760
|
+
bindTimeline(timeline, offset = 0) {
|
|
761
|
+
if (!this.target) return;
|
|
762
|
+
const scope = this.scope();
|
|
763
|
+
const routers = this.pathRouters();
|
|
764
|
+
applyInitial(this.target, this.spec.initial, scope, [], routers);
|
|
765
|
+
applyKeyframes(timeline, this.target, this.spec.keyframes, this.duration, scope, [], offset, routers);
|
|
766
|
+
const startTime = offset + this.at;
|
|
767
|
+
const endTime = startTime + this.duration;
|
|
768
|
+
this.absoluteStart = startTime;
|
|
769
|
+
this.target.renderable = startTime <= 0;
|
|
770
|
+
timeline.set(this.target, { renderable: true }, startTime);
|
|
771
|
+
timeline.set(this.target, { renderable: false }, endTime);
|
|
772
|
+
}
|
|
773
|
+
collectAudio(_out, _baseTime) {
|
|
774
|
+
}
|
|
775
|
+
/**
|
|
776
|
+
* 2.5D hook. Runs once per rendered frame, after the timeline seek and the
|
|
777
|
+
* per-frame media sync, before the render. Compositions override it to
|
|
778
|
+
* project their `threeD` layers; everything else has nothing to do.
|
|
779
|
+
*/
|
|
780
|
+
updateSpace(_t, _host) {
|
|
781
|
+
}
|
|
782
|
+
destroy() {
|
|
783
|
+
this.maskSequence?.destroy();
|
|
784
|
+
this.maskSequence = null;
|
|
785
|
+
this.target?.destroy?.();
|
|
786
|
+
this.target = null;
|
|
787
|
+
}
|
|
788
|
+
};
|
|
789
|
+
|
|
790
|
+
// src/space/math.ts
|
|
791
|
+
var DEG = Math.PI / 180;
|
|
792
|
+
var DEFAULT_FOV = 40;
|
|
793
|
+
var MIN_FOV = 1;
|
|
794
|
+
var MAX_FOV = 179;
|
|
795
|
+
var NEAR = 1;
|
|
796
|
+
function clampFov(fov) {
|
|
797
|
+
if (!Number.isFinite(fov)) return DEFAULT_FOV;
|
|
798
|
+
return Math.min(MAX_FOV, Math.max(MIN_FOV, fov));
|
|
799
|
+
}
|
|
800
|
+
function homeDistance(compHeight, fovDeg) {
|
|
801
|
+
return compHeight / 2 / Math.tan(clampFov(fovDeg) * DEG / 2);
|
|
802
|
+
}
|
|
803
|
+
function homeCamera(compW, compH, fov = DEFAULT_FOV) {
|
|
804
|
+
return {
|
|
805
|
+
x: compW / 2,
|
|
806
|
+
y: compH / 2,
|
|
807
|
+
z: homeDistance(compH, fov),
|
|
808
|
+
lookAtX: compW / 2,
|
|
809
|
+
lookAtY: compH / 2,
|
|
810
|
+
lookAtZ: 0,
|
|
811
|
+
fov
|
|
812
|
+
};
|
|
813
|
+
}
|
|
814
|
+
function cameraBasis(cam, compW, compH) {
|
|
815
|
+
let fx = cam.lookAtX - cam.x;
|
|
816
|
+
let fy = cam.lookAtY - cam.y;
|
|
817
|
+
let fz = cam.lookAtZ - cam.z;
|
|
818
|
+
let len = Math.hypot(fx, fy, fz);
|
|
819
|
+
if (len < 1e-9) {
|
|
820
|
+
fx = 0;
|
|
821
|
+
fy = 0;
|
|
822
|
+
fz = -1;
|
|
823
|
+
len = 1;
|
|
824
|
+
}
|
|
825
|
+
fx /= len;
|
|
826
|
+
fy /= len;
|
|
827
|
+
fz /= len;
|
|
828
|
+
let rx = -fz;
|
|
829
|
+
const ry = 0;
|
|
830
|
+
let rz = fx;
|
|
831
|
+
const rlen = Math.hypot(rx, rz);
|
|
832
|
+
if (rlen < 1e-9) {
|
|
833
|
+
rx = 1;
|
|
834
|
+
rz = 0;
|
|
835
|
+
} else {
|
|
836
|
+
rx /= rlen;
|
|
837
|
+
rz /= rlen;
|
|
838
|
+
}
|
|
839
|
+
const dx = ry * fz - rz * fy;
|
|
840
|
+
const dy = rz * fx - rx * fz;
|
|
841
|
+
const dz = rx * fy - ry * fx;
|
|
842
|
+
return {
|
|
843
|
+
cx: cam.x,
|
|
844
|
+
cy: cam.y,
|
|
845
|
+
cz: cam.z,
|
|
846
|
+
rx,
|
|
847
|
+
ry,
|
|
848
|
+
rz,
|
|
849
|
+
dx,
|
|
850
|
+
dy,
|
|
851
|
+
dz,
|
|
852
|
+
fx,
|
|
853
|
+
fy,
|
|
854
|
+
fz,
|
|
855
|
+
focal: compH / 2 / Math.tan(clampFov(cam.fov) * DEG / 2),
|
|
856
|
+
halfW: compW / 2,
|
|
857
|
+
halfH: compH / 2
|
|
858
|
+
};
|
|
859
|
+
}
|
|
860
|
+
function layerToWorld(t, px, py) {
|
|
861
|
+
let x = (px - t.pivotX) * t.scaleX;
|
|
862
|
+
let y = (py - t.pivotY) * t.scaleY;
|
|
863
|
+
let z = 0;
|
|
864
|
+
let c = Math.cos(t.rotationX);
|
|
865
|
+
let s = Math.sin(t.rotationX);
|
|
866
|
+
[y, z] = [y * c - z * s, y * s + z * c];
|
|
867
|
+
c = Math.cos(t.rotationY);
|
|
868
|
+
s = Math.sin(t.rotationY);
|
|
869
|
+
[x, z] = [x * c + z * s, -x * s + z * c];
|
|
870
|
+
c = Math.cos(t.rotationZ);
|
|
871
|
+
s = Math.sin(t.rotationZ);
|
|
872
|
+
[x, y] = [x * c - y * s, x * s + y * c];
|
|
873
|
+
return { x: x + t.x, y: y + t.y, z: z + t.z };
|
|
874
|
+
}
|
|
875
|
+
function projectPoint(b, p) {
|
|
876
|
+
const vx = p.x - b.cx;
|
|
877
|
+
const vy = p.y - b.cy;
|
|
878
|
+
const vz = p.z - b.cz;
|
|
879
|
+
const xc = vx * b.rx + vy * b.ry + vz * b.rz;
|
|
880
|
+
const yc = vx * b.dx + vy * b.dy + vz * b.dz;
|
|
881
|
+
const zc = vx * b.fx + vy * b.fy + vz * b.fz;
|
|
882
|
+
return { x: b.halfW + b.focal * xc / zc, y: b.halfH + b.focal * yc / zc, depth: zc };
|
|
883
|
+
}
|
|
884
|
+
function projectLayer(t, frame, b) {
|
|
885
|
+
const pts = [
|
|
886
|
+
[frame.x, frame.y],
|
|
887
|
+
[frame.x + frame.width, frame.y],
|
|
888
|
+
[frame.x + frame.width, frame.y + frame.height],
|
|
889
|
+
[frame.x, frame.y + frame.height]
|
|
890
|
+
];
|
|
891
|
+
let visible = true;
|
|
892
|
+
const corners = [];
|
|
893
|
+
for (const [px, py] of pts) {
|
|
894
|
+
const p = projectPoint(b, layerToWorld(t, px, py));
|
|
895
|
+
if (!(p.depth > NEAR)) visible = false;
|
|
896
|
+
corners.push(p.x, p.y);
|
|
897
|
+
}
|
|
898
|
+
const origin = projectPoint(b, { x: t.x, y: t.y, z: t.z });
|
|
899
|
+
return { corners, depth: origin.depth, visible };
|
|
900
|
+
}
|
|
901
|
+
|
|
902
|
+
// src/space/specKeys.ts
|
|
903
|
+
function collectPropKeys(spec) {
|
|
904
|
+
const out = /* @__PURE__ */ new Set();
|
|
905
|
+
for (const k of Object.keys(spec.initial ?? {})) out.add(k);
|
|
906
|
+
for (const kf of spec.keyframes ?? []) {
|
|
907
|
+
for (const bag of [kf.set, kf.to, kf.from]) {
|
|
908
|
+
for (const k of Object.keys(bag ?? {})) out.add(k);
|
|
909
|
+
}
|
|
910
|
+
}
|
|
911
|
+
return out;
|
|
912
|
+
}
|
|
913
|
+
|
|
914
|
+
// src/space/CameraSequence.ts
|
|
915
|
+
var CameraSequence = class extends Sequence {
|
|
916
|
+
/** True when `z` is never specified anywhere: z then follows `fov`. */
|
|
917
|
+
autoZ = true;
|
|
918
|
+
compH = 0;
|
|
919
|
+
warnedFov = false;
|
|
920
|
+
warnedLookAt = false;
|
|
921
|
+
async build() {
|
|
922
|
+
const compW = this.parent?.width ?? this.root.width;
|
|
923
|
+
this.compH = this.parent?.height ?? this.root.height;
|
|
924
|
+
if (this.duration === void 0) {
|
|
925
|
+
this.duration = this.parent?.duration ?? this.root.duration;
|
|
926
|
+
}
|
|
927
|
+
const home = homeCamera(compW, this.compH);
|
|
928
|
+
const carrier = new Container();
|
|
929
|
+
carrier.x = home.x;
|
|
930
|
+
carrier.y = home.y;
|
|
931
|
+
carrier.z = home.z;
|
|
932
|
+
carrier.lookAtX = home.lookAtX;
|
|
933
|
+
carrier.lookAtY = home.lookAtY;
|
|
934
|
+
carrier.lookAtZ = home.lookAtZ;
|
|
935
|
+
carrier.fov = home.fov;
|
|
936
|
+
this.target = carrier;
|
|
937
|
+
this.autoZ = !collectPropKeys(this.spec).has("z");
|
|
938
|
+
}
|
|
939
|
+
/** The camera's state at the current timeline position. */
|
|
940
|
+
state() {
|
|
941
|
+
const c = this.target;
|
|
942
|
+
if (!this.warnedFov && !(c.fov >= MIN_FOV && c.fov <= MAX_FOV)) {
|
|
943
|
+
this.warnedFov = true;
|
|
944
|
+
const who = this.spec.name ? ` "${this.spec.name}"` : "";
|
|
945
|
+
console.warn(`pixi-effects: camera${who}: fov ${c.fov} is outside [${MIN_FOV}, ${MAX_FOV}]; clamped`);
|
|
946
|
+
}
|
|
947
|
+
const fov = clampFov(c.fov);
|
|
948
|
+
const z = this.autoZ ? homeDistance(this.compH, fov) : c.z;
|
|
949
|
+
if (!this.warnedLookAt && c.lookAtX === c.x && c.lookAtY === c.y && c.lookAtZ === z) {
|
|
950
|
+
this.warnedLookAt = true;
|
|
951
|
+
const who = this.spec.name ? ` "${this.spec.name}"` : "";
|
|
952
|
+
console.warn(`pixi-effects: camera${who}: lookAt equals the camera position; looking along -z instead`);
|
|
953
|
+
}
|
|
954
|
+
return {
|
|
955
|
+
x: c.x,
|
|
956
|
+
y: c.y,
|
|
957
|
+
z,
|
|
958
|
+
lookAtX: c.lookAtX,
|
|
959
|
+
lookAtY: c.lookAtY,
|
|
960
|
+
lookAtZ: c.lookAtZ,
|
|
961
|
+
fov
|
|
962
|
+
};
|
|
963
|
+
}
|
|
964
|
+
/** Lifespan on the global timeline (valid after bindTimeline). */
|
|
965
|
+
window() {
|
|
966
|
+
const start = this.absoluteStart ?? this.at;
|
|
967
|
+
return { start, end: start + (this.duration ?? 0) };
|
|
968
|
+
}
|
|
969
|
+
};
|
|
970
|
+
|
|
971
|
+
// src/expr/colorInterp.ts
|
|
972
|
+
function buildColorInterp(from, to, space) {
|
|
973
|
+
const fromRGBA = parseToRGBA(from);
|
|
974
|
+
const toRGBA = parseToRGBA(to);
|
|
975
|
+
const fromOk = rgbToOklab(fromRGBA);
|
|
976
|
+
const toOk = rgbToOklab(toRGBA);
|
|
977
|
+
if (space === "oklab") {
|
|
978
|
+
return (t) => {
|
|
979
|
+
const L = lerp(fromOk.L, toOk.L, t);
|
|
980
|
+
const a = lerp(fromOk.a, toOk.a, t);
|
|
981
|
+
const b = lerp(fromOk.b, toOk.b, t);
|
|
982
|
+
const alpha = lerp(fromRGBA.a, toRGBA.a, t);
|
|
983
|
+
return rgbaString(oklabToRgb({ L, a, b }), alpha);
|
|
984
|
+
};
|
|
985
|
+
}
|
|
986
|
+
const fromLCH = oklabToOklch(fromOk);
|
|
987
|
+
const toLCH = oklabToOklch(toOk);
|
|
988
|
+
if (fromLCH.C < 1e-4) fromLCH.h = toLCH.h;
|
|
989
|
+
if (toLCH.C < 1e-4) toLCH.h = fromLCH.h;
|
|
990
|
+
const hueDelta = shortestHueDelta(fromLCH.h, toLCH.h);
|
|
991
|
+
return (t) => {
|
|
992
|
+
const L = lerp(fromLCH.L, toLCH.L, t);
|
|
993
|
+
const C = lerp(fromLCH.C, toLCH.C, t);
|
|
994
|
+
const h = fromLCH.h + hueDelta * t;
|
|
995
|
+
const alpha = lerp(fromRGBA.a, toRGBA.a, t);
|
|
996
|
+
const ab = oklchToOklab({ L, C, h });
|
|
997
|
+
return rgbaString(oklabToRgb(ab), alpha);
|
|
998
|
+
};
|
|
999
|
+
}
|
|
1000
|
+
function parseToRGBA(input) {
|
|
1001
|
+
if (typeof input === "number") {
|
|
1002
|
+
return { r: (input >> 16 & 255) / 255, g: (input >> 8 & 255) / 255, b: (input & 255) / 255, a: 1 };
|
|
1003
|
+
}
|
|
1004
|
+
const s = input.trim();
|
|
1005
|
+
if (s.startsWith("#")) return parseHex(s);
|
|
1006
|
+
if (s.startsWith("rgb")) return parseRgbFunc(s);
|
|
1007
|
+
return { r: 0, g: 0, b: 0, a: 1 };
|
|
1008
|
+
}
|
|
1009
|
+
function parseHex(s) {
|
|
1010
|
+
const hex = s.slice(1);
|
|
1011
|
+
let r = 0, g = 0, b = 0, a = 1;
|
|
1012
|
+
if (hex.length === 3 || hex.length === 4) {
|
|
1013
|
+
r = parseInt(hex[0] + hex[0], 16) / 255;
|
|
1014
|
+
g = parseInt(hex[1] + hex[1], 16) / 255;
|
|
1015
|
+
b = parseInt(hex[2] + hex[2], 16) / 255;
|
|
1016
|
+
if (hex.length === 4) a = parseInt(hex[3] + hex[3], 16) / 255;
|
|
1017
|
+
} else if (hex.length === 6 || hex.length === 8) {
|
|
1018
|
+
r = parseInt(hex.slice(0, 2), 16) / 255;
|
|
1019
|
+
g = parseInt(hex.slice(2, 4), 16) / 255;
|
|
1020
|
+
b = parseInt(hex.slice(4, 6), 16) / 255;
|
|
1021
|
+
if (hex.length === 8) a = parseInt(hex.slice(6, 8), 16) / 255;
|
|
1022
|
+
}
|
|
1023
|
+
return { r, g, b, a };
|
|
1024
|
+
}
|
|
1025
|
+
function parseRgbFunc(s) {
|
|
1026
|
+
const inner = s.replace(/^rgba?\(/, "").replace(/\)$/, "");
|
|
1027
|
+
const parts = inner.split(/[,\s/]+/).filter(Boolean).map(parseFloat);
|
|
1028
|
+
return {
|
|
1029
|
+
r: (parts[0] ?? 0) / 255,
|
|
1030
|
+
g: (parts[1] ?? 0) / 255,
|
|
1031
|
+
b: (parts[2] ?? 0) / 255,
|
|
1032
|
+
a: parts.length > 3 ? parts[3] ?? 1 : 1
|
|
1033
|
+
};
|
|
1034
|
+
}
|
|
1035
|
+
function rgbaString(rgb, alpha) {
|
|
1036
|
+
const r = Math.round(clamp01(rgb.r) * 255);
|
|
1037
|
+
const g = Math.round(clamp01(rgb.g) * 255);
|
|
1038
|
+
const b = Math.round(clamp01(rgb.b) * 255);
|
|
1039
|
+
return `rgba(${r},${g},${b},${alpha})`;
|
|
1040
|
+
}
|
|
1041
|
+
function srgbToLinear(c) {
|
|
1042
|
+
return c <= 0.04045 ? c / 12.92 : Math.pow((c + 0.055) / 1.055, 2.4);
|
|
1043
|
+
}
|
|
1044
|
+
function linearToSrgb(c) {
|
|
1045
|
+
return c <= 31308e-7 ? 12.92 * c : 1.055 * Math.pow(c, 1 / 2.4) - 0.055;
|
|
1046
|
+
}
|
|
1047
|
+
function rgbToOklab(rgba) {
|
|
1048
|
+
const r = srgbToLinear(rgba.r);
|
|
1049
|
+
const g = srgbToLinear(rgba.g);
|
|
1050
|
+
const b = srgbToLinear(rgba.b);
|
|
1051
|
+
const l = 0.4122214708 * r + 0.5363325363 * g + 0.0514459929 * b;
|
|
1052
|
+
const m = 0.2119034982 * r + 0.6806995451 * g + 0.1073969566 * b;
|
|
1053
|
+
const s = 0.0883024619 * r + 0.2817188376 * g + 0.6299787005 * b;
|
|
1054
|
+
const l_ = Math.cbrt(l);
|
|
1055
|
+
const m_ = Math.cbrt(m);
|
|
1056
|
+
const s_ = Math.cbrt(s);
|
|
1057
|
+
return {
|
|
1058
|
+
L: 0.2104542553 * l_ + 0.793617785 * m_ - 0.0040720468 * s_,
|
|
1059
|
+
a: 1.9779984951 * l_ - 2.428592205 * m_ + 0.4505937099 * s_,
|
|
1060
|
+
b: 0.0259040371 * l_ + 0.7827717662 * m_ - 0.808675766 * s_
|
|
1061
|
+
};
|
|
1062
|
+
}
|
|
1063
|
+
function oklabToRgb(lab) {
|
|
1064
|
+
const l_ = lab.L + 0.3963377774 * lab.a + 0.2158037573 * lab.b;
|
|
1065
|
+
const m_ = lab.L - 0.1055613458 * lab.a - 0.0638541728 * lab.b;
|
|
1066
|
+
const s_ = lab.L - 0.0894841775 * lab.a - 1.291485548 * lab.b;
|
|
1067
|
+
const l = l_ * l_ * l_;
|
|
1068
|
+
const m = m_ * m_ * m_;
|
|
1069
|
+
const s = s_ * s_ * s_;
|
|
1070
|
+
return {
|
|
1071
|
+
r: linearToSrgb(4.0767416621 * l - 3.3077115913 * m + 0.2309699292 * s),
|
|
1072
|
+
g: linearToSrgb(-1.2684380046 * l + 2.6097574011 * m - 0.3413193965 * s),
|
|
1073
|
+
b: linearToSrgb(-0.0041960863 * l - 0.7034186147 * m + 1.707614701 * s)
|
|
1074
|
+
};
|
|
1075
|
+
}
|
|
1076
|
+
function oklabToOklch(lab) {
|
|
1077
|
+
return { L: lab.L, C: Math.hypot(lab.a, lab.b), h: Math.atan2(lab.b, lab.a) };
|
|
1078
|
+
}
|
|
1079
|
+
function oklchToOklab(lch) {
|
|
1080
|
+
return { L: lch.L, a: lch.C * Math.cos(lch.h), b: lch.C * Math.sin(lch.h) };
|
|
1081
|
+
}
|
|
1082
|
+
function lerp(a, b, t) {
|
|
1083
|
+
return a + (b - a) * t;
|
|
1084
|
+
}
|
|
1085
|
+
function clamp01(v) {
|
|
1086
|
+
return v < 0 ? 0 : v > 1 ? 1 : v;
|
|
1087
|
+
}
|
|
1088
|
+
function shortestHueDelta(from, to) {
|
|
1089
|
+
const TWO_PI = Math.PI * 2;
|
|
1090
|
+
let d = (to - from) % TWO_PI;
|
|
1091
|
+
if (d > Math.PI) d -= TWO_PI;
|
|
1092
|
+
if (d < -Math.PI) d += TWO_PI;
|
|
1093
|
+
return d;
|
|
1094
|
+
}
|
|
1095
|
+
|
|
1096
|
+
// src/expr/colorTween.ts
|
|
1097
|
+
function tweenColor(timeline, target, key, fromValue, toValue, duration, ease, at, colorSpace, onUpdate) {
|
|
1098
|
+
let interp = null;
|
|
1099
|
+
const proxy = { p: 0 };
|
|
1100
|
+
timeline.fromTo(
|
|
1101
|
+
proxy,
|
|
1102
|
+
{ p: 0 },
|
|
1103
|
+
{
|
|
1104
|
+
p: 1,
|
|
1105
|
+
duration,
|
|
1106
|
+
ease,
|
|
1107
|
+
onStart: () => {
|
|
1108
|
+
const start = fromValue !== void 0 ? fromValue : target[key];
|
|
1109
|
+
if (start === void 0) return;
|
|
1110
|
+
interp = colorSpace === "rgb" ? gsap.utils.interpolate(start, toValue) : buildColorInterp(start, toValue, colorSpace);
|
|
1111
|
+
},
|
|
1112
|
+
onUpdate: () => {
|
|
1113
|
+
if (interp) {
|
|
1114
|
+
target[key] = interp(proxy.p);
|
|
1115
|
+
if (onUpdate) onUpdate();
|
|
1116
|
+
}
|
|
1117
|
+
}
|
|
1118
|
+
},
|
|
1119
|
+
at
|
|
1120
|
+
);
|
|
1121
|
+
}
|
|
1122
|
+
|
|
1123
|
+
// src/sequences/Image.ts
|
|
1124
|
+
var ImageSequence = class extends Sequence {
|
|
1125
|
+
async build() {
|
|
1126
|
+
const texture = await Assets.get(this.spec.asset);
|
|
1127
|
+
const sprite = new Sprite({ texture, label: this.spec.name });
|
|
1128
|
+
sprite.cullable = true;
|
|
1129
|
+
this.target = sprite;
|
|
1130
|
+
this.intrinsicWidth = texture.width;
|
|
1131
|
+
this.intrinsicHeight = texture.height;
|
|
1132
|
+
if (this.duration === void 0) {
|
|
1133
|
+
this.duration = this.parent?.duration ?? this.root.duration;
|
|
1134
|
+
}
|
|
1135
|
+
this.buildFilters();
|
|
1136
|
+
}
|
|
1137
|
+
bindTimeline(timeline, offset = 0) {
|
|
1138
|
+
if (!this.target) return;
|
|
1139
|
+
const colorSpace = this.spec.colorSpace;
|
|
1140
|
+
if (!colorSpace || colorSpace === "rgb") {
|
|
1141
|
+
super.bindTimeline(timeline, offset);
|
|
1142
|
+
return;
|
|
1143
|
+
}
|
|
1144
|
+
const scope = this.scope();
|
|
1145
|
+
const initial = stripKeys(this.spec.initial, ["tint"]);
|
|
1146
|
+
const keyframes = this.spec.keyframes ? this.spec.keyframes.map((kf) => stripKeysInKeyframe(kf, ["tint"])) : void 0;
|
|
1147
|
+
applyInitial(this.target, initial, scope);
|
|
1148
|
+
applyKeyframes(timeline, this.target, keyframes, this.duration, scope, [], offset);
|
|
1149
|
+
const initTint = this.spec.initial?.tint;
|
|
1150
|
+
if (initTint !== void 0) this.target.tint = initTint;
|
|
1151
|
+
for (const kf of this.spec.keyframes ?? []) {
|
|
1152
|
+
const at = offset + resolveAt(kf.at, this.duration);
|
|
1153
|
+
const duration = kf.duration ?? 0;
|
|
1154
|
+
const ease = kf.ease ?? "none";
|
|
1155
|
+
const fromTint = kf.from?.tint;
|
|
1156
|
+
const toTint = kf.to?.tint;
|
|
1157
|
+
const setTint = kf.set?.tint;
|
|
1158
|
+
if (setTint !== void 0) {
|
|
1159
|
+
timeline.call(() => {
|
|
1160
|
+
this.target.tint = setTint;
|
|
1161
|
+
}, [], at);
|
|
1162
|
+
}
|
|
1163
|
+
if (toTint !== void 0) {
|
|
1164
|
+
tweenColor(
|
|
1165
|
+
timeline,
|
|
1166
|
+
this.target,
|
|
1167
|
+
"tint",
|
|
1168
|
+
fromTint,
|
|
1169
|
+
toTint,
|
|
1170
|
+
duration,
|
|
1171
|
+
ease,
|
|
1172
|
+
at,
|
|
1173
|
+
colorSpace
|
|
1174
|
+
);
|
|
1175
|
+
}
|
|
1176
|
+
}
|
|
1177
|
+
const startTime = offset + this.at;
|
|
1178
|
+
this.absoluteStart = startTime;
|
|
1179
|
+
const endTime = startTime + this.duration;
|
|
1180
|
+
this.target.renderable = startTime <= 0;
|
|
1181
|
+
timeline.set(this.target, { renderable: true }, startTime);
|
|
1182
|
+
timeline.set(this.target, { renderable: false }, endTime);
|
|
1183
|
+
}
|
|
1184
|
+
};
|
|
1185
|
+
function stripKeys(props, keys) {
|
|
1186
|
+
if (!props) return props;
|
|
1187
|
+
const drop = new Set(keys);
|
|
1188
|
+
const out = {};
|
|
1189
|
+
let changed = false;
|
|
1190
|
+
for (const k of Object.keys(props)) {
|
|
1191
|
+
if (drop.has(k)) {
|
|
1192
|
+
changed = true;
|
|
1193
|
+
continue;
|
|
1194
|
+
}
|
|
1195
|
+
out[k] = props[k];
|
|
1196
|
+
}
|
|
1197
|
+
return changed ? out : props;
|
|
1198
|
+
}
|
|
1199
|
+
function stripKeysInKeyframe(kf, keys) {
|
|
1200
|
+
return {
|
|
1201
|
+
...kf,
|
|
1202
|
+
set: stripKeys(kf.set, keys),
|
|
1203
|
+
to: stripKeys(kf.to, keys),
|
|
1204
|
+
from: stripKeys(kf.from, keys)
|
|
1205
|
+
};
|
|
1206
|
+
}
|
|
1207
|
+
var STYLE_OPAQUE_KEYS = ["fontFamily", "fill", "align", "fontStyle", "fontWeight"];
|
|
1208
|
+
var TextSequence = class extends Sequence {
|
|
1209
|
+
async build() {
|
|
1210
|
+
const baseStyle = {
|
|
1211
|
+
fontFamily: "Arial",
|
|
1212
|
+
fontSize: 36,
|
|
1213
|
+
fill: "#ffffff",
|
|
1214
|
+
align: "center"
|
|
1215
|
+
};
|
|
1216
|
+
const text = new Text({
|
|
1217
|
+
text: this.spec.text ?? "",
|
|
1218
|
+
style: baseStyle,
|
|
1219
|
+
label: this.spec.name
|
|
1220
|
+
});
|
|
1221
|
+
text.cullable = true;
|
|
1222
|
+
this.target = text;
|
|
1223
|
+
this.intrinsicWidth = text.width;
|
|
1224
|
+
this.intrinsicHeight = text.height;
|
|
1225
|
+
if (this.duration === void 0) {
|
|
1226
|
+
this.duration = this.parent?.duration ?? this.root.duration;
|
|
1227
|
+
}
|
|
1228
|
+
if (this.spec.style) {
|
|
1229
|
+
const scope = this.scope();
|
|
1230
|
+
const resolved = normalizeProps(
|
|
1231
|
+
this.spec.style,
|
|
1232
|
+
scope,
|
|
1233
|
+
{ skipKeys: STYLE_OPAQUE_KEYS }
|
|
1234
|
+
);
|
|
1235
|
+
for (const k of Object.keys(resolved)) {
|
|
1236
|
+
text.style[k] = resolved[k];
|
|
1237
|
+
}
|
|
1238
|
+
}
|
|
1239
|
+
this.buildFilters();
|
|
1240
|
+
}
|
|
1241
|
+
bindTimeline(timeline, offset = 0) {
|
|
1242
|
+
if (!this.target) return;
|
|
1243
|
+
const colorSpace = this.spec.colorSpace ?? "rgb";
|
|
1244
|
+
const stripped = stripFill(this.spec);
|
|
1245
|
+
runSuperWithStrippedSpec(this, stripped, timeline, offset);
|
|
1246
|
+
bindFillKeyframes(timeline, this.target, this.spec.keyframes ?? [], this.duration, offset, colorSpace);
|
|
1247
|
+
}
|
|
1248
|
+
};
|
|
1249
|
+
function runSuperWithStrippedSpec(inst, strippedSpec, timeline, offset) {
|
|
1250
|
+
const originalSpec = inst.spec;
|
|
1251
|
+
inst.spec = strippedSpec;
|
|
1252
|
+
try {
|
|
1253
|
+
Sequence.prototype.bindTimeline.call(inst, timeline, offset);
|
|
1254
|
+
} finally {
|
|
1255
|
+
inst.spec = originalSpec;
|
|
1256
|
+
}
|
|
1257
|
+
}
|
|
1258
|
+
function stripFill(spec) {
|
|
1259
|
+
return {
|
|
1260
|
+
...spec,
|
|
1261
|
+
initial: stripKey(spec.initial, "fill"),
|
|
1262
|
+
keyframes: spec.keyframes ? spec.keyframes.map((kf) => ({
|
|
1263
|
+
...kf,
|
|
1264
|
+
set: stripKey(kf.set, "fill"),
|
|
1265
|
+
to: stripKey(kf.to, "fill"),
|
|
1266
|
+
from: stripKey(kf.from, "fill")
|
|
1267
|
+
})) : void 0
|
|
1268
|
+
};
|
|
1269
|
+
}
|
|
1270
|
+
function stripKey(props, key) {
|
|
1271
|
+
if (!props) return props;
|
|
1272
|
+
if (!(key in props)) return props;
|
|
1273
|
+
const out = {};
|
|
1274
|
+
for (const k of Object.keys(props)) if (k !== key) out[k] = props[k];
|
|
1275
|
+
return out;
|
|
1276
|
+
}
|
|
1277
|
+
function bindFillKeyframes(timeline, text, keyframes, parentDuration, offset, colorSpace) {
|
|
1278
|
+
const state = { fill: text.style.fill };
|
|
1279
|
+
const writeFill = () => {
|
|
1280
|
+
text.style.fill = state.fill;
|
|
1281
|
+
};
|
|
1282
|
+
for (const kf of keyframes) {
|
|
1283
|
+
const at = offset + resolveAt(kf.at, parentDuration);
|
|
1284
|
+
const duration = kf.duration ?? 0;
|
|
1285
|
+
const ease = kf.ease ?? "none";
|
|
1286
|
+
const setFill = pickFill(kf.set);
|
|
1287
|
+
const fromFill = pickFill(kf.from);
|
|
1288
|
+
const toFill = pickFill(kf.to);
|
|
1289
|
+
if (setFill !== void 0) {
|
|
1290
|
+
timeline.call(() => {
|
|
1291
|
+
state.fill = setFill;
|
|
1292
|
+
writeFill();
|
|
1293
|
+
}, [], at);
|
|
1294
|
+
}
|
|
1295
|
+
if (toFill !== void 0) {
|
|
1296
|
+
tweenColor(
|
|
1297
|
+
timeline,
|
|
1298
|
+
state,
|
|
1299
|
+
"fill",
|
|
1300
|
+
fromFill,
|
|
1301
|
+
toFill,
|
|
1302
|
+
duration,
|
|
1303
|
+
ease,
|
|
1304
|
+
at,
|
|
1305
|
+
colorSpace,
|
|
1306
|
+
writeFill
|
|
1307
|
+
);
|
|
1308
|
+
}
|
|
1309
|
+
}
|
|
1310
|
+
}
|
|
1311
|
+
function pickFill(props) {
|
|
1312
|
+
if (!props) return void 0;
|
|
1313
|
+
const v = props.fill;
|
|
1314
|
+
if (typeof v === "string" || typeof v === "number") return v;
|
|
1315
|
+
return void 0;
|
|
1316
|
+
}
|
|
1317
|
+
var AudioSequence = class extends Sequence {
|
|
1318
|
+
_audioBuffer = null;
|
|
1319
|
+
async build() {
|
|
1320
|
+
const data = await Assets.get(this.spec.asset);
|
|
1321
|
+
this._audioBuffer = data.audioBuffer;
|
|
1322
|
+
if (this.duration === void 0) {
|
|
1323
|
+
this.duration = this.spec.duration ?? data.duration ?? this.root.duration;
|
|
1324
|
+
}
|
|
1325
|
+
this.target = null;
|
|
1326
|
+
}
|
|
1327
|
+
bindTimeline(_timeline) {
|
|
1328
|
+
}
|
|
1329
|
+
collectAudio(out, baseTime) {
|
|
1330
|
+
if (!this._audioBuffer) return;
|
|
1331
|
+
const initialVolume = this.spec.volume ?? 1;
|
|
1332
|
+
const volumeKeyframes = [];
|
|
1333
|
+
const dur = this.duration;
|
|
1334
|
+
for (const raw of this.spec.keyframes ?? []) {
|
|
1335
|
+
const kf = normalizeKeyframe(raw, dur);
|
|
1336
|
+
const set = kf.set;
|
|
1337
|
+
const to = kf.to;
|
|
1338
|
+
const from = kf.from;
|
|
1339
|
+
if (kf.kind === "set" && set && "volume" in set) {
|
|
1340
|
+
volumeKeyframes.push({ time: baseTime + this.at + kf.at, value: set.volume });
|
|
1341
|
+
} else if (kf.kind === "to" && to && "volume" in to) {
|
|
1342
|
+
volumeKeyframes.push({ time: baseTime + this.at + kf.at + kf.duration, value: to.volume });
|
|
1343
|
+
} else if (kf.kind === "fromTo" && to && "volume" in to) {
|
|
1344
|
+
volumeKeyframes.push({ time: baseTime + this.at + kf.at, value: from?.volume ?? initialVolume });
|
|
1345
|
+
volumeKeyframes.push({ time: baseTime + this.at + kf.at + kf.duration, value: to.volume });
|
|
1346
|
+
} else if (kf.kind === "from" && from && "volume" in from) {
|
|
1347
|
+
volumeKeyframes.push({ time: baseTime + this.at + kf.at, value: from.volume });
|
|
1348
|
+
volumeKeyframes.push({ time: baseTime + this.at + kf.at + kf.duration, value: initialVolume });
|
|
1349
|
+
}
|
|
1350
|
+
}
|
|
1351
|
+
out.push({
|
|
1352
|
+
buffer: this._audioBuffer,
|
|
1353
|
+
loop: !!this.spec.loop,
|
|
1354
|
+
start: baseTime + this.at,
|
|
1355
|
+
end: baseTime + this.at + dur,
|
|
1356
|
+
initialVolume,
|
|
1357
|
+
volumeKeyframes
|
|
1358
|
+
});
|
|
1359
|
+
}
|
|
1360
|
+
destroy() {
|
|
1361
|
+
this._audioBuffer = null;
|
|
1362
|
+
}
|
|
1363
|
+
};
|
|
1364
|
+
|
|
1365
|
+
// src/core/FrameCache.ts
|
|
1366
|
+
var FrameCache = class {
|
|
1367
|
+
sink;
|
|
1368
|
+
capacity;
|
|
1369
|
+
cache = /* @__PURE__ */ new Map();
|
|
1370
|
+
_pending = /* @__PURE__ */ new Map();
|
|
1371
|
+
constructor(sink, options = {}) {
|
|
1372
|
+
this.sink = sink;
|
|
1373
|
+
this.capacity = options.capacity ?? 30;
|
|
1374
|
+
}
|
|
1375
|
+
_key(time) {
|
|
1376
|
+
return Math.round(time * 1e3);
|
|
1377
|
+
}
|
|
1378
|
+
async getFrameAt(time) {
|
|
1379
|
+
const key = this._key(time);
|
|
1380
|
+
if (this.cache.has(key)) {
|
|
1381
|
+
const v = this.cache.get(key);
|
|
1382
|
+
this.cache.delete(key);
|
|
1383
|
+
this.cache.set(key, v);
|
|
1384
|
+
return v;
|
|
1385
|
+
}
|
|
1386
|
+
const pending = this._pending.get(key);
|
|
1387
|
+
if (pending) return pending;
|
|
1388
|
+
const promise = this._fetch(time);
|
|
1389
|
+
this._pending.set(key, promise);
|
|
1390
|
+
try {
|
|
1391
|
+
const frame = await promise;
|
|
1392
|
+
if (frame) {
|
|
1393
|
+
if (this.cache.has(key)) {
|
|
1394
|
+
frame.close?.();
|
|
1395
|
+
const existing = this.cache.get(key);
|
|
1396
|
+
this.cache.delete(key);
|
|
1397
|
+
this.cache.set(key, existing);
|
|
1398
|
+
return existing;
|
|
1399
|
+
}
|
|
1400
|
+
this.cache.set(key, frame);
|
|
1401
|
+
this._evictIfNeeded();
|
|
1402
|
+
}
|
|
1403
|
+
return frame;
|
|
1404
|
+
} finally {
|
|
1405
|
+
this._pending.delete(key);
|
|
1406
|
+
}
|
|
1407
|
+
}
|
|
1408
|
+
async _fetch(time) {
|
|
1409
|
+
try {
|
|
1410
|
+
const sample = await this.sink.getSample(time);
|
|
1411
|
+
if (!sample) return null;
|
|
1412
|
+
const frame = sample.toVideoFrame();
|
|
1413
|
+
sample.close?.();
|
|
1414
|
+
return frame;
|
|
1415
|
+
} catch (err) {
|
|
1416
|
+
const isTransient = err instanceof DOMException && (err.name === "EncodingError" || err.name === "NotSupportedError");
|
|
1417
|
+
if (isTransient) {
|
|
1418
|
+
console.debug("pixi-effects: transient video decode failure at", time, "s \u2014", err.name);
|
|
1419
|
+
} else {
|
|
1420
|
+
console.warn("pixi-effects: video frame decode failed at", time, "s \u2014", err);
|
|
1421
|
+
}
|
|
1422
|
+
return null;
|
|
1423
|
+
}
|
|
1424
|
+
}
|
|
1425
|
+
_evictIfNeeded() {
|
|
1426
|
+
while (this.cache.size > this.capacity) {
|
|
1427
|
+
const oldestKey = this.cache.keys().next().value;
|
|
1428
|
+
if (oldestKey === void 0) break;
|
|
1429
|
+
const oldestFrame = this.cache.get(oldestKey);
|
|
1430
|
+
oldestFrame?.close?.();
|
|
1431
|
+
this.cache.delete(oldestKey);
|
|
1432
|
+
}
|
|
1433
|
+
}
|
|
1434
|
+
dispose() {
|
|
1435
|
+
for (const f of this.cache.values()) f?.close?.();
|
|
1436
|
+
this.cache.clear();
|
|
1437
|
+
this._pending.clear();
|
|
1438
|
+
}
|
|
1439
|
+
};
|
|
1440
|
+
|
|
1441
|
+
// src/sequences/Video.ts
|
|
1442
|
+
var VideoSequence = class extends Sequence {
|
|
1443
|
+
_sourceDuration = 0;
|
|
1444
|
+
_audioBuffer = null;
|
|
1445
|
+
_cache = null;
|
|
1446
|
+
_canvas = null;
|
|
1447
|
+
_ctx = null;
|
|
1448
|
+
_drawSeq = 0;
|
|
1449
|
+
async build() {
|
|
1450
|
+
const data = await Assets.get(this.spec.asset);
|
|
1451
|
+
this._sourceDuration = data.duration;
|
|
1452
|
+
this._audioBuffer = this.spec.audio !== false ? data.audioBuffer : null;
|
|
1453
|
+
this._cache = new FrameCache(data.sink, { capacity: 30 });
|
|
1454
|
+
const probeFrame = await this._cache.getFrameAt(0);
|
|
1455
|
+
const w = probeFrame?.displayWidth ?? 1920;
|
|
1456
|
+
const h = probeFrame?.displayHeight ?? 1080;
|
|
1457
|
+
const canvas = document.createElement("canvas");
|
|
1458
|
+
canvas.width = w;
|
|
1459
|
+
canvas.height = h;
|
|
1460
|
+
this._canvas = canvas;
|
|
1461
|
+
this._ctx = canvas.getContext("2d", { alpha: true });
|
|
1462
|
+
if (probeFrame && this._ctx) this._ctx.drawImage(probeFrame, 0, 0);
|
|
1463
|
+
const texture = Texture.from(canvas);
|
|
1464
|
+
const sprite = new Sprite({ texture, label: this.spec.name });
|
|
1465
|
+
sprite.cullable = true;
|
|
1466
|
+
this.target = sprite;
|
|
1467
|
+
this.intrinsicWidth = w;
|
|
1468
|
+
this.intrinsicHeight = h;
|
|
1469
|
+
if (this.duration === void 0) {
|
|
1470
|
+
this.duration = this.spec.duration ?? data.duration ?? this.root.duration;
|
|
1471
|
+
}
|
|
1472
|
+
let currentTime = 0;
|
|
1473
|
+
const seq = this;
|
|
1474
|
+
Object.defineProperty(sprite, "currentTime", {
|
|
1475
|
+
get() {
|
|
1476
|
+
return currentTime;
|
|
1477
|
+
},
|
|
1478
|
+
set(v) {
|
|
1479
|
+
currentTime = v;
|
|
1480
|
+
const mySeq = ++seq._drawSeq;
|
|
1481
|
+
const lookup = seq.spec.loop && seq._sourceDuration > 0 ? v % seq._sourceDuration : v;
|
|
1482
|
+
seq._cache.getFrameAt(lookup).then((frame) => {
|
|
1483
|
+
if (mySeq !== seq._drawSeq) return;
|
|
1484
|
+
if (!frame || !seq._ctx) return;
|
|
1485
|
+
seq._ctx.drawImage(frame, 0, 0);
|
|
1486
|
+
texture.source.update();
|
|
1487
|
+
}).catch((err) => {
|
|
1488
|
+
console.warn("pixi-effects: video frame draw failed:", err);
|
|
1489
|
+
});
|
|
1490
|
+
}
|
|
1491
|
+
});
|
|
1492
|
+
this.buildFilters();
|
|
1493
|
+
}
|
|
1494
|
+
bindTimeline(timeline) {
|
|
1495
|
+
super.bindTimeline(timeline);
|
|
1496
|
+
const playDuration = this.spec.loop ? this.duration : Math.min(this.duration, this._sourceDuration);
|
|
1497
|
+
timeline.fromTo(
|
|
1498
|
+
this.target,
|
|
1499
|
+
{ currentTime: 0 },
|
|
1500
|
+
{ currentTime: playDuration, duration: playDuration, ease: "none" },
|
|
1501
|
+
this.at
|
|
1502
|
+
);
|
|
1503
|
+
}
|
|
1504
|
+
collectAudio(out, baseTime) {
|
|
1505
|
+
if (!this._audioBuffer) return;
|
|
1506
|
+
const initialVolume = this.spec.volume ?? 1;
|
|
1507
|
+
out.push({
|
|
1508
|
+
buffer: this._audioBuffer,
|
|
1509
|
+
loop: !!this.spec.loop,
|
|
1510
|
+
start: baseTime + this.at,
|
|
1511
|
+
end: baseTime + this.at + this.duration,
|
|
1512
|
+
initialVolume,
|
|
1513
|
+
volumeKeyframes: []
|
|
1514
|
+
});
|
|
1515
|
+
}
|
|
1516
|
+
async awaitFrameAt(time) {
|
|
1517
|
+
const mySeq = ++this._drawSeq;
|
|
1518
|
+
const lookup = this.spec.loop && this._sourceDuration > 0 ? time % this._sourceDuration : time;
|
|
1519
|
+
let frame = null;
|
|
1520
|
+
try {
|
|
1521
|
+
frame = await this._cache.getFrameAt(lookup);
|
|
1522
|
+
} catch (err) {
|
|
1523
|
+
console.warn("pixi-effects: awaitFrameAt failed at", time, "s \u2014", err);
|
|
1524
|
+
return;
|
|
1525
|
+
}
|
|
1526
|
+
if (mySeq !== this._drawSeq) return;
|
|
1527
|
+
if (frame && this._ctx) {
|
|
1528
|
+
try {
|
|
1529
|
+
this._ctx.drawImage(frame, 0, 0);
|
|
1530
|
+
this.target.texture.source.update();
|
|
1531
|
+
} catch (err) {
|
|
1532
|
+
console.warn("pixi-effects: drawImage failed at", time, "s \u2014", err);
|
|
1533
|
+
}
|
|
1534
|
+
}
|
|
1535
|
+
}
|
|
1536
|
+
destroy() {
|
|
1537
|
+
this._cache?.dispose();
|
|
1538
|
+
this._cache = null;
|
|
1539
|
+
super.destroy();
|
|
1540
|
+
}
|
|
1541
|
+
};
|
|
1542
|
+
var STYLE_KEYS = ["fillColor", "fillAlpha", "strokeColor", "strokeAlpha", "strokeWidth"];
|
|
1543
|
+
var COLOR_KEYS = /* @__PURE__ */ new Set(["fillColor", "strokeColor"]);
|
|
1544
|
+
var GEOMETRY_KEYS_BY_SHAPE = {
|
|
1545
|
+
rect: ["width", "height", "cornerRadius", "anchorX", "anchorY"],
|
|
1546
|
+
circle: ["radius", "anchorX", "anchorY"],
|
|
1547
|
+
ellipse: ["radiusX", "radiusY", "anchorX", "anchorY"],
|
|
1548
|
+
line: [],
|
|
1549
|
+
polygon: [],
|
|
1550
|
+
path: []
|
|
1551
|
+
};
|
|
1552
|
+
var ANCHORED_SHAPES = /* @__PURE__ */ new Set(["rect", "circle", "ellipse"]);
|
|
1553
|
+
var ShapeSequence = class extends Sequence {
|
|
1554
|
+
_state = {
|
|
1555
|
+
fillColor: void 0,
|
|
1556
|
+
fillAlpha: 1,
|
|
1557
|
+
strokeColor: void 0,
|
|
1558
|
+
strokeAlpha: 1,
|
|
1559
|
+
strokeWidth: 0
|
|
1560
|
+
};
|
|
1561
|
+
// Closure that draws the path commands for this shape kind from the
|
|
1562
|
+
// (possibly tweened) `_state`. Bound once at build; called on every frame
|
|
1563
|
+
// by `Container.onRender`.
|
|
1564
|
+
_drawGeometry = () => {
|
|
1565
|
+
};
|
|
1566
|
+
_liveKeys = new Set(STYLE_KEYS);
|
|
1567
|
+
async build() {
|
|
1568
|
+
const graphics = new Graphics({ label: this.spec.name });
|
|
1569
|
+
graphics.cullable = true;
|
|
1570
|
+
this.target = graphics;
|
|
1571
|
+
if (this.duration === void 0) {
|
|
1572
|
+
this.duration = this.parent?.duration ?? this.root.duration;
|
|
1573
|
+
}
|
|
1574
|
+
const scope = this.scope();
|
|
1575
|
+
const geomKeys = GEOMETRY_KEYS_BY_SHAPE[this.spec.shape];
|
|
1576
|
+
for (const k of geomKeys) this._liveKeys.add(k);
|
|
1577
|
+
seedGeometry(this._state, this.spec, scope);
|
|
1578
|
+
seedStyle(this._state, this.spec.initial ?? {}, scope);
|
|
1579
|
+
this._drawGeometry = makeDrawGeometry(this.spec, scope, this._state);
|
|
1580
|
+
this._drawGeometry(graphics);
|
|
1581
|
+
applyState(graphics, this._state);
|
|
1582
|
+
const bounds = graphics.getLocalBounds();
|
|
1583
|
+
this.intrinsicWidth = bounds.width;
|
|
1584
|
+
this.intrinsicHeight = bounds.height;
|
|
1585
|
+
if (!ANCHORED_SHAPES.has(this.spec.shape)) {
|
|
1586
|
+
graphics.pivot.set(bounds.x + bounds.width / 2, bounds.y + bounds.height / 2);
|
|
1587
|
+
}
|
|
1588
|
+
graphics.onRender = () => {
|
|
1589
|
+
graphics.clear();
|
|
1590
|
+
this._drawGeometry(graphics);
|
|
1591
|
+
applyState(graphics, this._state);
|
|
1592
|
+
};
|
|
1593
|
+
this.buildFilters();
|
|
1594
|
+
}
|
|
1595
|
+
bindTimeline(timeline, offset = 0) {
|
|
1596
|
+
if (!this.target) return;
|
|
1597
|
+
const scope = this.scope();
|
|
1598
|
+
const initial = stripLive(this.spec.initial, this._liveKeys);
|
|
1599
|
+
const keyframes = this.spec.keyframes ? this.spec.keyframes.map((kf) => stripLiveKeyframe(kf, this._liveKeys)) : void 0;
|
|
1600
|
+
applyInitial(this.target, initial, scope);
|
|
1601
|
+
applyKeyframes(timeline, this.target, keyframes, this.duration, scope, [], offset);
|
|
1602
|
+
const colorSpace = this.spec.colorSpace ?? "rgb";
|
|
1603
|
+
bindLiveKeyframes(timeline, this._state, this._liveKeys, this.spec.keyframes ?? [], this.duration, scope, offset, colorSpace);
|
|
1604
|
+
const startTime = offset + this.at;
|
|
1605
|
+
this.absoluteStart = startTime;
|
|
1606
|
+
const endTime = startTime + this.duration;
|
|
1607
|
+
this.target.renderable = startTime <= 0;
|
|
1608
|
+
timeline.set(this.target, { renderable: true }, startTime);
|
|
1609
|
+
timeline.set(this.target, { renderable: false }, endTime);
|
|
1610
|
+
}
|
|
1611
|
+
};
|
|
1612
|
+
function seedGeometry(state, spec, scope) {
|
|
1613
|
+
const keys = GEOMETRY_KEYS_BY_SHAPE[spec.shape];
|
|
1614
|
+
for (const k of keys) {
|
|
1615
|
+
const v = spec[k];
|
|
1616
|
+
if (v !== void 0) state[k] = num(v, scope);
|
|
1617
|
+
}
|
|
1618
|
+
}
|
|
1619
|
+
function seedStyle(state, initial, scope) {
|
|
1620
|
+
for (const k of STYLE_KEYS) {
|
|
1621
|
+
const v = initial[k];
|
|
1622
|
+
if (v === void 0) continue;
|
|
1623
|
+
state[k] = COLOR_KEYS.has(k) ? v : numOrZero(v, scope);
|
|
1624
|
+
}
|
|
1625
|
+
}
|
|
1626
|
+
function stripLive(props, liveKeys) {
|
|
1627
|
+
if (!props) return props;
|
|
1628
|
+
const out = {};
|
|
1629
|
+
let changed = false;
|
|
1630
|
+
for (const k of Object.keys(props)) {
|
|
1631
|
+
if (liveKeys.has(k)) {
|
|
1632
|
+
changed = true;
|
|
1633
|
+
continue;
|
|
1634
|
+
}
|
|
1635
|
+
out[k] = props[k];
|
|
1636
|
+
}
|
|
1637
|
+
return changed ? out : props;
|
|
1638
|
+
}
|
|
1639
|
+
function stripLiveKeyframe(kf, liveKeys) {
|
|
1640
|
+
return {
|
|
1641
|
+
...kf,
|
|
1642
|
+
set: stripLive(kf.set, liveKeys),
|
|
1643
|
+
to: stripLive(kf.to, liveKeys),
|
|
1644
|
+
from: stripLive(kf.from, liveKeys)
|
|
1645
|
+
};
|
|
1646
|
+
}
|
|
1647
|
+
function pickLive(props, liveKeys) {
|
|
1648
|
+
if (!props) return null;
|
|
1649
|
+
const out = {};
|
|
1650
|
+
let any = false;
|
|
1651
|
+
for (const k of Object.keys(props)) {
|
|
1652
|
+
if (liveKeys.has(k)) {
|
|
1653
|
+
out[k] = props[k];
|
|
1654
|
+
any = true;
|
|
1655
|
+
}
|
|
1656
|
+
}
|
|
1657
|
+
return any ? out : null;
|
|
1658
|
+
}
|
|
1659
|
+
function resolveLiveValue(key, value, scope) {
|
|
1660
|
+
if (COLOR_KEYS.has(key)) return value;
|
|
1661
|
+
return numOrZero(value, scope);
|
|
1662
|
+
}
|
|
1663
|
+
function applyState(g, s) {
|
|
1664
|
+
if (s.fillColor !== void 0) {
|
|
1665
|
+
g.fill({ color: s.fillColor, alpha: s.fillAlpha });
|
|
1666
|
+
}
|
|
1667
|
+
if (s.strokeColor !== void 0 && s.strokeWidth > 0) {
|
|
1668
|
+
g.stroke({ color: s.strokeColor, alpha: s.strokeAlpha, width: s.strokeWidth });
|
|
1669
|
+
}
|
|
1670
|
+
}
|
|
1671
|
+
function drawRectFromState(g, s) {
|
|
1672
|
+
const w = s.width ?? 0;
|
|
1673
|
+
const h = s.height ?? 0;
|
|
1674
|
+
const r = s.cornerRadius ?? 0;
|
|
1675
|
+
const ax = s.anchorX ?? 0.5;
|
|
1676
|
+
const ay = s.anchorY ?? 0.5;
|
|
1677
|
+
const x = 0 - ax * w;
|
|
1678
|
+
const y = 0 - ay * h;
|
|
1679
|
+
if (r > 0) g.roundRect(x, y, w, h, r);
|
|
1680
|
+
else g.rect(x, y, w, h);
|
|
1681
|
+
}
|
|
1682
|
+
function drawCircleFromState(g, s) {
|
|
1683
|
+
const r = s.radius ?? 0;
|
|
1684
|
+
const ax = s.anchorX ?? 0.5;
|
|
1685
|
+
const ay = s.anchorY ?? 0.5;
|
|
1686
|
+
g.circle((0.5 - ax) * 2 * r, (0.5 - ay) * 2 * r, r);
|
|
1687
|
+
}
|
|
1688
|
+
function drawEllipseFromState(g, s) {
|
|
1689
|
+
const rx = s.radiusX ?? 0;
|
|
1690
|
+
const ry = s.radiusY ?? 0;
|
|
1691
|
+
const ax = s.anchorX ?? 0.5;
|
|
1692
|
+
const ay = s.anchorY ?? 0.5;
|
|
1693
|
+
g.ellipse((0.5 - ax) * 2 * rx, (0.5 - ay) * 2 * ry, rx, ry);
|
|
1694
|
+
}
|
|
1695
|
+
function makeLineDraw(spec, scope) {
|
|
1696
|
+
const fx = num(spec.from[0], scope);
|
|
1697
|
+
const fy = num(spec.from[1], scope);
|
|
1698
|
+
const tx = num(spec.to[0], scope);
|
|
1699
|
+
const ty = num(spec.to[1], scope);
|
|
1700
|
+
return (g) => {
|
|
1701
|
+
g.moveTo(fx, fy);
|
|
1702
|
+
g.lineTo(tx, ty);
|
|
1703
|
+
};
|
|
1704
|
+
}
|
|
1705
|
+
function makePolygonDraw(spec, scope) {
|
|
1706
|
+
if (spec.points.length === 0) return () => {
|
|
1707
|
+
};
|
|
1708
|
+
const flat = [];
|
|
1709
|
+
for (const [x, y] of spec.points) flat.push(num(x, scope), num(y, scope));
|
|
1710
|
+
const closed = !spec.open;
|
|
1711
|
+
return (g) => {
|
|
1712
|
+
g.poly(flat, closed);
|
|
1713
|
+
};
|
|
1714
|
+
}
|
|
1715
|
+
function makePathDraw(spec) {
|
|
1716
|
+
return (g) => {
|
|
1717
|
+
g.path(new GraphicsPath(spec.d));
|
|
1718
|
+
};
|
|
1719
|
+
}
|
|
1720
|
+
var STATE_DRAWERS = {
|
|
1721
|
+
rect: drawRectFromState,
|
|
1722
|
+
circle: drawCircleFromState,
|
|
1723
|
+
ellipse: drawEllipseFromState
|
|
1724
|
+
};
|
|
1725
|
+
function makeDrawGeometry(spec, scope, state) {
|
|
1726
|
+
const stateDrawer = STATE_DRAWERS[spec.shape];
|
|
1727
|
+
if (stateDrawer) return (g) => stateDrawer(g, state);
|
|
1728
|
+
switch (spec.shape) {
|
|
1729
|
+
case "line":
|
|
1730
|
+
return makeLineDraw(spec, scope);
|
|
1731
|
+
case "polygon":
|
|
1732
|
+
return makePolygonDraw(spec, scope);
|
|
1733
|
+
case "path":
|
|
1734
|
+
return makePathDraw(spec);
|
|
1735
|
+
}
|
|
1736
|
+
return () => {
|
|
1737
|
+
};
|
|
1738
|
+
}
|
|
1739
|
+
function bindLiveKeyframes(timeline, state, liveKeys, keyframes, parentDuration, scope, offset, colorSpace) {
|
|
1740
|
+
for (const kf of keyframes) {
|
|
1741
|
+
const at = offset + resolveAt(kf.at, parentDuration);
|
|
1742
|
+
const duration = kf.duration ?? 0;
|
|
1743
|
+
const ease = kf.ease ?? "none";
|
|
1744
|
+
if (kf.set) {
|
|
1745
|
+
const live = pickLive(kf.set, liveKeys);
|
|
1746
|
+
if (live) {
|
|
1747
|
+
for (const [k, v] of Object.entries(live)) {
|
|
1748
|
+
const resolved = resolveLiveValue(k, v, scope);
|
|
1749
|
+
timeline.call(() => {
|
|
1750
|
+
state[k] = resolved;
|
|
1751
|
+
}, [], at);
|
|
1752
|
+
}
|
|
1753
|
+
}
|
|
1754
|
+
}
|
|
1755
|
+
const fromLive = pickLive(kf.from, liveKeys);
|
|
1756
|
+
const toLive = pickLive(kf.to, liveKeys);
|
|
1757
|
+
const allKeys = /* @__PURE__ */ new Set([
|
|
1758
|
+
...fromLive ? Object.keys(fromLive) : [],
|
|
1759
|
+
...toLive ? Object.keys(toLive) : []
|
|
1760
|
+
]);
|
|
1761
|
+
for (const k of allKeys) {
|
|
1762
|
+
const key = k;
|
|
1763
|
+
const fromRaw = fromLive?.[key];
|
|
1764
|
+
const toRaw = toLive?.[key];
|
|
1765
|
+
const fromValue = fromRaw !== void 0 ? resolveLiveValue(key, fromRaw, scope) : void 0;
|
|
1766
|
+
const toValue = toRaw !== void 0 ? resolveLiveValue(key, toRaw, scope) : void 0;
|
|
1767
|
+
tweenLiveKey(timeline, state, key, fromValue, toValue, duration, ease, at, colorSpace);
|
|
1768
|
+
}
|
|
1769
|
+
}
|
|
1770
|
+
}
|
|
1771
|
+
function tweenLiveKey(timeline, state, key, fromValue, toValue, duration, ease, at, colorSpace) {
|
|
1772
|
+
if (COLOR_KEYS.has(key) && toValue !== void 0) {
|
|
1773
|
+
tweenColor(
|
|
1774
|
+
timeline,
|
|
1775
|
+
state,
|
|
1776
|
+
key,
|
|
1777
|
+
fromValue,
|
|
1778
|
+
toValue,
|
|
1779
|
+
duration,
|
|
1780
|
+
ease,
|
|
1781
|
+
at,
|
|
1782
|
+
colorSpace
|
|
1783
|
+
);
|
|
1784
|
+
} else if (toValue !== void 0) {
|
|
1785
|
+
if (fromValue !== void 0) {
|
|
1786
|
+
timeline.fromTo(state, { [key]: fromValue }, { [key]: toValue, duration, ease }, at);
|
|
1787
|
+
} else {
|
|
1788
|
+
timeline.to(state, { [key]: toValue, duration, ease }, at);
|
|
1789
|
+
}
|
|
1790
|
+
}
|
|
1791
|
+
}
|
|
1792
|
+
function num(v, scope) {
|
|
1793
|
+
if (typeof v === "number") return v;
|
|
1794
|
+
if (isExpr(v)) return evaluateExpr(v, scope);
|
|
1795
|
+
const parsed = parseFloat(v);
|
|
1796
|
+
return Number.isNaN(parsed) ? 0 : parsed;
|
|
1797
|
+
}
|
|
1798
|
+
function numOrZero(v, scope) {
|
|
1799
|
+
if (typeof v === "number") return v;
|
|
1800
|
+
if (typeof v === "string") return num(v, scope);
|
|
1801
|
+
return 0;
|
|
1802
|
+
}
|
|
1803
|
+
|
|
1804
|
+
// src/core/Composition.ts
|
|
1805
|
+
var _CompositionSequence = null;
|
|
1806
|
+
async function getCompositionSequence() {
|
|
1807
|
+
if (!_CompositionSequence) {
|
|
1808
|
+
_CompositionSequence = (await import('./Composition-IO7ZN32J.js')).CompositionSequence;
|
|
1809
|
+
}
|
|
1810
|
+
return _CompositionSequence;
|
|
1811
|
+
}
|
|
1812
|
+
var staticTypes = {
|
|
1813
|
+
image: ImageSequence,
|
|
1814
|
+
text: TextSequence,
|
|
1815
|
+
audio: AudioSequence,
|
|
1816
|
+
video: VideoSequence,
|
|
1817
|
+
shape: ShapeSequence,
|
|
1818
|
+
camera: CameraSequence
|
|
1819
|
+
};
|
|
1820
|
+
var registry2 = {};
|
|
1821
|
+
function registerSequenceType(type, ctor) {
|
|
1822
|
+
registry2[type] = ctor;
|
|
1823
|
+
}
|
|
1824
|
+
async function buildSequenceTree(specs, parent, root) {
|
|
1825
|
+
const out = [];
|
|
1826
|
+
for (const spec of specs) {
|
|
1827
|
+
let Cls = staticTypes[spec.type] ?? registry2[spec.type];
|
|
1828
|
+
if (!Cls && spec.type === "composition") {
|
|
1829
|
+
Cls = await getCompositionSequence();
|
|
1830
|
+
}
|
|
1831
|
+
if (!Cls) {
|
|
1832
|
+
console.warn(`pixi-effects: unknown sequence type "${spec.type}"`);
|
|
1833
|
+
continue;
|
|
1834
|
+
}
|
|
1835
|
+
const seq = new Cls(spec, parent, root);
|
|
1836
|
+
await seq.build();
|
|
1837
|
+
if (seq.duration === void 0) {
|
|
1838
|
+
seq.duration = parent?.duration ?? root.duration;
|
|
1839
|
+
}
|
|
1840
|
+
out.push(seq);
|
|
1841
|
+
}
|
|
1842
|
+
return out;
|
|
1843
|
+
}
|
|
1844
|
+
|
|
1845
|
+
export { CameraSequence, DEG, Sequence, buildScope, buildSequenceTree, cameraBasis, collectPropKeys, evaluateExpr, homeCamera, projectLayer, registerSequenceType, resolveAt };
|
|
1846
|
+
//# sourceMappingURL=chunk-VJCDG6YG.js.map
|
|
1847
|
+
//# sourceMappingURL=chunk-VJCDG6YG.js.map
|