pixi-effects 0.16.2 → 0.17.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 +39 -0
- package/README.md +6 -6
- package/ai/CHAT.md +4 -0
- package/ai/SKILL.md +2 -1
- package/ai/chat-template.html +3 -3
- package/ai/reference/cheatsheet.md +4 -2
- package/ai/reference/pitfalls.md +3 -0
- package/ai/reference/recipes.md +67 -0
- package/ai/template.html +5 -5
- package/ai/tools/check.mjs +162 -18
- package/ai/tools/render.mjs +21 -7
- package/dist/Composition-HYAOYPAX.js +5 -0
- package/dist/{Composition-VJWY2NDL.js.map → Composition-HYAOYPAX.js.map} +1 -1
- package/dist/Composition-LMVEASHF.cjs +14 -0
- package/dist/{Composition-3WWNVRR5.cjs.map → Composition-LMVEASHF.cjs.map} +1 -1
- package/dist/Controller.cjs +9 -3
- package/dist/Controller.cjs.map +1 -1
- package/dist/Controller.d.cts +1 -1
- package/dist/Controller.d.ts +1 -1
- package/dist/Controller.js +9 -3
- package/dist/Controller.js.map +1 -1
- package/dist/{Movie-oA0SUAyb.d.cts → Movie-CoxF2Z7t.d.cts} +82 -2
- package/dist/{Movie-DilBC348.d.ts → Movie-DHJRrUfI.d.ts} +82 -2
- package/dist/Presenter.d.cts +1 -1
- package/dist/Presenter.d.ts +1 -1
- package/dist/{chunk-2ROVCPSL.cjs → chunk-ETAIVOHP.cjs} +32 -27
- package/dist/chunk-ETAIVOHP.cjs.map +1 -0
- package/dist/{chunk-FJA5VUOF.cjs → chunk-LBJRXU7N.cjs} +15 -3
- package/dist/chunk-LBJRXU7N.cjs.map +1 -0
- package/dist/{chunk-ZNKQH356.js → chunk-ORTYBA42.js} +15 -4
- package/dist/chunk-ORTYBA42.js.map +1 -0
- package/dist/{chunk-XTIHKODH.js → chunk-Q3IVBYT7.js} +10 -5
- package/dist/chunk-Q3IVBYT7.js.map +1 -0
- package/dist/index.cjs +351 -41
- 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 +329 -19
- package/dist/index.js.map +1 -1
- package/dist/loader.css +1 -1
- package/dist/three.cjs +5 -5
- package/dist/three.js +1 -1
- package/docs/dsl.md +2 -1
- package/llms-full.txt +78 -4
- package/llms.txt +1 -1
- package/package.json +6 -3
- package/dist/Composition-3WWNVRR5.cjs +0 -14
- package/dist/Composition-VJWY2NDL.js +0 -5
- package/dist/chunk-2ROVCPSL.cjs.map +0 -1
- package/dist/chunk-FJA5VUOF.cjs.map +0 -1
- package/dist/chunk-XTIHKODH.js.map +0 -1
- package/dist/chunk-ZNKQH356.js.map +0 -1
package/dist/loader.css
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/*
|
|
2
2
|
* pixi-effects loader: one line of HTML on the page, a few lines of CSS, no JavaScript of your own.
|
|
3
3
|
*
|
|
4
|
-
* <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/pixi-effects@0.
|
|
4
|
+
* <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/pixi-effects@0.17.0/dist/loader.css">
|
|
5
5
|
* <div class="stage" style="position: relative"> <!-- any box the canvas sits in, with position: relative -->
|
|
6
6
|
* <canvas id="stage" width="1280" height="720"></canvas>
|
|
7
7
|
* <div class="pe-loader" data-label="LOADING"></div> <!-- or class="pe-loader pe-loader--bar" -->
|
package/dist/three.cjs
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var
|
|
3
|
+
var chunkLBJRXU7N_cjs = require('./chunk-LBJRXU7N.cjs');
|
|
4
4
|
require('./chunk-UQBGW5DO.cjs');
|
|
5
5
|
var pixi_js = require('pixi.js');
|
|
6
6
|
var three$1 = require('three');
|
|
7
7
|
|
|
8
|
-
var ThreeSequence = class extends
|
|
8
|
+
var ThreeSequence = class extends chunkLBJRXU7N_cjs.Sequence {
|
|
9
9
|
_renderer = null;
|
|
10
10
|
_scene = null;
|
|
11
11
|
_camera = null;
|
|
@@ -24,7 +24,7 @@ var ThreeSequence = class extends chunkFJA5VUOF_cjs.Sequence {
|
|
|
24
24
|
if (this.duration === void 0) {
|
|
25
25
|
this.duration = this.parent?.duration ?? this.root.duration;
|
|
26
26
|
}
|
|
27
|
-
const scope =
|
|
27
|
+
const scope = chunkLBJRXU7N_cjs.buildScope(this, this.parent, this.root);
|
|
28
28
|
const width = resolveDim(spec.width, scope) ?? this.parent?.width ?? this.root.width;
|
|
29
29
|
const height = resolveDim(spec.height, scope) ?? this.parent?.height ?? this.root.height;
|
|
30
30
|
const resolution = spec.resolution ?? 1;
|
|
@@ -144,12 +144,12 @@ var ThreeSequence = class extends chunkFJA5VUOF_cjs.Sequence {
|
|
|
144
144
|
function resolveDim(v, scope) {
|
|
145
145
|
if (v === void 0) return void 0;
|
|
146
146
|
if (typeof v === "number") return v;
|
|
147
|
-
return
|
|
147
|
+
return chunkLBJRXU7N_cjs.evaluateExpr(v, scope);
|
|
148
148
|
}
|
|
149
149
|
|
|
150
150
|
// src/three/index.ts
|
|
151
151
|
function registerThree() {
|
|
152
|
-
|
|
152
|
+
chunkLBJRXU7N_cjs.registerSequenceType("three", ThreeSequence);
|
|
153
153
|
}
|
|
154
154
|
function three(spec) {
|
|
155
155
|
return spec;
|
package/dist/three.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Sequence, buildScope, evaluateExpr, registerSequenceType } from './chunk-
|
|
1
|
+
import { Sequence, buildScope, evaluateExpr, registerSequenceType } from './chunk-ORTYBA42.js';
|
|
2
2
|
import './chunk-DKGG7WD4.js';
|
|
3
3
|
import { Texture, Sprite } from 'pixi.js';
|
|
4
4
|
import { WebGLRenderer, Scene, PerspectiveCamera } from 'three';
|
package/docs/dsl.md
CHANGED
|
@@ -650,11 +650,12 @@ The mask is itself a sequence, so it can have `keyframes` of its own — useful
|
|
|
650
650
|
Notes:
|
|
651
651
|
- The mask sequence is rendered as a mask, not as a normal child — its `fillColor` / `strokeColor` only matter for which pixels are kept, not for the visible colour.
|
|
652
652
|
- Any sequence type works as a mask (shape / image / text / nested composition); shapes are the natural choice for geometric reveals.
|
|
653
|
+
- A **text** mask is the letters, not their bounding box (it is drawn into a texture and its alpha is used, so the edges are soft and antialiased); so is any mask with `maskInverted`. Both cost one extra offscreen pass for the masked layer. An ordinary shape mask is a hard-edged stencil.
|
|
653
654
|
- For a left-to-right wipe, give the mask `anchorX: 0` (rect/circle/ellipse) so `width: 0 → full` grows rightward from the left edge.
|
|
654
655
|
|
|
655
656
|
#### `maskInverted`
|
|
656
657
|
|
|
657
|
-
When `true`, flip the mask sense: pixels INSIDE the mask shape become transparent, pixels OUTSIDE stay visible. Useful for knockout / cutout effects (punch a circular hole through a panel, etc.).
|
|
658
|
+
When `true`, flip the mask sense: pixels INSIDE the mask shape become transparent, pixels OUTSIDE stay visible. Useful for knockout / cutout effects (punch a circular hole through a panel, etc.). It works inside a masked group too: the hole is cut out of what the outer mask lets through, and nothing shows outside the outer mask.
|
|
658
659
|
|
|
659
660
|
```ts
|
|
660
661
|
// Photo with a circular hole punched through the middle
|
package/llms-full.txt
CHANGED
|
@@ -22,9 +22,10 @@ You describe a video as a tree of plain objects; the library plays it in a canva
|
|
|
22
22
|
- `await movie.inspect(frame)` → every layer's canvas bounds and visibility, plus `issues` (text off the canvas, cut off by an edge, empty, or overlapping other text). Run it at several frames; fix every issue it names.
|
|
23
23
|
- `await movie.snapshot(frame, { as: 'dataURL' })` → one frame, canvas only (no player bar).
|
|
24
24
|
- `movie.inspectAudio()` → **hear with numbers**: for every sound its `start`, `sound.length`, `sound.peakDb`, `sound.loudestAt` (line this up with the picture) and `sound.brightnessHz` (< 500 dark, > 8000 airy), plus `issues` (limited mix, inaudible or cut-off sounds). Check it whenever the movie has audio; `render()` encodes this same mix.
|
|
25
|
+
- **A moment, a movement, the sound (`pixi-effects-check` options):** `--at 3.5,title@end,50%,f120` writes those pictures to `check-out/<name>/frames/*.png` (and `at.png`): the last frame of a layer is `name@end`, the middle `name@mid`. `--onion 1:3` writes `onion.png`, eight frames of seconds 1–3 overlaid (the later the stronger): a trail that shows a movement's path and easing (even steps = constant speed, bunched = slow). `report.audio` now has `loudness` (`integratedLufs`, `truePeakDb`, `clippedSamples`, `silences`), `scenes` (per named top-level composition), `cues` (when each sound starts) and `notes` (advice: quiet or loud for web video, little headroom, silent stretches; web video is typically −14 to −16 LUFS); `waveform.png` draws the mix with the scene edges and the cues. A web font that failed to load fails the check; a text layer none of whose fonts is installed is listed for review.
|
|
25
26
|
- **A deck** (`composition.stops`, `deck()`): `await movie.contactSheet({ frames: movie.stops.map(s => s.frame), as: 'dataURL' })` is one picture of every stop (the check writes it as `stops.png`); what the audience reads while a page waits is that picture, so check every one. `await movie.inspectStops()` lists the stops where the picture is still changing (a stop landing before its animation ends); the check prints it as a review list: move the stop later or flag a settled one `pdf: true`. `await movie.stopImages({ which: 'pages' })` / `movie.exportPDF()` give the pages themselves.
|
|
26
27
|
From a browser tool the result is a `data:` URL string: `agent-browser eval "movie.contactSheet({ count: 6, as: 'dataURL' })" | python3 ai/tools/save-image.py /absolute/sheet.png`, then open the PNG. Sheet tiles are small (~480 px): use `snapshot` for detail. `inspect` output is long on busy scenes: pass `{ layers: 'none' }` and read `issues` first. Give layers a `name` so inspect paths are readable. A moving ticker is not flagged as cut off.
|
|
27
|
-
6. **Iterate** on what you saw (clipped text, collisions, wrong timing). Fast motion looks choppy: add `motionBlur: true` to `movie.init` (render, snapshot and contact sheet then show a film-style blur; renders take ~8× longer). Then export: `const blob = await movie.render({ format: 'mp4' })` (about real time; `movie.on('progress', …)` reports 0–100).
|
|
28
|
+
6. **Iterate** on what you saw (clipped text, collisions, wrong timing). Fast motion looks choppy: add `motionBlur: true` to `movie.init` (render, snapshot and contact sheet then show a film-style blur; renders take ~8× longer). Then export: `const blob = await movie.render({ format: 'mp4' })` (about real time; `movie.on('progress', …)` reports 0–100). **While you iterate, do not export the whole movie:** `check --no-export --at <the moment you changed>` takes seconds (3 to 10 s on the gallery pieces: most of it is loading the page and measuring the sound); to see motion, `render({ range: [from, to] })` / `pixi-effects-render --range 10:15` / `--scene name` exports only that part (picture and sound); `draft: true` / `--draft` is half size, low quality and no motion blur: a much smaller file (and a render with motion blur is several times faster without it), but the drawing itself is not faster. Export the whole movie once, at the end.
|
|
28
29
|
|
|
29
30
|
## The rules that cause most failures
|
|
30
31
|
|
|
@@ -223,13 +224,15 @@ const movie = new Movie();
|
|
|
223
224
|
await movie.init({ canvas, width, height, duration, frameRate, background, assets, composition });
|
|
224
225
|
movie.play(); movie.pause();
|
|
225
226
|
await movie.gotoFrame(n, true); // seek (frames; 30 fps => t = n/30)
|
|
226
|
-
const blob = await movie.render({ format: 'mp4' }); // 'mp4' | 'webm' | 'mov' | 'mkv'; ~real-time. Fast motion: `movie.init({ motionBlur: true })` (8 samples, 180° shutter; or a number / { samples: 2–64, shutter: 0–1 }) blurs render / snapshot / contactSheet, not live playback; a render takes `samples`× as long
|
|
227
|
+
const blob = await movie.render({ format: 'mp4' }); // 'mp4' | 'webm' | 'mov' | 'mkv'; ~real-time. Part of it: `range: [2, 5]` (seconds) or `range: 'title'` (a top-level layer's span; picture and sound); `scale: 0.5` / `draft: true` (half size, low quality, no motion blur: a smaller file, not a faster drawing). Fast motion: `movie.init({ motionBlur: true })` (8 samples, 180° shutter; or a number / { samples: 2–64, shutter: 0–1 }) blurs render / snapshot / contactSheet, not live playback; a render takes `samples`× as long
|
|
227
228
|
movie.on('progress', e => e.progress /* 0–100 */);
|
|
228
229
|
movie.audioBuffer // mixed audio (after init), if any audio layers
|
|
229
230
|
await movie.contactSheet({ count: 6, as: 'dataURL' }) // ONE image of several labelled frames — look at it. Options: frames | times | count, columns, cellWidth, as
|
|
230
231
|
await movie.snapshot(60, { as: 'dataURL' }) // one frame, canvas only (no player bar) — use it for detail; sheet tiles are small
|
|
231
232
|
await movie.inspect(60, { layers: 'none' }) // issues only (default lists the visible layers); text off-canvas / cut off / empty / overlapping text. Name your layers so paths are readable
|
|
232
|
-
movie.
|
|
233
|
+
await movie.onionSkin({ from: 1, to: 3, count: 8, as: 'dataURL' }) // frames overlaid into ONE picture of a movement (later = stronger): path and easing at a glance
|
|
234
|
+
movie.inspectFonts() // { missing: [{ layer, family }] (no font of the list is installed), failed: [web fonts whose file did not load] }
|
|
235
|
+
movie.inspectAudio() // the soundtrack as numbers; also loudness { integratedLufs, truePeakDb, clippedSamples, silences }, scenes, cues, notes (advice: -14 to -16 LUFS is typical for web video) — — you cannot listen: sources[i] = { layer, start, end, peakDb, sound: { length, peakDb, loudestAt, brightnessHz } }, windows (rms / peak / brightness per 0.1 s), issues (limited mix, inaudible / cut-off sounds)
|
|
233
236
|
new Controller(movie, { canvas }) // optional player bar (overlays the canvas bottom ~60px; not in the export)
|
|
234
237
|
```
|
|
235
238
|
|
|
@@ -291,6 +294,9 @@ A good habit that catches most of these: after `init`, read `window.__logs` (see
|
|
|
291
294
|
30c. **Look with the built-in tools, not OS screenshots**: `await movie.contactSheet({ count: 6, as: 'dataURL' })` returns one labelled image of the whole animation; `await movie.inspect(frame)` returns every layer's canvas bounds and `issues`. If you do use a browser tool, `screenshot` needs an **absolute path**, macOS `sed -i` needs `''`, and a static server may redirect `/x.html` → `/x` and drop `?query`. Keep the template's `try/catch` around `init` so failures land in `window.__logs`. Check ≥ 5 timestamps including mid-transition. *(fixed: tools built in)*
|
|
292
295
|
31. **Check audio with `movie.inspectAudio()`**: every sound measured on its own (`sound.length`, `peakDb`, `loudestAt`, `brightnessHz`), when it plays, loudness and brightness per 0.1 s window, and `issues` (the mix was limited, a layer is inaudible, an sfx is cut off by the end). Decoding a render by hand is no longer needed: `render()` encodes the same mix, on time in mp4 / mov / webm / mkv. *(fixed)*
|
|
293
296
|
|
|
297
|
+
31b. **A mistyped `ease` now warns** (`ease: 'power3.outt'` → did you mean `power3.out`). It used to run silently as GSAP's default (`power1.out`), so the motion changed and nothing said so.
|
|
298
|
+
31c. **`draft` and `scale` make the file smaller, not the drawing faster** (measured: a 48 s 1080p film exports in about the same time as a draft; the file is 7× smaller). To save time, export a part (`range` / `--range` / `--scene`), look at single moments (`check --no-export --at …`), and keep motion blur off while you iterate.
|
|
299
|
+
|
|
294
300
|
## Round 2 — found by building 16 portfolio pieces (examples/gallery)
|
|
295
301
|
|
|
296
302
|
32. **Seeking is safe now** *(fixed)*: a `set` keyframe (counter value, text fill, image tint, shape style) is undone when you seek back before it, and shapes are redrawn before they are culled, so a rect growing from `width: 0` at the left edge draws after any jump seek (contact sheet, snapshot, scrubbing, export). The old workarounds (`width: 0.01`, a one-frame `to` instead of `set`) are not needed.
|
|
@@ -1093,6 +1099,73 @@ three({
|
|
|
1093
1099
|
})
|
|
1094
1100
|
```
|
|
1095
1101
|
|
|
1102
|
+
## Subtitles from an SRT file
|
|
1103
|
+
|
|
1104
|
+
Subtitles are just text layers. The function reads a `.srt` (numbers, `00:00:01,000 --> 00:00:03,500`, the words) and returns one layer per cue: it appears and disappears over 0.12 s, sits 90 px above the bottom edge, wraps at 1000 px (about 80 % of a 1280 px picture) and has a black outline so it reads on any picture. Two cues that overlap in time are drawn on top of each other: split a long line into two lines in the `.srt` rather than relying on wrapping. Check the result with `pixi-effects-check`; it reports text that is cut off by the edge.
|
|
1105
|
+
|
|
1106
|
+
```js
|
|
1107
|
+
// @recipe subtitles
|
|
1108
|
+
// SRT text -> one text layer per cue. Paste your .srt into SRT; the function does the rest.
|
|
1109
|
+
const SRT = `1
|
|
1110
|
+
00:00:00,500 --> 00:00:02,800
|
|
1111
|
+
Hello, and welcome.
|
|
1112
|
+
|
|
1113
|
+
2
|
|
1114
|
+
00:00:03,000 --> 00:00:05,500
|
|
1115
|
+
Subtitles are just text layers
|
|
1116
|
+
that appear and disappear.`;
|
|
1117
|
+
|
|
1118
|
+
function srt(text, { bottom = 90, size = 44, color = '#ffffff', width = 1000 } = {}) {
|
|
1119
|
+
const sec = (h, m, s, ms) => +h * 3600 + +m * 60 + +s + +ms / 1000;
|
|
1120
|
+
return text.replace(/\r/g, '').trim().split(/\n\s*\n/).flatMap((block) => {
|
|
1121
|
+
const lines = block.split('\n');
|
|
1122
|
+
const i = lines.findIndex((l) => l.includes('-->'));
|
|
1123
|
+
const t = i < 0 ? null : lines[i].match(/(\d+):(\d+):(\d+)[,.](\d+)\s*-->\s*(\d+):(\d+):(\d+)[,.](\d+)/);
|
|
1124
|
+
if (!t) return [];
|
|
1125
|
+
const at = sec(t[1], t[2], t[3], t[4]), end = sec(t[5], t[6], t[7], t[8]);
|
|
1126
|
+
return [{
|
|
1127
|
+
type: 'text', name: `sub ${at.toFixed(2)}`, text: lines.slice(i + 1).join('\n'), at, duration: end - at,
|
|
1128
|
+
style: { fontFamily: 'Arial, sans-serif', fontSize: size, fontWeight: 'bold', fill: color, align: 'center', wordWrap: true, wordWrapWidth: width, stroke: { color: '#000000', width: 6 } },
|
|
1129
|
+
initial: { x: 'GW/2', y: `GH - ${bottom}`, anchorX: 0.5, anchorY: 1 },
|
|
1130
|
+
keyframes: [{ at: 0, from: { alpha: 0 }, to: { alpha: 1 }, duration: 0.12 }, { at: -0.12, to: { alpha: 0 }, duration: 0.12 }],
|
|
1131
|
+
}];
|
|
1132
|
+
});
|
|
1133
|
+
}
|
|
1134
|
+
|
|
1135
|
+
return [
|
|
1136
|
+
{ type: 'shape', shape: 'rect', width: 'GW', height: 'GH', initial: { x: 'GW/2', y: 'GH/2', fillColor: '#1d2433' } },
|
|
1137
|
+
...srt(SRT),
|
|
1138
|
+
];
|
|
1139
|
+
```
|
|
1140
|
+
|
|
1141
|
+
## A batch of videos from a table (one video per row)
|
|
1142
|
+
|
|
1143
|
+
A batch of videos is a loop outside the video. The page reads what changes from its address (`new URLSearchParams(location.search)`), and `pixi-effects-render --query "name=Aiko&score=92"` opens it with those values. Names with spaces or non-Latin letters must be URL-encoded (`jq -sRr @uri` as below, or `encodeURIComponent` in Node). Each video starts its own browser, so N videos cost N start-ups: try one with `--draft` (half size, fast), then render the real ones. To make the pieces of one row differ in more than text, use the same values to pick colours or an image name.
|
|
1144
|
+
|
|
1145
|
+
```js
|
|
1146
|
+
// @docs-only batch-page
|
|
1147
|
+
// batch.html: the same video, with the words taken from the address (?name=Aiko&score=92)
|
|
1148
|
+
const q = new URLSearchParams(location.search);
|
|
1149
|
+
const NAME = q.get('name') ?? 'World';
|
|
1150
|
+
const SCORE = q.get('score') ?? '0';
|
|
1151
|
+
// ...then use NAME and SCORE in the text layers of your composition:
|
|
1152
|
+
const sequences = [
|
|
1153
|
+
{ type: 'shape', shape: 'rect', width: 'GW', height: 'GH', initial: { x: 'GW/2', y: 'GH/2', fillColor: '#1d2433' } },
|
|
1154
|
+
{ type: 'text', text: `Well done, ${NAME}!`, at: 0.3, duration: 3.7, style: { fontSize: 96, fontWeight: 'bold', fill: '#ffffff' },
|
|
1155
|
+
initial: { x: 'GW/2', y: 300, anchorX: 0.5, anchorY: 0.5 }, keyframes: [{ at: 0, from: { alpha: 0, y: 340 }, to: { alpha: 1, y: 300 }, duration: 0.6 }] },
|
|
1156
|
+
{ type: 'text', text: `${SCORE} points`, at: 0.9, duration: 3.1, style: { fontSize: 64, fill: '#ffd166' },
|
|
1157
|
+
initial: { x: 'GW/2', y: 420, anchorX: 0.5, anchorY: 0.5 }, keyframes: [{ at: 0, from: { alpha: 0 }, to: { alpha: 1 }, duration: 0.6 }] },
|
|
1158
|
+
];
|
|
1159
|
+
```
|
|
1160
|
+
|
|
1161
|
+
```sh
|
|
1162
|
+
# batch.sh: one video per row of table.csv (name,score). --draft first, to look; remove it for the real files.
|
|
1163
|
+
mkdir -p out
|
|
1164
|
+
while IFS=, read -r name score; do
|
|
1165
|
+
npx pixi-effects-render batch.html --query "name=$(printf %s "$name" | jq -sRr @uri)&score=$score" -o "out/$name.mp4" --quiet
|
|
1166
|
+
done < table.csv
|
|
1167
|
+
```
|
|
1168
|
+
|
|
1096
1169
|
|
|
1097
1170
|
<!-- ===== docs/dsl.md ===== -->
|
|
1098
1171
|
|
|
@@ -1748,11 +1821,12 @@ The mask is itself a sequence, so it can have `keyframes` of its own — useful
|
|
|
1748
1821
|
Notes:
|
|
1749
1822
|
- The mask sequence is rendered as a mask, not as a normal child — its `fillColor` / `strokeColor` only matter for which pixels are kept, not for the visible colour.
|
|
1750
1823
|
- Any sequence type works as a mask (shape / image / text / nested composition); shapes are the natural choice for geometric reveals.
|
|
1824
|
+
- A **text** mask is the letters, not their bounding box (it is drawn into a texture and its alpha is used, so the edges are soft and antialiased); so is any mask with `maskInverted`. Both cost one extra offscreen pass for the masked layer. An ordinary shape mask is a hard-edged stencil.
|
|
1751
1825
|
- For a left-to-right wipe, give the mask `anchorX: 0` (rect/circle/ellipse) so `width: 0 → full` grows rightward from the left edge.
|
|
1752
1826
|
|
|
1753
1827
|
#### `maskInverted`
|
|
1754
1828
|
|
|
1755
|
-
When `true`, flip the mask sense: pixels INSIDE the mask shape become transparent, pixels OUTSIDE stay visible. Useful for knockout / cutout effects (punch a circular hole through a panel, etc.).
|
|
1829
|
+
When `true`, flip the mask sense: pixels INSIDE the mask shape become transparent, pixels OUTSIDE stay visible. Useful for knockout / cutout effects (punch a circular hole through a panel, etc.). It works inside a masked group too: the hole is cut out of what the outer mask lets through, and nothing shows outside the outer mask.
|
|
1756
1830
|
|
|
1757
1831
|
```ts
|
|
1758
1832
|
// Photo with a circular hole punched through the middle
|
package/llms.txt
CHANGED
|
@@ -28,4 +28,4 @@ Rules that cause most failures (details in the skill and pitfalls):
|
|
|
28
28
|
## Optional
|
|
29
29
|
|
|
30
30
|
- [Everything above in one file](https://raw.githubusercontent.com/yjmtmtk/pixi-effects/main/llms-full.txt)
|
|
31
|
-
- [
|
|
31
|
+
- [Examples and playground](https://yjmtmtk.github.io/pixi-effects/examples/)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pixi-effects",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.17.0",
|
|
4
4
|
"description": "Declarative composition and video rendering for the web. Built on PixiJS v8.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -44,14 +44,17 @@
|
|
|
44
44
|
"build": "tsup",
|
|
45
45
|
"dev": "tsup --watch",
|
|
46
46
|
"test": "vitest run",
|
|
47
|
+
"test:fast": "SKIP_BROWSER_TESTS=1 vitest run",
|
|
48
|
+
"release:check": "node scripts/release-check.mjs",
|
|
49
|
+
"site": "node scripts/preview-site.mjs",
|
|
50
|
+
"site:sync": "node scripts/sync-site.mjs",
|
|
47
51
|
"test:watch": "vitest",
|
|
48
52
|
"typecheck": "tsc --noEmit",
|
|
49
53
|
"build:ai": "node scripts/build-llms.mjs",
|
|
50
54
|
"build:gallery": "node scripts/build-gallery.mjs",
|
|
51
55
|
"posters": "node scripts/make-posters.mjs",
|
|
52
|
-
"landing": "node scripts/preview-landing.mjs",
|
|
53
56
|
"guide": "node scripts/build-guide.mjs guide-preview",
|
|
54
|
-
"prepublishOnly": "
|
|
57
|
+
"prepublishOnly": "node scripts/release-check.mjs --gate"
|
|
55
58
|
},
|
|
56
59
|
"dependencies": {
|
|
57
60
|
"mediabunny": "^1.61.3"
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
var chunk2ROVCPSL_cjs = require('./chunk-2ROVCPSL.cjs');
|
|
4
|
-
require('./chunk-FJA5VUOF.cjs');
|
|
5
|
-
require('./chunk-UQBGW5DO.cjs');
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
Object.defineProperty(exports, "CompositionSequence", {
|
|
10
|
-
enumerable: true,
|
|
11
|
-
get: function () { return chunk2ROVCPSL_cjs.CompositionSequence; }
|
|
12
|
-
});
|
|
13
|
-
//# sourceMappingURL=Composition-3WWNVRR5.cjs.map
|
|
14
|
-
//# sourceMappingURL=Composition-3WWNVRR5.cjs.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/space/camera.ts","../src/space/depth.ts","../src/space/Layer3D.ts","../src/space/lint.ts","../src/core/blend.ts","../src/sequences/Composition.ts"],"names":["i","DEG","frameOf","PerspectiveMesh","Texture","projectLayer","NEAR","describeLayer","Matrix","RenderTexture","collectPropKeys","Sequence","Container","Rectangle","lintTiming","lintText","summarizeWarnings","buildSequenceTree","CameraSequence","gsap","NullSequence","suggestName","cameraBasis","homeCamera"],"mappings":"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","file":"chunk-2ROVCPSL.cjs","sourcesContent":["/** A camera layer's lifespan on the global timeline, seconds. */\nexport interface CameraWindow { start: number; end: number }\n\n/**\n * The active camera at time `t`: the last-listed (top-most) camera whose\n * lifespan covers `t`. Windows are [start, end), except that a camera running\n * to the end of its composition also covers the final instant `t === compEnd`.\n */\nexport function pickActiveCamera<T extends CameraWindow>(\n cams: readonly T[],\n t: number,\n compEnd: number,\n): T | null {\n for (let i = cams.length - 1; i >= 0; i--) {\n const c = cams[i]!;\n const inside = t >= c.start && (t < c.end || (c.end >= compEnd && t <= compEnd));\n if (inside) return c;\n }\n return null;\n}\n\n/** First pair of camera indices whose lifespans strictly overlap, or null. */\nexport function findOverlap(cams: readonly CameraWindow[]): [number, number] | null {\n for (let i = 0; i < cams.length; i++) {\n for (let j = i + 1; j < cams.length; j++) {\n const a = cams[i]!;\n const b = cams[j]!;\n if (a.start < b.end && b.start < a.end) return [i, j];\n }\n }\n return null;\n}\n","export interface DepthItem {\n /** Position in the composition's stack (monotonic; gaps allowed). */\n stackIndex: number;\n threeD: boolean;\n /** Camera-space depth (larger = farther). Only read for threeD items. */\n depth: number;\n}\n\nconst finite = (n: number): number => (Number.isFinite(n) ? n : 0);\n\n/**\n * zIndex per item. Non-3D items keep their stack index. Each run of\n * consecutive threeD items is redistributed over the same index slots,\n * farthest first (drawn first), ties in stack order.\n */\nexport function assignDepthOrder(items: readonly DepthItem[]): number[] {\n const out = items.map(i => i.stackIndex);\n let i = 0;\n while (i < items.length) {\n if (!items[i]!.threeD) { i++; continue; }\n let j = i;\n while (j + 1 < items.length && items[j + 1]!.threeD) j++;\n const run = items.slice(i, j + 1).map((it, k) => ({ it, pos: i + k }));\n const slots = run.map(r => r.it.stackIndex);\n const farthestFirst = [...run].sort((a, b) => finite(b.it.depth) - finite(a.it.depth));\n farthestFirst.forEach((r, k) => { out[r.pos] = slots[k]!; });\n i = j + 1;\n }\n return out;\n}\n","import { Container, Matrix, PerspectiveMesh, RenderTexture, Texture } from 'pixi.js';\nimport type { Sequence } from '../sequences/Base';\nimport { describeLayer } from '../core/lint';\nimport { DEG, NEAR, projectLayer, type CameraBasis, type LayerTransform, type Rect } from './math';\n\n/** Render textures are capped at this many pixels on the long side; larger layers are downscaled. */\nexport const MAX_TEXTURE_SIZE = 4096;\n/**\n * Render textures are drawn at this multiple of the layer size (until the caps),\n * so a layer enlarged by perspective (z > 0) stays sharp instead of showing\n * jagged edges and soft text.\n */\nexport const SUPERSAMPLE = 2;\n/** Total texture pixels per layer are capped here (≈ one 2048² target); resolution drops to fit. */\nexport const MAX_TEXTURE_PIXELS = 2048 * 2048;\n/** MSAA multiplies target memory ~4x, so only small textures get it (supersampling covers the rest). */\nexport const ANTIALIAS_MAX_PIXELS = 1024 * 1024;\n/** Mesh grid per side. 20 keeps steeply tilted planes free of visible affine warping. */\nexport const MESH_GRID = 20;\n\n/** The slice of a Pixi renderer Layer3D needs (also what tests fake). */\nexport interface SpaceHost {\n render(options: {\n container: Container;\n target: RenderTexture;\n transform: Matrix;\n clear: boolean;\n clearColor: number[];\n }): void;\n}\n\ntype Carrier = Container & { z?: number; rotationX?: number; rotationY?: number };\n\ninterface TextureSize { w: number; h: number; resolution: number; antialias: boolean }\n\n/** Sum of the target's filter paddings: the texture must be this much larger on every side or filter output is clipped. */\nfunction filterPadding(target: Container): number {\n const filters = (target as unknown as { filters?: ReadonlyArray<{ padding?: number }> | null }).filters;\n if (!Array.isArray(filters)) return 0;\n let pad = 0;\n for (const f of filters) pad += f?.padding ?? 0;\n return Number.isFinite(pad) && pad > 0 ? Math.ceil(pad) : 0;\n}\n\nexport function readLayerTransform(target: Container): LayerTransform {\n const c = target as Carrier;\n return {\n x: c.x, y: c.y, z: c.z ?? 0,\n rotationX: (c.rotationX ?? 0) * DEG,\n rotationY: (c.rotationY ?? 0) * DEG,\n rotationZ: c.rotation,\n scaleX: c.scale.x, scaleY: c.scale.y,\n pivotX: c.pivot.x, pivotY: c.pivot.y,\n };\n}\n\n/**\n * Draws one `threeD` sequence: its display object is rendered into a\n * RenderTexture (with its own 2D transform replaced, so layer-local content\n * lands at the texture origin) and shown on a PerspectiveMesh whose four\n * corners come from projecting the layer through the active camera.\n *\n * The sequence's `target` is never in the scene graph; the composition adds\n * `display` instead. `z`, `rotationX`, `rotationY` live on `target` as plain\n * numeric props so GSAP tweens them like any other prop.\n */\nexport class Layer3D {\n readonly display: PerspectiveMesh;\n /** Camera-space depth from the last update (larger = farther). */\n depth = 0;\n private rt: RenderTexture | null = null;\n /**\n * Textures replaced by a bigger / smaller one. Destroying one right away warns \"destroyed while still\n * bound\": the mesh's bind group still points at it until the next render. They go at the next update,\n * by which time a frame has been drawn with the new texture.\n */\n private retired: RenderTexture[] = [];\n private size: TextureSize | null = null;\n private warnedBehind = false;\n\n constructor(\n private readonly seq: Sequence,\n private readonly frameOf: () => Rect,\n ) {\n const target = seq.target as Carrier;\n target.z ??= 0;\n target.rotationX ??= 0;\n target.rotationY ??= 0;\n this.display = new PerspectiveMesh({ texture: Texture.WHITE, verticesX: MESH_GRID, verticesY: MESH_GRID });\n if (seq.spec.name) this.display.label = seq.spec.name;\n this.display.visible = false;\n }\n\n update(host: SpaceHost, basis: CameraBasis): void {\n this.flushRetired();\n const target = this.seq.target as Carrier | null;\n // Pixi skips rendering (and clearing) a container that is not `visible`\n // (e.g. PixiPlugin autoAlpha at alpha 0), which would leave a stale texture.\n if (!target || !target.renderable || !target.visible) return this.hide();\n\n const bounds = this.frameOf();\n const pad = filterPadding(target);\n const needW = Math.ceil(bounds.width + 2 * pad);\n const needH = Math.ceil(bounds.height + 2 * pad);\n if (!(needW >= 1 && needH >= 1) || !Number.isFinite(bounds.x) || !Number.isFinite(bounds.y)) return this.hide();\n\n const size = this.sizeFor(needW, needH);\n // The quad covers exactly the texture's pixel rect in layer space.\n const frame: Rect = { x: bounds.x - pad, y: bounds.y - pad, width: size.w, height: size.h };\n const projected = projectLayer(readLayerTransform(target), frame, basis);\n this.depth = projected.depth;\n if (!projected.visible) {\n if (!this.warnedBehind) {\n this.warnedBehind = true;\n if ((this.seq.spec as { hideBehindCamera?: boolean }).hideBehindCamera) return this.hide(); // on purpose: say nothing\n if (projected.depth > NEAR) {\n // the layer's own origin is in front of the camera: a tilt or a large size swings a CORNER past the camera plane\n console.warn(\n `pixi-effects: ${describeLayer(this.seq.spec)}: part of it (a corner) reaches the camera plane (z = ${(target.z ?? 0).toFixed(0)}, rotationX ${(target.rotationX ?? 0).toFixed(0)}°, rotationY ${(target.rotationY ?? 0).toFixed(0)}°), ` +\n `and a layer is hidden whole when any corner is at or behind it. Tilt it less (rotationX / rotationY), make it smaller, or keep the camera further away from it ` +\n `(the default camera sits at z = (height/2)/tan(fov/2), about 989 for 720p at fov 40).`,\n );\n } else {\n console.warn(\n `pixi-effects: ${describeLayer(this.seq.spec)} is at or behind the camera plane (z = ${(target.z ?? 0).toFixed(0)}) and is hidden. ` +\n `If that is unintended, lower its z or move the camera back (the default camera sits at z = (height/2)/tan(fov/2), about 989 for 720p at fov 40).`,\n );\n }\n }\n return this.hide();\n }\n\n this.ensureTexture(size);\n // `transform` replaces the container's own local transform for this render\n // (Pixi RenderGroupSystem), while `container.alpha` still applies — so the\n // texture carries the layer's alpha and the mesh alpha stays at 1.\n host.render({\n container: target,\n target: this.rt!,\n transform: new Matrix().translate(-frame.x, -frame.y),\n clear: true,\n clearColor: [0, 0, 0, 0],\n });\n this.display.geometry.setCorners(...projected.corners);\n this.display.visible = true;\n }\n\n destroy(): void {\n this.display.destroy();\n this.rt?.destroy(true);\n this.rt = null;\n this.flushRetired();\n }\n\n private flushRetired(): void {\n for (const t of this.retired) t.destroy(true);\n this.retired = [];\n }\n\n private hide(): void {\n this.display.visible = false;\n }\n\n /**\n * Texture size for content of w×h. An existing texture is kept while it is at\n * least that big and at most 2x too big, so content whose bounds change every\n * frame (typewriter text, growing shapes) does not reallocate each frame.\n */\n private sizeFor(w: number, h: number): TextureSize {\n const cur = this.size;\n if (cur && cur.w >= w && cur.h >= h && cur.w <= 2 * w && cur.h <= 2 * h) return cur;\n const resolution = Math.min(\n SUPERSAMPLE,\n MAX_TEXTURE_SIZE / Math.max(w, h),\n Math.sqrt(MAX_TEXTURE_PIXELS / (w * h)),\n );\n const antialias = w * h * resolution * resolution <= ANTIALIAS_MAX_PIXELS;\n return { w, h, resolution, antialias };\n }\n\n private ensureTexture(size: TextureSize): void {\n if (this.rt && this.size === size) return;\n // Same sampling: resize the texture where it is. Replacing it makes Pixi warn that the old one was\n // destroyed while still bound, because the mesh keeps pointing at it until the next render.\n if (this.rt && this.size && this.size.antialias === size.antialias) {\n this.rt.resize(size.w, size.h, size.resolution);\n this.size = size;\n return;\n }\n const next = RenderTexture.create({\n width: size.w, height: size.h, resolution: size.resolution, antialias: size.antialias,\n });\n this.display.texture = next;\n if (this.rt) this.retired.push(this.rt);\n this.rt = next;\n this.size = size;\n }\n}\n","import { collectPropKeys } from './specKeys';\nimport { describeLayer } from '../core/lint';\nimport type { SequenceSpec } from '../types';\n\ntype Warn = (message: string) => void;\nconst defaultWarn: Warn = m => console.warn(m);\n\nconst FOV_HINT = 'perspective is the camera\\'s \"fov\" — add a { type: \"camera\" } layer and set fov in its initial / keyframes';\n\n/** Keys that are never valid for any layer, with a hint at what the author probably meant. */\nconst LAYER_ALIASES: Record<string, string> = {\n rotateX: 'did you mean \"rotationX\"?',\n rotateY: 'did you mean \"rotationY\"?',\n rotateZ: 'did you mean \"rotation\"?',\n rotationZ: 'did you mean \"rotation\" (it is the Z rotation)?',\n translateZ: 'did you mean \"z\"?',\n depth: 'did you mean \"z\"?',\n posZ: 'did you mean \"z\"?',\n perspective: FOV_HINT,\n zoom: FOV_HINT,\n};\n\n/** Camera-only mistakes. */\nconst CAMERA_ALIASES: Record<string, string> = {\n pointOfInterest: 'did you mean \"lookAtX / lookAtY / lookAtZ\"?',\n lookAt: 'did you mean \"lookAtX / lookAtY / lookAtZ\"?',\n};\n\nconst CAMERA_PROPS = ['x', 'y', 'z', 'fov', 'lookAtX', 'lookAtY', 'lookAtZ', 'offsetX', 'offsetY', 'offsetZ', 'lookOffsetX', 'lookOffsetY', 'lookOffsetZ'];\nconst NEEDS_THREE_D = ['z', 'rotationX', 'rotationY'];\nconst SKEW_KEYS = ['skew', 'skewX', 'skewY'];\n\n/**\n * Warn (never throw) about the inputs an AI author is most likely to get wrong,\n * with a message that says what to change. One call per layer, at build time.\n */\nexport function lintSequence(spec: SequenceSpec, warn: Warn = defaultWarn): void {\n const who = describeLayer(spec);\n const keys = collectPropKeys(spec);\n const explained = new Set<string>();\n\n for (const k of keys) {\n const hint = LAYER_ALIASES[k] ?? (spec.type === 'camera' ? CAMERA_ALIASES[k] : undefined);\n if (hint) {\n explained.add(k);\n warn(`pixi-effects: ${who}: \"${k}\" is not a property — ${hint}`);\n }\n }\n\n if (spec.type === 'camera') {\n const raw = spec as unknown as Record<string, unknown>;\n for (const p of CAMERA_PROPS) {\n if (p in raw) warn(`pixi-effects: ${who}: \"${p}\" must go inside initial / keyframes, not on the camera itself`);\n }\n for (const k of Object.keys(raw)) {\n const hint = CAMERA_ALIASES[k] ?? LAYER_ALIASES[k];\n if (hint) warn(`pixi-effects: ${who}: \"${k}\" is not a property — ${hint} (inside initial)`);\n }\n for (const k of keys) {\n if (!explained.has(k) && !CAMERA_PROPS.includes(k)) {\n warn(`pixi-effects: ${who}: \"${k}\" has no effect on a camera (use ${CAMERA_PROPS.join(', ')})`);\n }\n }\n return;\n }\n\n if (spec.type === 'audio') {\n if (spec.threeD) warn(`pixi-effects: ${who}: threeD has no effect on audio`);\n return;\n }\n\n if (!spec.threeD) {\n for (const k of NEEDS_THREE_D) {\n if (keys.has(k)) warn(`pixi-effects: ${who}: \"${k}\" needs threeD: true (it is ignored otherwise)`);\n }\n } else {\n for (const k of SKEW_KEYS) {\n if (keys.has(k)) warn(`pixi-effects: ${who}: \"${k}\" is ignored on threeD layers`);\n }\n }\n}\n","import { describeLayer } from './lint';\n\nconst MODES = ['normal', 'add', 'screen', 'multiply'];\n\n/** Apply a layer's `blendMode` to its display object; an unknown mode warns and is ignored. */\nexport function applyBlendMode(spec: { blendMode?: string; name?: string; type: string }, display: { blendMode?: unknown; filters?: unknown }): void {\n const mode = spec.blendMode;\n if (mode === undefined) return;\n if (!MODES.includes(mode)) {\n console.warn(`pixi-effects: ${describeLayer(spec)}: blendMode \"${mode}\" is not supported (use ${MODES.join(', ')})`);\n return;\n }\n // A container's blend mode applies INSIDE a filter's offscreen pass (against transparent black: multiply gave\n // solid black) and not to the filtered result. With filters, the pass that draws onto the backdrop is the last\n // filter's, so the mode goes there; the container stays normal.\n const filters = display.filters;\n if (Array.isArray(filters) && filters.length > 0) {\n (filters[filters.length - 1] as { blendMode?: unknown }).blendMode = mode;\n return;\n }\n display.blendMode = mode;\n}\n","import { Container, Rectangle } from 'pixi.js';\nimport { gsap } from 'gsap';\nimport { Sequence } from './Base';\nimport { NullSequence } from './Null';\nimport { suggestName } from '../core/options';\nimport { buildSequenceTree } from '../core/Composition';\nimport { CameraSequence } from '../space/CameraSequence';\nimport { findOverlap, pickActiveCamera } from '../space/camera';\nimport { assignDepthOrder } from '../space/depth';\nimport { Layer3D, type SpaceHost } from '../space/Layer3D';\nimport { lintSequence } from '../space/lint';\nimport { describeLayer, lintText, lintTiming, summarizeWarnings } from '../core/lint';\nimport { applyBlendMode } from '../core/blend';\nimport { cameraBasis, homeCamera } from '../space/math';\nimport type { CompositionSequenceSpec, AudioDescriptor, CompositionShape, SequenceSpec } from '../types';\n\ntype Timeline = ReturnType<typeof gsap.timeline>;\n\nexport class CompositionSequence extends Sequence {\n declare spec: CompositionSequenceSpec;\n private _innerContainer: Container | null = null;\n private _compositionShape: CompositionShape | null = null;\n _children: Sequence[] = [];\n private _cameras: CameraSequence[] = [];\n private _layers3d: Layer3D[] = [];\n /** Drawn children in stack order (cameras and target-less sequences excluded). */\n private _visual: Array<{ seq: Sequence; layer: Layer3D | null }> = [];\n /** Layers drawn inside a null layer (`parent`): they are not in the composition's own stack. */\n private _nested: Sequence[] = [];\n /** The null layer each parented layer is drawn inside. */\n private _parentOf = new Map<Sequence, NullSequence>();\n\n async build(): Promise<void> {\n const width = this.spec.width ?? this.parent?.width ?? this.root.width;\n const height = this.spec.height ?? this.parent?.height ?? this.root.height;\n if (this.duration === undefined) {\n this.duration = this.spec.duration ?? this.parent?.duration ?? this.root.duration;\n }\n const wrapper = new Container();\n wrapper.cullable = true;\n wrapper.cullableChildren = true;\n wrapper.cullArea = new Rectangle(0, 0, width, height);\n\n const inner = new Container();\n inner.cullable = true;\n inner.cullableChildren = true;\n wrapper.addChild(inner);\n\n this.target = wrapper;\n this.intrinsicWidth = width;\n this.intrinsicHeight = height;\n this._innerContainer = inner;\n this._compositionShape = { width, height, duration: this.duration };\n\n const lateKeyframes: string[] = [];\n for (const s of this.spec.sequences ?? []) {\n lintSequence(s);\n lintTiming(s, this.duration, (message, kind) => (kind === 'late-keyframe' ? lateKeyframes.push(message) : console.warn(message)));\n lintText(s);\n }\n for (const message of summarizeWarnings(lateKeyframes)) console.warn(message);\n\n this._children = await buildSequenceTree(\n this.spec.sequences ?? [],\n this._compositionShape,\n this.root,\n );\n this._resolveParents();\n for (const child of this._children) {\n // Cameras are display-less: they only feed the projection pass.\n if (child instanceof CameraSequence) {\n this._cameras.push(child);\n continue;\n }\n if (!child.target) continue;\n const holder = this._parentOf.get(child);\n const into = holder?.target ?? inner; // a layer with `parent` is drawn inside its null layer\n\n const maskSpec = (child.spec as { mask?: SequenceSpec }).mask;\n\n // threeD layers never enter the scene graph themselves: their display\n // object is rendered into a texture and a perspective mesh stands in.\n if (child.spec.threeD) {\n const layer = new Layer3D(child, frameOf(child));\n inner.addChild(layer.display);\n this._layers3d.push(layer);\n this._visual.push({ seq: child, layer });\n applyBlendMode(child.spec, layer.display);\n if (maskSpec) {\n console.warn(`pixi-effects: ${describeLayer(child.spec)}: mask is not supported on threeD layers yet; ignored`);\n }\n continue;\n }\n\n into.addChild(child.target);\n if (holder) this._nested.push(child);\n else this._visual.push({ seq: child, layer: null });\n applyBlendMode(child.spec, child.target);\n // If this child has a `mask` spec, build the mask sequence in the same\n // composition shape, add its target to the same parent (so its\n // transforms resolve in the same coord space), and wire PIXI's\n // mask channel. PIXI v8 renders mask containers into the stencil /\n // alpha buffer; they don't render as normal children.\n if (maskSpec) {\n // The mask lives and animates with the layer it masks: with no `at` of its own its `at` and\n // `duration` are the layer's, so its keyframes are measured from the layer's start too. An\n // explicit `at` is composition time, exactly like a layer's.\n const lifetime = maskSpec.at === undefined\n ? { at: child.at, duration: maskSpec.duration ?? child.duration }\n : {};\n const built = await buildSequenceTree([{ ...maskSpec, ...lifetime } as SequenceSpec], this._compositionShape, this.root);\n const maskSeq = built[0];\n if (maskSeq?.target) {\n into.addChild(maskSeq.target); // the mask shares the layer's space\n // PIXI v8 `setMask` accepts an `inverse` flag — that's how we\n // expose `maskInverted` from the spec. Falls back to plain\n // `target.mask = …` for runtimes that don't have setMask\n // (older PIXI builds).\n const inverse = (child.spec as { maskInverted?: boolean }).maskInverted ?? false;\n const t = child.target as Container & {\n setMask?: (opts: { mask: Container | null; inverse?: boolean }) => void;\n mask: Container | null;\n };\n if (typeof t.setMask === 'function') {\n t.setMask({ mask: maskSeq.target, inverse });\n } else {\n t.mask = maskSeq.target;\n }\n child.maskSequence = maskSeq;\n maskSeq.keepHidden = maskSpec.type === 'image' || maskSpec.type === 'video'; // PIXI: a Sprite mask is hidden by renderable = false\n }\n }\n }\n\n if (this._layers3d.length > 0) {\n // Stack order becomes explicit zIndex so threeD layers can be re-sorted\n // by depth each frame without reparenting.\n inner.sortableChildren = true;\n this._visual.forEach((v, i) => {\n (v.layer?.display ?? v.seq.target!).zIndex = i;\n });\n } else if (this._cameras.length > 0) {\n console.warn('pixi-effects: camera has no effect: this composition has no threeD layers');\n }\n\n this.buildFilters();\n }\n\n override bindTimeline(timeline: Timeline, offset = 0): void {\n super.bindTimeline(timeline, offset);\n // Children's `at` is relative to this composition's start, so push them\n // forward by our absolute start time on the global timeline.\n const childOffset = offset + this.at;\n for (const child of this._children) {\n // Each layer builds its tweens in a small timeline of its own, which is added to the parent once, finished. Adding\n // thousands of tweens one by one to a single timeline makes GSAP re-measure the whole timeline at every add (the cost\n // grows with the square of the count: ~3 s for a piece with 700 particle layers); this way the parent only ever holds\n // one child per layer. Positions stay absolute, so the picture at any time is the same.\n const own = gsap.timeline({ defaults: { ease: 'none' } });\n child.bindTimeline(own, childOffset);\n // The mask shares the maskee's offset — its `at` is interpreted\n // relative to the composition's start, just like the child itself,\n // so a reveal-from-zero animation lines up naturally.\n child.maskSequence?.bindTimeline(own, childOffset);\n timeline.add(own, 0);\n }\n const overlap = findOverlap(this._cameras.map(c => c.window()));\n if (overlap) {\n console.warn(`pixi-effects: cameras #${overlap[0] + 1} and #${overlap[1] + 1} overlap in time; the top-most one wins`);\n }\n }\n\n /**\n * Drawn children in stack order, with the display object that stands in for each (the mesh for a threeD layer).\n * Null layers draw nothing and are left out; the layers inside them follow, with the null layers that carry them.\n */\n layers(): Array<{ seq: Sequence; display: Container; threeD: boolean; carriers: NullSequence[] }> {\n const carriersOf = (seq: Sequence): NullSequence[] => {\n const out: NullSequence[] = [];\n for (let p = this._parentOf.get(seq); p; p = this._parentOf.get(p)) out.push(p);\n return out;\n };\n return [...this._visual.map(v => v.seq), ...this._nested]\n .filter(seq => !(seq instanceof NullSequence))\n .map(seq => {\n const layer = this._visual.find(v => v.seq === seq)?.layer ?? null;\n return {\n seq,\n display: (layer?.display ?? seq.target!) as unknown as Container,\n threeD: layer !== null,\n carriers: carriersOf(seq),\n };\n });\n }\n\n /** Match every `parent: 'name'` to a null layer; anything that cannot work is said out loud and drawn unparented. */\n private _resolveParents(): void {\n const nulls = this._children.filter((c): c is NullSequence => c instanceof NullSequence);\n const nullNames = nulls.map(n => n.spec.name).filter((n): n is string => !!n);\n const warned = new Set<string>();\n for (const child of this._children) {\n const want = child.spec.parent;\n if (want === undefined) continue;\n const who = describeLayer(child.spec);\n if (child instanceof CameraSequence) { console.warn(`pixi-effects: ${who}: a camera cannot have a parent; ignored`); continue; }\n if (child.spec.threeD) { console.warn(`pixi-effects: ${who}: a threeD layer cannot have a parent (parent \"${want}\" ignored)`); continue; }\n const same = this._children.filter(c => c.spec.name === want);\n if (same.length === 0) {\n const hint = suggestName(want, nullNames);\n console.warn(`pixi-effects: ${who}: parent \"${want}\": no layer with that name${hint ? `; did you mean \"${hint}\"?` : ''} (the layer is drawn without a parent)`);\n continue;\n }\n const holder = same.find((c): c is NullSequence => c instanceof NullSequence);\n if (!holder) {\n console.warn(`pixi-effects: ${who}: parent \"${want}\" is a ${same[0]!.spec.type} layer; only { type: 'null' } layers can be parents (the layer is drawn without a parent)`);\n continue;\n }\n if (same.length > 1 && !warned.has(want)) {\n warned.add(want);\n console.warn(`pixi-effects: ${same.length} layers are named \"${want}\"; parent \"${want}\" uses the first null layer with that name`);\n }\n if (holder === child) { console.warn(`pixi-effects: ${who} is its own parent; ignored`); continue; }\n this._parentOf.set(child, holder);\n }\n // a chain that leads back to itself: break it where we find it\n for (const start of this._children) {\n const seen = new Set<Sequence>([start]);\n for (let p = this._parentOf.get(start); p; p = this._parentOf.get(p)) {\n if (p === start) {\n const names = [...seen].map(s => s.spec.name ?? s.spec.type).join(' → ');\n console.warn(`pixi-effects: parent cycle: ${names} → ${start.spec.name ?? start.spec.type}; the parent of \"${start.spec.name ?? start.spec.type}\" is ignored`);\n this._parentOf.delete(start);\n break;\n }\n if (seen.has(p)) break;\n seen.add(p);\n }\n }\n }\n\n override syncFrame(): void {\n super.syncFrame();\n for (const child of this._children) child.syncFrame();\n }\n\n override updateSpace(t: number, host: SpaceHost): void {\n // Inner compositions first: a threeD composition is rendered into its own\n // texture by this level, so its content must already be projected.\n for (const child of this._children) child.updateSpace(t, host);\n if (this._layers3d.length === 0 || !this._compositionShape) return;\n\n const { width, height } = this._compositionShape;\n const compEnd = (this.absoluteStart ?? 0) + (this.duration ?? 0);\n const active = pickActiveCamera(\n this._cameras.map(cam => ({ cam, ...cam.window() })),\n t,\n compEnd,\n );\n const basis = cameraBasis(active ? active.cam.state() : homeCamera(width, height), width, height);\n\n for (const layer of this._layers3d) layer.update(host, basis);\n\n const order = assignDepthOrder(\n this._visual.map((v, i) => ({ stackIndex: i, threeD: v.layer !== null, depth: v.layer?.depth ?? 0 })),\n );\n this._visual.forEach((v, i) => {\n if (v.layer) v.layer.display.zIndex = order[i]!;\n });\n }\n\n override collectAudio(out: AudioDescriptor[], baseTime: number): void {\n super.collectAudio(out, baseTime);\n const childBase = baseTime + this.at;\n for (const child of this._children) child.collectAudio(out, childBase);\n }\n\n override destroy(): void {\n for (const l of this._layers3d) l.destroy();\n this._layers3d = [];\n for (const c of this._children) c.destroy();\n super.destroy();\n }\n}\n\n/** Layer-space rectangle a threeD child projects: a composition's frame, otherwise its content bounds. */\nfunction frameOf(child: Sequence): () => { x: number; y: number; width: number; height: number } {\n if (child instanceof CompositionSequence) {\n return () => ({ x: 0, y: 0, width: child.intrinsicWidth, height: child.intrinsicHeight });\n }\n return () => child.target!.getLocalBounds();\n}\n"]}
|