pixi-effects 0.8.0 → 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +22 -0
- package/README.md +6 -6
- package/ai/SKILL.md +8 -6
- package/ai/reference/cheatsheet.md +17 -5
- package/ai/reference/pitfalls.md +2 -2
- package/ai/reference/recipes.md +123 -2
- package/ai/template.html +4 -3
- package/ai/tools/check.mjs +8 -0
- package/dist/{Base-CSIulyIq.d.cts → Base-CwUxTfja.d.cts} +1 -1
- package/dist/{Base-DVgwyIUj.d.ts → Base-CyvSLoCP.d.ts} +1 -1
- package/dist/Composition-EKVTUYCI.cjs +14 -0
- package/dist/{Composition-CM62XJKT.cjs.map → Composition-EKVTUYCI.cjs.map} +1 -1
- package/dist/Composition-NJCOMGD7.js +5 -0
- package/dist/{Composition-RJDSQYTV.js.map → Composition-NJCOMGD7.js.map} +1 -1
- package/dist/Controller.cjs +34 -0
- package/dist/Controller.cjs.map +1 -1
- package/dist/Controller.d.cts +12 -2
- package/dist/Controller.d.ts +12 -2
- package/dist/Controller.js +34 -0
- package/dist/Controller.js.map +1 -1
- package/dist/{Movie-CQOhUmko.d.cts → Movie-puD9oZPg.d.ts} +26 -1
- package/dist/{Movie-HF3pSGjQ.d.ts → Movie-w3GliuP3.d.cts} +26 -1
- package/dist/{chunk-PDBDDGG5.cjs → chunk-4WJUSDJJ.cjs} +103 -30
- package/dist/chunk-4WJUSDJJ.cjs.map +1 -0
- package/dist/{chunk-FNKSXBKB.js → chunk-JAMNDBXW.js} +193 -14
- package/dist/chunk-JAMNDBXW.js.map +1 -0
- package/dist/{chunk-AGMNSJJH.cjs → chunk-S3ZI4MKS.cjs} +196 -12
- package/dist/chunk-S3ZI4MKS.cjs.map +1 -0
- package/dist/{chunk-O4AJYUPK.js → chunk-WDGAVOMS.js} +85 -12
- package/dist/chunk-WDGAVOMS.js.map +1 -0
- package/dist/index.cjs +369 -26
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +231 -5
- package/dist/index.d.ts +231 -5
- package/dist/index.js +342 -15
- package/dist/index.js.map +1 -1
- package/dist/three.cjs +5 -5
- package/dist/three.d.cts +2 -2
- package/dist/three.d.ts +2 -2
- package/dist/three.js +1 -1
- package/dist/{types-oJijbn-K.d.ts → types-BXu28nJj.d.cts} +26 -2
- package/dist/{types-oJijbn-K.d.cts → types-BXu28nJj.d.ts} +26 -2
- package/docs/api.md +17 -0
- package/docs/dsl.md +108 -2
- package/llms-full.txt +275 -17
- package/package.json +4 -1
- package/dist/Composition-CM62XJKT.cjs +0 -14
- package/dist/Composition-RJDSQYTV.js +0 -5
- package/dist/chunk-AGMNSJJH.cjs.map +0 -1
- package/dist/chunk-FNKSXBKB.js.map +0 -1
- package/dist/chunk-O4AJYUPK.js.map +0 -1
- package/dist/chunk-PDBDDGG5.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 chunk4WJUSDJJ_cjs = require('./chunk-4WJUSDJJ.cjs');
|
|
4
|
+
var chunkS3ZI4MKS_cjs = require('./chunk-S3ZI4MKS.cjs');
|
|
5
5
|
var chunkUQBGW5DO_cjs = require('./chunk-UQBGW5DO.cjs');
|
|
6
6
|
var PIXI = require('pixi.js');
|
|
7
7
|
var gsap = require('gsap');
|
|
@@ -843,16 +843,16 @@ function expandTransitions(spec) {
|
|
|
843
843
|
if (toUses > 1) {
|
|
844
844
|
throw new Error(`pixi-effects: ${tag} sequence "${t.to}" is used as \`to\` in more than one transition`);
|
|
845
845
|
}
|
|
846
|
-
const tStart =
|
|
846
|
+
const tStart = chunkS3ZI4MKS_cjs.resolveAt(t.at, parentDuration);
|
|
847
847
|
const tEnd = tStart + t.duration;
|
|
848
848
|
const fromSeq = fromEntry.seq;
|
|
849
|
-
const fromStart =
|
|
849
|
+
const fromStart = chunkS3ZI4MKS_cjs.resolveAt(fromSeq.at, parentDuration);
|
|
850
850
|
const fromEnd = fromStart + (fromSeq.duration ?? parentDuration);
|
|
851
851
|
if (tStart < fromStart || tEnd > fromEnd) {
|
|
852
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})`);
|
|
853
853
|
}
|
|
854
854
|
const toSeq = toEntry.seq;
|
|
855
|
-
const toStart =
|
|
855
|
+
const toStart = chunkS3ZI4MKS_cjs.resolveAt(toSeq.at, parentDuration);
|
|
856
856
|
const toEnd = toStart + (toSeq.duration ?? parentDuration);
|
|
857
857
|
if (tStart < toStart || tEnd > toEnd) {
|
|
858
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})`);
|
|
@@ -1173,12 +1173,12 @@ function inspectScene(root, frame, time, canvas, opts = {}) {
|
|
|
1173
1173
|
blending.add(w.to);
|
|
1174
1174
|
}
|
|
1175
1175
|
}
|
|
1176
|
-
comp.layers().forEach(({ seq, display, threeD }, i) => {
|
|
1176
|
+
comp.layers().forEach(({ seq, display, threeD, carriers }, i) => {
|
|
1177
1177
|
const t = seq.target;
|
|
1178
1178
|
const name = seq.spec.name;
|
|
1179
1179
|
const label = name ?? `${seq.spec.type}#${i}`;
|
|
1180
|
-
const alpha = t?.alpha ?? 1;
|
|
1181
|
-
const alive = threeD ? display.visible : t?.renderable ?? true;
|
|
1180
|
+
const alpha = (t?.alpha ?? 1) * carriers.reduce((a, c) => a * (c.target?.alpha ?? 1), 1);
|
|
1181
|
+
const alive = (threeD ? display.visible : t?.renderable ?? true) && carriers.every((c) => c.target?.renderable ?? true);
|
|
1182
1182
|
const visible = parentVisible && alive && alpha > 0;
|
|
1183
1183
|
let bounds = null;
|
|
1184
1184
|
if (!insideThreeD) {
|
|
@@ -1192,10 +1192,17 @@ function inspectScene(root, frame, time, canvas, opts = {}) {
|
|
|
1192
1192
|
onCanvas = inter === 0 && area > 0 ? "none" : inter >= area - 1e-6 ? "full" : "partial";
|
|
1193
1193
|
if (area === 0) onCanvas = "none";
|
|
1194
1194
|
}
|
|
1195
|
-
const keys =
|
|
1196
|
-
const
|
|
1197
|
-
|
|
1198
|
-
|
|
1195
|
+
const keys = chunkS3ZI4MKS_cjs.collectPropKeys(seq.spec);
|
|
1196
|
+
const carried = carriers.some((c) => {
|
|
1197
|
+
const k = chunkS3ZI4MKS_cjs.collectPropKeys(c.spec);
|
|
1198
|
+
return (c.spec.keyframes ?? []).length > 0 && (k.has("x") || k.has("y") || k.has("rotation") || k.has("scale"));
|
|
1199
|
+
});
|
|
1200
|
+
const moving = parentMoving || carried || (seq.spec.keyframes ?? []).length > 0 && (keys.has("x") || keys.has("y"));
|
|
1201
|
+
const zeroScale = (o) => {
|
|
1202
|
+
const s = o?.scale;
|
|
1203
|
+
return !!s && (s.x === 0 || s.y === 0);
|
|
1204
|
+
};
|
|
1205
|
+
const zero = parentZero || zeroScale(t) || carriers.some((c) => zeroScale(c.target));
|
|
1199
1206
|
const info = { path: prefix + label, name, type: seq.spec.type, threeD, visible, alpha, moving, bounds, onCanvas };
|
|
1200
1207
|
layers.push(info);
|
|
1201
1208
|
let shown = bounds ? clip(bounds, view) : null;
|
|
@@ -1208,7 +1215,7 @@ function inspectScene(root, frame, time, canvas, opts = {}) {
|
|
|
1208
1215
|
const group = parentScene ?? (name !== void 0 && blending.has(name) ? name : void 0);
|
|
1209
1216
|
if (group) scene.set(info, group);
|
|
1210
1217
|
if (zero || seq.showsNothingYet) scaledToNothing.add(info);
|
|
1211
|
-
if (seq instanceof
|
|
1218
|
+
if (seq instanceof chunk4WJUSDJJ_cjs.CompositionSequence) walk2(seq, prefix + label + "/", insideThreeD || threeD, visible, group, moving, zero);
|
|
1212
1219
|
});
|
|
1213
1220
|
};
|
|
1214
1221
|
walk2(root, "", false, true);
|
|
@@ -1268,10 +1275,10 @@ function walk(comp, parentStart, parentEnd, depth, prefix, rows, transitions) {
|
|
|
1268
1275
|
const start = parentStart + (seq.at ?? 0);
|
|
1269
1276
|
const end = Math.min(parentEnd, seq.duration !== void 0 ? start + seq.duration : parentEnd);
|
|
1270
1277
|
const span = end - start;
|
|
1271
|
-
const keys = (seq.spec.keyframes ?? []).map((kf) => round(start +
|
|
1278
|
+
const keys = (seq.spec.keyframes ?? []).map((kf) => round(start + chunkS3ZI4MKS_cjs.resolveAt(kf.at, span))).sort((a, b) => a - b).slice(0, MAX_KEYS);
|
|
1272
1279
|
const row = { path: prefix + name, name, type: seq.spec.type, depth, start: round(start), end: round(end), keys, detail: detailOf(seq) };
|
|
1273
1280
|
const inner = [];
|
|
1274
|
-
const isComp = seq instanceof
|
|
1281
|
+
const isComp = seq instanceof chunk4WJUSDJJ_cjs.CompositionSequence;
|
|
1275
1282
|
if (isComp) walk(seq, start, end, depth + 1, prefix + name + "/", inner, transitions);
|
|
1276
1283
|
entries.push({ row, inner, groupable: !isComp });
|
|
1277
1284
|
});
|
|
@@ -1328,7 +1335,8 @@ var COLORS = {
|
|
|
1328
1335
|
audio: "#ff7aa8",
|
|
1329
1336
|
composition: "#8ea4c8",
|
|
1330
1337
|
three: "#ff9f68",
|
|
1331
|
-
camera: "#b0b0b0"
|
|
1338
|
+
camera: "#b0b0b0",
|
|
1339
|
+
null: "#b0b0b0"
|
|
1332
1340
|
};
|
|
1333
1341
|
var esc = (s) => s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
1334
1342
|
var num = (n) => String(Math.round(n * 100) / 100);
|
|
@@ -1449,6 +1457,21 @@ function startWhenRunning(ctx, start, gesture) {
|
|
|
1449
1457
|
}
|
|
1450
1458
|
var GESTURES = ["pointerdown", "mousedown", "touchend", "keydown"];
|
|
1451
1459
|
|
|
1460
|
+
// src/core/poster.ts
|
|
1461
|
+
function normalizePoster(poster, duration, frameRate) {
|
|
1462
|
+
if (poster === void 0 || poster === null) return { seconds: null, frame: null };
|
|
1463
|
+
if (typeof poster !== "number" || !Number.isFinite(poster)) {
|
|
1464
|
+
console.warn(`pixi-effects: movie.init({ poster }): ${JSON.stringify(poster)} is not a time in seconds; ignored`);
|
|
1465
|
+
return { seconds: null, frame: null };
|
|
1466
|
+
}
|
|
1467
|
+
let seconds = poster < 0 ? Math.max(0, duration + poster) : poster;
|
|
1468
|
+
if (seconds > duration) {
|
|
1469
|
+
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`);
|
|
1470
|
+
seconds = duration;
|
|
1471
|
+
}
|
|
1472
|
+
return { seconds, frame: Math.round(seconds * frameRate) };
|
|
1473
|
+
}
|
|
1474
|
+
|
|
1452
1475
|
// src/core/Movie.ts
|
|
1453
1476
|
PIXI.extensions.add(PIXI.CullerPlugin);
|
|
1454
1477
|
gsap.gsap.registerPlugin(PixiPlugin.PixiPlugin);
|
|
@@ -1462,6 +1485,11 @@ var Movie = class {
|
|
|
1462
1485
|
audioSource = null;
|
|
1463
1486
|
gainNode = null;
|
|
1464
1487
|
_cancelAudioStart = null;
|
|
1488
|
+
/** The poster time in seconds (`movie.init({ poster })`), or null. */
|
|
1489
|
+
poster = null;
|
|
1490
|
+
_posterFrame = null;
|
|
1491
|
+
/** The canvas shows the poster picture while the playhead is still at frame 0 (like `<video poster>`): the first seek or play replaces it. */
|
|
1492
|
+
_atPoster = false;
|
|
1465
1493
|
/** > 0 while the movie moves its own playhead (playback, render, snapshot, contactSheet): those are not the seeks `seeking` / `seeked` report. */
|
|
1466
1494
|
_quiet = 0;
|
|
1467
1495
|
_volume = 1;
|
|
@@ -1505,6 +1533,36 @@ var Movie = class {
|
|
|
1505
1533
|
get isReady() {
|
|
1506
1534
|
return this._initState === "ready";
|
|
1507
1535
|
}
|
|
1536
|
+
/** The poster time as a frame number, or null. */
|
|
1537
|
+
get posterFrame() {
|
|
1538
|
+
return this._posterFrame;
|
|
1539
|
+
}
|
|
1540
|
+
/** Draw the poster frame on the canvas and leave the playhead where it is (frame 0): the picture before the movie plays. */
|
|
1541
|
+
async _showPoster() {
|
|
1542
|
+
if (this._posterFrame === null || !this.timeline) return;
|
|
1543
|
+
const back = this.currentFrame;
|
|
1544
|
+
this.currentFrame = this._posterFrame;
|
|
1545
|
+
try {
|
|
1546
|
+
this.timeline.time(this._posterFrame / this.frameRate);
|
|
1547
|
+
await this._awaitVideoFrames();
|
|
1548
|
+
this._updateSpace();
|
|
1549
|
+
this._renderNow();
|
|
1550
|
+
} finally {
|
|
1551
|
+
this.currentFrame = back;
|
|
1552
|
+
}
|
|
1553
|
+
this._atPoster = true;
|
|
1554
|
+
}
|
|
1555
|
+
async posterImage(opts = {}) {
|
|
1556
|
+
this._requireReady("posterImage");
|
|
1557
|
+
const back = this.currentFrame, wasAtPoster = this._atPoster;
|
|
1558
|
+
try {
|
|
1559
|
+
return await this.snapshot(this._posterFrame ?? 0, opts);
|
|
1560
|
+
} finally {
|
|
1561
|
+
this.currentFrame = back;
|
|
1562
|
+
if (wasAtPoster) await this._showPoster();
|
|
1563
|
+
else await this._quietly(() => this.gotoFrame(back, true));
|
|
1564
|
+
}
|
|
1565
|
+
}
|
|
1508
1566
|
_ensureAudioContext() {
|
|
1509
1567
|
if (!this._audioContext) {
|
|
1510
1568
|
const Ctx = window.AudioContext ?? window.webkitAudioContext;
|
|
@@ -1513,7 +1571,7 @@ var Movie = class {
|
|
|
1513
1571
|
return this._audioContext;
|
|
1514
1572
|
}
|
|
1515
1573
|
async init(options = {}) {
|
|
1516
|
-
chunkUQBGW5DO_cjs.warnUnknownOptions("movie.init()", options, ["width", "height", "duration", "frameRate", "background", "canvas", "assets", "composition"]);
|
|
1574
|
+
chunkUQBGW5DO_cjs.warnUnknownOptions("movie.init()", options, ["width", "height", "duration", "frameRate", "background", "canvas", "assets", "composition", "poster"]);
|
|
1517
1575
|
this._initState = "pending";
|
|
1518
1576
|
try {
|
|
1519
1577
|
this.width = options.width ?? 1920;
|
|
@@ -1521,6 +1579,9 @@ var Movie = class {
|
|
|
1521
1579
|
this.duration = options.duration ?? 10;
|
|
1522
1580
|
this.frameRate = options.frameRate ?? 30;
|
|
1523
1581
|
this.totalFrames = Math.round(this.duration * this.frameRate);
|
|
1582
|
+
const poster = normalizePoster(options.poster, this.duration, this.frameRate);
|
|
1583
|
+
this.poster = poster.seconds;
|
|
1584
|
+
this._posterFrame = poster.frame;
|
|
1524
1585
|
this.background = options.background ?? "#000000";
|
|
1525
1586
|
this.timeline = gsap.gsap.timeline({ paused: true, defaults: { ease: "none" } });
|
|
1526
1587
|
this.timeline.add(gsap.gsap.to({}, { duration: this.duration }));
|
|
@@ -1557,7 +1618,7 @@ var Movie = class {
|
|
|
1557
1618
|
height: options.composition.height ?? this.height,
|
|
1558
1619
|
duration: options.composition.duration ?? this.duration
|
|
1559
1620
|
} : void 0;
|
|
1560
|
-
await
|
|
1621
|
+
await chunkS3ZI4MKS_cjs.ensureFilterLibrary(seededComposition);
|
|
1561
1622
|
const userComposition = seededComposition ? expandTransitions(seededComposition) : void 0;
|
|
1562
1623
|
const rootSeqSpec = {
|
|
1563
1624
|
type: "composition",
|
|
@@ -1567,7 +1628,7 @@ var Movie = class {
|
|
|
1567
1628
|
...userComposition
|
|
1568
1629
|
};
|
|
1569
1630
|
if (userComposition) carryTransitionWindows(userComposition, rootSeqSpec);
|
|
1570
|
-
const composition = new
|
|
1631
|
+
const composition = new chunk4WJUSDJJ_cjs.CompositionSequence(rootSeqSpec, null, rootShape);
|
|
1571
1632
|
await composition.build();
|
|
1572
1633
|
this._rootSequence = composition;
|
|
1573
1634
|
if (composition.target) root.addChild(composition.target);
|
|
@@ -1584,6 +1645,7 @@ var Movie = class {
|
|
|
1584
1645
|
this._updateSpace();
|
|
1585
1646
|
this._renderNow();
|
|
1586
1647
|
this._initState = "ready";
|
|
1648
|
+
if (this._posterFrame !== null) await this._showPoster();
|
|
1587
1649
|
this.emit("ready");
|
|
1588
1650
|
} catch (err) {
|
|
1589
1651
|
try {
|
|
@@ -1597,7 +1659,7 @@ var Movie = class {
|
|
|
1597
1659
|
}
|
|
1598
1660
|
async gotoFrame(frame, force = false) {
|
|
1599
1661
|
if (this._initState !== "ready") return;
|
|
1600
|
-
if (!force && this.currentFrame === frame) return;
|
|
1662
|
+
if (!force && !this._atPoster && this.currentFrame === frame) return;
|
|
1601
1663
|
if (this._quiet > 0) return this._goto(frame);
|
|
1602
1664
|
const target = Math.max(0, Math.min(frame, this.totalFrames));
|
|
1603
1665
|
this.emit("seeking", { frame: target, totalFrames: this.totalFrames });
|
|
@@ -1617,6 +1679,7 @@ var Movie = class {
|
|
|
1617
1679
|
this.emit("error", { where, message: error instanceof Error ? error.message : String(error), error });
|
|
1618
1680
|
}
|
|
1619
1681
|
async _goto(frame) {
|
|
1682
|
+
this._atPoster = false;
|
|
1620
1683
|
this.currentFrame = Math.max(0, Math.min(frame, this.totalFrames));
|
|
1621
1684
|
this.timeline.time(this.currentFrame / this.frameRate);
|
|
1622
1685
|
await this._awaitVideoFrames();
|
|
@@ -1658,7 +1721,7 @@ var Movie = class {
|
|
|
1658
1721
|
const g = sheet.getContext("2d");
|
|
1659
1722
|
g.fillStyle = "#10131c";
|
|
1660
1723
|
g.fillRect(0, 0, L.width, L.height);
|
|
1661
|
-
const back = this.currentFrame;
|
|
1724
|
+
const back = this.currentFrame, wasAtPoster = this._atPoster;
|
|
1662
1725
|
try {
|
|
1663
1726
|
for (let i = 0; i < frames.length; i++) {
|
|
1664
1727
|
const f = frames[i];
|
|
@@ -1671,7 +1734,8 @@ var Movie = class {
|
|
|
1671
1734
|
g.fillText(`frame ${f} \xB7 ${(f / this.frameRate).toFixed(2)}s`, x + 8, y + LABEL_H / 2);
|
|
1672
1735
|
}
|
|
1673
1736
|
} finally {
|
|
1674
|
-
|
|
1737
|
+
if (wasAtPoster) await this._showPoster();
|
|
1738
|
+
else await this._quietly(() => this.gotoFrame(back, true));
|
|
1675
1739
|
}
|
|
1676
1740
|
return encodeCanvas(sheet, "image/png", opts.as ?? "blob");
|
|
1677
1741
|
}
|
|
@@ -2063,17 +2127,17 @@ function orbit(opts) {
|
|
|
2063
2127
|
throw new Error("pixi-effects: orbit() cannot combine `radius` with `dollyZoom` \u2014 the radius follows the animated fov");
|
|
2064
2128
|
}
|
|
2065
2129
|
const [cx, cy] = opts.center ?? [width / 2, height / 2];
|
|
2066
|
-
const fixedR = opts.radius ??
|
|
2130
|
+
const fixedR = opts.radius ?? chunkS3ZI4MKS_cjs.homeDistance(height, fov ?? chunkS3ZI4MKS_cjs.DEFAULT_FOV);
|
|
2067
2131
|
const start = opts.start ?? -degrees / 2;
|
|
2068
2132
|
const steps = Math.max(1, Math.round(duration * stepsPerSecond));
|
|
2069
2133
|
const easeFn = parseEase(ease);
|
|
2070
2134
|
const dollyEase = dolly ? parseEase(dolly.ease ?? ease) : null;
|
|
2071
2135
|
const pos = (i) => {
|
|
2072
2136
|
const p = i / steps;
|
|
2073
|
-
const a = (start + degrees * easeFn(p)) *
|
|
2137
|
+
const a = (start + degrees * easeFn(p)) * chunkS3ZI4MKS_cjs.DEG;
|
|
2074
2138
|
if (!dolly) return { x: cx + fixedR * Math.sin(a), z: fixedR * Math.cos(a) };
|
|
2075
2139
|
const f = dolly.from + (dolly.to - dolly.from) * dollyEase(p);
|
|
2076
|
-
const R =
|
|
2140
|
+
const R = chunkS3ZI4MKS_cjs.homeDistance(height, f);
|
|
2077
2141
|
return { x: cx + R * Math.sin(a), z: R * Math.cos(a), fov: f };
|
|
2078
2142
|
};
|
|
2079
2143
|
const keyframes = [];
|
|
@@ -2100,6 +2164,269 @@ function orbit(opts) {
|
|
|
2100
2164
|
return spec;
|
|
2101
2165
|
}
|
|
2102
2166
|
|
|
2167
|
+
// src/presets/followPath.ts
|
|
2168
|
+
var KEYS = ["d", "at", "duration", "ease", "from", "to", "orient", "rotate", "frameRate"];
|
|
2169
|
+
var MAX_STEPS = 2e3;
|
|
2170
|
+
function followPath(opts) {
|
|
2171
|
+
chunkUQBGW5DO_cjs.warnUnknownOptions("followPath()", opts, KEYS);
|
|
2172
|
+
const { d, at = 0, duration, ease = "none", from = 0, to = 1, orient = false, rotate = 0, frameRate = 30 } = opts;
|
|
2173
|
+
if (typeof d !== "string" || d.trim() === "") throw new Error('followPath(): d must be SVG path data, e.g. "M 100 600 C 300 100 900 100 1180 600"');
|
|
2174
|
+
if (!(Number.isFinite(duration) && duration > 0)) throw new Error(`followPath(): duration must be a positive number of seconds, got ${duration}`);
|
|
2175
|
+
if (!(Number.isFinite(frameRate) && frameRate > 0)) throw new Error(`followPath(): frameRate must be a positive number (samples per second), got ${frameRate}`);
|
|
2176
|
+
for (const [n, v] of [["from", from], ["to", to]]) {
|
|
2177
|
+
if (!(Number.isFinite(v) && v >= 0 && v <= 1)) throw new Error(`followPath(): ${n} must be a fraction of the path between 0 and 1, got ${v}`);
|
|
2178
|
+
}
|
|
2179
|
+
const steps = Math.max(1, Math.round(duration * frameRate));
|
|
2180
|
+
if (steps > MAX_STEPS) throw new Error(`followPath(): ${steps} samples is too many (max ${MAX_STEPS}); lower frameRate or duration`);
|
|
2181
|
+
const segments = [];
|
|
2182
|
+
let total = 0;
|
|
2183
|
+
for (const line of chunkS3ZI4MKS_cjs.flattenSvgPath(d)) {
|
|
2184
|
+
const p = line.closed ? [...line.pts, line.pts[0], line.pts[1]] : line.pts;
|
|
2185
|
+
for (let i = 2; i < p.length; i += 2) {
|
|
2186
|
+
const len = Math.hypot(p[i] - p[i - 2], p[i + 1] - p[i - 1]);
|
|
2187
|
+
if (len === 0) continue;
|
|
2188
|
+
segments.push({ x0: p[i - 2], y0: p[i - 1], x1: p[i], y1: p[i + 1], start: total, len });
|
|
2189
|
+
total += len;
|
|
2190
|
+
}
|
|
2191
|
+
}
|
|
2192
|
+
if (!(total > 0)) throw new Error(`followPath(): the path "${d}" has no length (it needs at least two different points)`);
|
|
2193
|
+
const find = (s) => {
|
|
2194
|
+
let lo = 0, hi = segments.length - 1;
|
|
2195
|
+
while (lo < hi) {
|
|
2196
|
+
const mid = lo + hi >> 1;
|
|
2197
|
+
if (segments[mid].start + segments[mid].len < s) lo = mid + 1;
|
|
2198
|
+
else hi = mid;
|
|
2199
|
+
}
|
|
2200
|
+
return segments[lo];
|
|
2201
|
+
};
|
|
2202
|
+
const at_ = (fraction) => {
|
|
2203
|
+
const s = Math.min(Math.max(fraction, 0), 1) * total;
|
|
2204
|
+
const g = find(s);
|
|
2205
|
+
const t = Math.min(Math.max((s - g.start) / g.len, 0), 1);
|
|
2206
|
+
return { x: g.x0 + (g.x1 - g.x0) * t, y: g.y0 + (g.y1 - g.y0) * t, heading: Math.atan2(g.y1 - g.y0, g.x1 - g.x0) * 180 / Math.PI };
|
|
2207
|
+
};
|
|
2208
|
+
const shape = parseEase(ease);
|
|
2209
|
+
const round2 = (v) => Math.round(v * 1e6) / 1e6;
|
|
2210
|
+
let turn = 0;
|
|
2211
|
+
const sample = (k) => {
|
|
2212
|
+
const p = from + (to - from) * shape(k / steps);
|
|
2213
|
+
const pt = at_(p);
|
|
2214
|
+
const out2 = { x: round2(pt.x), y: round2(pt.y) };
|
|
2215
|
+
if (orient) {
|
|
2216
|
+
let h = pt.heading + rotate;
|
|
2217
|
+
if (k === 0) turn = h;
|
|
2218
|
+
else {
|
|
2219
|
+
let delta = h - turn;
|
|
2220
|
+
delta -= 360 * Math.round(delta / 360);
|
|
2221
|
+
turn += delta;
|
|
2222
|
+
}
|
|
2223
|
+
out2.rotation = round2(turn);
|
|
2224
|
+
}
|
|
2225
|
+
return out2;
|
|
2226
|
+
};
|
|
2227
|
+
const out = [{ at, set: sample(0) }];
|
|
2228
|
+
const step = duration / steps;
|
|
2229
|
+
for (let k = 1; k <= steps; k++) out.push({ at: round2(at + (k - 1) * step), to: sample(k), duration: round2(step), ease: "none" });
|
|
2230
|
+
return out;
|
|
2231
|
+
}
|
|
2232
|
+
|
|
2233
|
+
// src/presets/stagger.ts
|
|
2234
|
+
var STAGGER_KEYS = ["each", "amount", "from", "grid", "ease", "seed"];
|
|
2235
|
+
var FROM_NAMES = ["start", "end", "center", "edges", "random"];
|
|
2236
|
+
function stagger(items, options = {}) {
|
|
2237
|
+
const isCount = typeof items === "number";
|
|
2238
|
+
const n = isCount ? items : items.length;
|
|
2239
|
+
if (!Number.isInteger(n) || n < 0) throw new Error(`stagger(): count must be a whole number of items, got ${n}`);
|
|
2240
|
+
chunkUQBGW5DO_cjs.warnUnknownOptions("stagger()", options, STAGGER_KEYS);
|
|
2241
|
+
const { each, amount, from = "start", grid, ease = "none", seed = 0 } = options;
|
|
2242
|
+
if (each !== void 0 && amount !== void 0) throw new Error("stagger(): give `each` (seconds between neighbours) or `amount` (seconds in total), not both");
|
|
2243
|
+
if (each !== void 0 && !(Number.isFinite(each) && each >= 0)) throw new Error(`stagger(): each must be a number of seconds >= 0, got ${each}`);
|
|
2244
|
+
if (amount !== void 0 && !(Number.isFinite(amount) && amount >= 0)) throw new Error(`stagger(): amount must be a number of seconds >= 0, got ${amount}`);
|
|
2245
|
+
if (typeof from === "string" && !FROM_NAMES.includes(from)) {
|
|
2246
|
+
throw new Error(`stagger(): from "${from}" is not one of ${FROM_NAMES.map((f) => `"${f}"`).join(", ")} or an item index`);
|
|
2247
|
+
}
|
|
2248
|
+
if (typeof from === "number" && !(Number.isInteger(from) && from >= 0 && from < Math.max(n, 1))) throw new Error(`stagger(): from ${from} is not an index of the ${n} items`);
|
|
2249
|
+
if (grid && !(Array.isArray(grid) && grid.length === 2 && grid.every((g) => Number.isInteger(g) && g > 0) && grid[0] * grid[1] >= n)) {
|
|
2250
|
+
throw new Error(`stagger(): grid must be [columns, rows] and hold all ${n} items, got ${JSON.stringify(grid)}`);
|
|
2251
|
+
}
|
|
2252
|
+
const dist = distances(n, from, grid, seed);
|
|
2253
|
+
const max = dist.reduce((m, d) => Math.max(m, d), 0);
|
|
2254
|
+
const total = amount !== void 0 ? amount : (each ?? 0.1) * max;
|
|
2255
|
+
const shape = parseEase(ease);
|
|
2256
|
+
const delays = dist.map((d) => max === 0 ? 0 : Math.round(total * shape(d / max) * 1e6) / 1e6);
|
|
2257
|
+
if (isCount) return delays;
|
|
2258
|
+
return items.map((layer, i) => ({ ...layer, at: Math.round(((layer.at ?? 0) + delays[i]) * 1e6) / 1e6 }));
|
|
2259
|
+
}
|
|
2260
|
+
function distances(n, from, grid, seed) {
|
|
2261
|
+
if (n === 0) return [];
|
|
2262
|
+
if (from === "random") {
|
|
2263
|
+
const order = Array.from({ length: n }, (_, i) => i);
|
|
2264
|
+
const r = chunkS3ZI4MKS_cjs.random(seed);
|
|
2265
|
+
for (let i = n - 1; i > 0; i--) {
|
|
2266
|
+
const j = Math.floor(r() * (i + 1));
|
|
2267
|
+
[order[i], order[j]] = [order[j], order[i]];
|
|
2268
|
+
}
|
|
2269
|
+
return order;
|
|
2270
|
+
}
|
|
2271
|
+
if (!grid) {
|
|
2272
|
+
const mid = (n - 1) / 2;
|
|
2273
|
+
return Array.from({ length: n }, (_, i) => {
|
|
2274
|
+
switch (from) {
|
|
2275
|
+
case "start":
|
|
2276
|
+
return i;
|
|
2277
|
+
case "end":
|
|
2278
|
+
return n - 1 - i;
|
|
2279
|
+
case "center":
|
|
2280
|
+
return Math.abs(i - mid);
|
|
2281
|
+
case "edges":
|
|
2282
|
+
return mid - Math.abs(i - mid);
|
|
2283
|
+
default:
|
|
2284
|
+
return Math.abs(i - from);
|
|
2285
|
+
}
|
|
2286
|
+
});
|
|
2287
|
+
}
|
|
2288
|
+
const [cols, rows] = grid;
|
|
2289
|
+
const at = (i) => ({ x: i % cols, y: Math.floor(i / cols) });
|
|
2290
|
+
const origin = from === "start" ? { x: 0, y: 0 } : from === "end" ? { x: cols - 1, y: rows - 1 } : from === "center" || from === "edges" ? { x: (cols - 1) / 2, y: (rows - 1) / 2 } : at(from);
|
|
2291
|
+
const d = Array.from({ length: n }, (_, i) => Math.hypot(at(i).x - origin.x, at(i).y - origin.y));
|
|
2292
|
+
if (from !== "edges") return d;
|
|
2293
|
+
const far = Math.max(...d);
|
|
2294
|
+
return d.map((v) => far - v);
|
|
2295
|
+
}
|
|
2296
|
+
|
|
2297
|
+
// src/presets/wiggle.ts
|
|
2298
|
+
var WIGGLE_KEYS = ["at", "duration", "freq", "seed", "ease", "props"];
|
|
2299
|
+
var MAX_STEPS2 = 2e3;
|
|
2300
|
+
function wiggle(opts) {
|
|
2301
|
+
chunkUQBGW5DO_cjs.warnUnknownOptions("wiggle()", opts, WIGGLE_KEYS);
|
|
2302
|
+
const { at = 0, duration, freq = 3, seed = 0, ease = "sine.inOut", props } = opts;
|
|
2303
|
+
if (!(Number.isFinite(duration) && duration > 0)) throw new Error(`wiggle(): duration must be a positive number of seconds, got ${duration}`);
|
|
2304
|
+
if (!(Number.isFinite(freq) && freq > 0)) throw new Error(`wiggle(): freq must be a positive number (steps per second), got ${freq}`);
|
|
2305
|
+
const names = Object.keys(props ?? {});
|
|
2306
|
+
if (names.length === 0) throw new Error("wiggle(): props is empty; name at least one property, e.g. props: { x: { around: 100, amp: 8 } }");
|
|
2307
|
+
for (const n of names) {
|
|
2308
|
+
const p = props[n];
|
|
2309
|
+
if (!Number.isFinite(p.amp)) throw new Error(`wiggle(): props.${n}.amp must be a number, got ${p.amp}`);
|
|
2310
|
+
if (p.around === void 0 || p.around === null || p.around === "") throw new Error(`wiggle(): props.${n}.around is missing`);
|
|
2311
|
+
}
|
|
2312
|
+
const steps = Math.ceil(duration * freq - 1e-9);
|
|
2313
|
+
if (steps > MAX_STEPS2) throw new Error(`wiggle(): ${steps} steps is too many (max ${MAX_STEPS2}); lower freq or duration`);
|
|
2314
|
+
const set = {};
|
|
2315
|
+
for (const n of names) set[n] = props[n].around;
|
|
2316
|
+
const out = [{ at, set }];
|
|
2317
|
+
const stepLen = duration / steps;
|
|
2318
|
+
for (let s = 0; s < steps; s++) {
|
|
2319
|
+
const to = {};
|
|
2320
|
+
names.forEach((n, pi) => {
|
|
2321
|
+
const p = props[n];
|
|
2322
|
+
if (s === steps - 1) {
|
|
2323
|
+
to[n] = p.around;
|
|
2324
|
+
return;
|
|
2325
|
+
}
|
|
2326
|
+
const off = (chunkS3ZI4MKS_cjs.rand(seed * 7919 + pi * 104729 + s * 31 + 1) * 2 - 1) * p.amp;
|
|
2327
|
+
to[n] = typeof p.around === "number" ? p.around + off : `(${p.around}) ${off < 0 ? "-" : "+"} ${Math.abs(off)}`;
|
|
2328
|
+
});
|
|
2329
|
+
out.push({ at: at + s * stepLen, to, duration: stepLen, ease });
|
|
2330
|
+
}
|
|
2331
|
+
return out;
|
|
2332
|
+
}
|
|
2333
|
+
|
|
2334
|
+
// src/presets/animateText.ts
|
|
2335
|
+
var KEYS2 = ["by", "x", "y", "align", "name", "at", "duration", "in", "out", "stagger", "idle", "styleFor"];
|
|
2336
|
+
var PRESETS = {
|
|
2337
|
+
rise: { from: { y: 40, alpha: 0 }, duration: 0.5, ease: "back.out(2)" },
|
|
2338
|
+
drop: { from: { y: -50, alpha: 0 }, duration: 0.7, ease: "bounce.out" },
|
|
2339
|
+
fade: { from: { alpha: 0 }, duration: 0.5, ease: "power1.out" },
|
|
2340
|
+
pop: { from: { scale: 0, alpha: 0 }, duration: 0.45, ease: "back.out(2.4)" },
|
|
2341
|
+
zoom: { from: { scale: 2.5, alpha: 0 }, duration: 0.6, ease: "power3.out" },
|
|
2342
|
+
slide: { from: { x: -60, alpha: 0 }, duration: 0.5, ease: "power3.out" },
|
|
2343
|
+
spin: { from: { rotation: -90, scale: 0, alpha: 0 }, duration: 0.6, ease: "back.out(1.7)" }
|
|
2344
|
+
};
|
|
2345
|
+
var PRESET_NAMES = Object.keys(PRESETS);
|
|
2346
|
+
function animateText(text, style, options) {
|
|
2347
|
+
chunkUQBGW5DO_cjs.warnUnknownOptions("animateText()", options, KEYS2);
|
|
2348
|
+
const { by = "chars", x = 0, y = 0, align = "left", name = "text", at = 0, duration, idle, styleFor } = options;
|
|
2349
|
+
if (!(Number.isFinite(duration) && duration > 0)) throw new Error(`animateText(): duration must be a positive number of seconds (how long until the last piece is gone), got ${duration}`);
|
|
2350
|
+
const pieces = splitText(text, style, { by, x, y, align });
|
|
2351
|
+
if (pieces.length === 0) return [];
|
|
2352
|
+
const lineHeight = measureText("M", style).height;
|
|
2353
|
+
const inTween = options.in === false ? null : resolveTween(options.in ?? "rise", "in");
|
|
2354
|
+
const outTween = options.out === void 0 || options.out === false ? null : resolveTween(options.out, "out");
|
|
2355
|
+
const delays = stagger(pieces.length, options.stagger ?? { each: 0.04 });
|
|
2356
|
+
const wave = Math.max(...delays);
|
|
2357
|
+
const need = (inTween?.duration ?? 0) + (outTween?.duration ?? 0) + wave;
|
|
2358
|
+
if (duration < need - 1e-9) {
|
|
2359
|
+
throw new Error(`animateText(): duration ${duration} is too short for this wave: the entrance${outTween ? ", the exit" : ""} and the stagger need at least ${+need.toFixed(3)} s`);
|
|
2360
|
+
}
|
|
2361
|
+
const end = at + duration;
|
|
2362
|
+
return pieces.map((p, i) => {
|
|
2363
|
+
const cx = p.x + p.width / 2, cy = p.y + lineHeight / 2;
|
|
2364
|
+
const rest = { x: cx, y: cy, alpha: 1, scale: 1, scaleX: 1, scaleY: 1, rotation: 0 };
|
|
2365
|
+
const start = at + delays[i];
|
|
2366
|
+
const len = end - start;
|
|
2367
|
+
const keyframes = [];
|
|
2368
|
+
const place = (key, v) => key === "x" || key === "y" ? rest[key] + v : v;
|
|
2369
|
+
if (inTween) {
|
|
2370
|
+
keyframes.push({
|
|
2371
|
+
at: 0,
|
|
2372
|
+
duration: inTween.duration,
|
|
2373
|
+
ease: inTween.ease,
|
|
2374
|
+
from: Object.fromEntries(Object.entries(inTween.from).map(([k, v]) => [k, place(k, v)])),
|
|
2375
|
+
to: Object.fromEntries(Object.keys(inTween.from).map((k) => [k, inTween.to[k] ?? rest[k]]))
|
|
2376
|
+
});
|
|
2377
|
+
}
|
|
2378
|
+
let idleEnd = len;
|
|
2379
|
+
if (outTween) {
|
|
2380
|
+
const begin = outTween.duration + (wave - delays[i]);
|
|
2381
|
+
idleEnd = len - begin;
|
|
2382
|
+
keyframes.push({
|
|
2383
|
+
at: -begin,
|
|
2384
|
+
duration: outTween.duration,
|
|
2385
|
+
ease: outTween.ease,
|
|
2386
|
+
from: Object.fromEntries(Object.keys(outTween.from).map((k) => [k, outTween.to[k] ?? rest[k]])),
|
|
2387
|
+
to: Object.fromEntries(Object.entries(outTween.from).map(([k, v]) => [k, place(k, v)]))
|
|
2388
|
+
});
|
|
2389
|
+
}
|
|
2390
|
+
if (idle) {
|
|
2391
|
+
const props = {};
|
|
2392
|
+
for (const k of ["x", "y", "rotation", "scale"]) if (idle[k] !== void 0) props[k] = { around: rest[k], amp: idle[k] };
|
|
2393
|
+
const freq = idle.freq ?? 1;
|
|
2394
|
+
const from = inTween?.duration ?? 0;
|
|
2395
|
+
if (Object.keys(props).length > 0 && idleEnd - from >= 1 / freq) {
|
|
2396
|
+
keyframes.push(...wiggle({ at: from, duration: idleEnd - from, freq, seed: (idle.seed ?? 0) * 1e3 + i, props }));
|
|
2397
|
+
}
|
|
2398
|
+
}
|
|
2399
|
+
return {
|
|
2400
|
+
type: "text",
|
|
2401
|
+
name: `${name}-${i}`,
|
|
2402
|
+
text: p.text,
|
|
2403
|
+
style: styleFor ? { ...style, ...styleFor(p) } : style,
|
|
2404
|
+
at: start,
|
|
2405
|
+
duration: len,
|
|
2406
|
+
initial: { x: cx, y: cy, anchorX: 0.5, anchorY: 0.5 },
|
|
2407
|
+
keyframes
|
|
2408
|
+
};
|
|
2409
|
+
});
|
|
2410
|
+
}
|
|
2411
|
+
function resolveTween(spec, which) {
|
|
2412
|
+
const t = typeof spec === "string" ? { preset: spec } : spec;
|
|
2413
|
+
if (t.preset !== void 0 && !(t.preset in PRESETS)) {
|
|
2414
|
+
const guess = chunkUQBGW5DO_cjs.suggestName(String(t.preset), PRESET_NAMES);
|
|
2415
|
+
throw new Error(`animateText(): ${which} "${t.preset}" is not a preset${guess ? `; did you mean "${guess}"?` : ""} (presets: ${PRESET_NAMES.map((n) => `"${n}"`).join(", ")})`);
|
|
2416
|
+
}
|
|
2417
|
+
const base = t.preset ? PRESETS[t.preset] : void 0;
|
|
2418
|
+
const from = { ...base?.from ?? {}, ...t.from ?? {} };
|
|
2419
|
+
const to = t.to ?? {};
|
|
2420
|
+
const known = /* @__PURE__ */ new Set(["x", "y", "alpha", "scale", "scaleX", "scaleY", "rotation"]);
|
|
2421
|
+
for (const k of Object.keys(from)) {
|
|
2422
|
+
if (!known.has(k) && to[k] === void 0) throw new Error(`animateText(): ${which}.from has "${k}", which has no resting value; give ${which}.to: { ${k}: \u2026 }`);
|
|
2423
|
+
}
|
|
2424
|
+
if (Object.keys(from).length === 0) throw new Error(`animateText(): ${which} needs a preset or a from`);
|
|
2425
|
+
let ease = t.ease ?? base?.ease ?? "power2.out";
|
|
2426
|
+
if (which === "out" && t.ease === void 0) ease = ease.replace(".out", ".in");
|
|
2427
|
+
return { from, to: { ...to }, duration: t.duration ?? base?.duration ?? 0.5, ease };
|
|
2428
|
+
}
|
|
2429
|
+
|
|
2103
2430
|
// src/transforms/withFade.ts
|
|
2104
2431
|
function withFade(spec, opts) {
|
|
2105
2432
|
chunkUQBGW5DO_cjs.warnUnknownOptions("withFade()", opts, ["in", "out"]);
|
|
@@ -2120,15 +2447,31 @@ function withFade(spec, opts) {
|
|
|
2120
2447
|
return spec;
|
|
2121
2448
|
}
|
|
2122
2449
|
|
|
2450
|
+
Object.defineProperty(exports, "noise", {
|
|
2451
|
+
enumerable: true,
|
|
2452
|
+
get: function () { return chunkS3ZI4MKS_cjs.noise; }
|
|
2453
|
+
});
|
|
2454
|
+
Object.defineProperty(exports, "rand", {
|
|
2455
|
+
enumerable: true,
|
|
2456
|
+
get: function () { return chunkS3ZI4MKS_cjs.rand; }
|
|
2457
|
+
});
|
|
2458
|
+
Object.defineProperty(exports, "random", {
|
|
2459
|
+
enumerable: true,
|
|
2460
|
+
get: function () { return chunkS3ZI4MKS_cjs.random; }
|
|
2461
|
+
});
|
|
2123
2462
|
Object.defineProperty(exports, "registerSequenceType", {
|
|
2124
2463
|
enumerable: true,
|
|
2125
|
-
get: function () { return
|
|
2464
|
+
get: function () { return chunkS3ZI4MKS_cjs.registerSequenceType; }
|
|
2126
2465
|
});
|
|
2127
2466
|
exports.Movie = Movie;
|
|
2467
|
+
exports.animateText = animateText;
|
|
2468
|
+
exports.followPath = followPath;
|
|
2128
2469
|
exports.kenBurns = kenBurns;
|
|
2129
2470
|
exports.measureText = measureText;
|
|
2130
2471
|
exports.orbit = orbit;
|
|
2131
2472
|
exports.splitText = splitText;
|
|
2473
|
+
exports.stagger = stagger;
|
|
2474
|
+
exports.wiggle = wiggle;
|
|
2132
2475
|
exports.withFade = withFade;
|
|
2133
2476
|
//# sourceMappingURL=index.cjs.map
|
|
2134
2477
|
//# sourceMappingURL=index.cjs.map
|