pixi-effects 0.1.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +31 -0
- package/README.md +117 -7
- package/ai/SKILL.md +44 -0
- package/ai/reference/cheatsheet.md +120 -0
- package/ai/reference/pitfalls.md +54 -0
- package/ai/reference/recipes.md +375 -0
- package/ai/template.html +82 -0
- package/ai/tools/save-image.py +24 -0
- package/dist/Base-BDw6dKRB.d.cts +92 -0
- package/dist/Base-Ba4Ta4ap.d.ts +92 -0
- package/dist/Composition-6FDH5OPM.cjs +13 -0
- package/dist/Composition-6FDH5OPM.cjs.map +1 -0
- package/dist/Composition-FEYAUMFI.js +4 -0
- package/dist/Composition-FEYAUMFI.js.map +1 -0
- package/dist/Controller.d.cts +2 -1
- package/dist/Controller.d.ts +2 -1
- package/dist/Movie-DMpaT67V.d.ts +179 -0
- package/dist/Movie-W8ISiEBB.d.cts +179 -0
- package/dist/chunk-DIJG2RSF.js +370 -0
- package/dist/chunk-DIJG2RSF.js.map +1 -0
- package/dist/chunk-PN5A6QA7.js +2031 -0
- package/dist/chunk-PN5A6QA7.js.map +1 -0
- package/dist/chunk-SIRVULFF.cjs +2049 -0
- package/dist/chunk-SIRVULFF.cjs.map +1 -0
- package/dist/chunk-ZL262ZRM.cjs +372 -0
- package/dist/chunk-ZL262ZRM.cjs.map +1 -0
- package/dist/index.cjs +371 -1798
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +95 -24
- package/dist/index.d.ts +95 -24
- package/dist/index.js +364 -1796
- 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-3c8Vymgw.d.cts} +84 -81
- package/dist/{Movie-Bp66rkaC.d.ts → types-3c8Vymgw.d.ts} +84 -81
- package/docs/api.md +333 -0
- package/docs/dsl.md +1017 -0
- package/llms-full.txt +1966 -0
- package/llms.txt +30 -0
- package/package.json +19 -7
package/dist/index.cjs
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var
|
|
3
|
+
var chunkZL262ZRM_cjs = require('./chunk-ZL262ZRM.cjs');
|
|
4
|
+
var chunkSIRVULFF_cjs = require('./chunk-SIRVULFF.cjs');
|
|
4
5
|
var PIXI = require('pixi.js');
|
|
6
|
+
var gsap = require('gsap');
|
|
5
7
|
var PixiPlugin = require('gsap/PixiPlugin');
|
|
6
8
|
var mediabunny = require('mediabunny');
|
|
7
9
|
|
|
@@ -25,1771 +27,6 @@ function _interopNamespace(e) {
|
|
|
25
27
|
|
|
26
28
|
var PIXI__namespace = /*#__PURE__*/_interopNamespace(PIXI);
|
|
27
29
|
|
|
28
|
-
var __defProp = Object.defineProperty;
|
|
29
|
-
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
30
|
-
var __esm = (fn, res) => function __init() {
|
|
31
|
-
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
32
|
-
};
|
|
33
|
-
var __export = (target, all) => {
|
|
34
|
-
for (var name in all)
|
|
35
|
-
__defProp(target, name, { get: all[name], enumerable: true });
|
|
36
|
-
};
|
|
37
|
-
|
|
38
|
-
// src/expr/Parser.ts
|
|
39
|
-
function isDigit(c) {
|
|
40
|
-
return c >= "0" && c <= "9";
|
|
41
|
-
}
|
|
42
|
-
function isIdStart(c) {
|
|
43
|
-
return c >= "a" && c <= "z" || c >= "A" && c <= "Z" || c === "_";
|
|
44
|
-
}
|
|
45
|
-
function isIdCont(c) {
|
|
46
|
-
return isIdStart(c) || isDigit(c);
|
|
47
|
-
}
|
|
48
|
-
function tokenize(src) {
|
|
49
|
-
const out = [];
|
|
50
|
-
for (let i = 0; i < src.length; ) {
|
|
51
|
-
const c = src[i];
|
|
52
|
-
if (c === " " || c === " " || c === "\n" || c === "\r") {
|
|
53
|
-
i++;
|
|
54
|
-
continue;
|
|
55
|
-
}
|
|
56
|
-
if (isDigit(c) || c === "." && isDigit(src[i + 1] ?? "")) {
|
|
57
|
-
let j = i + 1;
|
|
58
|
-
while (j < src.length && (isDigit(src[j]) || src[j] === ".")) j++;
|
|
59
|
-
const value = parseFloat(src.slice(i, j));
|
|
60
|
-
if (Number.isNaN(value)) throw new Error(`bad number at ${i}`);
|
|
61
|
-
out.push({ type: "num", value });
|
|
62
|
-
i = j;
|
|
63
|
-
continue;
|
|
64
|
-
}
|
|
65
|
-
if (isIdStart(c)) {
|
|
66
|
-
let j = i + 1;
|
|
67
|
-
while (j < src.length && isIdCont(src[j])) j++;
|
|
68
|
-
out.push({ type: "id", name: src.slice(i, j) });
|
|
69
|
-
i = j;
|
|
70
|
-
continue;
|
|
71
|
-
}
|
|
72
|
-
if (c === "+" || c === "-" || c === "*" || c === "/") {
|
|
73
|
-
out.push({ type: "op", op: c });
|
|
74
|
-
i++;
|
|
75
|
-
continue;
|
|
76
|
-
}
|
|
77
|
-
if (c === "(") {
|
|
78
|
-
out.push({ type: "lparen" });
|
|
79
|
-
i++;
|
|
80
|
-
continue;
|
|
81
|
-
}
|
|
82
|
-
if (c === ")") {
|
|
83
|
-
out.push({ type: "rparen" });
|
|
84
|
-
i++;
|
|
85
|
-
continue;
|
|
86
|
-
}
|
|
87
|
-
if (c === ",") {
|
|
88
|
-
out.push({ type: "comma" });
|
|
89
|
-
i++;
|
|
90
|
-
continue;
|
|
91
|
-
}
|
|
92
|
-
throw new Error(`unexpected character "${c}" at ${i}`);
|
|
93
|
-
}
|
|
94
|
-
return out;
|
|
95
|
-
}
|
|
96
|
-
function parseExpr(c) {
|
|
97
|
-
return parseAdd(c);
|
|
98
|
-
}
|
|
99
|
-
function parseAdd(c) {
|
|
100
|
-
let node = parseMul(c);
|
|
101
|
-
for (; ; ) {
|
|
102
|
-
const t = c.peek();
|
|
103
|
-
if (t?.type !== "op" || t.op !== "+" && t.op !== "-") break;
|
|
104
|
-
c.next();
|
|
105
|
-
node = { kind: "bin", op: t.op, left: node, right: parseMul(c) };
|
|
106
|
-
}
|
|
107
|
-
return node;
|
|
108
|
-
}
|
|
109
|
-
function parseMul(c) {
|
|
110
|
-
let node = parseUnary(c);
|
|
111
|
-
for (; ; ) {
|
|
112
|
-
const t = c.peek();
|
|
113
|
-
if (t?.type !== "op" || t.op !== "*" && t.op !== "/") break;
|
|
114
|
-
c.next();
|
|
115
|
-
node = { kind: "bin", op: t.op, left: node, right: parseUnary(c) };
|
|
116
|
-
}
|
|
117
|
-
return node;
|
|
118
|
-
}
|
|
119
|
-
function parseUnary(c) {
|
|
120
|
-
const t = c.peek();
|
|
121
|
-
if (t?.type === "op" && t.op === "-") {
|
|
122
|
-
c.next();
|
|
123
|
-
return { kind: "neg", child: parseUnary(c) };
|
|
124
|
-
}
|
|
125
|
-
if (t?.type === "op" && t.op === "+") {
|
|
126
|
-
c.next();
|
|
127
|
-
return parseUnary(c);
|
|
128
|
-
}
|
|
129
|
-
return parsePrimary(c);
|
|
130
|
-
}
|
|
131
|
-
function parsePrimary(c) {
|
|
132
|
-
const t = c.next();
|
|
133
|
-
if (!t) throw new Error("unexpected end of expression");
|
|
134
|
-
if (t.type === "num") return { kind: "num", value: t.value };
|
|
135
|
-
if (t.type === "lparen") {
|
|
136
|
-
const node = parseExpr(c);
|
|
137
|
-
const close = c.next();
|
|
138
|
-
if (close?.type !== "rparen") throw new Error('expected ")"');
|
|
139
|
-
return node;
|
|
140
|
-
}
|
|
141
|
-
if (t.type === "id") {
|
|
142
|
-
if (c.peek()?.type === "lparen") {
|
|
143
|
-
c.next();
|
|
144
|
-
const args = [];
|
|
145
|
-
if (c.peek()?.type !== "rparen") {
|
|
146
|
-
args.push(parseExpr(c));
|
|
147
|
-
while (c.peek()?.type === "comma") {
|
|
148
|
-
c.next();
|
|
149
|
-
args.push(parseExpr(c));
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
const close = c.next();
|
|
153
|
-
if (close?.type !== "rparen") throw new Error('expected ")"');
|
|
154
|
-
return { kind: "call", name: t.name, args };
|
|
155
|
-
}
|
|
156
|
-
return { kind: "var", name: t.name };
|
|
157
|
-
}
|
|
158
|
-
throw new Error(`unexpected token "${t.type}"`);
|
|
159
|
-
}
|
|
160
|
-
function parse(src) {
|
|
161
|
-
const c = new Cursor(tokenize(src));
|
|
162
|
-
const node = parseExpr(c);
|
|
163
|
-
if (c.peek()) throw new Error("trailing input");
|
|
164
|
-
return node;
|
|
165
|
-
}
|
|
166
|
-
function evalNode(node, scope) {
|
|
167
|
-
switch (node.kind) {
|
|
168
|
-
case "num":
|
|
169
|
-
return node.value;
|
|
170
|
-
case "var": {
|
|
171
|
-
const v = scope[node.name];
|
|
172
|
-
if (typeof v !== "number") throw new Error(`undefined variable: ${node.name}`);
|
|
173
|
-
return v;
|
|
174
|
-
}
|
|
175
|
-
case "neg":
|
|
176
|
-
return -evalNode(node.child, scope);
|
|
177
|
-
case "bin": {
|
|
178
|
-
const l = evalNode(node.left, scope);
|
|
179
|
-
const r = evalNode(node.right, scope);
|
|
180
|
-
switch (node.op) {
|
|
181
|
-
case "+":
|
|
182
|
-
return l + r;
|
|
183
|
-
case "-":
|
|
184
|
-
return l - r;
|
|
185
|
-
case "*":
|
|
186
|
-
return l * r;
|
|
187
|
-
case "/":
|
|
188
|
-
return l / r;
|
|
189
|
-
}
|
|
190
|
-
}
|
|
191
|
-
/* c8 ignore next 5 */
|
|
192
|
-
// eslint-disable-next-line no-fallthrough
|
|
193
|
-
case "call": {
|
|
194
|
-
const fn = FUNCS[node.name];
|
|
195
|
-
if (!fn) throw new Error(`unknown function: ${node.name}`);
|
|
196
|
-
return fn(...node.args.map((a) => evalNode(a, scope)));
|
|
197
|
-
}
|
|
198
|
-
}
|
|
199
|
-
}
|
|
200
|
-
function isExpr(v) {
|
|
201
|
-
return typeof v === "string" && v.length > 0;
|
|
202
|
-
}
|
|
203
|
-
function evaluateExpr(source, scope) {
|
|
204
|
-
let ast = astCache.get(source);
|
|
205
|
-
if (ast === void 0) {
|
|
206
|
-
try {
|
|
207
|
-
ast = parse(source);
|
|
208
|
-
} catch (e) {
|
|
209
|
-
console.warn(`pixi-effects: expression failed: ${source}`, e);
|
|
210
|
-
ast = ZERO_NODE;
|
|
211
|
-
}
|
|
212
|
-
astCache.set(source, ast);
|
|
213
|
-
}
|
|
214
|
-
try {
|
|
215
|
-
return evalNode(ast, scope);
|
|
216
|
-
} catch (e) {
|
|
217
|
-
console.warn(`pixi-effects: expression failed: ${source}`, e);
|
|
218
|
-
return 0;
|
|
219
|
-
}
|
|
220
|
-
}
|
|
221
|
-
var FUNCS, Cursor, ZERO_NODE, astCache;
|
|
222
|
-
var init_Parser = __esm({
|
|
223
|
-
"src/expr/Parser.ts"() {
|
|
224
|
-
FUNCS = {
|
|
225
|
-
min: Math.min,
|
|
226
|
-
max: Math.max,
|
|
227
|
-
abs: Math.abs,
|
|
228
|
-
floor: Math.floor,
|
|
229
|
-
ceil: Math.ceil,
|
|
230
|
-
round: Math.round,
|
|
231
|
-
sqrt: Math.sqrt,
|
|
232
|
-
pow: Math.pow,
|
|
233
|
-
sin: Math.sin,
|
|
234
|
-
cos: Math.cos,
|
|
235
|
-
tan: Math.tan
|
|
236
|
-
};
|
|
237
|
-
Cursor = class {
|
|
238
|
-
constructor(tokens) {
|
|
239
|
-
this.tokens = tokens;
|
|
240
|
-
}
|
|
241
|
-
tokens;
|
|
242
|
-
i = 0;
|
|
243
|
-
peek() {
|
|
244
|
-
return this.tokens[this.i];
|
|
245
|
-
}
|
|
246
|
-
next() {
|
|
247
|
-
return this.tokens[this.i++];
|
|
248
|
-
}
|
|
249
|
-
};
|
|
250
|
-
ZERO_NODE = { kind: "num", value: 0 };
|
|
251
|
-
astCache = /* @__PURE__ */ new Map();
|
|
252
|
-
}
|
|
253
|
-
});
|
|
254
|
-
|
|
255
|
-
// src/expr/normalizeProps.ts
|
|
256
|
-
function normalizeProps(input, scope, options = {}) {
|
|
257
|
-
const skip = new Set(options.skipKeys ?? []);
|
|
258
|
-
return walk(input, scope, skip, null);
|
|
259
|
-
}
|
|
260
|
-
function walk(value, scope, skip, currentKey) {
|
|
261
|
-
if (Array.isArray(value)) {
|
|
262
|
-
return value.map((v) => walk(v, scope, skip, currentKey));
|
|
263
|
-
}
|
|
264
|
-
if (value !== null && typeof value === "object") {
|
|
265
|
-
const out = {};
|
|
266
|
-
for (const k of Object.keys(value)) {
|
|
267
|
-
out[k] = walk(value[k], scope, skip, k);
|
|
268
|
-
}
|
|
269
|
-
return out;
|
|
270
|
-
}
|
|
271
|
-
if (isExpr(value) && !(currentKey !== null && skip.has(currentKey))) {
|
|
272
|
-
if (looksLikeColorString(value)) return value;
|
|
273
|
-
return evaluateExpr(value, scope);
|
|
274
|
-
}
|
|
275
|
-
return value;
|
|
276
|
-
}
|
|
277
|
-
function looksLikeColorString(s) {
|
|
278
|
-
if (s.length === 0) return false;
|
|
279
|
-
if (s.charCodeAt(0) === 35) return true;
|
|
280
|
-
return /^(rgb|hsl|oklab|oklch|lab|lch)\b/i.test(s);
|
|
281
|
-
}
|
|
282
|
-
var init_normalizeProps = __esm({
|
|
283
|
-
"src/expr/normalizeProps.ts"() {
|
|
284
|
-
init_Parser();
|
|
285
|
-
}
|
|
286
|
-
});
|
|
287
|
-
function resolveAt(at, duration) {
|
|
288
|
-
if (at === void 0 || at === null) return 0;
|
|
289
|
-
return at < 0 ? duration + at : at;
|
|
290
|
-
}
|
|
291
|
-
function normalizeKeyframe(kf, parentDuration) {
|
|
292
|
-
const at = resolveAt(kf.at, parentDuration);
|
|
293
|
-
const duration = kf.duration ?? 0;
|
|
294
|
-
const ease = kf.ease ?? "none";
|
|
295
|
-
const hasSet = !!kf.set;
|
|
296
|
-
const hasFrom = !!kf.from;
|
|
297
|
-
const hasTo = !!kf.to;
|
|
298
|
-
let kind;
|
|
299
|
-
if (hasSet) kind = "set";
|
|
300
|
-
else if (hasFrom && hasTo) kind = "fromTo";
|
|
301
|
-
else if (hasFrom) kind = "from";
|
|
302
|
-
else if (hasTo) kind = "to";
|
|
303
|
-
else kind = "to";
|
|
304
|
-
return { at, duration, ease, kind, set: kf.set, from: kf.from, to: kf.to };
|
|
305
|
-
}
|
|
306
|
-
function partitionProps(props) {
|
|
307
|
-
const ownProps = {};
|
|
308
|
-
const filterProps = {};
|
|
309
|
-
for (const k of Object.keys(props)) {
|
|
310
|
-
const m = k.match(/^filters\.([^.]+)\.(.+)$/);
|
|
311
|
-
if (m) {
|
|
312
|
-
const [, name, sub] = m;
|
|
313
|
-
(filterProps[name] ??= {})[sub] = props[k];
|
|
314
|
-
} else {
|
|
315
|
-
ownProps[k] = props[k];
|
|
316
|
-
}
|
|
317
|
-
}
|
|
318
|
-
return { ownProps, filterProps };
|
|
319
|
-
}
|
|
320
|
-
function findFilter(target, name) {
|
|
321
|
-
if (!target?.filters) return null;
|
|
322
|
-
for (const f of target.filters) {
|
|
323
|
-
if (f?._name === name) return f;
|
|
324
|
-
}
|
|
325
|
-
return null;
|
|
326
|
-
}
|
|
327
|
-
function pixiwrap(props) {
|
|
328
|
-
const out = {};
|
|
329
|
-
const pixi = {};
|
|
330
|
-
let usedPixi = false;
|
|
331
|
-
for (const [k, v] of Object.entries(props)) {
|
|
332
|
-
if (PIXI_SHORTHANDS.has(k)) {
|
|
333
|
-
pixi[k] = v;
|
|
334
|
-
usedPixi = true;
|
|
335
|
-
} else {
|
|
336
|
-
out[k] = v;
|
|
337
|
-
}
|
|
338
|
-
}
|
|
339
|
-
if (usedPixi) out.pixi = pixi;
|
|
340
|
-
return out;
|
|
341
|
-
}
|
|
342
|
-
function applyKeyframes(timeline, target, keyframes, parentDuration, scope, skipKeys = [], offset = 0) {
|
|
343
|
-
for (const raw of keyframes ?? []) {
|
|
344
|
-
const kf = normalizeKeyframe(raw, parentDuration);
|
|
345
|
-
const at = offset + kf.at;
|
|
346
|
-
if (kf.kind === "set") {
|
|
347
|
-
const resolved = normalizeProps(kf.set, scope, { skipKeys });
|
|
348
|
-
const { ownProps, filterProps } = partitionProps(resolved);
|
|
349
|
-
if (Object.keys(ownProps).length > 0) timeline.set(target, pixiwrap(ownProps), at);
|
|
350
|
-
for (const [name, props] of Object.entries(filterProps)) {
|
|
351
|
-
const f = findFilter(target, name);
|
|
352
|
-
if (!f) continue;
|
|
353
|
-
timeline.set(f, props, at);
|
|
354
|
-
}
|
|
355
|
-
} else if (kf.kind === "to") {
|
|
356
|
-
const resolved = normalizeProps(kf.to, scope, { skipKeys });
|
|
357
|
-
const { ownProps, filterProps } = partitionProps(resolved);
|
|
358
|
-
if (Object.keys(ownProps).length > 0)
|
|
359
|
-
timeline.to(target, { ...pixiwrap(ownProps), duration: kf.duration, ease: kf.ease }, at);
|
|
360
|
-
for (const [name, props] of Object.entries(filterProps)) {
|
|
361
|
-
const f = findFilter(target, name);
|
|
362
|
-
if (!f) continue;
|
|
363
|
-
timeline.to(f, { ...props, duration: kf.duration, ease: kf.ease }, at);
|
|
364
|
-
}
|
|
365
|
-
} else if (kf.kind === "from") {
|
|
366
|
-
const resolved = normalizeProps(kf.from, scope, { skipKeys });
|
|
367
|
-
const { ownProps, filterProps } = partitionProps(resolved);
|
|
368
|
-
if (Object.keys(ownProps).length > 0)
|
|
369
|
-
timeline.from(target, { ...pixiwrap(ownProps), duration: kf.duration, ease: kf.ease }, at);
|
|
370
|
-
for (const [name, props] of Object.entries(filterProps)) {
|
|
371
|
-
const f = findFilter(target, name);
|
|
372
|
-
if (!f) continue;
|
|
373
|
-
timeline.from(f, { ...props, duration: kf.duration, ease: kf.ease }, at);
|
|
374
|
-
}
|
|
375
|
-
} else {
|
|
376
|
-
const fromResolved = normalizeProps(kf.from, scope, { skipKeys });
|
|
377
|
-
const toResolved = normalizeProps(kf.to, scope, { skipKeys });
|
|
378
|
-
const fromSplit = partitionProps(fromResolved);
|
|
379
|
-
const toSplit = partitionProps(toResolved);
|
|
380
|
-
const ownKeys = /* @__PURE__ */ new Set([...Object.keys(fromSplit.ownProps), ...Object.keys(toSplit.ownProps)]);
|
|
381
|
-
if (ownKeys.size > 0) {
|
|
382
|
-
timeline.fromTo(
|
|
383
|
-
target,
|
|
384
|
-
{ ...pixiwrap(fromSplit.ownProps) },
|
|
385
|
-
{ ...pixiwrap(toSplit.ownProps), duration: kf.duration, ease: kf.ease },
|
|
386
|
-
at
|
|
387
|
-
);
|
|
388
|
-
}
|
|
389
|
-
const filterNames = /* @__PURE__ */ new Set([
|
|
390
|
-
...Object.keys(fromSplit.filterProps),
|
|
391
|
-
...Object.keys(toSplit.filterProps)
|
|
392
|
-
]);
|
|
393
|
-
for (const name of filterNames) {
|
|
394
|
-
const f = findFilter(target, name);
|
|
395
|
-
if (!f) continue;
|
|
396
|
-
timeline.fromTo(
|
|
397
|
-
f,
|
|
398
|
-
{ ...fromSplit.filterProps[name] ?? {} },
|
|
399
|
-
{ ...toSplit.filterProps[name] ?? {}, duration: kf.duration, ease: kf.ease },
|
|
400
|
-
at
|
|
401
|
-
);
|
|
402
|
-
}
|
|
403
|
-
}
|
|
404
|
-
}
|
|
405
|
-
}
|
|
406
|
-
function applyInitial(target, initial, scope, skipKeys = []) {
|
|
407
|
-
if (!initial) return;
|
|
408
|
-
const resolved = normalizeProps(initial, scope, { skipKeys });
|
|
409
|
-
const { ownProps, filterProps } = partitionProps(resolved);
|
|
410
|
-
if (Object.keys(ownProps).length > 0) gsap.gsap.set(target, pixiwrap(ownProps));
|
|
411
|
-
for (const [name, props] of Object.entries(filterProps)) {
|
|
412
|
-
const f = findFilter(target, name);
|
|
413
|
-
if (!f) continue;
|
|
414
|
-
gsap.gsap.set(f, props);
|
|
415
|
-
}
|
|
416
|
-
}
|
|
417
|
-
var PIXI_SHORTHANDS;
|
|
418
|
-
var init_Timeline = __esm({
|
|
419
|
-
"src/core/Timeline.ts"() {
|
|
420
|
-
init_normalizeProps();
|
|
421
|
-
PIXI_SHORTHANDS = /* @__PURE__ */ new Set([
|
|
422
|
-
"scale",
|
|
423
|
-
"scaleX",
|
|
424
|
-
"scaleY",
|
|
425
|
-
"anchor",
|
|
426
|
-
"anchorX",
|
|
427
|
-
"anchorY",
|
|
428
|
-
"pivot",
|
|
429
|
-
"pivotX",
|
|
430
|
-
"pivotY",
|
|
431
|
-
"skew",
|
|
432
|
-
"skewX",
|
|
433
|
-
"skewY",
|
|
434
|
-
"rotation",
|
|
435
|
-
"position",
|
|
436
|
-
"positionX",
|
|
437
|
-
"positionY",
|
|
438
|
-
"tilePosition",
|
|
439
|
-
"tilePositionX",
|
|
440
|
-
"tilePositionY",
|
|
441
|
-
"tileScale",
|
|
442
|
-
"tileScaleX",
|
|
443
|
-
"tileScaleY",
|
|
444
|
-
"tint",
|
|
445
|
-
"colorize",
|
|
446
|
-
"colorizeAmount",
|
|
447
|
-
"colorMatrixFilter",
|
|
448
|
-
"blur",
|
|
449
|
-
"blurX",
|
|
450
|
-
"blurY",
|
|
451
|
-
"blurPadding",
|
|
452
|
-
"autoAlpha",
|
|
453
|
-
"lineColor",
|
|
454
|
-
"lineAlpha",
|
|
455
|
-
"fillColor",
|
|
456
|
-
"fillAlpha"
|
|
457
|
-
]);
|
|
458
|
-
}
|
|
459
|
-
});
|
|
460
|
-
|
|
461
|
-
// src/expr/Scope.ts
|
|
462
|
-
function buildScope(sequence, parent, root) {
|
|
463
|
-
const w = sequence.intrinsicWidth || 0;
|
|
464
|
-
const h = sequence.intrinsicHeight || 0;
|
|
465
|
-
const W = parent?.width ?? root.width;
|
|
466
|
-
const H = parent?.height ?? root.height;
|
|
467
|
-
const GW = root.width;
|
|
468
|
-
const GH = root.height;
|
|
469
|
-
const contain = w === 0 || h === 0 ? 1 : Math.min(W / w, H / h);
|
|
470
|
-
const cover = w === 0 || h === 0 ? 1 : Math.max(W / w, H / h);
|
|
471
|
-
return {
|
|
472
|
-
w,
|
|
473
|
-
h,
|
|
474
|
-
W,
|
|
475
|
-
H,
|
|
476
|
-
GW,
|
|
477
|
-
GH,
|
|
478
|
-
contain,
|
|
479
|
-
cover,
|
|
480
|
-
t: sequence.at ?? 0,
|
|
481
|
-
d: sequence.duration ?? parent?.duration ?? root.duration,
|
|
482
|
-
T: parent?.duration ?? root.duration
|
|
483
|
-
};
|
|
484
|
-
}
|
|
485
|
-
var init_Scope = __esm({
|
|
486
|
-
"src/expr/Scope.ts"() {
|
|
487
|
-
}
|
|
488
|
-
});
|
|
489
|
-
function toRgb01(input) {
|
|
490
|
-
if (Array.isArray(input)) return [input[0], input[1], input[2]];
|
|
491
|
-
const c = new PIXI.Color(input);
|
|
492
|
-
return [c.red, c.green, c.blue];
|
|
493
|
-
}
|
|
494
|
-
var GL_FRAGMENT, WGSL_SOURCE, ChromaKeyFilter;
|
|
495
|
-
var init_ChromaKey = __esm({
|
|
496
|
-
"src/filters/ChromaKey.ts"() {
|
|
497
|
-
GL_FRAGMENT = `
|
|
498
|
-
in vec2 vTextureCoord;
|
|
499
|
-
uniform sampler2D uTexture;
|
|
500
|
-
uniform vec3 uKeyColor;
|
|
501
|
-
uniform float uThreshold;
|
|
502
|
-
uniform float uSmoothing;
|
|
503
|
-
uniform float uSpill;
|
|
504
|
-
|
|
505
|
-
out vec4 finalColor;
|
|
506
|
-
|
|
507
|
-
void main(void) {
|
|
508
|
-
vec4 raw = texture(uTexture, vTextureCoord);
|
|
509
|
-
|
|
510
|
-
// Pixi passes pre-multiplied alpha into filters. Un-premultiply so the
|
|
511
|
-
// chromakey distance is computed against the source color, not a darkened
|
|
512
|
-
// version that drifts away from the key as sprite.alpha drops.
|
|
513
|
-
vec3 rgb = raw.a > 0.0 ? raw.rgb / raw.a : vec3(0.0);
|
|
514
|
-
|
|
515
|
-
vec3 ycbcr = vec3(
|
|
516
|
-
0.299 * rgb.r + 0.587 * rgb.g + 0.114 * rgb.b,
|
|
517
|
-
-0.169 * rgb.r - 0.331 * rgb.g + 0.5 * rgb.b + 0.5,
|
|
518
|
-
0.5 * rgb.r - 0.419 * rgb.g - 0.081 * rgb.b + 0.5
|
|
519
|
-
);
|
|
520
|
-
vec3 keyYcbcr = vec3(
|
|
521
|
-
0.299 * uKeyColor.r + 0.587 * uKeyColor.g + 0.114 * uKeyColor.b,
|
|
522
|
-
-0.169 * uKeyColor.r - 0.331 * uKeyColor.g + 0.5 * uKeyColor.b + 0.5,
|
|
523
|
-
0.5 * uKeyColor.r - 0.419 * uKeyColor.g - 0.081 * uKeyColor.b + 0.5
|
|
524
|
-
);
|
|
525
|
-
|
|
526
|
-
float dist = length(ycbcr - keyYcbcr);
|
|
527
|
-
float alpha = smoothstep(uThreshold - uSmoothing, uThreshold + uSmoothing, dist);
|
|
528
|
-
|
|
529
|
-
vec3 despilled = rgb;
|
|
530
|
-
if (uSpill > 0.0) {
|
|
531
|
-
float spillAmount = max(0.0, rgb.g - max(rgb.r, rgb.b));
|
|
532
|
-
despilled.g -= spillAmount * uSpill;
|
|
533
|
-
}
|
|
534
|
-
|
|
535
|
-
float outA = raw.a * alpha;
|
|
536
|
-
finalColor = vec4(despilled * outA, outA);
|
|
537
|
-
}
|
|
538
|
-
`;
|
|
539
|
-
WGSL_SOURCE = `
|
|
540
|
-
struct GlobalFilterUniforms {
|
|
541
|
-
uInputSize: vec4<f32>,
|
|
542
|
-
uInputPixel: vec4<f32>,
|
|
543
|
-
uInputClamp: vec4<f32>,
|
|
544
|
-
uOutputFrame: vec4<f32>,
|
|
545
|
-
uGlobalFrame: vec4<f32>,
|
|
546
|
-
uOutputTexture: vec4<f32>,
|
|
547
|
-
};
|
|
548
|
-
|
|
549
|
-
struct ChromaUniforms {
|
|
550
|
-
uKeyColor: vec3<f32>,
|
|
551
|
-
uThreshold: f32,
|
|
552
|
-
uSmoothing: f32,
|
|
553
|
-
uSpill: f32,
|
|
554
|
-
};
|
|
555
|
-
|
|
556
|
-
@group(0) @binding(0) var<uniform> gfu: GlobalFilterUniforms;
|
|
557
|
-
@group(0) @binding(1) var uTexture: texture_2d<f32>;
|
|
558
|
-
@group(0) @binding(2) var uSampler : sampler;
|
|
559
|
-
@group(1) @binding(0) var<uniform> chromaUniforms : ChromaUniforms;
|
|
560
|
-
|
|
561
|
-
struct VSOutput {
|
|
562
|
-
@builtin(position) position: vec4<f32>,
|
|
563
|
-
@location(0) uv : vec2<f32>,
|
|
564
|
-
};
|
|
565
|
-
|
|
566
|
-
fn filterVertexPosition(aPosition : vec2<f32>) -> vec4<f32> {
|
|
567
|
-
var position = aPosition * gfu.uOutputFrame.zw + gfu.uOutputFrame.xy;
|
|
568
|
-
position.x = position.x * (2.0 / gfu.uOutputTexture.x) - 1.0;
|
|
569
|
-
position.y = position.y * (2.0 * gfu.uOutputTexture.z / gfu.uOutputTexture.y) - gfu.uOutputTexture.z;
|
|
570
|
-
return vec4<f32>(position, 0.0, 1.0);
|
|
571
|
-
}
|
|
572
|
-
|
|
573
|
-
fn filterTextureCoord(aPosition : vec2<f32>) -> vec2<f32> {
|
|
574
|
-
return aPosition * (gfu.uOutputFrame.zw * gfu.uInputSize.zw);
|
|
575
|
-
}
|
|
576
|
-
|
|
577
|
-
// NOTE on whitespace: PIXI v8's WGSL attribute extractor is regex-based and
|
|
578
|
-
// expects the noise/blur reference format \u2014 multi-line param list + space
|
|
579
|
-
// before the colon. A compact one-liner with no space before the colon parses
|
|
580
|
-
// as having NO vertex attributes, which then causes a "Vertex attribute slot 0
|
|
581
|
-
// not present in the VertexState" pipeline validation error.
|
|
582
|
-
@vertex
|
|
583
|
-
fn mainVertex(
|
|
584
|
-
@location(0) aPosition : vec2<f32>,
|
|
585
|
-
) -> VSOutput {
|
|
586
|
-
return VSOutput(
|
|
587
|
-
filterVertexPosition(aPosition),
|
|
588
|
-
filterTextureCoord(aPosition),
|
|
589
|
-
);
|
|
590
|
-
}
|
|
591
|
-
|
|
592
|
-
@fragment
|
|
593
|
-
fn mainFragment(@location(0) uv : vec2<f32>) -> @location(0) vec4<f32> {
|
|
594
|
-
let raw = textureSample(uTexture, uSampler, uv);
|
|
595
|
-
|
|
596
|
-
// Un-premultiply (see comment in the GLSL version above).
|
|
597
|
-
var rgb: vec3<f32>;
|
|
598
|
-
if (raw.a > 0.0) {
|
|
599
|
-
rgb = raw.rgb / raw.a;
|
|
600
|
-
} else {
|
|
601
|
-
rgb = vec3<f32>(0.0);
|
|
602
|
-
}
|
|
603
|
-
|
|
604
|
-
let key = chromaUniforms.uKeyColor;
|
|
605
|
-
|
|
606
|
-
let ycbcr = vec3<f32>(
|
|
607
|
-
0.299 * rgb.r + 0.587 * rgb.g + 0.114 * rgb.b,
|
|
608
|
-
-0.169 * rgb.r - 0.331 * rgb.g + 0.5 * rgb.b + 0.5,
|
|
609
|
-
0.5 * rgb.r - 0.419 * rgb.g - 0.081 * rgb.b + 0.5,
|
|
610
|
-
);
|
|
611
|
-
let keyYcbcr = vec3<f32>(
|
|
612
|
-
0.299 * key.r + 0.587 * key.g + 0.114 * key.b,
|
|
613
|
-
-0.169 * key.r - 0.331 * key.g + 0.5 * key.b + 0.5,
|
|
614
|
-
0.5 * key.r - 0.419 * key.g - 0.081 * key.b + 0.5,
|
|
615
|
-
);
|
|
616
|
-
|
|
617
|
-
let dist = length(ycbcr - keyYcbcr);
|
|
618
|
-
let alpha = smoothstep(
|
|
619
|
-
chromaUniforms.uThreshold - chromaUniforms.uSmoothing,
|
|
620
|
-
chromaUniforms.uThreshold + chromaUniforms.uSmoothing,
|
|
621
|
-
dist,
|
|
622
|
-
);
|
|
623
|
-
|
|
624
|
-
var despilled = rgb;
|
|
625
|
-
if (chromaUniforms.uSpill > 0.0) {
|
|
626
|
-
let spillAmount = max(0.0, rgb.g - max(rgb.r, rgb.b));
|
|
627
|
-
despilled.g = despilled.g - spillAmount * chromaUniforms.uSpill;
|
|
628
|
-
}
|
|
629
|
-
|
|
630
|
-
let outA = raw.a * alpha;
|
|
631
|
-
return vec4<f32>(despilled * outA, outA);
|
|
632
|
-
}
|
|
633
|
-
`;
|
|
634
|
-
ChromaKeyFilter = class extends PIXI.Filter {
|
|
635
|
-
constructor(options = {}) {
|
|
636
|
-
const {
|
|
637
|
-
keyColor = "#00ff00",
|
|
638
|
-
threshold = 0.4,
|
|
639
|
-
smoothing = 0.1,
|
|
640
|
-
spill = 0.2
|
|
641
|
-
} = options;
|
|
642
|
-
const glProgram = PIXI.GlProgram.from({
|
|
643
|
-
vertex: PIXI.defaultFilterVert,
|
|
644
|
-
fragment: GL_FRAGMENT,
|
|
645
|
-
name: "chroma-key-filter"
|
|
646
|
-
});
|
|
647
|
-
const gpuProgram = PIXI.GpuProgram.from({
|
|
648
|
-
vertex: { source: WGSL_SOURCE, entryPoint: "mainVertex" },
|
|
649
|
-
fragment: { source: WGSL_SOURCE, entryPoint: "mainFragment" }
|
|
650
|
-
});
|
|
651
|
-
super({
|
|
652
|
-
glProgram,
|
|
653
|
-
gpuProgram,
|
|
654
|
-
resources: {
|
|
655
|
-
chromaUniforms: new PIXI.UniformGroup({
|
|
656
|
-
uKeyColor: { value: toRgb01(keyColor), type: "vec3<f32>" },
|
|
657
|
-
uThreshold: { value: threshold, type: "f32" },
|
|
658
|
-
uSmoothing: { value: smoothing, type: "f32" },
|
|
659
|
-
uSpill: { value: spill, type: "f32" }
|
|
660
|
-
})
|
|
661
|
-
}
|
|
662
|
-
});
|
|
663
|
-
}
|
|
664
|
-
get threshold() {
|
|
665
|
-
return this.resources.chromaUniforms.uniforms.uThreshold;
|
|
666
|
-
}
|
|
667
|
-
set threshold(v) {
|
|
668
|
-
this.resources.chromaUniforms.uniforms.uThreshold = v;
|
|
669
|
-
}
|
|
670
|
-
get smoothing() {
|
|
671
|
-
return this.resources.chromaUniforms.uniforms.uSmoothing;
|
|
672
|
-
}
|
|
673
|
-
set smoothing(v) {
|
|
674
|
-
this.resources.chromaUniforms.uniforms.uSmoothing = v;
|
|
675
|
-
}
|
|
676
|
-
get spill() {
|
|
677
|
-
return this.resources.chromaUniforms.uniforms.uSpill;
|
|
678
|
-
}
|
|
679
|
-
set spill(v) {
|
|
680
|
-
this.resources.chromaUniforms.uniforms.uSpill = v;
|
|
681
|
-
}
|
|
682
|
-
setKeyColor(input) {
|
|
683
|
-
const rgb = toRgb01(input);
|
|
684
|
-
const u = this.resources.chromaUniforms.uniforms.uKeyColor;
|
|
685
|
-
u[0] = rgb[0];
|
|
686
|
-
u[1] = rgb[1];
|
|
687
|
-
u[2] = rgb[2];
|
|
688
|
-
}
|
|
689
|
-
};
|
|
690
|
-
}
|
|
691
|
-
});
|
|
692
|
-
|
|
693
|
-
// src/filters/index.ts
|
|
694
|
-
function looksLikePixiFilter(x) {
|
|
695
|
-
return !!x && typeof x === "object" && typeof x.apply === "function";
|
|
696
|
-
}
|
|
697
|
-
function createFilter(spec) {
|
|
698
|
-
if (spec.type === "custom") {
|
|
699
|
-
const inst2 = spec.filter;
|
|
700
|
-
if (!looksLikePixiFilter(inst2)) {
|
|
701
|
-
throw new Error('pixi-effects: { type: "custom" } requires a `filter` that is a PIXI Filter instance (duck-typed: must have an `apply` method).');
|
|
702
|
-
}
|
|
703
|
-
const named = inst2;
|
|
704
|
-
named._name = spec.name;
|
|
705
|
-
return named;
|
|
706
|
-
}
|
|
707
|
-
const Cls = registry[spec.type];
|
|
708
|
-
if (!Cls) throw new Error(`pixi-effects: unknown filter type "${spec.type}"`);
|
|
709
|
-
const { type: _t, name, ...params } = spec;
|
|
710
|
-
const inst = new Cls(params);
|
|
711
|
-
inst._name = name;
|
|
712
|
-
return inst;
|
|
713
|
-
}
|
|
714
|
-
var registry;
|
|
715
|
-
var init_filters = __esm({
|
|
716
|
-
"src/filters/index.ts"() {
|
|
717
|
-
init_ChromaKey();
|
|
718
|
-
registry = {
|
|
719
|
-
chromaKey: ChromaKeyFilter
|
|
720
|
-
};
|
|
721
|
-
}
|
|
722
|
-
});
|
|
723
|
-
var Sequence;
|
|
724
|
-
var init_Base = __esm({
|
|
725
|
-
"src/sequences/Base.ts"() {
|
|
726
|
-
init_Timeline();
|
|
727
|
-
init_Scope();
|
|
728
|
-
init_filters();
|
|
729
|
-
Sequence = class {
|
|
730
|
-
spec;
|
|
731
|
-
parent;
|
|
732
|
-
root;
|
|
733
|
-
target = null;
|
|
734
|
-
intrinsicWidth = 0;
|
|
735
|
-
intrinsicHeight = 0;
|
|
736
|
-
at;
|
|
737
|
-
duration;
|
|
738
|
-
filters = [];
|
|
739
|
-
/**
|
|
740
|
-
* Mask sequence built from `spec.mask`, if any. Built and added to the
|
|
741
|
-
* scene graph by the parent CompositionSequence; rendered as a mask
|
|
742
|
-
* (not a normal child) by PIXI when `target.mask = maskSequence.target`.
|
|
743
|
-
*/
|
|
744
|
-
maskSequence = null;
|
|
745
|
-
constructor(spec, parent, root) {
|
|
746
|
-
this.spec = spec;
|
|
747
|
-
this.parent = parent;
|
|
748
|
-
this.root = root;
|
|
749
|
-
this.at = spec.at ?? 0;
|
|
750
|
-
this.duration = spec.duration;
|
|
751
|
-
}
|
|
752
|
-
buildFilters() {
|
|
753
|
-
const specs = this.spec.filters ?? [];
|
|
754
|
-
this.filters = specs.map(createFilter);
|
|
755
|
-
if (this.target && "filters" in this.target) {
|
|
756
|
-
this.target.filters = this.filters;
|
|
757
|
-
}
|
|
758
|
-
const fa = this.spec.filterArea;
|
|
759
|
-
if (fa && this.target) {
|
|
760
|
-
this.target.filterArea = new PIXI.Rectangle(fa.x, fa.y, fa.width, fa.height);
|
|
761
|
-
}
|
|
762
|
-
}
|
|
763
|
-
scope() {
|
|
764
|
-
return buildScope(this, this.parent, this.root);
|
|
765
|
-
}
|
|
766
|
-
bindTimeline(timeline, offset = 0) {
|
|
767
|
-
if (!this.target) return;
|
|
768
|
-
const scope = this.scope();
|
|
769
|
-
applyInitial(this.target, this.spec.initial, scope);
|
|
770
|
-
applyKeyframes(timeline, this.target, this.spec.keyframes, this.duration, scope, [], offset);
|
|
771
|
-
const startTime = offset + this.at;
|
|
772
|
-
const endTime = startTime + this.duration;
|
|
773
|
-
this.target.renderable = startTime <= 0;
|
|
774
|
-
timeline.set(this.target, { renderable: true }, startTime);
|
|
775
|
-
timeline.set(this.target, { renderable: false }, endTime);
|
|
776
|
-
}
|
|
777
|
-
collectAudio(_out, _baseTime) {
|
|
778
|
-
}
|
|
779
|
-
destroy() {
|
|
780
|
-
this.maskSequence?.destroy();
|
|
781
|
-
this.maskSequence = null;
|
|
782
|
-
this.target?.destroy?.();
|
|
783
|
-
this.target = null;
|
|
784
|
-
}
|
|
785
|
-
};
|
|
786
|
-
}
|
|
787
|
-
});
|
|
788
|
-
|
|
789
|
-
// src/expr/colorInterp.ts
|
|
790
|
-
function buildColorInterp(from, to, space) {
|
|
791
|
-
const fromRGBA = parseToRGBA(from);
|
|
792
|
-
const toRGBA = parseToRGBA(to);
|
|
793
|
-
const fromOk = rgbToOklab(fromRGBA);
|
|
794
|
-
const toOk = rgbToOklab(toRGBA);
|
|
795
|
-
if (space === "oklab") {
|
|
796
|
-
return (t) => {
|
|
797
|
-
const L = lerp(fromOk.L, toOk.L, t);
|
|
798
|
-
const a = lerp(fromOk.a, toOk.a, t);
|
|
799
|
-
const b = lerp(fromOk.b, toOk.b, t);
|
|
800
|
-
const alpha = lerp(fromRGBA.a, toRGBA.a, t);
|
|
801
|
-
return rgbaString(oklabToRgb({ L, a, b }), alpha);
|
|
802
|
-
};
|
|
803
|
-
}
|
|
804
|
-
const fromLCH = oklabToOklch(fromOk);
|
|
805
|
-
const toLCH = oklabToOklch(toOk);
|
|
806
|
-
if (fromLCH.C < 1e-4) fromLCH.h = toLCH.h;
|
|
807
|
-
if (toLCH.C < 1e-4) toLCH.h = fromLCH.h;
|
|
808
|
-
const hueDelta = shortestHueDelta(fromLCH.h, toLCH.h);
|
|
809
|
-
return (t) => {
|
|
810
|
-
const L = lerp(fromLCH.L, toLCH.L, t);
|
|
811
|
-
const C = lerp(fromLCH.C, toLCH.C, t);
|
|
812
|
-
const h = fromLCH.h + hueDelta * t;
|
|
813
|
-
const alpha = lerp(fromRGBA.a, toRGBA.a, t);
|
|
814
|
-
const ab = oklchToOklab({ L, C, h });
|
|
815
|
-
return rgbaString(oklabToRgb(ab), alpha);
|
|
816
|
-
};
|
|
817
|
-
}
|
|
818
|
-
function parseToRGBA(input) {
|
|
819
|
-
if (typeof input === "number") {
|
|
820
|
-
return { r: (input >> 16 & 255) / 255, g: (input >> 8 & 255) / 255, b: (input & 255) / 255, a: 1 };
|
|
821
|
-
}
|
|
822
|
-
const s = input.trim();
|
|
823
|
-
if (s.startsWith("#")) return parseHex(s);
|
|
824
|
-
if (s.startsWith("rgb")) return parseRgbFunc(s);
|
|
825
|
-
return { r: 0, g: 0, b: 0, a: 1 };
|
|
826
|
-
}
|
|
827
|
-
function parseHex(s) {
|
|
828
|
-
const hex = s.slice(1);
|
|
829
|
-
let r = 0, g = 0, b = 0, a = 1;
|
|
830
|
-
if (hex.length === 3 || hex.length === 4) {
|
|
831
|
-
r = parseInt(hex[0] + hex[0], 16) / 255;
|
|
832
|
-
g = parseInt(hex[1] + hex[1], 16) / 255;
|
|
833
|
-
b = parseInt(hex[2] + hex[2], 16) / 255;
|
|
834
|
-
if (hex.length === 4) a = parseInt(hex[3] + hex[3], 16) / 255;
|
|
835
|
-
} else if (hex.length === 6 || hex.length === 8) {
|
|
836
|
-
r = parseInt(hex.slice(0, 2), 16) / 255;
|
|
837
|
-
g = parseInt(hex.slice(2, 4), 16) / 255;
|
|
838
|
-
b = parseInt(hex.slice(4, 6), 16) / 255;
|
|
839
|
-
if (hex.length === 8) a = parseInt(hex.slice(6, 8), 16) / 255;
|
|
840
|
-
}
|
|
841
|
-
return { r, g, b, a };
|
|
842
|
-
}
|
|
843
|
-
function parseRgbFunc(s) {
|
|
844
|
-
const inner = s.replace(/^rgba?\(/, "").replace(/\)$/, "");
|
|
845
|
-
const parts = inner.split(/[,\s/]+/).filter(Boolean).map(parseFloat);
|
|
846
|
-
return {
|
|
847
|
-
r: (parts[0] ?? 0) / 255,
|
|
848
|
-
g: (parts[1] ?? 0) / 255,
|
|
849
|
-
b: (parts[2] ?? 0) / 255,
|
|
850
|
-
a: parts.length > 3 ? parts[3] ?? 1 : 1
|
|
851
|
-
};
|
|
852
|
-
}
|
|
853
|
-
function rgbaString(rgb, alpha) {
|
|
854
|
-
const r = Math.round(clamp01(rgb.r) * 255);
|
|
855
|
-
const g = Math.round(clamp01(rgb.g) * 255);
|
|
856
|
-
const b = Math.round(clamp01(rgb.b) * 255);
|
|
857
|
-
return `rgba(${r},${g},${b},${alpha})`;
|
|
858
|
-
}
|
|
859
|
-
function srgbToLinear(c) {
|
|
860
|
-
return c <= 0.04045 ? c / 12.92 : Math.pow((c + 0.055) / 1.055, 2.4);
|
|
861
|
-
}
|
|
862
|
-
function linearToSrgb(c) {
|
|
863
|
-
return c <= 31308e-7 ? 12.92 * c : 1.055 * Math.pow(c, 1 / 2.4) - 0.055;
|
|
864
|
-
}
|
|
865
|
-
function rgbToOklab(rgba) {
|
|
866
|
-
const r = srgbToLinear(rgba.r);
|
|
867
|
-
const g = srgbToLinear(rgba.g);
|
|
868
|
-
const b = srgbToLinear(rgba.b);
|
|
869
|
-
const l = 0.4122214708 * r + 0.5363325363 * g + 0.0514459929 * b;
|
|
870
|
-
const m = 0.2119034982 * r + 0.6806995451 * g + 0.1073969566 * b;
|
|
871
|
-
const s = 0.0883024619 * r + 0.2817188376 * g + 0.6299787005 * b;
|
|
872
|
-
const l_ = Math.cbrt(l);
|
|
873
|
-
const m_ = Math.cbrt(m);
|
|
874
|
-
const s_ = Math.cbrt(s);
|
|
875
|
-
return {
|
|
876
|
-
L: 0.2104542553 * l_ + 0.793617785 * m_ - 0.0040720468 * s_,
|
|
877
|
-
a: 1.9779984951 * l_ - 2.428592205 * m_ + 0.4505937099 * s_,
|
|
878
|
-
b: 0.0259040371 * l_ + 0.7827717662 * m_ - 0.808675766 * s_
|
|
879
|
-
};
|
|
880
|
-
}
|
|
881
|
-
function oklabToRgb(lab) {
|
|
882
|
-
const l_ = lab.L + 0.3963377774 * lab.a + 0.2158037573 * lab.b;
|
|
883
|
-
const m_ = lab.L - 0.1055613458 * lab.a - 0.0638541728 * lab.b;
|
|
884
|
-
const s_ = lab.L - 0.0894841775 * lab.a - 1.291485548 * lab.b;
|
|
885
|
-
const l = l_ * l_ * l_;
|
|
886
|
-
const m = m_ * m_ * m_;
|
|
887
|
-
const s = s_ * s_ * s_;
|
|
888
|
-
return {
|
|
889
|
-
r: linearToSrgb(4.0767416621 * l - 3.3077115913 * m + 0.2309699292 * s),
|
|
890
|
-
g: linearToSrgb(-1.2684380046 * l + 2.6097574011 * m - 0.3413193965 * s),
|
|
891
|
-
b: linearToSrgb(-0.0041960863 * l - 0.7034186147 * m + 1.707614701 * s)
|
|
892
|
-
};
|
|
893
|
-
}
|
|
894
|
-
function oklabToOklch(lab) {
|
|
895
|
-
return { L: lab.L, C: Math.hypot(lab.a, lab.b), h: Math.atan2(lab.b, lab.a) };
|
|
896
|
-
}
|
|
897
|
-
function oklchToOklab(lch) {
|
|
898
|
-
return { L: lch.L, a: lch.C * Math.cos(lch.h), b: lch.C * Math.sin(lch.h) };
|
|
899
|
-
}
|
|
900
|
-
function lerp(a, b, t) {
|
|
901
|
-
return a + (b - a) * t;
|
|
902
|
-
}
|
|
903
|
-
function clamp01(v) {
|
|
904
|
-
return v < 0 ? 0 : v > 1 ? 1 : v;
|
|
905
|
-
}
|
|
906
|
-
function shortestHueDelta(from, to) {
|
|
907
|
-
const TWO_PI = Math.PI * 2;
|
|
908
|
-
let d = (to - from) % TWO_PI;
|
|
909
|
-
if (d > Math.PI) d -= TWO_PI;
|
|
910
|
-
if (d < -Math.PI) d += TWO_PI;
|
|
911
|
-
return d;
|
|
912
|
-
}
|
|
913
|
-
var init_colorInterp = __esm({
|
|
914
|
-
"src/expr/colorInterp.ts"() {
|
|
915
|
-
}
|
|
916
|
-
});
|
|
917
|
-
function tweenColor(timeline, target, key, fromValue, toValue, duration, ease, at, colorSpace, onUpdate) {
|
|
918
|
-
let interp = null;
|
|
919
|
-
const proxy = { p: 0 };
|
|
920
|
-
timeline.fromTo(
|
|
921
|
-
proxy,
|
|
922
|
-
{ p: 0 },
|
|
923
|
-
{
|
|
924
|
-
p: 1,
|
|
925
|
-
duration,
|
|
926
|
-
ease,
|
|
927
|
-
onStart: () => {
|
|
928
|
-
const start = fromValue !== void 0 ? fromValue : target[key];
|
|
929
|
-
if (start === void 0) return;
|
|
930
|
-
interp = colorSpace === "rgb" ? gsap.gsap.utils.interpolate(start, toValue) : buildColorInterp(start, toValue, colorSpace);
|
|
931
|
-
},
|
|
932
|
-
onUpdate: () => {
|
|
933
|
-
if (interp) {
|
|
934
|
-
target[key] = interp(proxy.p);
|
|
935
|
-
if (onUpdate) onUpdate();
|
|
936
|
-
}
|
|
937
|
-
}
|
|
938
|
-
},
|
|
939
|
-
at
|
|
940
|
-
);
|
|
941
|
-
}
|
|
942
|
-
var init_colorTween = __esm({
|
|
943
|
-
"src/expr/colorTween.ts"() {
|
|
944
|
-
init_colorInterp();
|
|
945
|
-
}
|
|
946
|
-
});
|
|
947
|
-
function stripKeys(props, keys) {
|
|
948
|
-
if (!props) return props;
|
|
949
|
-
const drop = new Set(keys);
|
|
950
|
-
const out = {};
|
|
951
|
-
let changed = false;
|
|
952
|
-
for (const k of Object.keys(props)) {
|
|
953
|
-
if (drop.has(k)) {
|
|
954
|
-
changed = true;
|
|
955
|
-
continue;
|
|
956
|
-
}
|
|
957
|
-
out[k] = props[k];
|
|
958
|
-
}
|
|
959
|
-
return changed ? out : props;
|
|
960
|
-
}
|
|
961
|
-
function stripKeysInKeyframe(kf, keys) {
|
|
962
|
-
return {
|
|
963
|
-
...kf,
|
|
964
|
-
set: stripKeys(kf.set, keys),
|
|
965
|
-
to: stripKeys(kf.to, keys),
|
|
966
|
-
from: stripKeys(kf.from, keys)
|
|
967
|
-
};
|
|
968
|
-
}
|
|
969
|
-
var ImageSequence;
|
|
970
|
-
var init_Image = __esm({
|
|
971
|
-
"src/sequences/Image.ts"() {
|
|
972
|
-
init_Base();
|
|
973
|
-
init_Timeline();
|
|
974
|
-
init_colorTween();
|
|
975
|
-
ImageSequence = class extends Sequence {
|
|
976
|
-
async build() {
|
|
977
|
-
const texture = await PIXI.Assets.get(this.spec.asset);
|
|
978
|
-
const sprite = new PIXI.Sprite({ texture, label: this.spec.name });
|
|
979
|
-
sprite.cullable = true;
|
|
980
|
-
this.target = sprite;
|
|
981
|
-
this.intrinsicWidth = texture.width;
|
|
982
|
-
this.intrinsicHeight = texture.height;
|
|
983
|
-
if (this.duration === void 0) {
|
|
984
|
-
this.duration = this.parent?.duration ?? this.root.duration;
|
|
985
|
-
}
|
|
986
|
-
this.buildFilters();
|
|
987
|
-
}
|
|
988
|
-
bindTimeline(timeline, offset = 0) {
|
|
989
|
-
if (!this.target) return;
|
|
990
|
-
const colorSpace = this.spec.colorSpace;
|
|
991
|
-
if (!colorSpace || colorSpace === "rgb") {
|
|
992
|
-
super.bindTimeline(timeline, offset);
|
|
993
|
-
return;
|
|
994
|
-
}
|
|
995
|
-
const scope = this.scope();
|
|
996
|
-
const initial = stripKeys(this.spec.initial, ["tint"]);
|
|
997
|
-
const keyframes = this.spec.keyframes ? this.spec.keyframes.map((kf) => stripKeysInKeyframe(kf, ["tint"])) : void 0;
|
|
998
|
-
applyInitial(this.target, initial, scope);
|
|
999
|
-
applyKeyframes(timeline, this.target, keyframes, this.duration, scope, [], offset);
|
|
1000
|
-
const initTint = this.spec.initial?.tint;
|
|
1001
|
-
if (initTint !== void 0) this.target.tint = initTint;
|
|
1002
|
-
for (const kf of this.spec.keyframes ?? []) {
|
|
1003
|
-
const at = offset + resolveAt(kf.at, this.duration);
|
|
1004
|
-
const duration = kf.duration ?? 0;
|
|
1005
|
-
const ease = kf.ease ?? "none";
|
|
1006
|
-
const fromTint = kf.from?.tint;
|
|
1007
|
-
const toTint = kf.to?.tint;
|
|
1008
|
-
const setTint = kf.set?.tint;
|
|
1009
|
-
if (setTint !== void 0) {
|
|
1010
|
-
timeline.call(() => {
|
|
1011
|
-
this.target.tint = setTint;
|
|
1012
|
-
}, [], at);
|
|
1013
|
-
}
|
|
1014
|
-
if (toTint !== void 0) {
|
|
1015
|
-
tweenColor(
|
|
1016
|
-
timeline,
|
|
1017
|
-
this.target,
|
|
1018
|
-
"tint",
|
|
1019
|
-
fromTint,
|
|
1020
|
-
toTint,
|
|
1021
|
-
duration,
|
|
1022
|
-
ease,
|
|
1023
|
-
at,
|
|
1024
|
-
colorSpace
|
|
1025
|
-
);
|
|
1026
|
-
}
|
|
1027
|
-
}
|
|
1028
|
-
const startTime = offset + this.at;
|
|
1029
|
-
const endTime = startTime + this.duration;
|
|
1030
|
-
this.target.renderable = startTime <= 0;
|
|
1031
|
-
timeline.set(this.target, { renderable: true }, startTime);
|
|
1032
|
-
timeline.set(this.target, { renderable: false }, endTime);
|
|
1033
|
-
}
|
|
1034
|
-
};
|
|
1035
|
-
}
|
|
1036
|
-
});
|
|
1037
|
-
function runSuperWithStrippedSpec(inst, strippedSpec, timeline, offset) {
|
|
1038
|
-
const originalSpec = inst.spec;
|
|
1039
|
-
inst.spec = strippedSpec;
|
|
1040
|
-
try {
|
|
1041
|
-
Sequence.prototype.bindTimeline.call(inst, timeline, offset);
|
|
1042
|
-
} finally {
|
|
1043
|
-
inst.spec = originalSpec;
|
|
1044
|
-
}
|
|
1045
|
-
}
|
|
1046
|
-
function stripFill(spec) {
|
|
1047
|
-
return {
|
|
1048
|
-
...spec,
|
|
1049
|
-
initial: stripKey(spec.initial, "fill"),
|
|
1050
|
-
keyframes: spec.keyframes ? spec.keyframes.map((kf) => ({
|
|
1051
|
-
...kf,
|
|
1052
|
-
set: stripKey(kf.set, "fill"),
|
|
1053
|
-
to: stripKey(kf.to, "fill"),
|
|
1054
|
-
from: stripKey(kf.from, "fill")
|
|
1055
|
-
})) : void 0
|
|
1056
|
-
};
|
|
1057
|
-
}
|
|
1058
|
-
function stripKey(props, key) {
|
|
1059
|
-
if (!props) return props;
|
|
1060
|
-
if (!(key in props)) return props;
|
|
1061
|
-
const out = {};
|
|
1062
|
-
for (const k of Object.keys(props)) if (k !== key) out[k] = props[k];
|
|
1063
|
-
return out;
|
|
1064
|
-
}
|
|
1065
|
-
function bindFillKeyframes(timeline, text, keyframes, parentDuration, offset, colorSpace) {
|
|
1066
|
-
const state = { fill: text.style.fill };
|
|
1067
|
-
const writeFill = () => {
|
|
1068
|
-
text.style.fill = state.fill;
|
|
1069
|
-
};
|
|
1070
|
-
for (const kf of keyframes) {
|
|
1071
|
-
const at = offset + resolveAt(kf.at, parentDuration);
|
|
1072
|
-
const duration = kf.duration ?? 0;
|
|
1073
|
-
const ease = kf.ease ?? "none";
|
|
1074
|
-
const setFill = pickFill(kf.set);
|
|
1075
|
-
const fromFill = pickFill(kf.from);
|
|
1076
|
-
const toFill = pickFill(kf.to);
|
|
1077
|
-
if (setFill !== void 0) {
|
|
1078
|
-
timeline.call(() => {
|
|
1079
|
-
state.fill = setFill;
|
|
1080
|
-
writeFill();
|
|
1081
|
-
}, [], at);
|
|
1082
|
-
}
|
|
1083
|
-
if (toFill !== void 0) {
|
|
1084
|
-
tweenColor(
|
|
1085
|
-
timeline,
|
|
1086
|
-
state,
|
|
1087
|
-
"fill",
|
|
1088
|
-
fromFill,
|
|
1089
|
-
toFill,
|
|
1090
|
-
duration,
|
|
1091
|
-
ease,
|
|
1092
|
-
at,
|
|
1093
|
-
colorSpace,
|
|
1094
|
-
writeFill
|
|
1095
|
-
);
|
|
1096
|
-
}
|
|
1097
|
-
}
|
|
1098
|
-
}
|
|
1099
|
-
function pickFill(props) {
|
|
1100
|
-
if (!props) return void 0;
|
|
1101
|
-
const v = props.fill;
|
|
1102
|
-
if (typeof v === "string" || typeof v === "number") return v;
|
|
1103
|
-
return void 0;
|
|
1104
|
-
}
|
|
1105
|
-
var STYLE_OPAQUE_KEYS, TextSequence;
|
|
1106
|
-
var init_Text = __esm({
|
|
1107
|
-
"src/sequences/Text.ts"() {
|
|
1108
|
-
init_Base();
|
|
1109
|
-
init_normalizeProps();
|
|
1110
|
-
init_Timeline();
|
|
1111
|
-
init_colorTween();
|
|
1112
|
-
STYLE_OPAQUE_KEYS = ["fontFamily", "fill", "align", "fontStyle", "fontWeight"];
|
|
1113
|
-
TextSequence = class extends Sequence {
|
|
1114
|
-
async build() {
|
|
1115
|
-
const baseStyle = {
|
|
1116
|
-
fontFamily: "Arial",
|
|
1117
|
-
fontSize: 36,
|
|
1118
|
-
fill: "#ffffff",
|
|
1119
|
-
align: "center"
|
|
1120
|
-
};
|
|
1121
|
-
const text = new PIXI.Text({
|
|
1122
|
-
text: this.spec.text ?? "",
|
|
1123
|
-
style: baseStyle,
|
|
1124
|
-
label: this.spec.name
|
|
1125
|
-
});
|
|
1126
|
-
text.cullable = true;
|
|
1127
|
-
this.target = text;
|
|
1128
|
-
this.intrinsicWidth = text.width;
|
|
1129
|
-
this.intrinsicHeight = text.height;
|
|
1130
|
-
if (this.duration === void 0) {
|
|
1131
|
-
this.duration = this.parent?.duration ?? this.root.duration;
|
|
1132
|
-
}
|
|
1133
|
-
if (this.spec.style) {
|
|
1134
|
-
const scope = this.scope();
|
|
1135
|
-
const resolved = normalizeProps(
|
|
1136
|
-
this.spec.style,
|
|
1137
|
-
scope,
|
|
1138
|
-
{ skipKeys: STYLE_OPAQUE_KEYS }
|
|
1139
|
-
);
|
|
1140
|
-
for (const k of Object.keys(resolved)) {
|
|
1141
|
-
text.style[k] = resolved[k];
|
|
1142
|
-
}
|
|
1143
|
-
}
|
|
1144
|
-
this.buildFilters();
|
|
1145
|
-
}
|
|
1146
|
-
bindTimeline(timeline, offset = 0) {
|
|
1147
|
-
if (!this.target) return;
|
|
1148
|
-
const colorSpace = this.spec.colorSpace ?? "rgb";
|
|
1149
|
-
const stripped = stripFill(this.spec);
|
|
1150
|
-
runSuperWithStrippedSpec(this, stripped, timeline, offset);
|
|
1151
|
-
bindFillKeyframes(timeline, this.target, this.spec.keyframes ?? [], this.duration, offset, colorSpace);
|
|
1152
|
-
}
|
|
1153
|
-
};
|
|
1154
|
-
}
|
|
1155
|
-
});
|
|
1156
|
-
var AudioSequence;
|
|
1157
|
-
var init_Audio = __esm({
|
|
1158
|
-
"src/sequences/Audio.ts"() {
|
|
1159
|
-
init_Base();
|
|
1160
|
-
init_Timeline();
|
|
1161
|
-
AudioSequence = class extends Sequence {
|
|
1162
|
-
_audioBuffer = null;
|
|
1163
|
-
async build() {
|
|
1164
|
-
const data = await PIXI.Assets.get(this.spec.asset);
|
|
1165
|
-
this._audioBuffer = data.audioBuffer;
|
|
1166
|
-
if (this.duration === void 0) {
|
|
1167
|
-
this.duration = this.spec.duration ?? data.duration ?? this.root.duration;
|
|
1168
|
-
}
|
|
1169
|
-
this.target = null;
|
|
1170
|
-
}
|
|
1171
|
-
bindTimeline(_timeline) {
|
|
1172
|
-
}
|
|
1173
|
-
collectAudio(out, baseTime) {
|
|
1174
|
-
if (!this._audioBuffer) return;
|
|
1175
|
-
const initialVolume = this.spec.volume ?? 1;
|
|
1176
|
-
const volumeKeyframes = [];
|
|
1177
|
-
const dur = this.duration;
|
|
1178
|
-
for (const raw of this.spec.keyframes ?? []) {
|
|
1179
|
-
const kf = normalizeKeyframe(raw, dur);
|
|
1180
|
-
const set = kf.set;
|
|
1181
|
-
const to = kf.to;
|
|
1182
|
-
const from = kf.from;
|
|
1183
|
-
if (kf.kind === "set" && set && "volume" in set) {
|
|
1184
|
-
volumeKeyframes.push({ time: baseTime + this.at + kf.at, value: set.volume });
|
|
1185
|
-
} else if (kf.kind === "to" && to && "volume" in to) {
|
|
1186
|
-
volumeKeyframes.push({ time: baseTime + this.at + kf.at + kf.duration, value: to.volume });
|
|
1187
|
-
} else if (kf.kind === "fromTo" && to && "volume" in to) {
|
|
1188
|
-
volumeKeyframes.push({ time: baseTime + this.at + kf.at, value: from?.volume ?? initialVolume });
|
|
1189
|
-
volumeKeyframes.push({ time: baseTime + this.at + kf.at + kf.duration, value: to.volume });
|
|
1190
|
-
} else if (kf.kind === "from" && from && "volume" in from) {
|
|
1191
|
-
volumeKeyframes.push({ time: baseTime + this.at + kf.at, value: from.volume });
|
|
1192
|
-
volumeKeyframes.push({ time: baseTime + this.at + kf.at + kf.duration, value: initialVolume });
|
|
1193
|
-
}
|
|
1194
|
-
}
|
|
1195
|
-
out.push({
|
|
1196
|
-
buffer: this._audioBuffer,
|
|
1197
|
-
loop: !!this.spec.loop,
|
|
1198
|
-
start: baseTime + this.at,
|
|
1199
|
-
end: baseTime + this.at + dur,
|
|
1200
|
-
initialVolume,
|
|
1201
|
-
volumeKeyframes
|
|
1202
|
-
});
|
|
1203
|
-
}
|
|
1204
|
-
destroy() {
|
|
1205
|
-
this._audioBuffer = null;
|
|
1206
|
-
}
|
|
1207
|
-
};
|
|
1208
|
-
}
|
|
1209
|
-
});
|
|
1210
|
-
|
|
1211
|
-
// src/core/FrameCache.ts
|
|
1212
|
-
var FrameCache;
|
|
1213
|
-
var init_FrameCache = __esm({
|
|
1214
|
-
"src/core/FrameCache.ts"() {
|
|
1215
|
-
FrameCache = class {
|
|
1216
|
-
sink;
|
|
1217
|
-
capacity;
|
|
1218
|
-
cache = /* @__PURE__ */ new Map();
|
|
1219
|
-
_pending = /* @__PURE__ */ new Map();
|
|
1220
|
-
constructor(sink, options = {}) {
|
|
1221
|
-
this.sink = sink;
|
|
1222
|
-
this.capacity = options.capacity ?? 30;
|
|
1223
|
-
}
|
|
1224
|
-
_key(time) {
|
|
1225
|
-
return Math.round(time * 1e3);
|
|
1226
|
-
}
|
|
1227
|
-
async getFrameAt(time) {
|
|
1228
|
-
const key = this._key(time);
|
|
1229
|
-
if (this.cache.has(key)) {
|
|
1230
|
-
const v = this.cache.get(key);
|
|
1231
|
-
this.cache.delete(key);
|
|
1232
|
-
this.cache.set(key, v);
|
|
1233
|
-
return v;
|
|
1234
|
-
}
|
|
1235
|
-
const pending = this._pending.get(key);
|
|
1236
|
-
if (pending) return pending;
|
|
1237
|
-
const promise = this._fetch(time);
|
|
1238
|
-
this._pending.set(key, promise);
|
|
1239
|
-
try {
|
|
1240
|
-
const frame = await promise;
|
|
1241
|
-
if (frame) {
|
|
1242
|
-
if (this.cache.has(key)) {
|
|
1243
|
-
frame.close?.();
|
|
1244
|
-
const existing = this.cache.get(key);
|
|
1245
|
-
this.cache.delete(key);
|
|
1246
|
-
this.cache.set(key, existing);
|
|
1247
|
-
return existing;
|
|
1248
|
-
}
|
|
1249
|
-
this.cache.set(key, frame);
|
|
1250
|
-
this._evictIfNeeded();
|
|
1251
|
-
}
|
|
1252
|
-
return frame;
|
|
1253
|
-
} finally {
|
|
1254
|
-
this._pending.delete(key);
|
|
1255
|
-
}
|
|
1256
|
-
}
|
|
1257
|
-
async _fetch(time) {
|
|
1258
|
-
try {
|
|
1259
|
-
const sample = await this.sink.getSample(time);
|
|
1260
|
-
if (!sample) return null;
|
|
1261
|
-
const frame = sample.toVideoFrame();
|
|
1262
|
-
sample.close?.();
|
|
1263
|
-
return frame;
|
|
1264
|
-
} catch (err) {
|
|
1265
|
-
const isTransient = err instanceof DOMException && (err.name === "EncodingError" || err.name === "NotSupportedError");
|
|
1266
|
-
if (isTransient) {
|
|
1267
|
-
console.debug("pixi-effects: transient video decode failure at", time, "s \u2014", err.name);
|
|
1268
|
-
} else {
|
|
1269
|
-
console.warn("pixi-effects: video frame decode failed at", time, "s \u2014", err);
|
|
1270
|
-
}
|
|
1271
|
-
return null;
|
|
1272
|
-
}
|
|
1273
|
-
}
|
|
1274
|
-
_evictIfNeeded() {
|
|
1275
|
-
while (this.cache.size > this.capacity) {
|
|
1276
|
-
const oldestKey = this.cache.keys().next().value;
|
|
1277
|
-
if (oldestKey === void 0) break;
|
|
1278
|
-
const oldestFrame = this.cache.get(oldestKey);
|
|
1279
|
-
oldestFrame?.close?.();
|
|
1280
|
-
this.cache.delete(oldestKey);
|
|
1281
|
-
}
|
|
1282
|
-
}
|
|
1283
|
-
dispose() {
|
|
1284
|
-
for (const f of this.cache.values()) f?.close?.();
|
|
1285
|
-
this.cache.clear();
|
|
1286
|
-
this._pending.clear();
|
|
1287
|
-
}
|
|
1288
|
-
};
|
|
1289
|
-
}
|
|
1290
|
-
});
|
|
1291
|
-
var VideoSequence;
|
|
1292
|
-
var init_Video = __esm({
|
|
1293
|
-
"src/sequences/Video.ts"() {
|
|
1294
|
-
init_Base();
|
|
1295
|
-
init_FrameCache();
|
|
1296
|
-
VideoSequence = class extends Sequence {
|
|
1297
|
-
_sourceDuration = 0;
|
|
1298
|
-
_audioBuffer = null;
|
|
1299
|
-
_cache = null;
|
|
1300
|
-
_canvas = null;
|
|
1301
|
-
_ctx = null;
|
|
1302
|
-
_drawSeq = 0;
|
|
1303
|
-
async build() {
|
|
1304
|
-
const data = await PIXI.Assets.get(this.spec.asset);
|
|
1305
|
-
this._sourceDuration = data.duration;
|
|
1306
|
-
this._audioBuffer = this.spec.audio !== false ? data.audioBuffer : null;
|
|
1307
|
-
this._cache = new FrameCache(data.sink, { capacity: 30 });
|
|
1308
|
-
const probeFrame = await this._cache.getFrameAt(0);
|
|
1309
|
-
const w = probeFrame?.displayWidth ?? 1920;
|
|
1310
|
-
const h = probeFrame?.displayHeight ?? 1080;
|
|
1311
|
-
const canvas = document.createElement("canvas");
|
|
1312
|
-
canvas.width = w;
|
|
1313
|
-
canvas.height = h;
|
|
1314
|
-
this._canvas = canvas;
|
|
1315
|
-
this._ctx = canvas.getContext("2d", { alpha: true });
|
|
1316
|
-
if (probeFrame && this._ctx) this._ctx.drawImage(probeFrame, 0, 0);
|
|
1317
|
-
const texture = PIXI.Texture.from(canvas);
|
|
1318
|
-
const sprite = new PIXI.Sprite({ texture, label: this.spec.name });
|
|
1319
|
-
sprite.cullable = true;
|
|
1320
|
-
this.target = sprite;
|
|
1321
|
-
this.intrinsicWidth = w;
|
|
1322
|
-
this.intrinsicHeight = h;
|
|
1323
|
-
if (this.duration === void 0) {
|
|
1324
|
-
this.duration = this.spec.duration ?? data.duration ?? this.root.duration;
|
|
1325
|
-
}
|
|
1326
|
-
let currentTime = 0;
|
|
1327
|
-
const seq = this;
|
|
1328
|
-
Object.defineProperty(sprite, "currentTime", {
|
|
1329
|
-
get() {
|
|
1330
|
-
return currentTime;
|
|
1331
|
-
},
|
|
1332
|
-
set(v) {
|
|
1333
|
-
currentTime = v;
|
|
1334
|
-
const mySeq = ++seq._drawSeq;
|
|
1335
|
-
const lookup = seq.spec.loop && seq._sourceDuration > 0 ? v % seq._sourceDuration : v;
|
|
1336
|
-
seq._cache.getFrameAt(lookup).then((frame) => {
|
|
1337
|
-
if (mySeq !== seq._drawSeq) return;
|
|
1338
|
-
if (!frame || !seq._ctx) return;
|
|
1339
|
-
seq._ctx.drawImage(frame, 0, 0);
|
|
1340
|
-
texture.source.update();
|
|
1341
|
-
}).catch((err) => {
|
|
1342
|
-
console.warn("pixi-effects: video frame draw failed:", err);
|
|
1343
|
-
});
|
|
1344
|
-
}
|
|
1345
|
-
});
|
|
1346
|
-
this.buildFilters();
|
|
1347
|
-
}
|
|
1348
|
-
bindTimeline(timeline) {
|
|
1349
|
-
super.bindTimeline(timeline);
|
|
1350
|
-
const playDuration = this.spec.loop ? this.duration : Math.min(this.duration, this._sourceDuration);
|
|
1351
|
-
timeline.fromTo(
|
|
1352
|
-
this.target,
|
|
1353
|
-
{ currentTime: 0 },
|
|
1354
|
-
{ currentTime: playDuration, duration: playDuration, ease: "none" },
|
|
1355
|
-
this.at
|
|
1356
|
-
);
|
|
1357
|
-
}
|
|
1358
|
-
collectAudio(out, baseTime) {
|
|
1359
|
-
if (!this._audioBuffer) return;
|
|
1360
|
-
const initialVolume = this.spec.volume ?? 1;
|
|
1361
|
-
out.push({
|
|
1362
|
-
buffer: this._audioBuffer,
|
|
1363
|
-
loop: !!this.spec.loop,
|
|
1364
|
-
start: baseTime + this.at,
|
|
1365
|
-
end: baseTime + this.at + this.duration,
|
|
1366
|
-
initialVolume,
|
|
1367
|
-
volumeKeyframes: []
|
|
1368
|
-
});
|
|
1369
|
-
}
|
|
1370
|
-
async awaitFrameAt(time) {
|
|
1371
|
-
const mySeq = ++this._drawSeq;
|
|
1372
|
-
const lookup = this.spec.loop && this._sourceDuration > 0 ? time % this._sourceDuration : time;
|
|
1373
|
-
let frame = null;
|
|
1374
|
-
try {
|
|
1375
|
-
frame = await this._cache.getFrameAt(lookup);
|
|
1376
|
-
} catch (err) {
|
|
1377
|
-
console.warn("pixi-effects: awaitFrameAt failed at", time, "s \u2014", err);
|
|
1378
|
-
return;
|
|
1379
|
-
}
|
|
1380
|
-
if (mySeq !== this._drawSeq) return;
|
|
1381
|
-
if (frame && this._ctx) {
|
|
1382
|
-
try {
|
|
1383
|
-
this._ctx.drawImage(frame, 0, 0);
|
|
1384
|
-
this.target.texture.source.update();
|
|
1385
|
-
} catch (err) {
|
|
1386
|
-
console.warn("pixi-effects: drawImage failed at", time, "s \u2014", err);
|
|
1387
|
-
}
|
|
1388
|
-
}
|
|
1389
|
-
}
|
|
1390
|
-
destroy() {
|
|
1391
|
-
this._cache?.dispose();
|
|
1392
|
-
this._cache = null;
|
|
1393
|
-
super.destroy();
|
|
1394
|
-
}
|
|
1395
|
-
};
|
|
1396
|
-
}
|
|
1397
|
-
});
|
|
1398
|
-
function seedGeometry(state, spec, scope) {
|
|
1399
|
-
const keys = GEOMETRY_KEYS_BY_SHAPE[spec.shape];
|
|
1400
|
-
for (const k of keys) {
|
|
1401
|
-
const v = spec[k];
|
|
1402
|
-
if (v !== void 0) state[k] = num(v, scope);
|
|
1403
|
-
}
|
|
1404
|
-
}
|
|
1405
|
-
function seedStyle(state, initial, scope) {
|
|
1406
|
-
for (const k of STYLE_KEYS) {
|
|
1407
|
-
const v = initial[k];
|
|
1408
|
-
if (v === void 0) continue;
|
|
1409
|
-
state[k] = COLOR_KEYS.has(k) ? v : numOrZero(v, scope);
|
|
1410
|
-
}
|
|
1411
|
-
}
|
|
1412
|
-
function stripLive(props, liveKeys) {
|
|
1413
|
-
if (!props) return props;
|
|
1414
|
-
const out = {};
|
|
1415
|
-
let changed = false;
|
|
1416
|
-
for (const k of Object.keys(props)) {
|
|
1417
|
-
if (liveKeys.has(k)) {
|
|
1418
|
-
changed = true;
|
|
1419
|
-
continue;
|
|
1420
|
-
}
|
|
1421
|
-
out[k] = props[k];
|
|
1422
|
-
}
|
|
1423
|
-
return changed ? out : props;
|
|
1424
|
-
}
|
|
1425
|
-
function stripLiveKeyframe(kf, liveKeys) {
|
|
1426
|
-
return {
|
|
1427
|
-
...kf,
|
|
1428
|
-
set: stripLive(kf.set, liveKeys),
|
|
1429
|
-
to: stripLive(kf.to, liveKeys),
|
|
1430
|
-
from: stripLive(kf.from, liveKeys)
|
|
1431
|
-
};
|
|
1432
|
-
}
|
|
1433
|
-
function pickLive(props, liveKeys) {
|
|
1434
|
-
if (!props) return null;
|
|
1435
|
-
const out = {};
|
|
1436
|
-
let any = false;
|
|
1437
|
-
for (const k of Object.keys(props)) {
|
|
1438
|
-
if (liveKeys.has(k)) {
|
|
1439
|
-
out[k] = props[k];
|
|
1440
|
-
any = true;
|
|
1441
|
-
}
|
|
1442
|
-
}
|
|
1443
|
-
return any ? out : null;
|
|
1444
|
-
}
|
|
1445
|
-
function resolveLiveValue(key, value, scope) {
|
|
1446
|
-
if (COLOR_KEYS.has(key)) return value;
|
|
1447
|
-
return numOrZero(value, scope);
|
|
1448
|
-
}
|
|
1449
|
-
function applyState(g, s) {
|
|
1450
|
-
if (s.fillColor !== void 0) {
|
|
1451
|
-
g.fill({ color: s.fillColor, alpha: s.fillAlpha });
|
|
1452
|
-
}
|
|
1453
|
-
if (s.strokeColor !== void 0 && s.strokeWidth > 0) {
|
|
1454
|
-
g.stroke({ color: s.strokeColor, alpha: s.strokeAlpha, width: s.strokeWidth });
|
|
1455
|
-
}
|
|
1456
|
-
}
|
|
1457
|
-
function drawRectFromState(g, s) {
|
|
1458
|
-
const w = s.width ?? 0;
|
|
1459
|
-
const h = s.height ?? 0;
|
|
1460
|
-
const r = s.cornerRadius ?? 0;
|
|
1461
|
-
const ax = s.anchorX ?? 0.5;
|
|
1462
|
-
const ay = s.anchorY ?? 0.5;
|
|
1463
|
-
const x = 0 - ax * w;
|
|
1464
|
-
const y = 0 - ay * h;
|
|
1465
|
-
if (r > 0) g.roundRect(x, y, w, h, r);
|
|
1466
|
-
else g.rect(x, y, w, h);
|
|
1467
|
-
}
|
|
1468
|
-
function drawCircleFromState(g, s) {
|
|
1469
|
-
const r = s.radius ?? 0;
|
|
1470
|
-
const ax = s.anchorX ?? 0.5;
|
|
1471
|
-
const ay = s.anchorY ?? 0.5;
|
|
1472
|
-
g.circle((0.5 - ax) * 2 * r, (0.5 - ay) * 2 * r, r);
|
|
1473
|
-
}
|
|
1474
|
-
function drawEllipseFromState(g, s) {
|
|
1475
|
-
const rx = s.radiusX ?? 0;
|
|
1476
|
-
const ry = s.radiusY ?? 0;
|
|
1477
|
-
const ax = s.anchorX ?? 0.5;
|
|
1478
|
-
const ay = s.anchorY ?? 0.5;
|
|
1479
|
-
g.ellipse((0.5 - ax) * 2 * rx, (0.5 - ay) * 2 * ry, rx, ry);
|
|
1480
|
-
}
|
|
1481
|
-
function makeLineDraw(spec, scope) {
|
|
1482
|
-
const fx = num(spec.from[0], scope);
|
|
1483
|
-
const fy = num(spec.from[1], scope);
|
|
1484
|
-
const tx = num(spec.to[0], scope);
|
|
1485
|
-
const ty = num(spec.to[1], scope);
|
|
1486
|
-
return (g) => {
|
|
1487
|
-
g.moveTo(fx, fy);
|
|
1488
|
-
g.lineTo(tx, ty);
|
|
1489
|
-
};
|
|
1490
|
-
}
|
|
1491
|
-
function makePolygonDraw(spec, scope) {
|
|
1492
|
-
if (spec.points.length === 0) return () => {
|
|
1493
|
-
};
|
|
1494
|
-
const flat = [];
|
|
1495
|
-
for (const [x, y] of spec.points) flat.push(num(x, scope), num(y, scope));
|
|
1496
|
-
const closed = !spec.open;
|
|
1497
|
-
return (g) => {
|
|
1498
|
-
g.poly(flat, closed);
|
|
1499
|
-
};
|
|
1500
|
-
}
|
|
1501
|
-
function makePathDraw(spec) {
|
|
1502
|
-
return (g) => {
|
|
1503
|
-
g.path(new PIXI.GraphicsPath(spec.d));
|
|
1504
|
-
};
|
|
1505
|
-
}
|
|
1506
|
-
function makeDrawGeometry(spec, scope, state) {
|
|
1507
|
-
const stateDrawer = STATE_DRAWERS[spec.shape];
|
|
1508
|
-
if (stateDrawer) return (g) => stateDrawer(g, state);
|
|
1509
|
-
switch (spec.shape) {
|
|
1510
|
-
case "line":
|
|
1511
|
-
return makeLineDraw(spec, scope);
|
|
1512
|
-
case "polygon":
|
|
1513
|
-
return makePolygonDraw(spec, scope);
|
|
1514
|
-
case "path":
|
|
1515
|
-
return makePathDraw(spec);
|
|
1516
|
-
}
|
|
1517
|
-
return () => {
|
|
1518
|
-
};
|
|
1519
|
-
}
|
|
1520
|
-
function bindLiveKeyframes(timeline, state, liveKeys, keyframes, parentDuration, scope, offset, colorSpace) {
|
|
1521
|
-
for (const kf of keyframes) {
|
|
1522
|
-
const at = offset + resolveAt(kf.at, parentDuration);
|
|
1523
|
-
const duration = kf.duration ?? 0;
|
|
1524
|
-
const ease = kf.ease ?? "none";
|
|
1525
|
-
if (kf.set) {
|
|
1526
|
-
const live = pickLive(kf.set, liveKeys);
|
|
1527
|
-
if (live) {
|
|
1528
|
-
for (const [k, v] of Object.entries(live)) {
|
|
1529
|
-
const resolved = resolveLiveValue(k, v, scope);
|
|
1530
|
-
timeline.call(() => {
|
|
1531
|
-
state[k] = resolved;
|
|
1532
|
-
}, [], at);
|
|
1533
|
-
}
|
|
1534
|
-
}
|
|
1535
|
-
}
|
|
1536
|
-
const fromLive = pickLive(kf.from, liveKeys);
|
|
1537
|
-
const toLive = pickLive(kf.to, liveKeys);
|
|
1538
|
-
const allKeys = /* @__PURE__ */ new Set([
|
|
1539
|
-
...fromLive ? Object.keys(fromLive) : [],
|
|
1540
|
-
...toLive ? Object.keys(toLive) : []
|
|
1541
|
-
]);
|
|
1542
|
-
for (const k of allKeys) {
|
|
1543
|
-
const key = k;
|
|
1544
|
-
const fromRaw = fromLive?.[key];
|
|
1545
|
-
const toRaw = toLive?.[key];
|
|
1546
|
-
const fromValue = fromRaw !== void 0 ? resolveLiveValue(key, fromRaw, scope) : void 0;
|
|
1547
|
-
const toValue = toRaw !== void 0 ? resolveLiveValue(key, toRaw, scope) : void 0;
|
|
1548
|
-
tweenLiveKey(timeline, state, key, fromValue, toValue, duration, ease, at, colorSpace);
|
|
1549
|
-
}
|
|
1550
|
-
}
|
|
1551
|
-
}
|
|
1552
|
-
function tweenLiveKey(timeline, state, key, fromValue, toValue, duration, ease, at, colorSpace) {
|
|
1553
|
-
if (COLOR_KEYS.has(key) && toValue !== void 0) {
|
|
1554
|
-
tweenColor(
|
|
1555
|
-
timeline,
|
|
1556
|
-
state,
|
|
1557
|
-
key,
|
|
1558
|
-
fromValue,
|
|
1559
|
-
toValue,
|
|
1560
|
-
duration,
|
|
1561
|
-
ease,
|
|
1562
|
-
at,
|
|
1563
|
-
colorSpace
|
|
1564
|
-
);
|
|
1565
|
-
} else if (toValue !== void 0) {
|
|
1566
|
-
if (fromValue !== void 0) {
|
|
1567
|
-
timeline.fromTo(state, { [key]: fromValue }, { [key]: toValue, duration, ease }, at);
|
|
1568
|
-
} else {
|
|
1569
|
-
timeline.to(state, { [key]: toValue, duration, ease }, at);
|
|
1570
|
-
}
|
|
1571
|
-
}
|
|
1572
|
-
}
|
|
1573
|
-
function num(v, scope) {
|
|
1574
|
-
if (typeof v === "number") return v;
|
|
1575
|
-
if (isExpr(v)) return evaluateExpr(v, scope);
|
|
1576
|
-
const parsed = parseFloat(v);
|
|
1577
|
-
return Number.isNaN(parsed) ? 0 : parsed;
|
|
1578
|
-
}
|
|
1579
|
-
function numOrZero(v, scope) {
|
|
1580
|
-
if (typeof v === "number") return v;
|
|
1581
|
-
if (typeof v === "string") return num(v, scope);
|
|
1582
|
-
return 0;
|
|
1583
|
-
}
|
|
1584
|
-
var STYLE_KEYS, COLOR_KEYS, GEOMETRY_KEYS_BY_SHAPE, ANCHORED_SHAPES, ShapeSequence, STATE_DRAWERS;
|
|
1585
|
-
var init_Shape = __esm({
|
|
1586
|
-
"src/sequences/Shape.ts"() {
|
|
1587
|
-
init_Base();
|
|
1588
|
-
init_Parser();
|
|
1589
|
-
init_Timeline();
|
|
1590
|
-
init_colorTween();
|
|
1591
|
-
STYLE_KEYS = ["fillColor", "fillAlpha", "strokeColor", "strokeAlpha", "strokeWidth"];
|
|
1592
|
-
COLOR_KEYS = /* @__PURE__ */ new Set(["fillColor", "strokeColor"]);
|
|
1593
|
-
GEOMETRY_KEYS_BY_SHAPE = {
|
|
1594
|
-
rect: ["width", "height", "cornerRadius", "anchorX", "anchorY"],
|
|
1595
|
-
circle: ["radius", "anchorX", "anchorY"],
|
|
1596
|
-
ellipse: ["radiusX", "radiusY", "anchorX", "anchorY"],
|
|
1597
|
-
line: [],
|
|
1598
|
-
polygon: [],
|
|
1599
|
-
path: []
|
|
1600
|
-
};
|
|
1601
|
-
ANCHORED_SHAPES = /* @__PURE__ */ new Set(["rect", "circle", "ellipse"]);
|
|
1602
|
-
ShapeSequence = class extends Sequence {
|
|
1603
|
-
_state = {
|
|
1604
|
-
fillColor: void 0,
|
|
1605
|
-
fillAlpha: 1,
|
|
1606
|
-
strokeColor: void 0,
|
|
1607
|
-
strokeAlpha: 1,
|
|
1608
|
-
strokeWidth: 0
|
|
1609
|
-
};
|
|
1610
|
-
// Closure that draws the path commands for this shape kind from the
|
|
1611
|
-
// (possibly tweened) `_state`. Bound once at build; called on every frame
|
|
1612
|
-
// by `Container.onRender`.
|
|
1613
|
-
_drawGeometry = () => {
|
|
1614
|
-
};
|
|
1615
|
-
_liveKeys = new Set(STYLE_KEYS);
|
|
1616
|
-
async build() {
|
|
1617
|
-
const graphics = new PIXI.Graphics({ label: this.spec.name });
|
|
1618
|
-
graphics.cullable = true;
|
|
1619
|
-
this.target = graphics;
|
|
1620
|
-
if (this.duration === void 0) {
|
|
1621
|
-
this.duration = this.parent?.duration ?? this.root.duration;
|
|
1622
|
-
}
|
|
1623
|
-
const scope = this.scope();
|
|
1624
|
-
const geomKeys = GEOMETRY_KEYS_BY_SHAPE[this.spec.shape];
|
|
1625
|
-
for (const k of geomKeys) this._liveKeys.add(k);
|
|
1626
|
-
seedGeometry(this._state, this.spec, scope);
|
|
1627
|
-
seedStyle(this._state, this.spec.initial ?? {}, scope);
|
|
1628
|
-
this._drawGeometry = makeDrawGeometry(this.spec, scope, this._state);
|
|
1629
|
-
this._drawGeometry(graphics);
|
|
1630
|
-
applyState(graphics, this._state);
|
|
1631
|
-
const bounds = graphics.getLocalBounds();
|
|
1632
|
-
this.intrinsicWidth = bounds.width;
|
|
1633
|
-
this.intrinsicHeight = bounds.height;
|
|
1634
|
-
if (!ANCHORED_SHAPES.has(this.spec.shape)) {
|
|
1635
|
-
graphics.pivot.set(bounds.x + bounds.width / 2, bounds.y + bounds.height / 2);
|
|
1636
|
-
}
|
|
1637
|
-
graphics.onRender = () => {
|
|
1638
|
-
graphics.clear();
|
|
1639
|
-
this._drawGeometry(graphics);
|
|
1640
|
-
applyState(graphics, this._state);
|
|
1641
|
-
};
|
|
1642
|
-
this.buildFilters();
|
|
1643
|
-
}
|
|
1644
|
-
bindTimeline(timeline, offset = 0) {
|
|
1645
|
-
if (!this.target) return;
|
|
1646
|
-
const scope = this.scope();
|
|
1647
|
-
const initial = stripLive(this.spec.initial, this._liveKeys);
|
|
1648
|
-
const keyframes = this.spec.keyframes ? this.spec.keyframes.map((kf) => stripLiveKeyframe(kf, this._liveKeys)) : void 0;
|
|
1649
|
-
applyInitial(this.target, initial, scope);
|
|
1650
|
-
applyKeyframes(timeline, this.target, keyframes, this.duration, scope, [], offset);
|
|
1651
|
-
const colorSpace = this.spec.colorSpace ?? "rgb";
|
|
1652
|
-
bindLiveKeyframes(timeline, this._state, this._liveKeys, this.spec.keyframes ?? [], this.duration, scope, offset, colorSpace);
|
|
1653
|
-
const startTime = offset + this.at;
|
|
1654
|
-
const endTime = startTime + this.duration;
|
|
1655
|
-
this.target.renderable = startTime <= 0;
|
|
1656
|
-
timeline.set(this.target, { renderable: true }, startTime);
|
|
1657
|
-
timeline.set(this.target, { renderable: false }, endTime);
|
|
1658
|
-
}
|
|
1659
|
-
};
|
|
1660
|
-
STATE_DRAWERS = {
|
|
1661
|
-
rect: drawRectFromState,
|
|
1662
|
-
circle: drawCircleFromState,
|
|
1663
|
-
ellipse: drawEllipseFromState
|
|
1664
|
-
};
|
|
1665
|
-
}
|
|
1666
|
-
});
|
|
1667
|
-
|
|
1668
|
-
// src/core/Composition.ts
|
|
1669
|
-
async function getCompositionSequence() {
|
|
1670
|
-
if (!_CompositionSequence) {
|
|
1671
|
-
_CompositionSequence = (await Promise.resolve().then(() => (init_Composition2(), Composition_exports))).CompositionSequence;
|
|
1672
|
-
}
|
|
1673
|
-
return _CompositionSequence;
|
|
1674
|
-
}
|
|
1675
|
-
async function buildSequenceTree(specs, parent, root) {
|
|
1676
|
-
const out = [];
|
|
1677
|
-
for (const spec of specs) {
|
|
1678
|
-
let Cls = staticTypes[spec.type];
|
|
1679
|
-
if (!Cls && spec.type === "composition") {
|
|
1680
|
-
Cls = await getCompositionSequence();
|
|
1681
|
-
}
|
|
1682
|
-
if (!Cls) {
|
|
1683
|
-
console.warn(`pixi-effects: unknown sequence type "${spec.type}"`);
|
|
1684
|
-
continue;
|
|
1685
|
-
}
|
|
1686
|
-
const seq = new Cls(spec, parent, root);
|
|
1687
|
-
await seq.build();
|
|
1688
|
-
if (seq.duration === void 0) {
|
|
1689
|
-
seq.duration = parent?.duration ?? root.duration;
|
|
1690
|
-
}
|
|
1691
|
-
out.push(seq);
|
|
1692
|
-
}
|
|
1693
|
-
return out;
|
|
1694
|
-
}
|
|
1695
|
-
var _CompositionSequence, staticTypes;
|
|
1696
|
-
var init_Composition = __esm({
|
|
1697
|
-
"src/core/Composition.ts"() {
|
|
1698
|
-
init_Image();
|
|
1699
|
-
init_Text();
|
|
1700
|
-
init_Audio();
|
|
1701
|
-
init_Video();
|
|
1702
|
-
init_Shape();
|
|
1703
|
-
_CompositionSequence = null;
|
|
1704
|
-
staticTypes = {
|
|
1705
|
-
image: ImageSequence,
|
|
1706
|
-
text: TextSequence,
|
|
1707
|
-
audio: AudioSequence,
|
|
1708
|
-
video: VideoSequence,
|
|
1709
|
-
shape: ShapeSequence
|
|
1710
|
-
};
|
|
1711
|
-
}
|
|
1712
|
-
});
|
|
1713
|
-
|
|
1714
|
-
// src/sequences/Composition.ts
|
|
1715
|
-
var Composition_exports = {};
|
|
1716
|
-
__export(Composition_exports, {
|
|
1717
|
-
CompositionSequence: () => CompositionSequence
|
|
1718
|
-
});
|
|
1719
|
-
var CompositionSequence;
|
|
1720
|
-
var init_Composition2 = __esm({
|
|
1721
|
-
"src/sequences/Composition.ts"() {
|
|
1722
|
-
init_Base();
|
|
1723
|
-
init_Composition();
|
|
1724
|
-
CompositionSequence = class extends Sequence {
|
|
1725
|
-
_innerContainer = null;
|
|
1726
|
-
_compositionShape = null;
|
|
1727
|
-
_children = [];
|
|
1728
|
-
async build() {
|
|
1729
|
-
const width = this.spec.width ?? this.parent?.width ?? this.root.width;
|
|
1730
|
-
const height = this.spec.height ?? this.parent?.height ?? this.root.height;
|
|
1731
|
-
if (this.duration === void 0) {
|
|
1732
|
-
this.duration = this.spec.duration ?? this.parent?.duration ?? this.root.duration;
|
|
1733
|
-
}
|
|
1734
|
-
const wrapper = new PIXI.Container();
|
|
1735
|
-
wrapper.cullable = true;
|
|
1736
|
-
wrapper.cullableChildren = true;
|
|
1737
|
-
wrapper.cullArea = new PIXI.Rectangle(0, 0, width, height);
|
|
1738
|
-
const inner = new PIXI.Container();
|
|
1739
|
-
inner.cullable = true;
|
|
1740
|
-
inner.cullableChildren = true;
|
|
1741
|
-
wrapper.addChild(inner);
|
|
1742
|
-
this.target = wrapper;
|
|
1743
|
-
this.intrinsicWidth = width;
|
|
1744
|
-
this.intrinsicHeight = height;
|
|
1745
|
-
this._innerContainer = inner;
|
|
1746
|
-
this._compositionShape = { width, height, duration: this.duration };
|
|
1747
|
-
this._children = await buildSequenceTree(
|
|
1748
|
-
this.spec.sequences ?? [],
|
|
1749
|
-
this._compositionShape,
|
|
1750
|
-
this.root
|
|
1751
|
-
);
|
|
1752
|
-
for (const child of this._children) {
|
|
1753
|
-
if (child.target) inner.addChild(child.target);
|
|
1754
|
-
const maskSpec = child.spec.mask;
|
|
1755
|
-
if (maskSpec && child.target) {
|
|
1756
|
-
const built = await buildSequenceTree([maskSpec], this._compositionShape, this.root);
|
|
1757
|
-
const maskSeq = built[0];
|
|
1758
|
-
if (maskSeq?.target) {
|
|
1759
|
-
inner.addChild(maskSeq.target);
|
|
1760
|
-
const inverse = child.spec.maskInverted ?? false;
|
|
1761
|
-
const t = child.target;
|
|
1762
|
-
if (typeof t.setMask === "function") {
|
|
1763
|
-
t.setMask({ mask: maskSeq.target, inverse });
|
|
1764
|
-
} else {
|
|
1765
|
-
t.mask = maskSeq.target;
|
|
1766
|
-
}
|
|
1767
|
-
child.maskSequence = maskSeq;
|
|
1768
|
-
}
|
|
1769
|
-
}
|
|
1770
|
-
}
|
|
1771
|
-
this.buildFilters();
|
|
1772
|
-
}
|
|
1773
|
-
bindTimeline(timeline, offset = 0) {
|
|
1774
|
-
super.bindTimeline(timeline, offset);
|
|
1775
|
-
const childOffset = offset + this.at;
|
|
1776
|
-
for (const child of this._children) {
|
|
1777
|
-
child.bindTimeline(timeline, childOffset);
|
|
1778
|
-
child.maskSequence?.bindTimeline(timeline, childOffset);
|
|
1779
|
-
}
|
|
1780
|
-
}
|
|
1781
|
-
collectAudio(out, baseTime) {
|
|
1782
|
-
super.collectAudio(out, baseTime);
|
|
1783
|
-
const childBase = baseTime + this.at;
|
|
1784
|
-
for (const child of this._children) child.collectAudio(out, childBase);
|
|
1785
|
-
}
|
|
1786
|
-
destroy() {
|
|
1787
|
-
for (const c of this._children) c.destroy();
|
|
1788
|
-
super.destroy();
|
|
1789
|
-
}
|
|
1790
|
-
};
|
|
1791
|
-
}
|
|
1792
|
-
});
|
|
1793
30
|
var registered = false;
|
|
1794
31
|
function ensureLoadersRegistered(audioContext) {
|
|
1795
32
|
if (registered) return;
|
|
@@ -1847,9 +84,6 @@ async function loadAssetBundle(assets, audioContext) {
|
|
|
1847
84
|
return await PIXI.Assets.loadBundle("pixi-effects");
|
|
1848
85
|
}
|
|
1849
86
|
|
|
1850
|
-
// src/core/Movie.ts
|
|
1851
|
-
init_Composition2();
|
|
1852
|
-
|
|
1853
87
|
// src/core/AudioMixer.ts
|
|
1854
88
|
async function mixdown(audios, totalDuration, sampleRate = 44100) {
|
|
1855
89
|
if (audios.length === 0) return null;
|
|
@@ -1952,9 +186,6 @@ async function exportFrames(movie, options = {}) {
|
|
|
1952
186
|
movie.app.ticker.start();
|
|
1953
187
|
}
|
|
1954
188
|
}
|
|
1955
|
-
|
|
1956
|
-
// src/core/Transitions.ts
|
|
1957
|
-
init_Timeline();
|
|
1958
189
|
var MODE_CODES = {
|
|
1959
190
|
"wipe-left": 0,
|
|
1960
191
|
"wipe-right": 1,
|
|
@@ -1964,7 +195,7 @@ var MODE_CODES = {
|
|
|
1964
195
|
"iris-out": 5,
|
|
1965
196
|
"dissolve": 6
|
|
1966
197
|
};
|
|
1967
|
-
var
|
|
198
|
+
var GL_FRAGMENT = `
|
|
1968
199
|
in vec2 vTextureCoord;
|
|
1969
200
|
uniform sampler2D uTexture;
|
|
1970
201
|
uniform vec4 uInputSize; // PIXI-bound: xy = sprite render texture size (with padding)
|
|
@@ -2084,7 +315,7 @@ void main(void) {
|
|
|
2084
315
|
finalColor = raw * reveal;
|
|
2085
316
|
}
|
|
2086
317
|
`;
|
|
2087
|
-
var
|
|
318
|
+
var WGSL_SOURCE = `
|
|
2088
319
|
struct GlobalFilterUniforms {
|
|
2089
320
|
uInputSize: vec4<f32>,
|
|
2090
321
|
uInputPixel: vec4<f32>,
|
|
@@ -2232,12 +463,12 @@ var TransitionMaskFilter = class extends PIXI.Filter {
|
|
|
2232
463
|
super({
|
|
2233
464
|
glProgram: PIXI.GlProgram.from({
|
|
2234
465
|
vertex: PIXI.defaultFilterVert,
|
|
2235
|
-
fragment:
|
|
466
|
+
fragment: GL_FRAGMENT,
|
|
2236
467
|
name: "transition-mask-filter"
|
|
2237
468
|
}),
|
|
2238
469
|
gpuProgram: PIXI.GpuProgram.from({
|
|
2239
|
-
vertex: { source:
|
|
2240
|
-
fragment: { source:
|
|
470
|
+
vertex: { source: WGSL_SOURCE, entryPoint: "mainVertex" },
|
|
471
|
+
fragment: { source: WGSL_SOURCE, entryPoint: "mainFragment" }
|
|
2241
472
|
}),
|
|
2242
473
|
resources: {
|
|
2243
474
|
transitionUniforms: new PIXI.UniformGroup({
|
|
@@ -2321,6 +552,14 @@ function expandTransitions(spec) {
|
|
|
2321
552
|
const parentDuration = out.duration ?? Infinity;
|
|
2322
553
|
const compW = out.width;
|
|
2323
554
|
const compH = out.height;
|
|
555
|
+
const threeDByName = new Set(sequences.filter((s) => s.name && s.threeD).map((s) => s.name));
|
|
556
|
+
transitions.forEach((t, i) => {
|
|
557
|
+
for (const name of [t.from, t.to]) {
|
|
558
|
+
if (threeDByName.has(name)) {
|
|
559
|
+
console.warn(`pixi-effects: transitions[${i}]: "${name}" is threeD, but transitions are not supported on threeD layers yet \u2014 it will render without the camera`);
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
});
|
|
2324
563
|
const masksParticipants = /* @__PURE__ */ new Set();
|
|
2325
564
|
for (const t of transitions) {
|
|
2326
565
|
if (t.kind === "wipe" || t.kind === "iris" || t.kind === "dissolve" || t.kind === "zoom") {
|
|
@@ -2378,43 +617,48 @@ function expandTransitions(spec) {
|
|
|
2378
617
|
if (toUses > 1) {
|
|
2379
618
|
throw new Error(`pixi-effects: ${tag} sequence "${t.to}" is used as \`to\` in more than one transition`);
|
|
2380
619
|
}
|
|
2381
|
-
const tStart = resolveAt(t.at, parentDuration);
|
|
620
|
+
const tStart = chunkSIRVULFF_cjs.resolveAt(t.at, parentDuration);
|
|
2382
621
|
const tEnd = tStart + t.duration;
|
|
2383
622
|
const fromSeq = fromEntry.seq;
|
|
2384
|
-
const fromStart = resolveAt(fromSeq.at, parentDuration);
|
|
623
|
+
const fromStart = chunkSIRVULFF_cjs.resolveAt(fromSeq.at, parentDuration);
|
|
2385
624
|
const fromEnd = fromStart + (fromSeq.duration ?? parentDuration);
|
|
2386
625
|
if (tStart < fromStart || tEnd > fromEnd) {
|
|
2387
626
|
throw new Error(`pixi-effects: ${tag} window [${tStart}, ${tEnd}] is not covered by \`from\` "${t.from}" (lives [${fromStart}, ${fromEnd}], so it ends at ${fromEnd})`);
|
|
2388
627
|
}
|
|
2389
628
|
const toSeq = toEntry.seq;
|
|
2390
|
-
const toStart = resolveAt(toSeq.at, parentDuration);
|
|
629
|
+
const toStart = chunkSIRVULFF_cjs.resolveAt(toSeq.at, parentDuration);
|
|
2391
630
|
const toEnd = toStart + (toSeq.duration ?? parentDuration);
|
|
2392
631
|
if (tStart < toStart || tEnd > toEnd) {
|
|
2393
632
|
throw new Error(`pixi-effects: ${tag} window [${tStart}, ${tEnd}] is not covered by \`to\` "${t.to}" (lives [${toStart}, ${toEnd}], so it starts at ${toStart})`);
|
|
2394
633
|
}
|
|
2395
|
-
|
|
634
|
+
const abs = { ...t, at: tStart };
|
|
635
|
+
const nFrom = keyframeCount(fromEntry.seq);
|
|
636
|
+
const nTo = keyframeCount(toEntry.seq);
|
|
637
|
+
switch (abs.kind) {
|
|
2396
638
|
case "crossfade":
|
|
2397
|
-
expandCrossfade(out,
|
|
639
|
+
expandCrossfade(out, abs, fromEntry.seq, toEntry.seq);
|
|
2398
640
|
break;
|
|
2399
641
|
case "wipe":
|
|
2400
|
-
expandMask(out,
|
|
642
|
+
expandMask(out, abs, fromEntry.seq, toEntry.seq, i, wipeMode(abs.direction), abs.smoothing);
|
|
2401
643
|
break;
|
|
2402
644
|
case "iris":
|
|
2403
|
-
expandMask(out,
|
|
645
|
+
expandMask(out, abs, fromEntry.seq, toEntry.seq, i, abs.mode === "out" ? "iris-out" : "iris-in", abs.smoothing);
|
|
2404
646
|
break;
|
|
2405
647
|
case "slide":
|
|
2406
|
-
expandSlide(out,
|
|
648
|
+
expandSlide(out, abs, fromEntry.seq, toEntry.seq);
|
|
2407
649
|
break;
|
|
2408
650
|
case "dip":
|
|
2409
|
-
expandDip(out,
|
|
651
|
+
expandDip(out, abs, fromEntry.seq, toEntry.seq);
|
|
2410
652
|
break;
|
|
2411
653
|
case "zoom":
|
|
2412
|
-
expandZoom(out,
|
|
654
|
+
expandZoom(out, abs, fromEntry.seq, toEntry.seq);
|
|
2413
655
|
break;
|
|
2414
656
|
case "dissolve":
|
|
2415
|
-
expandDissolve(out,
|
|
657
|
+
expandDissolve(out, abs, fromEntry.seq, toEntry.seq, i);
|
|
2416
658
|
break;
|
|
2417
659
|
}
|
|
660
|
+
rebaseNewKeyframes(fromEntry.seq, nFrom, fromStart);
|
|
661
|
+
rebaseNewKeyframes(toEntry.seq, nTo, toStart);
|
|
2418
662
|
}
|
|
2419
663
|
delete out.transitions;
|
|
2420
664
|
return out;
|
|
@@ -2446,6 +690,17 @@ function wrapAsFullComposition(seq, compW, compH) {
|
|
|
2446
690
|
sequences: [inner]
|
|
2447
691
|
};
|
|
2448
692
|
}
|
|
693
|
+
function keyframeCount(seq) {
|
|
694
|
+
return seq.keyframes?.length ?? 0;
|
|
695
|
+
}
|
|
696
|
+
function rebaseNewKeyframes(seq, from, seqStart) {
|
|
697
|
+
const kfs = seq.keyframes;
|
|
698
|
+
if (!kfs) return;
|
|
699
|
+
for (let i = from; i < kfs.length; i++) {
|
|
700
|
+
const kf = kfs[i];
|
|
701
|
+
if (typeof kf.at === "number") kfs[i] = { ...kf, at: kf.at - seqStart };
|
|
702
|
+
}
|
|
703
|
+
}
|
|
2449
704
|
function ensureKeyframes(seq) {
|
|
2450
705
|
const s = seq;
|
|
2451
706
|
if (!s.keyframes) s.keyframes = [];
|
|
@@ -2662,6 +917,153 @@ function rejectReservedNameCollision(filters, seqName) {
|
|
|
2662
917
|
}
|
|
2663
918
|
}
|
|
2664
919
|
|
|
920
|
+
// src/core/inspect.ts
|
|
921
|
+
var FAINT = 0.3;
|
|
922
|
+
var intersection = (a, b) => {
|
|
923
|
+
const w = Math.min(a.x + a.width, b.x + b.width) - Math.max(a.x, b.x);
|
|
924
|
+
const h = Math.min(a.y + a.height, b.y + b.height) - Math.max(a.y, b.y);
|
|
925
|
+
return w > 0 && h > 0 ? w * h : 0;
|
|
926
|
+
};
|
|
927
|
+
function inspectScene(root, frame, time, canvas, opts = {}) {
|
|
928
|
+
const layers = [];
|
|
929
|
+
const view = { x: 0, y: 0, width: canvas.width, height: canvas.height };
|
|
930
|
+
const walk = (comp, prefix, insideThreeD, parentVisible) => {
|
|
931
|
+
comp.layers().forEach(({ seq, display, threeD }, i) => {
|
|
932
|
+
const t = seq.target;
|
|
933
|
+
const name = seq.spec.name;
|
|
934
|
+
const label = name ?? `${seq.spec.type}#${i}`;
|
|
935
|
+
const alpha = t?.alpha ?? 1;
|
|
936
|
+
const alive = threeD ? display.visible : t?.renderable ?? true;
|
|
937
|
+
const visible = parentVisible && alive && alpha > 0;
|
|
938
|
+
let bounds = null;
|
|
939
|
+
if (!insideThreeD) {
|
|
940
|
+
const b = display.getBounds();
|
|
941
|
+
bounds = { x: b.x, y: b.y, width: b.width, height: b.height };
|
|
942
|
+
}
|
|
943
|
+
let onCanvas = null;
|
|
944
|
+
if (bounds) {
|
|
945
|
+
const inter = intersection(bounds, view);
|
|
946
|
+
const area = bounds.width * bounds.height;
|
|
947
|
+
onCanvas = inter === 0 && area > 0 ? "none" : inter >= area - 1e-6 ? "full" : "partial";
|
|
948
|
+
if (area === 0) onCanvas = "none";
|
|
949
|
+
}
|
|
950
|
+
const keys = chunkSIRVULFF_cjs.collectPropKeys(seq.spec);
|
|
951
|
+
const moving = (seq.spec.keyframes ?? []).length > 0 && (keys.has("x") || keys.has("y"));
|
|
952
|
+
layers.push({ path: prefix + label, name, type: seq.spec.type, threeD, visible, alpha, moving, bounds, onCanvas });
|
|
953
|
+
if (seq instanceof chunkZL262ZRM_cjs.CompositionSequence) walk(seq, prefix + label + "/", insideThreeD || threeD, visible);
|
|
954
|
+
});
|
|
955
|
+
};
|
|
956
|
+
walk(root, "", false, true);
|
|
957
|
+
const issues = [];
|
|
958
|
+
const texts = layers.filter((l) => l.type === "text" && l.visible && l.alpha >= FAINT && l.bounds);
|
|
959
|
+
for (const l of texts) {
|
|
960
|
+
const b = l.bounds;
|
|
961
|
+
const who = `text layer "${l.path}"`;
|
|
962
|
+
if (b.width < 1 || b.height < 1) {
|
|
963
|
+
issues.push(`${who} has no size (empty text, or not drawn yet)`);
|
|
964
|
+
} else if (l.moving) ; else if (l.onCanvas === "none") {
|
|
965
|
+
issues.push(`${who} is entirely outside the canvas (at ${Math.round(b.x)},${Math.round(b.y)}, ${Math.round(b.width)}\xD7${Math.round(b.height)})`);
|
|
966
|
+
} else if (l.onCanvas === "partial") {
|
|
967
|
+
const over = [];
|
|
968
|
+
if (b.x < 0) over.push(`${Math.round(-b.x)}px beyond the left edge`);
|
|
969
|
+
if (b.x + b.width > canvas.width) over.push(`${Math.round(b.x + b.width - canvas.width)}px beyond the right edge`);
|
|
970
|
+
if (b.y < 0) over.push(`${Math.round(-b.y)}px beyond the top edge`);
|
|
971
|
+
if (b.y + b.height > canvas.height) over.push(`${Math.round(b.y + b.height - canvas.height)}px beyond the bottom edge`);
|
|
972
|
+
issues.push(`${who} is cut off by the canvas edge: ${over.join(", ")}`);
|
|
973
|
+
}
|
|
974
|
+
}
|
|
975
|
+
for (let i = 0; i < texts.length; i++) {
|
|
976
|
+
for (let j = i + 1; j < texts.length; j++) {
|
|
977
|
+
const a = texts[i].bounds, b = texts[j].bounds;
|
|
978
|
+
const smaller = Math.min(a.width * a.height, b.width * b.height);
|
|
979
|
+
if (smaller <= 0) continue;
|
|
980
|
+
const share = intersection(a, b) / smaller;
|
|
981
|
+
if (share > 0.25) issues.push(`text layers "${texts[i].path}" and "${texts[j].path}" overlap by ${Math.round(share * 100)}% of the smaller one`);
|
|
982
|
+
}
|
|
983
|
+
}
|
|
984
|
+
const mode = opts.layers ?? "all";
|
|
985
|
+
const listed = mode === "none" ? [] : mode === "visible" ? layers.filter((l) => l.visible) : layers;
|
|
986
|
+
return { frame, time, canvas, summary: { layers: layers.length, visible: layers.filter((l) => l.visible).length }, issues, layers: listed };
|
|
987
|
+
}
|
|
988
|
+
|
|
989
|
+
// src/core/frames.ts
|
|
990
|
+
function pickFrames(lastFrame, count) {
|
|
991
|
+
const last = Math.max(0, Math.floor(lastFrame));
|
|
992
|
+
const n = Math.max(1, Math.floor(count));
|
|
993
|
+
if (n === 1) return [0];
|
|
994
|
+
const out = [];
|
|
995
|
+
for (let i = 0; i < n; i++) {
|
|
996
|
+
const f = Math.round(last * i / (n - 1));
|
|
997
|
+
if (out[out.length - 1] !== f) out.push(f);
|
|
998
|
+
}
|
|
999
|
+
return out;
|
|
1000
|
+
}
|
|
1001
|
+
function sheetLayout(n, columns, cellW, srcW, srcH, labelH) {
|
|
1002
|
+
const cols = Math.max(1, Math.min(Math.floor(columns), n));
|
|
1003
|
+
const rows = Math.ceil(n / cols);
|
|
1004
|
+
const cellH = Math.round(cellW * srcH / srcW);
|
|
1005
|
+
const positions = Array.from({ length: n }, (_, i) => ({ x: i % cols * cellW, y: Math.floor(i / cols) * (cellH + labelH) }));
|
|
1006
|
+
return { cols, rows, cellW, cellH, width: cols * cellW, height: rows * (cellH + labelH), positions };
|
|
1007
|
+
}
|
|
1008
|
+
|
|
1009
|
+
// src/core/options.ts
|
|
1010
|
+
function distance(a, b) {
|
|
1011
|
+
const dp = Array.from({ length: a.length + 1 }, (_, i) => [i, ...Array(b.length).fill(0)]);
|
|
1012
|
+
for (let j = 1; j <= b.length; j++) dp[0][j] = j;
|
|
1013
|
+
for (let i = 1; i <= a.length; i++) {
|
|
1014
|
+
for (let j = 1; j <= b.length; j++) {
|
|
1015
|
+
dp[i][j] = Math.min(dp[i - 1][j] + 1, dp[i][j - 1] + 1, dp[i - 1][j - 1] + (a[i - 1] === b[j - 1] ? 0 : 1));
|
|
1016
|
+
if (i > 1 && j > 1 && a[i - 1] === b[j - 2] && a[i - 2] === b[j - 1]) dp[i][j] = Math.min(dp[i][j], dp[i - 2][j - 2] + 1);
|
|
1017
|
+
}
|
|
1018
|
+
}
|
|
1019
|
+
return dp[a.length][b.length];
|
|
1020
|
+
}
|
|
1021
|
+
var isSubsequence = (small, big) => {
|
|
1022
|
+
let i = 0;
|
|
1023
|
+
for (const ch of big) if (i < small.length && ch === small[i]) i++;
|
|
1024
|
+
return i === small.length;
|
|
1025
|
+
};
|
|
1026
|
+
var COMMON_ALIASES = {
|
|
1027
|
+
fps: "frameRate",
|
|
1028
|
+
framerate: "frameRate",
|
|
1029
|
+
frame_rate: "frameRate",
|
|
1030
|
+
bg: "background",
|
|
1031
|
+
backgroundcolor: "background",
|
|
1032
|
+
bgcolor: "background",
|
|
1033
|
+
seconds: "duration",
|
|
1034
|
+
length: "duration",
|
|
1035
|
+
time: "duration",
|
|
1036
|
+
w: "width",
|
|
1037
|
+
h: "height",
|
|
1038
|
+
cols: "columns",
|
|
1039
|
+
sweep: "degrees",
|
|
1040
|
+
angle: "degrees"
|
|
1041
|
+
};
|
|
1042
|
+
function suggestName(name, valid) {
|
|
1043
|
+
const n = name.toLowerCase();
|
|
1044
|
+
const alias = COMMON_ALIASES[n];
|
|
1045
|
+
if (alias && valid.includes(alias)) return alias;
|
|
1046
|
+
let best = null;
|
|
1047
|
+
for (const v of valid) {
|
|
1048
|
+
const lv = v.toLowerCase();
|
|
1049
|
+
const d = distance(n, lv);
|
|
1050
|
+
const close = d <= Math.max(1, Math.floor(Math.min(n.length, lv.length) / 3));
|
|
1051
|
+
const abbreviation = n.length >= 3 && isSubsequence(n, lv) || lv.length >= 2 && n.length > lv.length && n.includes(lv);
|
|
1052
|
+
if (!close && !abbreviation) continue;
|
|
1053
|
+
const score = close ? d : 3 + (lv.length - n.length) / 100;
|
|
1054
|
+
if (!best || score < best.score) best = { v, score };
|
|
1055
|
+
}
|
|
1056
|
+
return best ? best.v : null;
|
|
1057
|
+
}
|
|
1058
|
+
function warnUnknownOptions(where, opts, valid) {
|
|
1059
|
+
if (!opts || typeof opts !== "object") return;
|
|
1060
|
+
for (const key of Object.keys(opts)) {
|
|
1061
|
+
if (valid.includes(key)) continue;
|
|
1062
|
+
const guess = suggestName(key, valid);
|
|
1063
|
+
console.warn(`pixi-effects: ${where}: unknown option "${key}"${guess ? ` \u2014 did you mean "${guess}"?` : ""} (valid: ${valid.join(", ")})`);
|
|
1064
|
+
}
|
|
1065
|
+
}
|
|
1066
|
+
|
|
2665
1067
|
// src/core/Movie.ts
|
|
2666
1068
|
PIXI.extensions.add(PIXI.CullerPlugin);
|
|
2667
1069
|
gsap.gsap.registerPlugin(PixiPlugin.PixiPlugin);
|
|
@@ -2720,6 +1122,7 @@ var Movie = class {
|
|
|
2720
1122
|
return this._audioContext;
|
|
2721
1123
|
}
|
|
2722
1124
|
async init(options = {}) {
|
|
1125
|
+
warnUnknownOptions("movie.init()", options, ["width", "height", "duration", "frameRate", "background", "canvas", "assets", "composition"]);
|
|
2723
1126
|
this._initState = "pending";
|
|
2724
1127
|
try {
|
|
2725
1128
|
this.width = options.width ?? 1920;
|
|
@@ -2771,7 +1174,7 @@ var Movie = class {
|
|
|
2771
1174
|
duration: this.duration,
|
|
2772
1175
|
...userComposition
|
|
2773
1176
|
};
|
|
2774
|
-
const composition = new CompositionSequence(rootSeqSpec, null, rootShape);
|
|
1177
|
+
const composition = new chunkZL262ZRM_cjs.CompositionSequence(rootSeqSpec, null, rootShape);
|
|
2775
1178
|
await composition.build();
|
|
2776
1179
|
this._rootSequence = composition;
|
|
2777
1180
|
if (composition.target) root.addChild(composition.target);
|
|
@@ -2782,7 +1185,9 @@ var Movie = class {
|
|
|
2782
1185
|
this.audioBuffer = await mixdown(audios, this.duration, audioContext.sampleRate);
|
|
2783
1186
|
}
|
|
2784
1187
|
this.timeline.progress(1).progress(0);
|
|
2785
|
-
this.
|
|
1188
|
+
await this._awaitVideoFrames();
|
|
1189
|
+
this._updateSpace();
|
|
1190
|
+
this._renderNow();
|
|
2786
1191
|
this._initState = "ready";
|
|
2787
1192
|
this.emit("ready");
|
|
2788
1193
|
} catch (err) {
|
|
@@ -2800,7 +1205,8 @@ var Movie = class {
|
|
|
2800
1205
|
this.currentFrame = Math.max(0, Math.min(frame, this.totalFrames));
|
|
2801
1206
|
this.timeline.time(this.currentFrame / this.frameRate);
|
|
2802
1207
|
await this._awaitVideoFrames();
|
|
2803
|
-
this.
|
|
1208
|
+
this._updateSpace();
|
|
1209
|
+
this._renderNow();
|
|
2804
1210
|
this.emit("frame", { frame: this.currentFrame, totalFrames: this.totalFrames });
|
|
2805
1211
|
}
|
|
2806
1212
|
async _awaitVideoFrames() {
|
|
@@ -2809,11 +1215,85 @@ var Movie = class {
|
|
|
2809
1215
|
collectVideoSequences(this._rootSequence, collected);
|
|
2810
1216
|
const t = this.currentFrame / this.frameRate;
|
|
2811
1217
|
await Promise.all(collected.map((v) => {
|
|
2812
|
-
const local = t - v.at;
|
|
1218
|
+
const local = t - (v.absoluteStart ?? v.at);
|
|
2813
1219
|
if (local < 0 || local > (v.duration ?? 0)) return Promise.resolve();
|
|
2814
1220
|
return v.awaitFrameAt(local);
|
|
2815
1221
|
}));
|
|
2816
1222
|
}
|
|
1223
|
+
async snapshot(frame = this.currentFrame, opts = {}) {
|
|
1224
|
+
warnUnknownOptions("movie.snapshot()", opts, ["scale", "type", "as"]);
|
|
1225
|
+
this._requireReady("snapshot");
|
|
1226
|
+
await this.gotoFrame(clampFrame(frame, this.totalFrames), true);
|
|
1227
|
+
const scale = opts.scale ?? 1;
|
|
1228
|
+
const out = document.createElement("canvas");
|
|
1229
|
+
out.width = Math.max(1, Math.round(this.width * scale));
|
|
1230
|
+
out.height = Math.max(1, Math.round(this.height * scale));
|
|
1231
|
+
out.getContext("2d").drawImage(this.app.canvas, 0, 0, out.width, out.height);
|
|
1232
|
+
return encodeCanvas(out, opts.type ?? "image/png", opts.as ?? "blob");
|
|
1233
|
+
}
|
|
1234
|
+
async contactSheet(opts = {}) {
|
|
1235
|
+
warnUnknownOptions("movie.contactSheet()", opts, ["frames", "times", "count", "columns", "cellWidth", "as"]);
|
|
1236
|
+
this._requireReady("contactSheet");
|
|
1237
|
+
const frames = (opts.frames ?? opts.times?.map((t) => Math.round(t * this.frameRate)) ?? pickFrames(this.totalFrames, opts.count ?? 6)).map((f) => clampFrame(f, this.totalFrames));
|
|
1238
|
+
const LABEL_H = 26;
|
|
1239
|
+
const L = sheetLayout(frames.length, opts.columns ?? 3, opts.cellWidth ?? 480, this.width, this.height, LABEL_H);
|
|
1240
|
+
const sheet = document.createElement("canvas");
|
|
1241
|
+
sheet.width = L.width;
|
|
1242
|
+
sheet.height = L.height;
|
|
1243
|
+
const g = sheet.getContext("2d");
|
|
1244
|
+
g.fillStyle = "#10131c";
|
|
1245
|
+
g.fillRect(0, 0, L.width, L.height);
|
|
1246
|
+
const back = this.currentFrame;
|
|
1247
|
+
try {
|
|
1248
|
+
for (let i = 0; i < frames.length; i++) {
|
|
1249
|
+
const f = frames[i];
|
|
1250
|
+
await this.gotoFrame(f, true);
|
|
1251
|
+
const { x, y } = L.positions[i];
|
|
1252
|
+
g.drawImage(this.app.canvas, x, y + LABEL_H, L.cellW, L.cellH);
|
|
1253
|
+
g.fillStyle = "#e8ecf8";
|
|
1254
|
+
g.font = "600 15px system-ui, sans-serif";
|
|
1255
|
+
g.textBaseline = "middle";
|
|
1256
|
+
g.fillText(`frame ${f} \xB7 ${(f / this.frameRate).toFixed(2)}s`, x + 8, y + LABEL_H / 2);
|
|
1257
|
+
}
|
|
1258
|
+
} finally {
|
|
1259
|
+
await this.gotoFrame(back, true);
|
|
1260
|
+
}
|
|
1261
|
+
return encodeCanvas(sheet, "image/png", opts.as ?? "blob");
|
|
1262
|
+
}
|
|
1263
|
+
/**
|
|
1264
|
+
* Where every layer is drawn at `frame` (canvas pixels, visibility) plus `issues` — text that is off
|
|
1265
|
+
* the canvas, cut by an edge, empty, or overlapping other text. Seeks there and stays there.
|
|
1266
|
+
*/
|
|
1267
|
+
async inspect(frame = this.currentFrame, opts = { layers: "visible" }) {
|
|
1268
|
+
warnUnknownOptions("movie.inspect()", opts, ["layers"]);
|
|
1269
|
+
this._requireReady("inspect");
|
|
1270
|
+
const f = clampFrame(frame, this.totalFrames);
|
|
1271
|
+
await this.gotoFrame(f, true);
|
|
1272
|
+
return inspectScene(this._rootSequence, f, f / this.frameRate, { width: this.width, height: this.height }, opts);
|
|
1273
|
+
}
|
|
1274
|
+
/**
|
|
1275
|
+
* Draw the stage now. PixiJS's CullerPlugin only culls inside `app.render()` (the ticker), and we render
|
|
1276
|
+
* by hand — while seeking and especially during `render()`, which stops the ticker — so without this the
|
|
1277
|
+
* `culled` flags are whatever the last tick left: a layer that is now on screen can stay culled and be
|
|
1278
|
+
* missing from the frame or from the exported video. Cull again (updating transforms: the timeline just
|
|
1279
|
+
* moved things) right before drawing.
|
|
1280
|
+
*/
|
|
1281
|
+
_renderNow() {
|
|
1282
|
+
const app = this.app;
|
|
1283
|
+
if (!app?.renderer) return;
|
|
1284
|
+
PIXI.Culler.shared.cull(app.stage, app.renderer.screen, false);
|
|
1285
|
+
app.renderer.render({ container: app.stage });
|
|
1286
|
+
}
|
|
1287
|
+
_requireReady(what) {
|
|
1288
|
+
if (this._initState !== "ready" || !this._rootSequence || !this.app) {
|
|
1289
|
+
throw new Error(`pixi-effects: movie.${what}() needs a ready movie \u2014 await movie.init(...) first`);
|
|
1290
|
+
}
|
|
1291
|
+
}
|
|
1292
|
+
/** Projects every `threeD` layer for the current frame (see src/space). */
|
|
1293
|
+
_updateSpace() {
|
|
1294
|
+
if (!this._rootSequence || !this.app) return;
|
|
1295
|
+
this._rootSequence.updateSpace(this.currentFrame / this.frameRate, this.app.renderer);
|
|
1296
|
+
}
|
|
2817
1297
|
play() {
|
|
2818
1298
|
if (this._initState !== "ready") return;
|
|
2819
1299
|
if (this.currentFrame >= this.totalFrames) this.currentFrame = 0;
|
|
@@ -2894,6 +1374,7 @@ var Movie = class {
|
|
|
2894
1374
|
return this.muted;
|
|
2895
1375
|
}
|
|
2896
1376
|
async render(options) {
|
|
1377
|
+
warnUnknownOptions("movie.render()", options, ["format", "video", "audio"]);
|
|
2897
1378
|
return await exportFrames(this, options);
|
|
2898
1379
|
}
|
|
2899
1380
|
async destroy() {
|
|
@@ -2911,6 +1392,11 @@ var Movie = class {
|
|
|
2911
1392
|
this._initState = "destroyed";
|
|
2912
1393
|
}
|
|
2913
1394
|
};
|
|
1395
|
+
var clampFrame = (f, last) => Math.max(0, Math.min(Math.round(f), last));
|
|
1396
|
+
function encodeCanvas(canvas, type, as) {
|
|
1397
|
+
if (as === "dataURL") return Promise.resolve(canvas.toDataURL(type));
|
|
1398
|
+
return new Promise((resolve, reject) => canvas.toBlob((b) => b ? resolve(b) : reject(new Error("pixi-effects: could not encode the image")), type));
|
|
1399
|
+
}
|
|
2914
1400
|
function safeRun(fn) {
|
|
2915
1401
|
try {
|
|
2916
1402
|
fn();
|
|
@@ -2922,13 +1408,16 @@ function collectVideoSequences(seq, out) {
|
|
|
2922
1408
|
if (typeof seq.awaitFrameAt === "function") {
|
|
2923
1409
|
out.push(seq);
|
|
2924
1410
|
}
|
|
1411
|
+
if (seq.maskSequence) collectVideoSequences(seq.maskSequence, out);
|
|
2925
1412
|
for (const child of seq._children ?? []) {
|
|
2926
1413
|
collectVideoSequences(child, out);
|
|
2927
1414
|
}
|
|
2928
1415
|
}
|
|
2929
1416
|
|
|
2930
1417
|
// src/presets/kenBurns.ts
|
|
1418
|
+
var KEN_BURNS_KEYS = ["asset", "name", "at", "duration", "fit", "ease", "motion", "origin", "zoom", "direction", "angle", "from", "to"];
|
|
2931
1419
|
function kenBurns(opts) {
|
|
1420
|
+
warnUnknownOptions("kenBurns()", opts, KEN_BURNS_KEYS);
|
|
2932
1421
|
const {
|
|
2933
1422
|
asset,
|
|
2934
1423
|
name,
|
|
@@ -2938,7 +1427,6 @@ function kenBurns(opts) {
|
|
|
2938
1427
|
ease = "sine.inOut"
|
|
2939
1428
|
} = opts;
|
|
2940
1429
|
const baseScale = fit;
|
|
2941
|
-
const baseAt = at ?? 0;
|
|
2942
1430
|
let initial;
|
|
2943
1431
|
let keyframes;
|
|
2944
1432
|
if (opts.motion === "still") {
|
|
@@ -2959,8 +1447,8 @@ function kenBurns(opts) {
|
|
|
2959
1447
|
const pivotXExpr = `w * ${origin[0]}`;
|
|
2960
1448
|
const pivotYExpr = `h * ${origin[1]}`;
|
|
2961
1449
|
const initialScale = `${baseScale} * ${fromZ}`;
|
|
2962
|
-
const xExpr = `W
|
|
2963
|
-
const yExpr = `H
|
|
1450
|
+
const xExpr = `W * ${origin[0]}`;
|
|
1451
|
+
const yExpr = `H * ${origin[1]}`;
|
|
2964
1452
|
initial = {
|
|
2965
1453
|
x: xExpr,
|
|
2966
1454
|
y: yExpr,
|
|
@@ -2969,7 +1457,7 @@ function kenBurns(opts) {
|
|
|
2969
1457
|
scale: initialScale
|
|
2970
1458
|
};
|
|
2971
1459
|
keyframes = [
|
|
2972
|
-
{ at:
|
|
1460
|
+
{ at: 0, to: { scale: `${baseScale} * ${toZ}` }, duration, ease }
|
|
2973
1461
|
];
|
|
2974
1462
|
} else if (opts.motion === "rotation") {
|
|
2975
1463
|
const angle = clamp(opts.angle ?? 8, 0, 30);
|
|
@@ -3000,7 +1488,7 @@ function kenBurns(opts) {
|
|
|
3000
1488
|
const tHere = duration * tFrac;
|
|
3001
1489
|
const tPrev = duration * ((i - 1) / (sampleCount - 1));
|
|
3002
1490
|
keyframes.push({
|
|
3003
|
-
at:
|
|
1491
|
+
at: tPrev,
|
|
3004
1492
|
to: { rotation: angleHere, scale: scaleAt(angleHere) },
|
|
3005
1493
|
duration: tHere - tPrev,
|
|
3006
1494
|
ease: i === 1 ? ease : "none"
|
|
@@ -3021,7 +1509,7 @@ function kenBurns(opts) {
|
|
|
3021
1509
|
scale: scaleExpr
|
|
3022
1510
|
};
|
|
3023
1511
|
keyframes = [
|
|
3024
|
-
{ at:
|
|
1512
|
+
{ at: 0, to: { x: xExpr(to[0]), y: yExpr(to[1]) }, duration, ease }
|
|
3025
1513
|
];
|
|
3026
1514
|
}
|
|
3027
1515
|
const spec = {
|
|
@@ -3038,8 +1526,93 @@ function kenBurns(opts) {
|
|
|
3038
1526
|
function clamp(v, lo, hi) {
|
|
3039
1527
|
return Math.max(lo, Math.min(hi, v));
|
|
3040
1528
|
}
|
|
1529
|
+
function parseEase(name) {
|
|
1530
|
+
return gsap.gsap.parseEase(name);
|
|
1531
|
+
}
|
|
1532
|
+
|
|
1533
|
+
// src/presets/orbit.ts
|
|
1534
|
+
var ORBIT_KEYS = ["duration", "degrees", "start", "radius", "center", "width", "height", "fov", "ease", "dollyZoom", "at", "name", "stepsPerSecond"];
|
|
1535
|
+
function orbit(opts) {
|
|
1536
|
+
warnUnknownOptions("orbit()", opts, ORBIT_KEYS);
|
|
1537
|
+
const {
|
|
1538
|
+
duration,
|
|
1539
|
+
degrees,
|
|
1540
|
+
width = 1280,
|
|
1541
|
+
height = 720,
|
|
1542
|
+
fov,
|
|
1543
|
+
ease = "sine.inOut",
|
|
1544
|
+
stepsPerSecond = 10
|
|
1545
|
+
} = opts;
|
|
1546
|
+
if (!(duration > 0)) throw new Error(`pixi-effects: orbit duration must be > 0 (got ${duration})`);
|
|
1547
|
+
const dolly = opts.dollyZoom;
|
|
1548
|
+
if (dolly && opts.radius !== void 0) {
|
|
1549
|
+
throw new Error("pixi-effects: orbit() cannot combine `radius` with `dollyZoom` \u2014 the radius follows the animated fov");
|
|
1550
|
+
}
|
|
1551
|
+
const [cx, cy] = opts.center ?? [width / 2, height / 2];
|
|
1552
|
+
const fixedR = opts.radius ?? chunkSIRVULFF_cjs.homeDistance(height, fov ?? chunkSIRVULFF_cjs.DEFAULT_FOV);
|
|
1553
|
+
const start = opts.start ?? -degrees / 2;
|
|
1554
|
+
const steps = Math.max(1, Math.round(duration * stepsPerSecond));
|
|
1555
|
+
const easeFn = parseEase(ease);
|
|
1556
|
+
const dollyEase = dolly ? parseEase(dolly.ease ?? ease) : null;
|
|
1557
|
+
const pos = (i) => {
|
|
1558
|
+
const p = i / steps;
|
|
1559
|
+
const a = (start + degrees * easeFn(p)) * chunkSIRVULFF_cjs.DEG;
|
|
1560
|
+
if (!dolly) return { x: cx + fixedR * Math.sin(a), z: fixedR * Math.cos(a) };
|
|
1561
|
+
const f = dolly.from + (dolly.to - dolly.from) * dollyEase(p);
|
|
1562
|
+
const R = chunkSIRVULFF_cjs.homeDistance(height, f);
|
|
1563
|
+
return { x: cx + R * Math.sin(a), z: R * Math.cos(a), fov: f };
|
|
1564
|
+
};
|
|
1565
|
+
const keyframes = [];
|
|
1566
|
+
for (let i = 0; i < steps; i++) {
|
|
1567
|
+
keyframes.push({ at: duration * i / steps, to: pos(i + 1), duration: duration / steps, ease: "none" });
|
|
1568
|
+
}
|
|
1569
|
+
const p0 = pos(0);
|
|
1570
|
+
const spec = {
|
|
1571
|
+
type: "camera",
|
|
1572
|
+
duration,
|
|
1573
|
+
initial: {
|
|
1574
|
+
x: p0.x,
|
|
1575
|
+
y: cy,
|
|
1576
|
+
z: p0.z,
|
|
1577
|
+
lookAtX: cx,
|
|
1578
|
+
lookAtY: cy,
|
|
1579
|
+
lookAtZ: 0,
|
|
1580
|
+
...p0.fov !== void 0 ? { fov: p0.fov } : fov !== void 0 ? { fov } : {}
|
|
1581
|
+
},
|
|
1582
|
+
keyframes
|
|
1583
|
+
};
|
|
1584
|
+
if (opts.at !== void 0) spec.at = opts.at;
|
|
1585
|
+
if (opts.name !== void 0) spec.name = opts.name;
|
|
1586
|
+
return spec;
|
|
1587
|
+
}
|
|
1588
|
+
|
|
1589
|
+
// src/transforms/withFade.ts
|
|
1590
|
+
function withFade(spec, opts) {
|
|
1591
|
+
warnUnknownOptions("withFade()", opts, ["in", "out"]);
|
|
1592
|
+
if (opts.in != null && opts.in > 0) {
|
|
1593
|
+
spec.initial = { ...spec.initial ?? {}, alpha: 0 };
|
|
1594
|
+
const kf = { at: 0, to: { alpha: 1 }, duration: opts.in };
|
|
1595
|
+
spec.keyframes = [...spec.keyframes ?? [], kf];
|
|
1596
|
+
}
|
|
1597
|
+
if (opts.out != null && opts.out > 0) {
|
|
1598
|
+
if (spec.duration == null) {
|
|
1599
|
+
throw new Error(
|
|
1600
|
+
"withFade(spec, { out }) requires spec.duration to be set so the fade-out can be anchored to the sequence end. Set spec.duration before passing the spec in."
|
|
1601
|
+
);
|
|
1602
|
+
}
|
|
1603
|
+
const kf = { at: spec.duration - opts.out, to: { alpha: 0 }, duration: opts.out };
|
|
1604
|
+
spec.keyframes = [...spec.keyframes ?? [], kf];
|
|
1605
|
+
}
|
|
1606
|
+
return spec;
|
|
1607
|
+
}
|
|
3041
1608
|
|
|
1609
|
+
Object.defineProperty(exports, "registerSequenceType", {
|
|
1610
|
+
enumerable: true,
|
|
1611
|
+
get: function () { return chunkSIRVULFF_cjs.registerSequenceType; }
|
|
1612
|
+
});
|
|
3042
1613
|
exports.Movie = Movie;
|
|
3043
1614
|
exports.kenBurns = kenBurns;
|
|
1615
|
+
exports.orbit = orbit;
|
|
1616
|
+
exports.withFade = withFade;
|
|
3044
1617
|
//# sourceMappingURL=index.cjs.map
|
|
3045
1618
|
//# sourceMappingURL=index.cjs.map
|