pixi-effects 0.7.0 → 0.9.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 +21 -0
- package/README.md +8 -7
- package/ai/SKILL.md +6 -5
- package/ai/reference/cheatsheet.md +9 -1
- package/ai/template.html +4 -3
- package/ai/tools/check.mjs +8 -0
- package/dist/Composition-CM62XJKT.cjs +14 -0
- package/dist/{Composition-3KHQPRBA.cjs.map → Composition-CM62XJKT.cjs.map} +1 -1
- package/dist/Composition-RJDSQYTV.js +5 -0
- package/dist/{Composition-DT657FO5.js.map → Composition-RJDSQYTV.js.map} +1 -1
- package/dist/Controller.cjs +55 -20
- package/dist/Controller.cjs.map +1 -1
- package/dist/Controller.d.cts +31 -2
- package/dist/Controller.d.ts +31 -2
- package/dist/Controller.js +55 -21
- package/dist/Controller.js.map +1 -1
- package/dist/{Movie-B0OUKlZI.d.ts → Movie-QMlFyHkf.d.ts} +49 -1
- package/dist/{Movie-BK9daiTt.d.cts → Movie-qGHp15c_.d.cts} +49 -1
- package/dist/{chunk-6I55T4OH.cjs → chunk-AGMNSJJH.cjs} +13 -4
- package/dist/chunk-AGMNSJJH.cjs.map +1 -0
- package/dist/{chunk-4O2NYDJG.js → chunk-FNKSXBKB.js} +13 -4
- package/dist/chunk-FNKSXBKB.js.map +1 -0
- package/dist/{chunk-36Z4RIRK.js → chunk-O4AJYUPK.js} +3 -3
- package/dist/{chunk-36Z4RIRK.js.map → chunk-O4AJYUPK.js.map} +1 -1
- package/dist/{chunk-4VSIJLU3.cjs → chunk-PDBDDGG5.cjs} +21 -21
- package/dist/{chunk-4VSIJLU3.cjs.map → chunk-PDBDDGG5.cjs.map} +1 -1
- package/dist/index.cjs +141 -26
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +130 -15
- package/dist/index.js.map +1 -1
- package/dist/three.cjs +5 -5
- package/dist/three.js +1 -1
- package/docs/api.md +67 -0
- package/docs/dsl.md +14 -2
- package/llms-full.txt +96 -8
- package/package.json +4 -1
- package/dist/Composition-3KHQPRBA.cjs +0 -14
- package/dist/Composition-DT657FO5.js +0 -5
- package/dist/chunk-4O2NYDJG.js.map +0 -1
- package/dist/chunk-6I55T4OH.cjs.map +0 -1
package/dist/index.cjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var
|
|
4
|
-
var
|
|
3
|
+
var chunkPDBDDGG5_cjs = require('./chunk-PDBDDGG5.cjs');
|
|
4
|
+
var chunkAGMNSJJH_cjs = require('./chunk-AGMNSJJH.cjs');
|
|
5
5
|
var chunkUQBGW5DO_cjs = require('./chunk-UQBGW5DO.cjs');
|
|
6
6
|
var PIXI = require('pixi.js');
|
|
7
7
|
var gsap = require('gsap');
|
|
@@ -308,6 +308,28 @@ var AUDIO_CODEC_BY_FORMAT = {
|
|
|
308
308
|
webm: "opus",
|
|
309
309
|
mkv: "opus"
|
|
310
310
|
};
|
|
311
|
+
var AUDIO_PREFERENCE = {
|
|
312
|
+
mp4: ["aac", "opus", "mp3", "flac"],
|
|
313
|
+
mov: ["aac", "mp3"],
|
|
314
|
+
webm: ["opus", "vorbis"],
|
|
315
|
+
mkv: ["opus", "vorbis", "aac", "flac", "mp3"]
|
|
316
|
+
};
|
|
317
|
+
async function chooseAudioCodec(fmt, requested, container, audio, bitrate) {
|
|
318
|
+
const supported = container.getSupportedAudioCodecs?.();
|
|
319
|
+
const preferred = AUDIO_PREFERENCE[fmt].filter((c) => !supported || supported.includes(c));
|
|
320
|
+
const canEncode = (list) => mediabunny.getFirstEncodableAudioCodec(list, { numberOfChannels: audio.numberOfChannels ?? 2, sampleRate: audio.sampleRate, bitrate });
|
|
321
|
+
if (requested) {
|
|
322
|
+
if (await canEncode([requested])) return requested;
|
|
323
|
+
const alt = await canEncode(preferred.filter((c) => c !== requested));
|
|
324
|
+
throw new Error(`pixi-effects: this browser cannot encode "${requested}" audio for ${fmt}` + (alt ? `; "${alt}" would work: render({ format: '${fmt}', audio: { codec: '${alt}' } }), or leave audio.codec out and the best one is chosen.` : "; it has no audio encoder for this format at all (try another browser, or a movie without sound)."));
|
|
325
|
+
}
|
|
326
|
+
const chosen = await canEncode(preferred);
|
|
327
|
+
if (!chosen) throw new Error(`pixi-effects: this browser has no audio encoder for ${fmt} (it tried ${preferred.join(", ")}): try another browser, or render a movie without sound.`);
|
|
328
|
+
if (chosen !== preferred[0]) {
|
|
329
|
+
console.warn(`pixi-effects: this browser cannot encode ${preferred[0].toUpperCase()} audio (Chrome on Linux has no AAC encoder), so the ${fmt} carries ${chosen} instead; current players play it. A webm export needs no AAC.`);
|
|
330
|
+
}
|
|
331
|
+
return chosen;
|
|
332
|
+
}
|
|
311
333
|
var qualityMap = {
|
|
312
334
|
"very-low": mediabunny.QUALITY_VERY_LOW,
|
|
313
335
|
"low": mediabunny.QUALITY_LOW,
|
|
@@ -340,8 +362,10 @@ async function exportFrames(movie, options = {}) {
|
|
|
340
362
|
bitrate: qualityMap[options.audio?.bitrate ?? "high"] ?? mediabunny.QUALITY_HIGH
|
|
341
363
|
}
|
|
342
364
|
};
|
|
365
|
+
const container = makeOutputFormat(opts.format);
|
|
366
|
+
if (movie.audioBuffer) opts.audio.codec = await chooseAudioCodec(fmt, options.audio?.codec, container, movie.audioBuffer, opts.audio.bitrate);
|
|
343
367
|
const output = new mediabunny.Output({
|
|
344
|
-
format:
|
|
368
|
+
format: container,
|
|
345
369
|
target: new mediabunny.BufferTarget()
|
|
346
370
|
});
|
|
347
371
|
const canvasSource = new mediabunny.CanvasSource(movie.app.canvas, {
|
|
@@ -819,16 +843,16 @@ function expandTransitions(spec) {
|
|
|
819
843
|
if (toUses > 1) {
|
|
820
844
|
throw new Error(`pixi-effects: ${tag} sequence "${t.to}" is used as \`to\` in more than one transition`);
|
|
821
845
|
}
|
|
822
|
-
const tStart =
|
|
846
|
+
const tStart = chunkAGMNSJJH_cjs.resolveAt(t.at, parentDuration);
|
|
823
847
|
const tEnd = tStart + t.duration;
|
|
824
848
|
const fromSeq = fromEntry.seq;
|
|
825
|
-
const fromStart =
|
|
849
|
+
const fromStart = chunkAGMNSJJH_cjs.resolveAt(fromSeq.at, parentDuration);
|
|
826
850
|
const fromEnd = fromStart + (fromSeq.duration ?? parentDuration);
|
|
827
851
|
if (tStart < fromStart || tEnd > fromEnd) {
|
|
828
852
|
throw new Error(`pixi-effects: ${tag} window [${tStart}, ${tEnd}] is not covered by \`from\` "${t.from}" (lives [${fromStart}, ${fromEnd}], so it ends at ${fromEnd})`);
|
|
829
853
|
}
|
|
830
854
|
const toSeq = toEntry.seq;
|
|
831
|
-
const toStart =
|
|
855
|
+
const toStart = chunkAGMNSJJH_cjs.resolveAt(toSeq.at, parentDuration);
|
|
832
856
|
const toEnd = toStart + (toSeq.duration ?? parentDuration);
|
|
833
857
|
if (tStart < toStart || tEnd > toEnd) {
|
|
834
858
|
throw new Error(`pixi-effects: ${tag} window [${tStart}, ${tEnd}] is not covered by \`to\` "${t.to}" (lives [${toStart}, ${toEnd}], so it starts at ${toStart})`);
|
|
@@ -1168,7 +1192,7 @@ function inspectScene(root, frame, time, canvas, opts = {}) {
|
|
|
1168
1192
|
onCanvas = inter === 0 && area > 0 ? "none" : inter >= area - 1e-6 ? "full" : "partial";
|
|
1169
1193
|
if (area === 0) onCanvas = "none";
|
|
1170
1194
|
}
|
|
1171
|
-
const keys =
|
|
1195
|
+
const keys = chunkAGMNSJJH_cjs.collectPropKeys(seq.spec);
|
|
1172
1196
|
const moving = parentMoving || (seq.spec.keyframes ?? []).length > 0 && (keys.has("x") || keys.has("y"));
|
|
1173
1197
|
const scale = t?.scale;
|
|
1174
1198
|
const zero = parentZero || !!scale && (scale.x === 0 || scale.y === 0);
|
|
@@ -1184,7 +1208,7 @@ function inspectScene(root, frame, time, canvas, opts = {}) {
|
|
|
1184
1208
|
const group = parentScene ?? (name !== void 0 && blending.has(name) ? name : void 0);
|
|
1185
1209
|
if (group) scene.set(info, group);
|
|
1186
1210
|
if (zero || seq.showsNothingYet) scaledToNothing.add(info);
|
|
1187
|
-
if (seq instanceof
|
|
1211
|
+
if (seq instanceof chunkPDBDDGG5_cjs.CompositionSequence) walk2(seq, prefix + label + "/", insideThreeD || threeD, visible, group, moving, zero);
|
|
1188
1212
|
});
|
|
1189
1213
|
};
|
|
1190
1214
|
walk2(root, "", false, true);
|
|
@@ -1244,10 +1268,10 @@ function walk(comp, parentStart, parentEnd, depth, prefix, rows, transitions) {
|
|
|
1244
1268
|
const start = parentStart + (seq.at ?? 0);
|
|
1245
1269
|
const end = Math.min(parentEnd, seq.duration !== void 0 ? start + seq.duration : parentEnd);
|
|
1246
1270
|
const span = end - start;
|
|
1247
|
-
const keys = (seq.spec.keyframes ?? []).map((kf) => round(start +
|
|
1271
|
+
const keys = (seq.spec.keyframes ?? []).map((kf) => round(start + chunkAGMNSJJH_cjs.resolveAt(kf.at, span))).sort((a, b) => a - b).slice(0, MAX_KEYS);
|
|
1248
1272
|
const row = { path: prefix + name, name, type: seq.spec.type, depth, start: round(start), end: round(end), keys, detail: detailOf(seq) };
|
|
1249
1273
|
const inner = [];
|
|
1250
|
-
const isComp = seq instanceof
|
|
1274
|
+
const isComp = seq instanceof chunkPDBDDGG5_cjs.CompositionSequence;
|
|
1251
1275
|
if (isComp) walk(seq, start, end, depth + 1, prefix + name + "/", inner, transitions);
|
|
1252
1276
|
entries.push({ row, inner, groupable: !isComp });
|
|
1253
1277
|
});
|
|
@@ -1425,6 +1449,21 @@ function startWhenRunning(ctx, start, gesture) {
|
|
|
1425
1449
|
}
|
|
1426
1450
|
var GESTURES = ["pointerdown", "mousedown", "touchend", "keydown"];
|
|
1427
1451
|
|
|
1452
|
+
// src/core/poster.ts
|
|
1453
|
+
function normalizePoster(poster, duration, frameRate) {
|
|
1454
|
+
if (poster === void 0 || poster === null) return { seconds: null, frame: null };
|
|
1455
|
+
if (typeof poster !== "number" || !Number.isFinite(poster)) {
|
|
1456
|
+
console.warn(`pixi-effects: movie.init({ poster }): ${JSON.stringify(poster)} is not a time in seconds; ignored`);
|
|
1457
|
+
return { seconds: null, frame: null };
|
|
1458
|
+
}
|
|
1459
|
+
let seconds = poster < 0 ? Math.max(0, duration + poster) : poster;
|
|
1460
|
+
if (seconds > duration) {
|
|
1461
|
+
console.warn(`pixi-effects: movie.init({ poster }): the poster time ${poster} s is past the end of the movie (${duration} s); using the last frame`);
|
|
1462
|
+
seconds = duration;
|
|
1463
|
+
}
|
|
1464
|
+
return { seconds, frame: Math.round(seconds * frameRate) };
|
|
1465
|
+
}
|
|
1466
|
+
|
|
1428
1467
|
// src/core/Movie.ts
|
|
1429
1468
|
PIXI.extensions.add(PIXI.CullerPlugin);
|
|
1430
1469
|
gsap.gsap.registerPlugin(PixiPlugin.PixiPlugin);
|
|
@@ -1438,6 +1477,13 @@ var Movie = class {
|
|
|
1438
1477
|
audioSource = null;
|
|
1439
1478
|
gainNode = null;
|
|
1440
1479
|
_cancelAudioStart = null;
|
|
1480
|
+
/** The poster time in seconds (`movie.init({ poster })`), or null. */
|
|
1481
|
+
poster = null;
|
|
1482
|
+
_posterFrame = null;
|
|
1483
|
+
/** The canvas shows the poster picture while the playhead is still at frame 0 (like `<video poster>`): the first seek or play replaces it. */
|
|
1484
|
+
_atPoster = false;
|
|
1485
|
+
/** > 0 while the movie moves its own playhead (playback, render, snapshot, contactSheet): those are not the seeks `seeking` / `seeked` report. */
|
|
1486
|
+
_quiet = 0;
|
|
1441
1487
|
_volume = 1;
|
|
1442
1488
|
_muted = false;
|
|
1443
1489
|
isPlaying = false;
|
|
@@ -1479,6 +1525,36 @@ var Movie = class {
|
|
|
1479
1525
|
get isReady() {
|
|
1480
1526
|
return this._initState === "ready";
|
|
1481
1527
|
}
|
|
1528
|
+
/** The poster time as a frame number, or null. */
|
|
1529
|
+
get posterFrame() {
|
|
1530
|
+
return this._posterFrame;
|
|
1531
|
+
}
|
|
1532
|
+
/** Draw the poster frame on the canvas and leave the playhead where it is (frame 0): the picture before the movie plays. */
|
|
1533
|
+
async _showPoster() {
|
|
1534
|
+
if (this._posterFrame === null || !this.timeline) return;
|
|
1535
|
+
const back = this.currentFrame;
|
|
1536
|
+
this.currentFrame = this._posterFrame;
|
|
1537
|
+
try {
|
|
1538
|
+
this.timeline.time(this._posterFrame / this.frameRate);
|
|
1539
|
+
await this._awaitVideoFrames();
|
|
1540
|
+
this._updateSpace();
|
|
1541
|
+
this._renderNow();
|
|
1542
|
+
} finally {
|
|
1543
|
+
this.currentFrame = back;
|
|
1544
|
+
}
|
|
1545
|
+
this._atPoster = true;
|
|
1546
|
+
}
|
|
1547
|
+
async posterImage(opts = {}) {
|
|
1548
|
+
this._requireReady("posterImage");
|
|
1549
|
+
const back = this.currentFrame, wasAtPoster = this._atPoster;
|
|
1550
|
+
try {
|
|
1551
|
+
return await this.snapshot(this._posterFrame ?? 0, opts);
|
|
1552
|
+
} finally {
|
|
1553
|
+
this.currentFrame = back;
|
|
1554
|
+
if (wasAtPoster) await this._showPoster();
|
|
1555
|
+
else await this._quietly(() => this.gotoFrame(back, true));
|
|
1556
|
+
}
|
|
1557
|
+
}
|
|
1482
1558
|
_ensureAudioContext() {
|
|
1483
1559
|
if (!this._audioContext) {
|
|
1484
1560
|
const Ctx = window.AudioContext ?? window.webkitAudioContext;
|
|
@@ -1487,7 +1563,7 @@ var Movie = class {
|
|
|
1487
1563
|
return this._audioContext;
|
|
1488
1564
|
}
|
|
1489
1565
|
async init(options = {}) {
|
|
1490
|
-
chunkUQBGW5DO_cjs.warnUnknownOptions("movie.init()", options, ["width", "height", "duration", "frameRate", "background", "canvas", "assets", "composition"]);
|
|
1566
|
+
chunkUQBGW5DO_cjs.warnUnknownOptions("movie.init()", options, ["width", "height", "duration", "frameRate", "background", "canvas", "assets", "composition", "poster"]);
|
|
1491
1567
|
this._initState = "pending";
|
|
1492
1568
|
try {
|
|
1493
1569
|
this.width = options.width ?? 1920;
|
|
@@ -1495,6 +1571,9 @@ var Movie = class {
|
|
|
1495
1571
|
this.duration = options.duration ?? 10;
|
|
1496
1572
|
this.frameRate = options.frameRate ?? 30;
|
|
1497
1573
|
this.totalFrames = Math.round(this.duration * this.frameRate);
|
|
1574
|
+
const poster = normalizePoster(options.poster, this.duration, this.frameRate);
|
|
1575
|
+
this.poster = poster.seconds;
|
|
1576
|
+
this._posterFrame = poster.frame;
|
|
1498
1577
|
this.background = options.background ?? "#000000";
|
|
1499
1578
|
this.timeline = gsap.gsap.timeline({ paused: true, defaults: { ease: "none" } });
|
|
1500
1579
|
this.timeline.add(gsap.gsap.to({}, { duration: this.duration }));
|
|
@@ -1531,7 +1610,7 @@ var Movie = class {
|
|
|
1531
1610
|
height: options.composition.height ?? this.height,
|
|
1532
1611
|
duration: options.composition.duration ?? this.duration
|
|
1533
1612
|
} : void 0;
|
|
1534
|
-
await
|
|
1613
|
+
await chunkAGMNSJJH_cjs.ensureFilterLibrary(seededComposition);
|
|
1535
1614
|
const userComposition = seededComposition ? expandTransitions(seededComposition) : void 0;
|
|
1536
1615
|
const rootSeqSpec = {
|
|
1537
1616
|
type: "composition",
|
|
@@ -1541,7 +1620,7 @@ var Movie = class {
|
|
|
1541
1620
|
...userComposition
|
|
1542
1621
|
};
|
|
1543
1622
|
if (userComposition) carryTransitionWindows(userComposition, rootSeqSpec);
|
|
1544
|
-
const composition = new
|
|
1623
|
+
const composition = new chunkPDBDDGG5_cjs.CompositionSequence(rootSeqSpec, null, rootShape);
|
|
1545
1624
|
await composition.build();
|
|
1546
1625
|
this._rootSequence = composition;
|
|
1547
1626
|
if (composition.target) root.addChild(composition.target);
|
|
@@ -1558,6 +1637,7 @@ var Movie = class {
|
|
|
1558
1637
|
this._updateSpace();
|
|
1559
1638
|
this._renderNow();
|
|
1560
1639
|
this._initState = "ready";
|
|
1640
|
+
if (this._posterFrame !== null) await this._showPoster();
|
|
1561
1641
|
this.emit("ready");
|
|
1562
1642
|
} catch (err) {
|
|
1563
1643
|
try {
|
|
@@ -1565,12 +1645,33 @@ var Movie = class {
|
|
|
1565
1645
|
} catch (cleanupErr) {
|
|
1566
1646
|
console.warn("pixi-effects: cleanup after init failure also threw:", cleanupErr);
|
|
1567
1647
|
}
|
|
1648
|
+
this._emitError("init", err);
|
|
1568
1649
|
throw err;
|
|
1569
1650
|
}
|
|
1570
1651
|
}
|
|
1571
1652
|
async gotoFrame(frame, force = false) {
|
|
1572
1653
|
if (this._initState !== "ready") return;
|
|
1573
|
-
if (!force && this.currentFrame === frame) return;
|
|
1654
|
+
if (!force && !this._atPoster && this.currentFrame === frame) return;
|
|
1655
|
+
if (this._quiet > 0) return this._goto(frame);
|
|
1656
|
+
const target = Math.max(0, Math.min(frame, this.totalFrames));
|
|
1657
|
+
this.emit("seeking", { frame: target, totalFrames: this.totalFrames });
|
|
1658
|
+
await this._goto(frame);
|
|
1659
|
+
this.emit("seeked", { frame: this.currentFrame, totalFrames: this.totalFrames });
|
|
1660
|
+
}
|
|
1661
|
+
/** Run `fn` with `seeking` / `seeked` silenced: the movie is moving its own playhead, not answering a seek. */
|
|
1662
|
+
async _quietly(fn) {
|
|
1663
|
+
this._quiet++;
|
|
1664
|
+
try {
|
|
1665
|
+
return await fn();
|
|
1666
|
+
} finally {
|
|
1667
|
+
this._quiet--;
|
|
1668
|
+
}
|
|
1669
|
+
}
|
|
1670
|
+
_emitError(where, error) {
|
|
1671
|
+
this.emit("error", { where, message: error instanceof Error ? error.message : String(error), error });
|
|
1672
|
+
}
|
|
1673
|
+
async _goto(frame) {
|
|
1674
|
+
this._atPoster = false;
|
|
1574
1675
|
this.currentFrame = Math.max(0, Math.min(frame, this.totalFrames));
|
|
1575
1676
|
this.timeline.time(this.currentFrame / this.frameRate);
|
|
1576
1677
|
await this._awaitVideoFrames();
|
|
@@ -1592,7 +1693,7 @@ var Movie = class {
|
|
|
1592
1693
|
async snapshot(frame = this.currentFrame, opts = {}) {
|
|
1593
1694
|
chunkUQBGW5DO_cjs.warnUnknownOptions("movie.snapshot()", opts, ["scale", "type", "as"]);
|
|
1594
1695
|
this._requireReady("snapshot");
|
|
1595
|
-
await this.gotoFrame(clampFrame(frame, this.totalFrames), true);
|
|
1696
|
+
await this._quietly(() => this.gotoFrame(clampFrame(frame, this.totalFrames), true));
|
|
1596
1697
|
const scale = opts.scale ?? 1;
|
|
1597
1698
|
const out = document.createElement("canvas");
|
|
1598
1699
|
out.width = Math.max(1, Math.round(this.width * scale));
|
|
@@ -1612,11 +1713,11 @@ var Movie = class {
|
|
|
1612
1713
|
const g = sheet.getContext("2d");
|
|
1613
1714
|
g.fillStyle = "#10131c";
|
|
1614
1715
|
g.fillRect(0, 0, L.width, L.height);
|
|
1615
|
-
const back = this.currentFrame;
|
|
1716
|
+
const back = this.currentFrame, wasAtPoster = this._atPoster;
|
|
1616
1717
|
try {
|
|
1617
1718
|
for (let i = 0; i < frames.length; i++) {
|
|
1618
1719
|
const f = frames[i];
|
|
1619
|
-
await this.gotoFrame(f, true);
|
|
1720
|
+
await this._quietly(() => this.gotoFrame(f, true));
|
|
1620
1721
|
const { x, y } = L.positions[i];
|
|
1621
1722
|
g.drawImage(this.app.canvas, x, y + LABEL_H, L.cellW, L.cellH);
|
|
1622
1723
|
g.fillStyle = "#e8ecf8";
|
|
@@ -1625,7 +1726,8 @@ var Movie = class {
|
|
|
1625
1726
|
g.fillText(`frame ${f} \xB7 ${(f / this.frameRate).toFixed(2)}s`, x + 8, y + LABEL_H / 2);
|
|
1626
1727
|
}
|
|
1627
1728
|
} finally {
|
|
1628
|
-
await this.
|
|
1729
|
+
if (wasAtPoster) await this._showPoster();
|
|
1730
|
+
else await this._quietly(() => this.gotoFrame(back, true));
|
|
1629
1731
|
}
|
|
1630
1732
|
return encodeCanvas(sheet, "image/png", opts.as ?? "blob");
|
|
1631
1733
|
}
|
|
@@ -1708,17 +1810,20 @@ var Movie = class {
|
|
|
1708
1810
|
const frame = Math.floor(elapsed * this.frameRate);
|
|
1709
1811
|
if (frame <= this.totalFrames) {
|
|
1710
1812
|
inFlight = true;
|
|
1711
|
-
this.
|
|
1813
|
+
this._goto(frame).catch((err) => {
|
|
1712
1814
|
console.warn("pixi-effects: gotoFrame failed during playback:", err);
|
|
1815
|
+
this._emitError("playback", err);
|
|
1713
1816
|
}).finally(() => {
|
|
1714
1817
|
inFlight = false;
|
|
1715
1818
|
});
|
|
1716
1819
|
this._raf = requestAnimationFrame(tick);
|
|
1717
1820
|
} else {
|
|
1718
1821
|
this.pause();
|
|
1719
|
-
this.
|
|
1822
|
+
this._goto(this.totalFrames).catch((err) => {
|
|
1720
1823
|
console.warn("pixi-effects: final gotoFrame failed:", err);
|
|
1824
|
+
this._emitError("playback", err);
|
|
1721
1825
|
});
|
|
1826
|
+
this.emit("ended");
|
|
1722
1827
|
}
|
|
1723
1828
|
};
|
|
1724
1829
|
this._raf = requestAnimationFrame(tick);
|
|
@@ -1762,15 +1867,20 @@ var Movie = class {
|
|
|
1762
1867
|
if (wasPlaying) this.emit("pause");
|
|
1763
1868
|
}
|
|
1764
1869
|
set volume(v) {
|
|
1765
|
-
|
|
1870
|
+
const next = Math.max(0, Math.min(1, v));
|
|
1871
|
+
const changed = next !== this._volume;
|
|
1872
|
+
this._volume = next;
|
|
1766
1873
|
if (this.gainNode) this.gainNode.gain.value = this._muted ? 0 : this._volume;
|
|
1874
|
+
if (changed) this.emit("volumechange", { volume: this._volume, muted: this._muted });
|
|
1767
1875
|
}
|
|
1768
1876
|
get volume() {
|
|
1769
1877
|
return this._volume;
|
|
1770
1878
|
}
|
|
1771
1879
|
set muted(v) {
|
|
1880
|
+
const changed = !!v !== this._muted;
|
|
1772
1881
|
this._muted = !!v;
|
|
1773
1882
|
if (this.gainNode) this.gainNode.gain.value = this._muted ? 0 : this._volume;
|
|
1883
|
+
if (changed) this.emit("volumechange", { volume: this._volume, muted: this._muted });
|
|
1774
1884
|
}
|
|
1775
1885
|
get muted() {
|
|
1776
1886
|
return this._muted;
|
|
@@ -1781,7 +1891,12 @@ var Movie = class {
|
|
|
1781
1891
|
}
|
|
1782
1892
|
async render(options) {
|
|
1783
1893
|
chunkUQBGW5DO_cjs.warnUnknownOptions("movie.render()", options, ["format", "video", "audio"]);
|
|
1784
|
-
|
|
1894
|
+
try {
|
|
1895
|
+
return await this._quietly(() => exportFrames(this, options));
|
|
1896
|
+
} catch (err) {
|
|
1897
|
+
this._emitError("render", err);
|
|
1898
|
+
throw err;
|
|
1899
|
+
}
|
|
1785
1900
|
}
|
|
1786
1901
|
async destroy() {
|
|
1787
1902
|
safeRun(() => this.pause());
|
|
@@ -2004,17 +2119,17 @@ function orbit(opts) {
|
|
|
2004
2119
|
throw new Error("pixi-effects: orbit() cannot combine `radius` with `dollyZoom` \u2014 the radius follows the animated fov");
|
|
2005
2120
|
}
|
|
2006
2121
|
const [cx, cy] = opts.center ?? [width / 2, height / 2];
|
|
2007
|
-
const fixedR = opts.radius ??
|
|
2122
|
+
const fixedR = opts.radius ?? chunkAGMNSJJH_cjs.homeDistance(height, fov ?? chunkAGMNSJJH_cjs.DEFAULT_FOV);
|
|
2008
2123
|
const start = opts.start ?? -degrees / 2;
|
|
2009
2124
|
const steps = Math.max(1, Math.round(duration * stepsPerSecond));
|
|
2010
2125
|
const easeFn = parseEase(ease);
|
|
2011
2126
|
const dollyEase = dolly ? parseEase(dolly.ease ?? ease) : null;
|
|
2012
2127
|
const pos = (i) => {
|
|
2013
2128
|
const p = i / steps;
|
|
2014
|
-
const a = (start + degrees * easeFn(p)) *
|
|
2129
|
+
const a = (start + degrees * easeFn(p)) * chunkAGMNSJJH_cjs.DEG;
|
|
2015
2130
|
if (!dolly) return { x: cx + fixedR * Math.sin(a), z: fixedR * Math.cos(a) };
|
|
2016
2131
|
const f = dolly.from + (dolly.to - dolly.from) * dollyEase(p);
|
|
2017
|
-
const R =
|
|
2132
|
+
const R = chunkAGMNSJJH_cjs.homeDistance(height, f);
|
|
2018
2133
|
return { x: cx + R * Math.sin(a), z: R * Math.cos(a), fov: f };
|
|
2019
2134
|
};
|
|
2020
2135
|
const keyframes = [];
|
|
@@ -2063,7 +2178,7 @@ function withFade(spec, opts) {
|
|
|
2063
2178
|
|
|
2064
2179
|
Object.defineProperty(exports, "registerSequenceType", {
|
|
2065
2180
|
enumerable: true,
|
|
2066
|
-
get: function () { return
|
|
2181
|
+
get: function () { return chunkAGMNSJJH_cjs.registerSequenceType; }
|
|
2067
2182
|
});
|
|
2068
2183
|
exports.Movie = Movie;
|
|
2069
2184
|
exports.kenBurns = kenBurns;
|