pixi-effects 0.30.1 → 0.32.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 +33 -0
- package/README.md +6 -5
- package/ai/chat-template.html +3 -3
- package/ai/tools/check.mjs +42 -2
- package/ai/tools/render.mjs +34 -4
- package/ai/tools/varArgs.mjs +75 -0
- package/dist/{Base-Ne74ztNl.d.ts → Base-CXsMdmJB.d.ts} +1 -1
- package/dist/{Base-CB1_FPZD.d.cts → Base-pNq6sVGl.d.cts} +1 -1
- package/dist/Composition-LNMKWXSL.cjs +16 -0
- package/dist/{Composition-MUKR3FYD.cjs.map → Composition-LNMKWXSL.cjs.map} +1 -1
- package/dist/Composition-RF7QOEXT.js +7 -0
- package/dist/{Composition-RJGA7KDU.js.map → Composition-RF7QOEXT.js.map} +1 -1
- package/dist/Controller.cjs +42 -1
- package/dist/Controller.cjs.map +1 -1
- package/dist/Controller.d.cts +10 -2
- package/dist/Controller.d.ts +10 -2
- package/dist/Controller.js +42 -1
- package/dist/Controller.js.map +1 -1
- package/dist/{Movie-DdnpLpw1.d.cts → Movie-CL6SsAWe.d.cts} +90 -3
- package/dist/{Movie-C2rZFkrO.d.ts → Movie-f0tQG9y5.d.ts} +90 -3
- package/dist/Spans-NQGKVMR5.cjs +55 -0
- package/dist/Spans-NQGKVMR5.cjs.map +1 -0
- package/dist/Spans-SDXABOV6.js +53 -0
- package/dist/Spans-SDXABOV6.js.map +1 -0
- package/dist/{blendModes-RIVZXL5A.js → blendModes-MVRLG3T7.js} +4 -4
- package/dist/{blendModes-RIVZXL5A.js.map → blendModes-MVRLG3T7.js.map} +1 -1
- package/dist/{blendModes-ETMYWATF.cjs → blendModes-VTKDXWEN.cjs} +5 -5
- package/dist/{blendModes-ETMYWATF.cjs.map → blendModes-VTKDXWEN.cjs.map} +1 -1
- package/dist/{chunk-ZAXYPXKW.js → chunk-AW5NPDE4.js} +1367 -1150
- package/dist/chunk-AW5NPDE4.js.map +1 -0
- package/dist/{chunk-RCVZX2JP.js → chunk-C5BNHVKK.js} +36 -3
- package/dist/chunk-C5BNHVKK.js.map +1 -0
- package/dist/{chunk-WHNADXXG.cjs → chunk-CRUDOKSD.cjs} +91 -91
- package/dist/chunk-CRUDOKSD.cjs.map +1 -0
- package/dist/{chunk-A3Q3SDSH.js → chunk-D5BFQVKL.js} +6 -6
- package/dist/chunk-D5BFQVKL.js.map +1 -0
- package/dist/{chunk-LYOKH7KE.cjs → chunk-FCGYJCVO.cjs} +36 -3
- package/dist/chunk-FCGYJCVO.cjs.map +1 -0
- package/dist/{chunk-CRQFDY7E.js → chunk-FCMOREIS.js} +467 -58
- package/dist/chunk-FCMOREIS.js.map +1 -0
- package/dist/{chunk-HREBQAY4.cjs → chunk-K3MAEKFM.cjs} +493 -84
- package/dist/chunk-K3MAEKFM.cjs.map +1 -0
- package/dist/{chunk-4KB3GUEK.js → chunk-QFNS6BFL.js} +4 -4
- package/dist/{chunk-4KB3GUEK.js.map → chunk-QFNS6BFL.js.map} +1 -1
- package/dist/{chunk-ZVUO325O.cjs → chunk-QHDN6TF5.cjs} +1382 -1160
- package/dist/chunk-QHDN6TF5.cjs.map +1 -0
- package/dist/{chunk-2DXPTKQZ.cjs → chunk-Z437UWI4.cjs} +5 -5
- package/dist/{chunk-2DXPTKQZ.cjs.map → chunk-Z437UWI4.cjs.map} +1 -1
- package/dist/index.cjs +33 -33
- package/dist/index.d.cts +5 -5
- package/dist/index.d.ts +5 -5
- package/dist/index.js +5 -5
- package/dist/loader.css +1 -1
- package/dist/present.cjs +5 -5
- package/dist/present.d.cts +2 -2
- package/dist/present.d.ts +2 -2
- package/dist/present.js +5 -5
- package/dist/three.cjs +6 -6
- package/dist/three.d.cts +2 -2
- package/dist/three.d.ts +2 -2
- package/dist/three.js +2 -2
- package/dist/{types-BzIB65Vk.d.ts → types-CavnUvd5.d.cts} +28 -1
- package/dist/{types-BzIB65Vk.d.cts → types-CavnUvd5.d.ts} +28 -1
- package/docs/api.md +28 -1
- package/docs/dsl.md +46 -5
- package/llms-full.txt +136 -10
- package/package.json +83 -20
- package/skills/pixi-effects/SKILL.md +1 -1
- package/skills/pixi-effects/reference/cheatsheet.md +5 -2
- package/skills/pixi-effects/reference/pitfalls.md +5 -1
- package/skills/pixi-effects/reference/recipes.md +52 -0
- package/skills/pixi-effects/template.html +6 -6
- package/dist/Composition-MUKR3FYD.cjs +0 -16
- package/dist/Composition-RJGA7KDU.js +0 -7
- package/dist/chunk-A3Q3SDSH.js.map +0 -1
- package/dist/chunk-CRQFDY7E.js.map +0 -1
- package/dist/chunk-HREBQAY4.cjs.map +0 -1
- package/dist/chunk-LYOKH7KE.cjs.map +0 -1
- package/dist/chunk-RCVZX2JP.js.map +0 -1
- package/dist/chunk-WHNADXXG.cjs.map +0 -1
- package/dist/chunk-ZAXYPXKW.js.map +0 -1
- package/dist/chunk-ZVUO325O.cjs.map +0 -1
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { CompositionSequence, maskSourceOf } from './chunk-
|
|
2
|
-
import { createTimeline, lengthen, ensureFilterLibrary, checkEase, parseEase, homeDistance, DEFAULT_FOV, random, noise, flattenSvgPath, rand, DEG, timeRemapOf, contentLength, resolveAt } from './chunk-
|
|
3
|
-
import { usesAdvancedBlend, enableAdvancedBlend } from './chunk-
|
|
4
|
-
import { collectPropKeys } from './chunk-
|
|
1
|
+
import { CompositionSequence, maskSourceOf } from './chunk-D5BFQVKL.js';
|
|
2
|
+
import { EXPRESSION_NAMES, createTimeline, lengthen, ensureFilterLibrary, checkEase, parseEase, homeDistance, DEFAULT_FOV, random, noise, flattenSvgPath, rand, DEG, timeRemapOf, contentLength, resolveAt, spansMeta } from './chunk-AW5NPDE4.js';
|
|
3
|
+
import { usesAdvancedBlend, enableAdvancedBlend } from './chunk-QFNS6BFL.js';
|
|
4
|
+
import { collectPropKeys } from './chunk-C5BNHVKK.js';
|
|
5
5
|
import { warnUnknownOptions, suggestName } from './chunk-BXOCEZ4U.js';
|
|
6
6
|
import { extensions, CullerPlugin, Application, Container, Rectangle, Culler, CanvasTextMetrics, TextStyle, Assets, ExtensionType, Filter, Texture, UniformGroup, GpuProgram, GlProgram, defaultFilterVert } from 'pixi.js';
|
|
7
7
|
import { Output, BufferTarget, CanvasSource, AudioBufferSource, Input, ALL_FORMATS, BlobSource, WavOutputFormat, Conversion, VideoSampleSink, OggOutputFormat, QUALITY_VERY_HIGH, QUALITY_HIGH, QUALITY_MEDIUM, QUALITY_LOW, QUALITY_VERY_LOW, MkvOutputFormat, WebMOutputFormat, MovOutputFormat, Mp4OutputFormat, canEncodeVideo, getFirstEncodableAudioCodec } from 'mediabunny';
|
|
@@ -1996,6 +1996,8 @@ var round = (n) => Math.round(n * 1e6) / 1e6;
|
|
|
1996
1996
|
var family = (name) => name.replace(/\d+/g, "#");
|
|
1997
1997
|
function detailOf(seq) {
|
|
1998
1998
|
const s = seq.spec;
|
|
1999
|
+
const spans = spansMeta.get(seq.spec)?.preview;
|
|
2000
|
+
if (spans) return spans.length > 60 ? spans.slice(0, 57) + "\u2026" : spans;
|
|
1999
2001
|
if (typeof s.text === "string") return s.text.length > 60 ? s.text.slice(0, 57) + "\u2026" : s.text;
|
|
2000
2002
|
if (typeof s.asset === "string") return `asset \u201C${s.asset}\u201D`;
|
|
2001
2003
|
if (typeof s.sfx === "string") return `sfx \u201C${s.sfx}\u201D`;
|
|
@@ -2171,6 +2173,124 @@ function sheetLayout(n, columns, cellW, srcW, srcH, labelH) {
|
|
|
2171
2173
|
return { cols, rows, cellW, cellH, width: cols * cellW, height: rows * (cellH + labelH), positions };
|
|
2172
2174
|
}
|
|
2173
2175
|
|
|
2176
|
+
// src/core/vars.ts
|
|
2177
|
+
var RESERVED_NAMES = ["w", "h", "W", "H", "GW", "GH", "t", "d", "T", "contain", "cover", "value", ...EXPRESSION_NAMES];
|
|
2178
|
+
var NAME = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
2179
|
+
var isPlain = (v) => !!v && typeof v === "object" && (Object.getPrototypeOf(v) === Object.prototype || Object.getPrototypeOf(v) === null);
|
|
2180
|
+
function normalizeVars(raw, where, warn = (m) => console.warn(m)) {
|
|
2181
|
+
if (raw === void 0) return {};
|
|
2182
|
+
if (!isPlain(raw)) {
|
|
2183
|
+
warn(`pixi-effects: ${where}: vars must be an object of names and values ({ title: 'NORD', shift: 40 }); ignored`);
|
|
2184
|
+
return {};
|
|
2185
|
+
}
|
|
2186
|
+
const out = {};
|
|
2187
|
+
for (const [name, value] of Object.entries(raw)) {
|
|
2188
|
+
if (!NAME.test(name)) {
|
|
2189
|
+
warn(`pixi-effects: ${where}: "${name}" is not a name for a variable (letters, digits and _, not starting with a digit); ignored`);
|
|
2190
|
+
continue;
|
|
2191
|
+
}
|
|
2192
|
+
if (RESERVED_NAMES.includes(name)) {
|
|
2193
|
+
warn(`pixi-effects: ${where}: "${name}" is reserved (an expression word, a function, PI or the {value} counter); choose another name; ignored`);
|
|
2194
|
+
continue;
|
|
2195
|
+
}
|
|
2196
|
+
if (typeof value === "boolean") {
|
|
2197
|
+
warn(`pixi-effects: ${where}: variable "${name}" is a boolean; write 0 or 1 (a variable is a number or a string); ignored`);
|
|
2198
|
+
continue;
|
|
2199
|
+
}
|
|
2200
|
+
if (typeof value === "number" && Number.isFinite(value)) out[name] = value;
|
|
2201
|
+
else if (typeof value === "string") out[name] = value;
|
|
2202
|
+
else warn(`pixi-effects: ${where}: variable "${name}" must be a number or a string, got ${JSON.stringify(value)}; ignored`);
|
|
2203
|
+
}
|
|
2204
|
+
return out;
|
|
2205
|
+
}
|
|
2206
|
+
function numericVars(vars) {
|
|
2207
|
+
const out = {};
|
|
2208
|
+
for (const [k, v] of Object.entries(vars)) if (typeof v === "number") out[k] = v;
|
|
2209
|
+
return out;
|
|
2210
|
+
}
|
|
2211
|
+
var TOKEN = /\{([A-Za-z_][A-Za-z0-9_]*)\}/g;
|
|
2212
|
+
function substitute(s, vars) {
|
|
2213
|
+
return s.replace(TOKEN, (whole, name) => Object.prototype.hasOwnProperty.call(vars, name) ? String(vars[name]) : whole);
|
|
2214
|
+
}
|
|
2215
|
+
function applyVars(spec, vars) {
|
|
2216
|
+
if (Object.keys(vars).length === 0) return spec;
|
|
2217
|
+
const walk2 = (v) => {
|
|
2218
|
+
if (typeof v === "string") return substitute(v, vars);
|
|
2219
|
+
if (Array.isArray(v)) return v.map(walk2);
|
|
2220
|
+
if (isPlain(v)) {
|
|
2221
|
+
const o = {};
|
|
2222
|
+
for (const [k, x] of Object.entries(v)) o[k] = walk2(x);
|
|
2223
|
+
return o;
|
|
2224
|
+
}
|
|
2225
|
+
return v;
|
|
2226
|
+
};
|
|
2227
|
+
return walk2(spec);
|
|
2228
|
+
}
|
|
2229
|
+
var escapeRe = (s) => s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
2230
|
+
function lintVars(spec, vars) {
|
|
2231
|
+
const declared = Object.keys(vars);
|
|
2232
|
+
const strings = [];
|
|
2233
|
+
const walk2 = (v) => {
|
|
2234
|
+
if (typeof v === "string") strings.push(v);
|
|
2235
|
+
else if (Array.isArray(v)) v.forEach(walk2);
|
|
2236
|
+
else if (isPlain(v)) Object.values(v).forEach(walk2);
|
|
2237
|
+
};
|
|
2238
|
+
walk2(spec);
|
|
2239
|
+
const out = [];
|
|
2240
|
+
for (const name of declared) {
|
|
2241
|
+
const word = new RegExp(`(?<![A-Za-z0-9_.])${escapeRe(name)}(?![A-Za-z0-9_])`);
|
|
2242
|
+
if (!strings.some((s) => word.test(s))) out.push(`pixi-effects: vars: variable "${name}" is declared but nothing uses it (write {${name}} in a string, or "${name}" in an expression); a typo on one side?`);
|
|
2243
|
+
}
|
|
2244
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2245
|
+
for (const s of strings) {
|
|
2246
|
+
for (const m of s.matchAll(TOKEN)) {
|
|
2247
|
+
const name = m[1];
|
|
2248
|
+
if (seen.has(name) || Object.prototype.hasOwnProperty.call(vars, name) || RESERVED_NAMES.includes(name) || declared.length === 0) continue;
|
|
2249
|
+
seen.add(name);
|
|
2250
|
+
const guess = suggestName(name, declared);
|
|
2251
|
+
out.push(`pixi-effects: vars: {${name}} is not a declared variable${guess ? ` \u2014 did you mean "${guess}"?` : ` (declared: ${declared.join(", ")})`}`);
|
|
2252
|
+
}
|
|
2253
|
+
}
|
|
2254
|
+
return out;
|
|
2255
|
+
}
|
|
2256
|
+
|
|
2257
|
+
// src/core/mixCache.ts
|
|
2258
|
+
var ids = /* @__PURE__ */ new WeakMap();
|
|
2259
|
+
var nextId = 1;
|
|
2260
|
+
var idOf = (o) => {
|
|
2261
|
+
let n = ids.get(o);
|
|
2262
|
+
if (!n) {
|
|
2263
|
+
n = nextId++;
|
|
2264
|
+
ids.set(o, n);
|
|
2265
|
+
}
|
|
2266
|
+
return n;
|
|
2267
|
+
};
|
|
2268
|
+
function mixKey(audios, duration, sampleRate) {
|
|
2269
|
+
const parts = [];
|
|
2270
|
+
for (const a of audios) {
|
|
2271
|
+
if (a.warp || a.sourceMap) return null;
|
|
2272
|
+
const what = a.buffer ? `b${idOf(a.buffer)}` : a.synth ? `s${a.synth.key}` : null;
|
|
2273
|
+
if (what === null) return null;
|
|
2274
|
+
parts.push([what, a.loop, a.start, a.end, a.initialVolume, a.volumeKeyframes.map((k) => [k.time, k.value])]);
|
|
2275
|
+
}
|
|
2276
|
+
return JSON.stringify([duration, sampleRate, parts]);
|
|
2277
|
+
}
|
|
2278
|
+
var MixCache = class {
|
|
2279
|
+
key = null;
|
|
2280
|
+
buffer = null;
|
|
2281
|
+
extra;
|
|
2282
|
+
get(key) {
|
|
2283
|
+
if (key === null || key !== this.key || !this.buffer) return null;
|
|
2284
|
+
return { buffer: this.buffer, extra: this.extra };
|
|
2285
|
+
}
|
|
2286
|
+
/** Keep this mix for `key`; a null key (not comparable) keeps nothing. */
|
|
2287
|
+
set(key, buffer, extra) {
|
|
2288
|
+
this.key = key;
|
|
2289
|
+
this.buffer = key === null ? null : buffer;
|
|
2290
|
+
this.extra = extra;
|
|
2291
|
+
}
|
|
2292
|
+
};
|
|
2293
|
+
|
|
2174
2294
|
// src/core/startWhenRunning.ts
|
|
2175
2295
|
function startWhenRunning(ctx, start, gesture) {
|
|
2176
2296
|
if (ctx.state === "running") {
|
|
@@ -2454,6 +2574,7 @@ var Movie = class {
|
|
|
2454
2574
|
_quiet = 0;
|
|
2455
2575
|
_volume = 1;
|
|
2456
2576
|
_muted = false;
|
|
2577
|
+
_loop = false;
|
|
2457
2578
|
isPlaying = false;
|
|
2458
2579
|
currentFrame = 0;
|
|
2459
2580
|
totalFrames = 0;
|
|
@@ -2475,6 +2596,21 @@ var Movie = class {
|
|
|
2475
2596
|
}
|
|
2476
2597
|
_rootContainer = null;
|
|
2477
2598
|
_raf = null;
|
|
2599
|
+
/** The variables now, and the names that were declared (a `setVars` of any other name is refused). */
|
|
2600
|
+
_vars = {};
|
|
2601
|
+
_declared = /* @__PURE__ */ new Set();
|
|
2602
|
+
/** The composition as the author wrote it (before `{name}` is replaced): a rebuild starts from it. */
|
|
2603
|
+
_composition;
|
|
2604
|
+
/** True once `resize()` was called: the size of the movie then replaces an explicit `width` / `height` of the composition. */
|
|
2605
|
+
_resized = false;
|
|
2606
|
+
_mixCache = new MixCache();
|
|
2607
|
+
_rebuildRun = null;
|
|
2608
|
+
_rebuildNext = null;
|
|
2609
|
+
/** > 0 while a rebuild stops and restarts playback: `play` / `pause` are not announced then (a player's button must not flicker). */
|
|
2610
|
+
_silent = 0;
|
|
2611
|
+
/** render() / peek() in flight: a rebuild does not swap the composition under them. */
|
|
2612
|
+
_busy = 0;
|
|
2613
|
+
_idleWaiters = [];
|
|
2478
2614
|
/** What went into the mix, and its peak before limiting — kept for inspectAudio(). */
|
|
2479
2615
|
_audioSources = [];
|
|
2480
2616
|
_mixStats = null;
|
|
@@ -2550,7 +2686,7 @@ var Movie = class {
|
|
|
2550
2686
|
return this._audioContext;
|
|
2551
2687
|
}
|
|
2552
2688
|
async init(options = {}) {
|
|
2553
|
-
warnUnknownOptions("movie.init()", options, ["width", "height", "duration", "frameRate", "background", "canvas", "assets", "composition", "poster", "motionBlur", "loader"]);
|
|
2689
|
+
warnUnknownOptions("movie.init()", options, ["width", "height", "duration", "frameRate", "background", "canvas", "assets", "composition", "vars", "poster", "motionBlur", "loader", "loop"]);
|
|
2554
2690
|
this._initState = "pending";
|
|
2555
2691
|
try {
|
|
2556
2692
|
this.width = options.width ?? 1920;
|
|
@@ -2561,7 +2697,14 @@ var Movie = class {
|
|
|
2561
2697
|
const poster = normalizePoster(options.poster, this.duration, this.frameRate);
|
|
2562
2698
|
this.poster = poster.seconds;
|
|
2563
2699
|
this.motionBlur = resolveMotionBlur(options.motionBlur, "movie.init()");
|
|
2564
|
-
this.
|
|
2700
|
+
this._loop = options.loop === true;
|
|
2701
|
+
this._vars = normalizeVars(options.vars, "movie.init()");
|
|
2702
|
+
this._declared = new Set(Object.keys(this._vars));
|
|
2703
|
+
this._composition = options.composition;
|
|
2704
|
+
this._resized = false;
|
|
2705
|
+
for (const w of lintVars(options.composition, this._vars)) console.warn(w);
|
|
2706
|
+
this._mixCache.set(null, null);
|
|
2707
|
+
this.stops = normalizeStops(applyVars(options.composition, this._vars)?.stops, this.duration, this.frameRate);
|
|
2565
2708
|
this._posterFrame = poster.frame;
|
|
2566
2709
|
this.background = options.background ?? "#000000";
|
|
2567
2710
|
this.timeline = createTimeline({ paused: true, defaults: { ease: "none" } });
|
|
@@ -2584,7 +2727,6 @@ var Movie = class {
|
|
|
2584
2727
|
this.app = new Application();
|
|
2585
2728
|
await this.app.init({ ...baseAppOptions, preference: "webgl" });
|
|
2586
2729
|
}
|
|
2587
|
-
if (usesAdvancedBlend(options.composition)) await enableAdvancedBlend(this.app.renderer);
|
|
2588
2730
|
const root = new Container();
|
|
2589
2731
|
root.cullable = true;
|
|
2590
2732
|
root.cullableChildren = true;
|
|
@@ -2608,51 +2750,13 @@ var Movie = class {
|
|
|
2608
2750
|
assetsSeen = n;
|
|
2609
2751
|
}
|
|
2610
2752
|
});
|
|
2611
|
-
const
|
|
2612
|
-
width: this.width,
|
|
2613
|
-
height: this.height,
|
|
2614
|
-
duration: this.duration,
|
|
2615
|
-
frameRate: this.frameRate,
|
|
2616
|
-
onLayerBuilt: async () => {
|
|
2617
|
-
lp.tick();
|
|
2618
|
-
await lp.yieldIfDue();
|
|
2619
|
-
}
|
|
2620
|
-
};
|
|
2621
|
-
const seededComposition = options.composition ? {
|
|
2622
|
-
...options.composition,
|
|
2623
|
-
width: options.composition.width ?? this.width,
|
|
2624
|
-
height: options.composition.height ?? this.height,
|
|
2625
|
-
duration: options.composition.duration ?? this.duration
|
|
2626
|
-
} : void 0;
|
|
2627
|
-
await ensureFilterLibrary(seededComposition);
|
|
2628
|
-
const userComposition = seededComposition ? expandTransitions(seededComposition) : void 0;
|
|
2629
|
-
const rootSeqSpec = {
|
|
2630
|
-
type: "composition",
|
|
2631
|
-
width: this.width,
|
|
2632
|
-
height: this.height,
|
|
2633
|
-
duration: this.duration,
|
|
2634
|
-
...userComposition
|
|
2635
|
-
};
|
|
2636
|
-
if (userComposition) carryTransitionWindows(userComposition, rootSeqSpec);
|
|
2637
|
-
const composition = new CompositionSequence(rootSeqSpec, null, rootShape);
|
|
2638
|
-
lp.begin("build", countLayers(rootSeqSpec));
|
|
2639
|
-
await composition.build();
|
|
2753
|
+
const composition = await this._construct(this._composition, this._vars, this.width, this.height, lp);
|
|
2640
2754
|
this._rootSequence = composition;
|
|
2641
2755
|
if (composition.target) root.addChild(composition.target);
|
|
2642
2756
|
composition.bindTimeline(this.timeline);
|
|
2643
2757
|
this._timeFilters = collectTimeFilters(composition);
|
|
2644
|
-
const audios = [];
|
|
2645
|
-
composition.collectAudio(audios, 0);
|
|
2646
2758
|
lp.begin("sound", 1);
|
|
2647
|
-
|
|
2648
|
-
this.audioBuffer = await mixdown(audios, this.duration, audioContext.sampleRate, async (done) => {
|
|
2649
|
-
lp.part(done);
|
|
2650
|
-
await lp.yieldIfDue();
|
|
2651
|
-
});
|
|
2652
|
-
lp.tick();
|
|
2653
|
-
if (this.audioBuffer) this._mixStats = limitMix(this.audioBuffer, audios);
|
|
2654
|
-
}
|
|
2655
|
-
this._audioSources = audios;
|
|
2759
|
+
await this._mixSound(composition, audioContext, lp);
|
|
2656
2760
|
lp.begin("frames", 0);
|
|
2657
2761
|
this.timeline.time(this._timeOf(0));
|
|
2658
2762
|
await this._awaitVideoFrames();
|
|
@@ -2675,6 +2779,254 @@ var Movie = class {
|
|
|
2675
2779
|
throw err;
|
|
2676
2780
|
}
|
|
2677
2781
|
}
|
|
2782
|
+
/**
|
|
2783
|
+
* Build the composition tree for these variables and this size, off stage: `{name}` replaced, the root shape (with the numeric variables for the
|
|
2784
|
+
* expressions), transitions expanded, every layer built. Used by `init` and by `rebuild`.
|
|
2785
|
+
*/
|
|
2786
|
+
async _construct(composition, vars, width, height, lp, sizeWins = this._resized) {
|
|
2787
|
+
const spec = applyVars(composition, vars);
|
|
2788
|
+
const rootShape = {
|
|
2789
|
+
width,
|
|
2790
|
+
height,
|
|
2791
|
+
duration: this.duration,
|
|
2792
|
+
frameRate: this.frameRate,
|
|
2793
|
+
vars: numericVars(vars),
|
|
2794
|
+
...lp ? { onLayerBuilt: async () => {
|
|
2795
|
+
lp.tick();
|
|
2796
|
+
await lp.yieldIfDue();
|
|
2797
|
+
} } : {}
|
|
2798
|
+
};
|
|
2799
|
+
const seededComposition = spec ? {
|
|
2800
|
+
...spec,
|
|
2801
|
+
width: sizeWins ? width : spec.width ?? width,
|
|
2802
|
+
height: sizeWins ? height : spec.height ?? height,
|
|
2803
|
+
duration: spec.duration ?? this.duration
|
|
2804
|
+
} : void 0;
|
|
2805
|
+
await ensureFilterLibrary(seededComposition);
|
|
2806
|
+
if (usesAdvancedBlend(seededComposition)) await enableAdvancedBlend(this.app.renderer);
|
|
2807
|
+
const userComposition = seededComposition ? expandTransitions(seededComposition) : void 0;
|
|
2808
|
+
const rootSeqSpec = {
|
|
2809
|
+
type: "composition",
|
|
2810
|
+
width,
|
|
2811
|
+
height,
|
|
2812
|
+
duration: this.duration,
|
|
2813
|
+
...userComposition
|
|
2814
|
+
};
|
|
2815
|
+
if (userComposition) carryTransitionWindows(userComposition, rootSeqSpec);
|
|
2816
|
+
const sequence = new CompositionSequence(rootSeqSpec, null, rootShape);
|
|
2817
|
+
lp?.begin("build", countLayers(rootSeqSpec));
|
|
2818
|
+
try {
|
|
2819
|
+
await sequence.build();
|
|
2820
|
+
} catch (err) {
|
|
2821
|
+
safeRun(() => sequence.destroy());
|
|
2822
|
+
throw err;
|
|
2823
|
+
}
|
|
2824
|
+
return sequence;
|
|
2825
|
+
}
|
|
2826
|
+
/** Make the sound of a built composition: the same mix as last time when the sounds are the same (`'reused'`), else mix it. */
|
|
2827
|
+
async _mixSound(sequence, audioContext, lp) {
|
|
2828
|
+
const audios = [];
|
|
2829
|
+
sequence.collectAudio(audios, 0);
|
|
2830
|
+
let status = "none";
|
|
2831
|
+
if (audios.length === 0) {
|
|
2832
|
+
this.audioBuffer = null;
|
|
2833
|
+
this._mixStats = null;
|
|
2834
|
+
this._mixCache.set(null, null);
|
|
2835
|
+
} else {
|
|
2836
|
+
const key = mixKey(audios, this.duration, audioContext.sampleRate);
|
|
2837
|
+
const hit = this._mixCache.get(key);
|
|
2838
|
+
if (hit) {
|
|
2839
|
+
this.audioBuffer = hit.buffer;
|
|
2840
|
+
this._mixStats = hit.extra;
|
|
2841
|
+
status = "reused";
|
|
2842
|
+
} else {
|
|
2843
|
+
this.audioBuffer = await mixdown(audios, this.duration, audioContext.sampleRate, lp ? async (done) => {
|
|
2844
|
+
lp.part(done);
|
|
2845
|
+
await lp.yieldIfDue();
|
|
2846
|
+
} : void 0);
|
|
2847
|
+
this._mixStats = this.audioBuffer ? limitMix(this.audioBuffer, audios) : null;
|
|
2848
|
+
this._mixCache.set(key, this.audioBuffer, this._mixStats);
|
|
2849
|
+
status = "mixed";
|
|
2850
|
+
}
|
|
2851
|
+
lp?.tick();
|
|
2852
|
+
}
|
|
2853
|
+
this._audioSources = audios;
|
|
2854
|
+
return status;
|
|
2855
|
+
}
|
|
2856
|
+
/** The variables now (a copy; change them with `setVars()`). */
|
|
2857
|
+
get vars() {
|
|
2858
|
+
return { ...this._vars };
|
|
2859
|
+
}
|
|
2860
|
+
/**
|
|
2861
|
+
* Change variables and rebuild at the same time. Only the declared names (`movie.init({ vars })`) are accepted; another name says which one it
|
|
2862
|
+
* looks like and is ignored. Values it already has change nothing: no rebuild (`unchanged: true`).
|
|
2863
|
+
*/
|
|
2864
|
+
async setVars(changes) {
|
|
2865
|
+
this._requireReady("setVars");
|
|
2866
|
+
return this.rebuild({ vars: changes }, "vars");
|
|
2867
|
+
}
|
|
2868
|
+
/** Make the movie `width` x `height` pixels (the canvas, and `W` / `H` / `GW` / `GH` in every expression) and rebuild at the same time. */
|
|
2869
|
+
async resize(width, height) {
|
|
2870
|
+
this._requireReady("resize");
|
|
2871
|
+
return this.rebuild({ width, height }, "resize");
|
|
2872
|
+
}
|
|
2873
|
+
/**
|
|
2874
|
+
* Build the composition again, with what `change` replaces (variables, the composition, the size), and put the movie back where it was: the
|
|
2875
|
+
* same frame, playing or not, the poster if that was shown. The new composition is built first: when that fails the old one stays, `error` is
|
|
2876
|
+
* emitted and the promise rejects. The sound is mixed again only when it changed (`audio: 'reused'` otherwise). Calls that arrive while a
|
|
2877
|
+
* rebuild runs are joined into one more rebuild (the latest values win), so a slider can call it on every move.
|
|
2878
|
+
*/
|
|
2879
|
+
async rebuild(change = {}, reason = "rebuild") {
|
|
2880
|
+
this._requireReady("rebuild");
|
|
2881
|
+
warnUnknownOptions("movie.rebuild()", change, ["composition", "vars", "width", "height"]);
|
|
2882
|
+
if (this._rebuildRun) {
|
|
2883
|
+
if (!this._rebuildNext) {
|
|
2884
|
+
let resolve, reject;
|
|
2885
|
+
const promise = new Promise((res, rej) => {
|
|
2886
|
+
resolve = res;
|
|
2887
|
+
reject = rej;
|
|
2888
|
+
});
|
|
2889
|
+
this._rebuildNext = { change: {}, reason, promise, resolve, reject };
|
|
2890
|
+
}
|
|
2891
|
+
const next = this._rebuildNext;
|
|
2892
|
+
next.change = { ...next.change, ...change, vars: { ...next.change.vars ?? {}, ...change.vars ?? {} } };
|
|
2893
|
+
if (reason !== "vars") next.reason = reason;
|
|
2894
|
+
return next.promise;
|
|
2895
|
+
}
|
|
2896
|
+
return this._runRebuild(change, reason);
|
|
2897
|
+
}
|
|
2898
|
+
_runRebuild(change, reason) {
|
|
2899
|
+
const run = this._doRebuild(change, reason);
|
|
2900
|
+
this._rebuildRun = run;
|
|
2901
|
+
const settled = () => {
|
|
2902
|
+
if (this._rebuildRun === run) this._rebuildRun = null;
|
|
2903
|
+
const next = this._rebuildNext;
|
|
2904
|
+
if (next && this._initState === "ready") {
|
|
2905
|
+
this._rebuildNext = null;
|
|
2906
|
+
this._runRebuild(next.change, next.reason).then(next.resolve, next.reject);
|
|
2907
|
+
} else if (next) {
|
|
2908
|
+
this._rebuildNext = null;
|
|
2909
|
+
next.reject(new Error("pixi-effects: the movie was destroyed before the rebuild could run"));
|
|
2910
|
+
}
|
|
2911
|
+
};
|
|
2912
|
+
run.then(settled, settled);
|
|
2913
|
+
return run;
|
|
2914
|
+
}
|
|
2915
|
+
async _doRebuild(change, reason) {
|
|
2916
|
+
const started = performance.now();
|
|
2917
|
+
const root = this._rootContainer, app = this.app;
|
|
2918
|
+
if (!root || !app) throw new Error("pixi-effects: movie.rebuild() needs a ready movie \u2014 await movie.init(...) first");
|
|
2919
|
+
const who = `movie.${reason === "vars" ? "setVars" : "rebuild"}()`;
|
|
2920
|
+
let sequence = null;
|
|
2921
|
+
const alive = () => {
|
|
2922
|
+
if (this._initState !== "ready") throw new Error("pixi-effects: the movie was destroyed while it was being rebuilt");
|
|
2923
|
+
};
|
|
2924
|
+
try {
|
|
2925
|
+
const vars = { ...this._vars };
|
|
2926
|
+
let changed = false;
|
|
2927
|
+
if (change.vars !== void 0) {
|
|
2928
|
+
const given = normalizeVars(change.vars, who);
|
|
2929
|
+
for (const [name, value] of Object.entries(given)) {
|
|
2930
|
+
if (!this._declared.has(name)) {
|
|
2931
|
+
const guess = suggestName(name, [...this._declared]);
|
|
2932
|
+
console.warn(`pixi-effects: ${who}: "${name}" is not a declared variable${guess ? ` \u2014 did you mean "${guess}"?` : ""} (declared in movie.init({ vars }): ${[...this._declared].join(", ") || "none"}); ignored`);
|
|
2933
|
+
continue;
|
|
2934
|
+
}
|
|
2935
|
+
if (vars[name] !== value) {
|
|
2936
|
+
vars[name] = value;
|
|
2937
|
+
changed = true;
|
|
2938
|
+
}
|
|
2939
|
+
}
|
|
2940
|
+
}
|
|
2941
|
+
for (const [k, v] of [["width", change.width], ["height", change.height]]) {
|
|
2942
|
+
if (v !== void 0 && !(Number.isFinite(v) && v >= 1)) throw new Error(`pixi-effects: movie.rebuild(): ${k} must be a number of pixels (1 or more), got ${JSON.stringify(v)}`);
|
|
2943
|
+
}
|
|
2944
|
+
const width = change.width ?? this.width, height = change.height ?? this.height;
|
|
2945
|
+
const sizeChanged = width !== this.width || height !== this.height;
|
|
2946
|
+
const composition = change.composition ?? this._composition;
|
|
2947
|
+
if (change.composition !== void 0) for (const w of lintVars(change.composition, vars)) console.warn(w);
|
|
2948
|
+
changed = changed || sizeChanged || change.composition !== void 0 || reason === "rebuild";
|
|
2949
|
+
if (!changed) return { reason, ms: 0, audio: "none", frame: this.currentFrame, playing: this.isPlaying, unchanged: true };
|
|
2950
|
+
sequence = await this._construct(composition, vars, width, height, void 0, this._resized || sizeChanged);
|
|
2951
|
+
alive();
|
|
2952
|
+
const timeline = createTimeline({ paused: true, defaults: { ease: "none" } });
|
|
2953
|
+
lengthen(timeline, this.duration);
|
|
2954
|
+
sequence.bindTimeline(timeline);
|
|
2955
|
+
const hadSound = this.audioBuffer !== null;
|
|
2956
|
+
const audio = await this._mixSound(sequence, this._ensureAudioContext());
|
|
2957
|
+
const soundChanged = audio === "mixed" || audio === "none" && hadSound;
|
|
2958
|
+
while (this._busy > 0) {
|
|
2959
|
+
await new Promise((r) => this._idleWaiters.push(r));
|
|
2960
|
+
}
|
|
2961
|
+
alive();
|
|
2962
|
+
const wasPlaying = this.isPlaying, frame = this.currentFrame, wasAtPoster = this._atPoster;
|
|
2963
|
+
const oldSize = { w: this.width, h: this.height }, oldResized = this._resized;
|
|
2964
|
+
const goOn = wasPlaying && !soundChanged;
|
|
2965
|
+
this._silent++;
|
|
2966
|
+
try {
|
|
2967
|
+
if (goOn) this._cancelUntil();
|
|
2968
|
+
else this.pause();
|
|
2969
|
+
if (sizeChanged) {
|
|
2970
|
+
try {
|
|
2971
|
+
this.width = width;
|
|
2972
|
+
this.height = height;
|
|
2973
|
+
this._resized = true;
|
|
2974
|
+
app.renderer.resize(width, height);
|
|
2975
|
+
root.cullArea = new Rectangle(0, 0, width, height);
|
|
2976
|
+
} catch (e) {
|
|
2977
|
+
this.width = oldSize.w;
|
|
2978
|
+
this.height = oldSize.h;
|
|
2979
|
+
this._resized = oldResized;
|
|
2980
|
+
safeRun(() => app.renderer.resize(oldSize.w, oldSize.h));
|
|
2981
|
+
root.cullArea = new Rectangle(0, 0, oldSize.w, oldSize.h);
|
|
2982
|
+
if (wasPlaying && !goOn) this.play();
|
|
2983
|
+
throw e;
|
|
2984
|
+
}
|
|
2985
|
+
}
|
|
2986
|
+
const old = this._rootSequence, oldTimeline = this.timeline;
|
|
2987
|
+
if (old?.target) root.removeChild(old.target);
|
|
2988
|
+
if (sequence.target) root.addChild(sequence.target);
|
|
2989
|
+
this._rootSequence = sequence;
|
|
2990
|
+
this.timeline = timeline;
|
|
2991
|
+
this._timeFilters = collectTimeFilters(sequence);
|
|
2992
|
+
const unmute = muteBoundTextureWarning();
|
|
2993
|
+
try {
|
|
2994
|
+
safeRun(() => old?.destroy());
|
|
2995
|
+
} finally {
|
|
2996
|
+
unmute();
|
|
2997
|
+
}
|
|
2998
|
+
safeRun(() => oldTimeline?.kill());
|
|
2999
|
+
this._vars = vars;
|
|
3000
|
+
this._composition = composition;
|
|
3001
|
+
this.stops = normalizeStops(applyVars(composition, vars)?.stops, this.duration, this.frameRate);
|
|
3002
|
+
sequence = null;
|
|
3003
|
+
} finally {
|
|
3004
|
+
this._silent--;
|
|
3005
|
+
}
|
|
3006
|
+
try {
|
|
3007
|
+
if (wasAtPoster) await this._showPoster();
|
|
3008
|
+
else await this._quietly(() => this.gotoFrame(frame, true));
|
|
3009
|
+
} catch (e) {
|
|
3010
|
+
console.warn("pixi-effects: could not draw the frame after a rebuild:", e);
|
|
3011
|
+
this._emitError("playback", e);
|
|
3012
|
+
}
|
|
3013
|
+
if (wasPlaying && !goOn && this._initState === "ready" && !this.isPlaying) {
|
|
3014
|
+
this._silent++;
|
|
3015
|
+
try {
|
|
3016
|
+
this.play();
|
|
3017
|
+
} finally {
|
|
3018
|
+
this._silent--;
|
|
3019
|
+
}
|
|
3020
|
+
}
|
|
3021
|
+
const report = { reason, ms: Math.round((performance.now() - started) * 10) / 10, audio, frame, playing: wasPlaying };
|
|
3022
|
+
this.emit("rebuilt", report);
|
|
3023
|
+
return report;
|
|
3024
|
+
} catch (err) {
|
|
3025
|
+
if (sequence) safeRun(() => sequence.destroy());
|
|
3026
|
+
this._emitError("rebuild", err);
|
|
3027
|
+
throw err;
|
|
3028
|
+
}
|
|
3029
|
+
}
|
|
2678
3030
|
async gotoFrame(frame, force = false) {
|
|
2679
3031
|
if (this._initState !== "ready") return;
|
|
2680
3032
|
this._cancelUntil();
|
|
@@ -2987,13 +3339,30 @@ var Movie = class {
|
|
|
2987
3339
|
*/
|
|
2988
3340
|
async peek(fn) {
|
|
2989
3341
|
this._requireReady("peek");
|
|
2990
|
-
this.
|
|
2991
|
-
|
|
3342
|
+
return this._exclusive(async () => {
|
|
3343
|
+
this.pause();
|
|
3344
|
+
const back = this.currentFrame, wasAtPoster = this._atPoster;
|
|
3345
|
+
try {
|
|
3346
|
+
return await this._quietly(fn);
|
|
3347
|
+
} finally {
|
|
3348
|
+
if (wasAtPoster) await this._showPoster();
|
|
3349
|
+
else await this._quietly(() => this.gotoFrame(back, true));
|
|
3350
|
+
}
|
|
3351
|
+
});
|
|
3352
|
+
}
|
|
3353
|
+
/** Run `fn` as the only thing that moves the composition: waits for a rebuild in flight, and holds the next one's swap until `fn` is done. */
|
|
3354
|
+
async _exclusive(fn) {
|
|
3355
|
+
while (this._rebuildRun) await this._rebuildRun.catch(() => {
|
|
3356
|
+
});
|
|
3357
|
+
this._busy++;
|
|
2992
3358
|
try {
|
|
2993
|
-
return await
|
|
3359
|
+
return await fn();
|
|
2994
3360
|
} finally {
|
|
2995
|
-
if (
|
|
2996
|
-
|
|
3361
|
+
if (--this._busy === 0) {
|
|
3362
|
+
const waiting = this._idleWaiters;
|
|
3363
|
+
this._idleWaiters = [];
|
|
3364
|
+
waiting.forEach((w) => w());
|
|
3365
|
+
}
|
|
2997
3366
|
}
|
|
2998
3367
|
}
|
|
2999
3368
|
_settleUntil(landed) {
|
|
@@ -3027,6 +3396,7 @@ var Movie = class {
|
|
|
3027
3396
|
if (this.currentFrame >= this.totalFrames) this.currentFrame = 0;
|
|
3028
3397
|
const wasPlaying = this.isPlaying;
|
|
3029
3398
|
this.isPlaying = true;
|
|
3399
|
+
const startFrame = this.currentFrame;
|
|
3030
3400
|
const startTime = performance.now() - this.currentFrame / this.frameRate * 1e3;
|
|
3031
3401
|
let inFlight = false;
|
|
3032
3402
|
const tick = (time) => {
|
|
@@ -3036,7 +3406,7 @@ var Movie = class {
|
|
|
3036
3406
|
return;
|
|
3037
3407
|
}
|
|
3038
3408
|
const elapsed = (time - startTime) / 1e3;
|
|
3039
|
-
const frame = Math.floor(elapsed * this.frameRate);
|
|
3409
|
+
const frame = Math.max(startFrame, Math.floor(elapsed * this.frameRate));
|
|
3040
3410
|
if (this._until !== null && frame >= this._until) {
|
|
3041
3411
|
void this._arriveUntil();
|
|
3042
3412
|
return;
|
|
@@ -3050,6 +3420,18 @@ var Movie = class {
|
|
|
3050
3420
|
inFlight = false;
|
|
3051
3421
|
});
|
|
3052
3422
|
this._raf = requestAnimationFrame(tick);
|
|
3423
|
+
} else if (this._loop && this._untilWaiters.length === 0) {
|
|
3424
|
+
this._silent++;
|
|
3425
|
+
try {
|
|
3426
|
+
this.pause();
|
|
3427
|
+
this._goto(0).catch((err) => {
|
|
3428
|
+
console.warn("pixi-effects: gotoFrame failed at the loop:", err);
|
|
3429
|
+
this._emitError("playback", err);
|
|
3430
|
+
});
|
|
3431
|
+
this.play();
|
|
3432
|
+
} finally {
|
|
3433
|
+
this._silent--;
|
|
3434
|
+
}
|
|
3053
3435
|
} else {
|
|
3054
3436
|
const toEnd = this._untilWaiters;
|
|
3055
3437
|
this._untilWaiters = [];
|
|
@@ -3078,7 +3460,7 @@ var Movie = class {
|
|
|
3078
3460
|
this.audioSource.start(0, Math.min(this.currentFrame / this.frameRate, buffer.duration));
|
|
3079
3461
|
}, typeof window !== "undefined" ? window : void 0);
|
|
3080
3462
|
}
|
|
3081
|
-
if (!wasPlaying) this.emit("play");
|
|
3463
|
+
if (!wasPlaying && !this._silent) this.emit("play");
|
|
3082
3464
|
}
|
|
3083
3465
|
pause() {
|
|
3084
3466
|
const wasPlaying = this.isPlaying;
|
|
@@ -3101,7 +3483,7 @@ var Movie = class {
|
|
|
3101
3483
|
this.gainNode.disconnect();
|
|
3102
3484
|
this.gainNode = null;
|
|
3103
3485
|
}
|
|
3104
|
-
if (wasPlaying) this.emit("pause");
|
|
3486
|
+
if (wasPlaying && !this._silent) this.emit("pause");
|
|
3105
3487
|
this._cancelUntil();
|
|
3106
3488
|
}
|
|
3107
3489
|
set volume(v) {
|
|
@@ -3127,10 +3509,23 @@ var Movie = class {
|
|
|
3127
3509
|
this.muted = !this.muted;
|
|
3128
3510
|
return this.muted;
|
|
3129
3511
|
}
|
|
3512
|
+
/**
|
|
3513
|
+
* At the end of an ordinary `play()` the movie goes back to the start and plays on (no `ended`, no `pause` / `play` events at the wrap; the sound
|
|
3514
|
+
* starts again). Default false. `playUntil()` and `render()` ignore it. Can be changed at any time; `loopchange` says so.
|
|
3515
|
+
*/
|
|
3516
|
+
set loop(v) {
|
|
3517
|
+
const next = !!v;
|
|
3518
|
+
if (next === this._loop) return;
|
|
3519
|
+
this._loop = next;
|
|
3520
|
+
this.emit("loopchange", { loop: next });
|
|
3521
|
+
}
|
|
3522
|
+
get loop() {
|
|
3523
|
+
return this._loop;
|
|
3524
|
+
}
|
|
3130
3525
|
async render(options) {
|
|
3131
3526
|
warnUnknownOptions("movie.render()", options, ["format", "video", "audio", "motionBlur", "range", "scale", "draft"]);
|
|
3132
3527
|
try {
|
|
3133
|
-
return await this._quietly(() => exportFrames(this, options));
|
|
3528
|
+
return await this._exclusive(() => this._quietly(() => exportFrames(this, options)));
|
|
3134
3529
|
} catch (err) {
|
|
3135
3530
|
this._emitError("render", err);
|
|
3136
3531
|
throw err;
|
|
@@ -3148,6 +3543,7 @@ var Movie = class {
|
|
|
3148
3543
|
this.audioBuffer = null;
|
|
3149
3544
|
this._audioSources = [];
|
|
3150
3545
|
this._mixStats = null;
|
|
3546
|
+
this._mixCache.set(null, null);
|
|
3151
3547
|
safeRun(() => this._audioContext?.close().catch(() => {
|
|
3152
3548
|
}));
|
|
3153
3549
|
this._audioContext = null;
|
|
@@ -3155,6 +3551,17 @@ var Movie = class {
|
|
|
3155
3551
|
}
|
|
3156
3552
|
};
|
|
3157
3553
|
var END_MARGIN = 1e-4;
|
|
3554
|
+
function muteBoundTextureWarning() {
|
|
3555
|
+
const original = console.warn;
|
|
3556
|
+
const ours = (...args) => {
|
|
3557
|
+
if (args.some((a) => /destroyed while still bound/.test(String(a)))) return;
|
|
3558
|
+
original.apply(console, args);
|
|
3559
|
+
};
|
|
3560
|
+
console.warn = ours;
|
|
3561
|
+
return () => {
|
|
3562
|
+
if (console.warn === ours) console.warn = original;
|
|
3563
|
+
};
|
|
3564
|
+
}
|
|
3158
3565
|
var clampFrame = (f, last) => Math.max(0, Math.min(Math.round(f), last));
|
|
3159
3566
|
function encodeCanvas(canvas, type, as, quality) {
|
|
3160
3567
|
if (as === "dataURL") return Promise.resolve(canvas.toDataURL(type, quality));
|
|
@@ -3183,6 +3590,8 @@ function countLayers(spec) {
|
|
|
3183
3590
|
for (const s of spec?.sequences ?? []) {
|
|
3184
3591
|
const mask = s.mask;
|
|
3185
3592
|
n += 1 + countLayers(s);
|
|
3593
|
+
const spans = s.spans;
|
|
3594
|
+
if (Array.isArray(spans)) n += spans.length;
|
|
3186
3595
|
const src = maskSourceOf(mask);
|
|
3187
3596
|
if (src.kind === "inline") n += 1 + countLayers(src.spec);
|
|
3188
3597
|
}
|
|
@@ -4178,5 +4587,5 @@ function withFade(spec, opts) {
|
|
|
4178
4587
|
}
|
|
4179
4588
|
|
|
4180
4589
|
export { Movie, animateText, audioEnvelope, bpmEnvelope, cameraPath, computeEnvelope, dismissLoader, failLoader, followPath, kenBurns, measureText, musicBuffer, musicEnvelope, musicEvents, orbit, parseEase2 as parseEase, particles, react, resolveLoader, splitText, stagger, wiggle, withFade };
|
|
4181
|
-
//# sourceMappingURL=chunk-
|
|
4182
|
-
//# sourceMappingURL=chunk-
|
|
4590
|
+
//# sourceMappingURL=chunk-FCMOREIS.js.map
|
|
4591
|
+
//# sourceMappingURL=chunk-FCMOREIS.js.map
|