pixi-effects 0.16.1 → 0.16.2
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 +23 -0
- package/README.md +11 -9
- package/ai/SKILL.md +1 -1
- package/ai/chat-template.html +3 -3
- package/ai/reference/cheatsheet.md +7 -3
- package/ai/reference/pitfalls.md +1 -0
- package/ai/reference/recipes.md +23 -0
- package/ai/template.html +5 -5
- package/dist/{Base-qvqNxvyt.d.ts → Base-BQUodYZK.d.ts} +1 -1
- package/dist/{Base-DPEqyeB0.d.cts → Base-OHqp8qvX.d.cts} +1 -1
- package/dist/Composition-3WWNVRR5.cjs +14 -0
- package/dist/{Composition-ZZGSMQ4E.cjs.map → Composition-3WWNVRR5.cjs.map} +1 -1
- package/dist/Composition-VJWY2NDL.js +5 -0
- package/dist/{Composition-X2VZSVPD.js.map → Composition-VJWY2NDL.js.map} +1 -1
- package/dist/Controller.d.cts +2 -2
- package/dist/Controller.d.ts +2 -2
- package/dist/{Movie-BIGDIla0.d.ts → Movie-DilBC348.d.ts} +8 -2
- package/dist/{Movie-CJMfAizW.d.cts → Movie-oA0SUAyb.d.cts} +8 -2
- package/dist/Presenter.d.cts +2 -2
- package/dist/Presenter.d.ts +2 -2
- package/dist/{chunk-V54NBCWG.cjs → chunk-2ROVCPSL.cjs} +28 -27
- package/dist/chunk-2ROVCPSL.cjs.map +1 -0
- package/dist/{chunk-SPPNTV7E.cjs → chunk-FJA5VUOF.cjs} +30 -13
- package/dist/chunk-FJA5VUOF.cjs.map +1 -0
- package/dist/{chunk-I2YXLHIU.js → chunk-XTIHKODH.js} +5 -4
- package/dist/chunk-XTIHKODH.js.map +1 -0
- package/dist/{chunk-7D7RAQWM.js → chunk-ZNKQH356.js} +30 -13
- package/dist/chunk-ZNKQH356.js.map +1 -0
- package/dist/index.cjs +128 -38
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +37 -5
- package/dist/index.d.ts +37 -5
- package/dist/index.js +106 -17
- package/dist/index.js.map +1 -1
- package/dist/loader.css +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-C8XEUoqe.d.cts → types-SS51lP6O.d.cts} +7 -1
- package/dist/{types-C8XEUoqe.d.ts → types-SS51lP6O.d.ts} +7 -1
- package/docs/dsl.md +31 -1
- package/llms-full.txt +63 -5
- package/package.json +2 -1
- package/dist/Composition-X2VZSVPD.js +0 -5
- package/dist/Composition-ZZGSMQ4E.cjs +0 -14
- package/dist/chunk-7D7RAQWM.js.map +0 -1
- package/dist/chunk-I2YXLHIU.js.map +0 -1
- package/dist/chunk-SPPNTV7E.cjs.map +0 -1
- package/dist/chunk-V54NBCWG.cjs.map +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,28 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.16.2
|
|
4
|
+
|
|
5
|
+
**Site**
|
|
6
|
+
|
|
7
|
+
- **A landing page** at the root of the GitHub Pages site (`site/landing/index.html`, replacing the plain list of examples): a hero whose video is the exact snippet shown beside it, running live in the browser after a click; "Start with one sentence" (`Use https://github.com/yjmtmtk/pixi-effects to make a video: …`); what you get; how an AI writes and checks it (contact sheet, timeline); a gallery strip; and an honest comparison with Remotion and fframes (facts read from their own pages on 2026-10-07, with when NOT to choose pixi-effects). A poster in its gallery strip plays that piece in a player on the page (its link goes to that piece in the gallery; the frame shows only the picture: the piece's page is scaled so its canvas sits exactly on the frame, with no scrollbars, measured for all 16 posters at 1440 and 390 px); "A real piece, live" plays on the first click. `npm run landing` builds a local preview. Written by Fable from a fact sheet (`site/landing/FACTS.md`); tests keep it honest (the release it names, the links, its size, no third-party loads).
|
|
8
|
+
- **`AGENTS.md`** (and `CLAUDE.md`, which imports it) and a one-sentence start at the top of the README: an AI that is told only "Use https://github.com/yjmtmtk/pixi-effects to make a video" is sent to the right file for what it can do (a shell, or a chat with no shell). Tried with a fresh session given only that sentence: it found its way and made a video.
|
|
9
|
+
|
|
10
|
+
**Changed**
|
|
11
|
+
|
|
12
|
+
- **`animateText()` and `splitText()` reject an expression string for `x` / `y`** with an error that says what to write (`x: 640`): `animateText({ x: 'GW/2' })` used to draw nothing, silently, because the letters are measured at build time. The docs now say that `y` is the top of the line. (Found by a fresh AI session given only the repository URL.)
|
|
13
|
+
|
|
14
|
+
**Fixed**
|
|
15
|
+
|
|
16
|
+
- **A `threeD` card showed the previous frame after a seek.** A card (a `threeD` composition) is drawn into its own texture before the stage is drawn, but the shapes inside it were only brought up to the playhead after that, so after a jump seek, `snapshot()`, `contactSheet()` and the paused canvas showed a card whose children had animated (a `trimEnd` draw-on, a growing box) as it was one seek earlier: lines half drawn or missing. Seeking the same frame twice hid it; playing forward and the export were one frame late. The layers are now synced first. (Found by the Opus showpiece `ma`, whose gate lines were missing in review pictures.)
|
|
17
|
+
|
|
18
|
+
**Added**
|
|
19
|
+
|
|
20
|
+
- **`cameraPath({ points, duration, ease, look, … })`**: a camera flight through `[x, y, z]` points as keyframes (a smooth curve at an even speed; an `ease` such as `'power2.in'` accelerates; it faces where it flies, or a fixed `look`). With the recipe `camera-fly-through`.
|
|
21
|
+
- **Camera `offsetX` / `offsetY` / `offsetZ` and `lookOffsetX` / `lookOffsetY` / `lookOffsetZ`**: added to the camera's position and look-at point, so a handheld shake (`wiggle()`) on them never collides with a dolly or an orbit (two tweens on one property overwrite each other).
|
|
22
|
+
- **`hideBehindCamera: true`** on a threeD layer the camera passes on purpose: it is hidden quietly behind the camera instead of warning.
|
|
23
|
+
- **`trimEach: true`** on a `path` with several sub-paths: every sub-path is trimmed on its own, so they all draw on at the same time (a glyph or logo made of several strokes). The default is unchanged: the trim walks the sub-paths in order as one outline. (From the Opus showpiece's list: it had given every sub-path its own layer.)
|
|
24
|
+
- **A showpiece in the gallery: `ma`** (「間 MA — the space between」, 48 s, 1920×1080, by Opus): a kinetic essay about one word, with a corridor of 2.5D gates, a beat of true silence and a score written as text. 39 pieces. Its notes list the library gaps it ran into (a seek redraw bug for 3D cards, `trimEnd` across subpaths, camera paths, a shared mask, grain, an animatable gradient).
|
|
25
|
+
|
|
3
26
|
## 0.16.1
|
|
4
27
|
|
|
5
28
|
**Added**
|
package/README.md
CHANGED
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
# pixi-effects
|
|
2
2
|
|
|
3
|
-
> **Status**: experimental — current release `0.16.
|
|
3
|
+
> **Status**: experimental — current release `0.16.2`. The API may still change between minor versions (see [CHANGELOG](./CHANGELOG.md): `0.16.2` adds a landing page, `AGENTS.md` and a one-sentence start for AI, camera flights (`cameraPath`, shake offsets), `trimEach`, the showpiece `ma` and a fix for 3D cards after a seek, `0.16.1` adds `musicBuffer()` and a music lab page with eight tunes, `0.16.0` adds music written as text (`{ type: 'audio', music }`, `musicEnvelope()`), `0.15.0` adds a way in for an AI in a browser chat (`ai/CHAT.md` and `ai/chat-template.html`), `0.14.0` adds `movie.inspectStops()` (and a check review) for stops that land mid-animation, `0.13.1` fixes decks starting past their first stop in the guide and gallery and draws the stop marks as dots, `0.13.0` makes the player bar play a deck stop by stop (`pauseAtStops`), marks the stops more clearly, adds PDF to the download panel and a `pdf` flag on stops, `0.12.0` adds presentations (`stops`, `Presenter`, `deck()`, page overview, presenter view, PDF export) and audio-reactive visuals (`audioEnvelope()`, `react()`), `0.11.0` adds particles, motion blur, a shared loader and a much faster build for pieces with many layers, `0.10.0` adds seeded randomness, `wiggle` and `stagger`, null layers with `parent`, `animateText`, `followPath` and path morphing, and click-to-play in the player, `0.9.0` adds a poster time (`movie.init({ poster })`) and a guide site, `0.8.0` adds events and a theme for building your own player, `0.7.0` adds filters by name, the `pixi-effects-render` and `pixi-effects-view` commands and the timeline chart, `0.6.0` adds draw-on strokes (`trimEnd`) and text that changes over time, `0.5.0` adds synthesised sound effects (`sfx`, no files) and the `check` tool, `0.4.0` makes a mask share its layer's lifetime, `0.3.0` made keyframe `at` relative to the layer).
|
|
4
4
|
|
|
5
|
-
**[Guide →](https://yjmtmtk.github.io/pixi-effects/guide/)** · **[Gallery →](https://yjmtmtk.github.io/pixi-effects/examples/gallery/)** ·
|
|
5
|
+
**[Guide →](https://yjmtmtk.github.io/pixi-effects/guide/)** · **[Gallery →](https://yjmtmtk.github.io/pixi-effects/examples/gallery/)** · 39 portfolio pieces written as plain data by AI models · **[Live demos →](https://yjmtmtk.github.io/pixi-effects/)** · 12 numbered examples + an in-browser playground.
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
## Make a video by asking an AI
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
Give any AI (ChatGPT, Claude, Codex, Cursor, Claude Code …) one sentence:
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
> Use https://github.com/yjmtmtk/pixi-effects to make a video: a 10-second title for a bakery, warm colours, a soft chime at the end.
|
|
12
|
+
|
|
13
|
+
That is all it needs. **If you are the AI** that was asked to do this, do not read the whole repository: if you can run commands (a shell, Node 22, Chrome), read [`AGENTS.md`](./AGENTS.md) and [`ai/SKILL.md`](./ai/SKILL.md); if you can only write text (a browser chat), fetch https://raw.githubusercontent.com/yjmtmtk/pixi-effects/main/ai/CHAT.md and follow it. You will write the video as plain data in one HTML file, check it (the check tool writes a contact sheet to look at), and hand it over; the person opens the file and downloads the MP4 with the button in the player bar.
|
|
12
14
|
|
|
13
15
|
Declarative composition and video rendering for the web. After Effects-style timelines on top of [PixiJS v8](https://pixijs.com/) and [GSAP](https://gsap.com/), with strict TypeScript types. Render to MP4 / WebM / MOV via [mediabunny](https://mediabunny.dev/).
|
|
14
16
|
|
|
@@ -49,8 +51,8 @@ Drop the imports into an [importmap](https://developer.mozilla.org/docs/Web/HTML
|
|
|
49
51
|
"gsap": "https://esm.sh/gsap@3.12.5",
|
|
50
52
|
"gsap/PixiPlugin": "https://esm.sh/gsap@3.12.5/PixiPlugin",
|
|
51
53
|
"mediabunny": "https://esm.sh/mediabunny",
|
|
52
|
-
"pixi-effects": "https://cdn.jsdelivr.net/npm/pixi-effects@0.16.
|
|
53
|
-
"pixi-effects/controller": "https://cdn.jsdelivr.net/npm/pixi-effects@0.16.
|
|
54
|
+
"pixi-effects": "https://cdn.jsdelivr.net/npm/pixi-effects@0.16.2/dist/index.js",
|
|
55
|
+
"pixi-effects/controller": "https://cdn.jsdelivr.net/npm/pixi-effects@0.16.2/dist/Controller.js"
|
|
54
56
|
}
|
|
55
57
|
}
|
|
56
58
|
</script>
|
|
@@ -61,7 +63,7 @@ Drop the imports into an [importmap](https://developer.mozilla.org/docs/Web/HTML
|
|
|
61
63
|
</script>
|
|
62
64
|
```
|
|
63
65
|
|
|
64
|
-
Load the `dist/` files **as they are** (jsDelivr's `/npm/…/dist/…`, or unpkg's `https://unpkg.com/pixi-effects@0.16.
|
|
66
|
+
Load the `dist/` files **as they are** (jsDelivr's `/npm/…/dist/…`, or unpkg's `https://unpkg.com/pixi-effects@0.16.2/dist/index.js`) rather than a CDN-rebundled build such as `esm.sh/pixi-effects` or jsDelivr's `+esm`: the entries (`pixi-effects`, `…/controller`, `…/three`) share internal chunks, which only works when each file is served untouched.
|
|
65
67
|
|
|
66
68
|
> **Using three.js?** Add two more entries (`three` and `pixi-effects/three`) to this importmap — see [Adding three.js](#adding-threejs-optional) below.
|
|
67
69
|
>
|
|
@@ -77,7 +79,7 @@ Add two more entries to the importmap above: three.js itself, and the `pixi-effe
|
|
|
77
79
|
"imports": {
|
|
78
80
|
"...": "(everything from the importmap above)",
|
|
79
81
|
"three": "https://esm.sh/three@0.178.0",
|
|
80
|
-
"pixi-effects/three": "https://cdn.jsdelivr.net/npm/pixi-effects@0.16.
|
|
82
|
+
"pixi-effects/three": "https://cdn.jsdelivr.net/npm/pixi-effects@0.16.2/dist/three.js"
|
|
81
83
|
}
|
|
82
84
|
}
|
|
83
85
|
</script>
|
package/ai/SKILL.md
CHANGED
|
@@ -33,7 +33,7 @@ You describe a video as a tree of plain objects; the library plays it in a canva
|
|
|
33
33
|
- **Masks live in the parent's coordinates and don't follow the masked layer.** **`filterArea` is in the layer's own coordinates.**
|
|
34
34
|
- **2.5D needs `threeD: true`** on each layer plus a `{ type: 'camera' }` layer; camera props go in `initial`/keyframes; keep every `z` below the camera distance (≈ 989 at 720p, fov 40); under a dolly zoom only `z = 0` stays fixed.
|
|
35
35
|
- **Group / parent layers = `{ type: 'null', name: 'rig' }`** (draws nothing, only moves) + `parent: 'rig'` on the layers that should follow it: their `x`/`y` are then measured from the null's origin, and the null's position / rotation / scale / alpha carry them (orbits, clock hands, grouped fades). Nulls chain. Only `null` layers can be parents; not for `threeD` layers.
|
|
36
|
-
- **Built in:** `animateText()` (per-letter / per-word / per-line entrances and exits in one call: `...animateText('Title', style, { x, y, align: 'center', at: 0.5, duration: 5, in: 'rise', out: 'fade', stagger: { each: 0.05 } })`), `splitText()` / `measureText()` (per-letter / per-word layers, exact positions, by hand), `shape: 'arc'` (progress rings, pie / donut slices: animate `endAngle`), `fillGradient` (linear/radial, alpha stops → vignettes), `{value}` counters, `repeat`/`yoyo`, `orbit()`, seeded randomness (`rand(seed)` / `noise(x, seed)` in expressions, `random(seed)` in JS, never `Math.random()`) `wiggle()` (a seeded shake as keyframes) `music` (a tune with no file: `{ type: 'audio', music: { bpm, tracks: [{ inst: 'keys', notes: 'Cmaj7:4 Am7:4' }, { inst: 'bass', notes: 'c2:2 g1:2' }], drums: { from: 8, kick: 'x...x...' } } }`: notes and chords as text, counted in beats; instruments `keys pluck pad bass sub lead bell musicbox`; `musicEnvelope(music)` feeds `react()`), `audioEnvelope()` / `bpmEnvelope()` + `react()` (visuals that follow music: `const env = await audioEnvelope('music.mp3', { frameRate })` before `movie.init`, then `keyframes: react(env, { duration, props: { scale: { base: 1, amount: 0.4, band: 'bass', release: 0.2 } } })`; `beats: true` pulses on the beats; `bpmEnvelope(120, …)` when there is no file), `deck()` + `Presenter` (a talk as one movie: pages, steps, stops that pause playback, arrow keys / click / swipe to move on: `...deck({ transition: { kind: 'slide', duration: 0.6 }, pages: [{ name, duration, stops, sequences }] })`, `new Presenter(movie, { canvas })` from `pixi-effects/presenter`; G = page overview, P = presenter view with notes and a timer; `pixi-effects-render talk.html -o talk.pdf` = a PDF of the pages), `particles()` (bursts, snow, confetti, sparks as seeded layers: `...particles({ count: 90, at: 2, life: [1, 1.8], area: { x: 640, y: 330 }, speed: [120, 420], gravity: 240, fade: { out: 0.6 } })`), `followPath()` (a layer along an SVG path: `keyframes: followPath({ d, duration, orient: true })`), path morphing (`{ shape: 'path', d, morphTo, initial/keyframes: morph 0 → 1 }`) and `stagger()` (delays for a wave of layers: `stagger(letters, { each: 0.05, from: 'center' })`, or `stagger(n, …)` for numbers; GSAP-style `each` / `amount` / `from` / `grid` / `ease`). **Not in the DSL (hand-roll with a JS loop):** other per-frame curves (waves, spirals: sample them into short linear keyframes), shared masks. (Draw-on strokes `trimEnd`, text that changes over time `set: { text }` / `visibleChars`, shake `wiggle()` and particles are built in.)
|
|
36
|
+
- **Built in:** `animateText()` (per-letter / per-word / per-line entrances and exits in one call: `...animateText('Title', style, { x, y, align: 'center', at: 0.5, duration: 5, in: 'rise', out: 'fade', stagger: { each: 0.05 } })`), `splitText()` / `measureText()` (per-letter / per-word layers, exact positions, by hand), `shape: 'arc'` (progress rings, pie / donut slices: animate `endAngle`), `fillGradient` (linear/radial, alpha stops → vignettes), `{value}` counters, `repeat`/`yoyo`, `orbit()`, seeded randomness (`rand(seed)` / `noise(x, seed)` in expressions, `random(seed)` in JS, never `Math.random()`) `wiggle()` (a seeded shake as keyframes; on a camera put it on `offsetX` / `offsetY` so it rides on the move) `cameraPath()` (a camera flight through `[x, y, z]` points as keyframes; layers it passes: `hideBehindCamera: true`) `music` (a tune with no file: `{ type: 'audio', music: { bpm, tracks: [{ inst: 'keys', notes: 'Cmaj7:4 Am7:4' }, { inst: 'bass', notes: 'c2:2 g1:2' }], drums: { from: 8, kick: 'x...x...' } } }`: notes and chords as text, counted in beats; instruments `keys pluck pad bass sub lead bell musicbox`; `musicEnvelope(music)` feeds `react()`), `audioEnvelope()` / `bpmEnvelope()` + `react()` (visuals that follow music: `const env = await audioEnvelope('music.mp3', { frameRate })` before `movie.init`, then `keyframes: react(env, { duration, props: { scale: { base: 1, amount: 0.4, band: 'bass', release: 0.2 } } })`; `beats: true` pulses on the beats; `bpmEnvelope(120, …)` when there is no file), `deck()` + `Presenter` (a talk as one movie: pages, steps, stops that pause playback, arrow keys / click / swipe to move on: `...deck({ transition: { kind: 'slide', duration: 0.6 }, pages: [{ name, duration, stops, sequences }] })`, `new Presenter(movie, { canvas })` from `pixi-effects/presenter`; G = page overview, P = presenter view with notes and a timer; `pixi-effects-render talk.html -o talk.pdf` = a PDF of the pages), `particles()` (bursts, snow, confetti, sparks as seeded layers: `...particles({ count: 90, at: 2, life: [1, 1.8], area: { x: 640, y: 330 }, speed: [120, 420], gravity: 240, fade: { out: 0.6 } })`), `followPath()` (a layer along an SVG path: `keyframes: followPath({ d, duration, orient: true })`), path morphing (`{ shape: 'path', d, morphTo, initial/keyframes: morph 0 → 1 }`) and `stagger()` (delays for a wave of layers: `stagger(letters, { each: 0.05, from: 'center' })`, or `stagger(n, …)` for numbers; GSAP-style `each` / `amount` / `from` / `grid` / `ease`). **Not in the DSL (hand-roll with a JS loop):** other per-frame curves (waves, spirals: sample them into short linear keyframes), shared masks. (Draw-on strokes `trimEnd`, text that changes over time `set: { text }` / `visibleChars`, shake `wiggle()` and particles are built in.)
|
|
37
37
|
- **Blend modes:** `blendMode: 'add' | 'screen' | 'multiply'` on any layer (glows, light leaks).
|
|
38
38
|
- **One `Movie` per page.** A sound effect needs no `duration` (it lasts as long as its clip). Seeking is safe: counters, colours and growing shapes are right after any jump.
|
|
39
39
|
|
package/ai/chat-template.html
CHANGED
|
@@ -61,9 +61,9 @@
|
|
|
61
61
|
"gsap": "https://cdn.jsdelivr.net/npm/gsap@3.12.5/+esm",
|
|
62
62
|
"gsap/PixiPlugin": "https://cdn.jsdelivr.net/npm/gsap@3.12.5/PixiPlugin/+esm",
|
|
63
63
|
"mediabunny": "https://cdn.jsdelivr.net/npm/mediabunny@1.61.3/+esm",
|
|
64
|
-
"pixi-effects": "https://cdn.jsdelivr.net/npm/pixi-effects@0.16.
|
|
65
|
-
"pixi-effects/controller": "https://cdn.jsdelivr.net/npm/pixi-effects@0.16.
|
|
66
|
-
"pixi-effects/presenter": "https://cdn.jsdelivr.net/npm/pixi-effects@0.16.
|
|
64
|
+
"pixi-effects": "https://cdn.jsdelivr.net/npm/pixi-effects@0.16.2/dist/index.js",
|
|
65
|
+
"pixi-effects/controller": "https://cdn.jsdelivr.net/npm/pixi-effects@0.16.2/dist/Controller.js",
|
|
66
|
+
"pixi-effects/presenter": "https://cdn.jsdelivr.net/npm/pixi-effects@0.16.2/dist/Presenter.js"
|
|
67
67
|
}
|
|
68
68
|
}
|
|
69
69
|
</script>
|
|
@@ -70,7 +70,7 @@ Keyframe kinds: `set` (jump at `at`; undone when you seek back), `to` (animate t
|
|
|
70
70
|
| polygon | `points: [[x,y],…] open` — canvas coordinates like `line` |
|
|
71
71
|
| path | `d` (SVG path data) — canvas coordinates like `line`. **Morph:** `morphTo: 'M…Z'` + animate `morph` 0 → 1 (points half way between, closed outlines lined up so they do not twist; `morphPoints` 8–2048) |
|
|
72
72
|
|
|
73
|
-
**Draw-on:** every shape but `arc` has `trimStart` / `trimEnd` (0–1 of the outline, default 0 / 1, animatable): `trimEnd: 0` + keyframe `to: { trimEnd: 1 }` draws a line / border / path on; `trimStart` 0 → 1 after it wipes it off; both = a travelling dash. The fill is NOT trimmed. Rect outline starts top-left, circle / ellipse at 12 o'clock, both clockwise. Use `strokeCap: 'round'`.
|
|
73
|
+
**Draw-on:** every shape but `arc` has `trimStart` / `trimEnd` (0–1 of the outline, default 0 / 1, animatable): `trimEnd: 0` + keyframe `to: { trimEnd: 1 }` draws a line / border / path on; `trimStart` 0 → 1 after it wipes it off; both = a travelling dash. The fill is NOT trimmed. Rect outline starts top-left, circle / ellipse at 12 o'clock, both clockwise. A `path` with several sub-paths walks them in order as one length; `trimEach: true` trims each on its own (all draw on together). Use `strokeCap: 'round'`.
|
|
74
74
|
|
|
75
75
|
Style (`initial` / keyframes): `fillColor fillAlpha strokeColor strokeAlpha strokeWidth`; `colorSpace: 'oklab'|'oklch'` for clean colour tweens. rect/circle/ellipse are **centred on `x,y` by default** (`anchorX/anchorY` default 0.5): for a bar growing from its base use `anchorY: 1` (or `anchorX: 0` for left-to-right). **`fillGradient`** (top level or `initial`; instead of `fillColor`): `{ type?: 'linear'|'radial', stops: [[0, '#000'], [1, 'rgba(0,0,0,.6)']], angle? /* linear, deg, 90 = top→bottom */, center?, innerRadius?, radius? /* radial, 0–1 of bounds */ }` — stops may have alpha, so a radial transparent→dark is a vignette. Stroke ends and corners: `strokeCap: 'butt'|'round'|'square'`, `strokeJoin: 'miter'|'round'|'bevel'` (top level or in `initial`; not animated — `round` keeps sharp corners like an M from poking out).
|
|
76
76
|
|
|
@@ -82,6 +82,10 @@ Style (`initial` / keyframes): `fillColor fillAlpha strokeColor strokeAlpha stro
|
|
|
82
82
|
|
|
83
83
|
**three** (optional entry `pixi-effects/three`) — `three({ type:'three', width, height, setup(ctx) { …; return { objects: { knot } } }, update?, dispose? })`; drive with `'three.knot.rotation.y'` keyframes. Call `registerThree()` before `init`.
|
|
84
84
|
|
|
85
|
+
## Camera flight and a camera shake
|
|
86
|
+
|
|
87
|
+
`...cameraPath({ points: [[x, y, z], …], duration, ease, look: 'ahead' | [x, y, z], lookAhead: 0.08, smooth: true, from, to, frameRate })` → camera keyframes (`x y z lookAtX lookAtY lookAtZ`) along a smooth curve at even speed (`ease: 'power2.in'` accelerates). A handheld shake goes on the camera's **offsets** so it never collides with the move: `...wiggle({ duration, props: { offsetX: { around: 0, amp: 5 }, offsetY: {…} } })` (`offsetX/Y/Z`, `lookOffsetX/Y/Z` are added to the position and the look-at point). A threeD layer the camera passes on purpose: `hideBehindCamera: true` (hidden quietly behind the camera, no warning). Recipe `camera-fly-through`.
|
|
88
|
+
|
|
85
89
|
## 2.5D in one paragraph
|
|
86
90
|
|
|
87
91
|
Add `threeD: true` to any visual layer, then use `z`, `rotationX`, `rotationY` (centre rotation with `anchorX/Y: 0.5` or `pivotX/Y`). Add a `{ type: 'camera' }` layer for a view. A `threeD` layer at `z: 0` with the default camera looks identical to a 2D one. Consecutive `threeD` layers are drawn farthest-first (equal `z` keeps array order); non-`threeD` layers ignore the camera and keep array order. Layers at/behind the camera plane are hidden. Not supported on `threeD` layers: masks, transitions, `filterArea`.
|
|
@@ -111,7 +115,7 @@ transitions: [{ kind: 'crossfade' | 'wipe' | 'iris' | 'slide' | 'dip' | 'zoom' |
|
|
|
111
115
|
## Presets
|
|
112
116
|
|
|
113
117
|
```js
|
|
114
|
-
import { kenBurns, withFade, wiggle, stagger, followPath, particles, deck, audioEnvelope, bpmEnvelope, musicEnvelope, react, random } from 'pixi-effects';
|
|
118
|
+
import { kenBurns, withFade, wiggle, stagger, followPath, cameraPath, particles, deck, audioEnvelope, bpmEnvelope, musicEnvelope, react, random } from 'pixi-effects';
|
|
115
119
|
kenBurns({ asset, name, at, duration, motion: 'still'|'scale'|'rotation'|'position',
|
|
116
120
|
fit: 'cover'|'contain', ease, /* scale */ origin, zoom, direction, /* rotation */ angle, /* position */ from, to })
|
|
117
121
|
withFade(spec, { in: 0.5, out: 0.5 }) // alpha fade; `out` needs spec.duration
|
|
@@ -146,7 +150,7 @@ measureText('Hello', style) // → { width, height,
|
|
|
146
150
|
splitText('Hello world', style, { by: 'chars' | 'words' | 'lines', x, y, align: 'left' | 'center' | 'right' })
|
|
147
151
|
// → [{ text, x, y, width, index, line }] — one text layer per piece, same style, anchor top-left
|
|
148
152
|
```
|
|
149
|
-
Per-letter / per-word / per-line animation in one call: `...animateText(text, style, { x, y, align, by: 'chars'|'words'|'lines', at, duration /* required: until the last piece is gone */, in: 'rise'|'drop'|'fade'|'pop'|'zoom'|'slide'|'spin'|{ from, to, duration, ease }|false, out: <same, runs in the same wave>, stagger: { each, from, … }, idle: { y: 4, rotation: 1 }, styleFor: p => ({ fill }) })` → text layers `<name>-0…` (anchor centre; `x`/`y` in `from` are offsets) to spread into `sequences` (recipe `animate-text`). By hand: `splitText(...)` + one layer per piece (recipe `split-text`). A web font must be loaded before measuring.
|
|
153
|
+
Per-letter / per-word / per-line animation in one call: `...animateText(text, style, { x, y, align, by: 'chars'|'words'|'lines', at, duration /* required: until the last piece is gone */, in: 'rise'|'drop'|'fade'|'pop'|'zoom'|'slide'|'spin'|{ from, to, duration, ease }|false, out: <same, runs in the same wave>, stagger: { each, from, … }, idle: { y: 4, rotation: 1 }, styleFor: p => ({ fill }) })` → text layers `<name>-0…` (anchor centre; `x`/`y` in `from` are offsets) to spread into `sequences`. **`x` / `y` are pixel NUMBERS (an expression like `'GW/2'` throws: compute `W / 2`), and `y` is the TOP of the line box**: the visible text is centred `lineHeight / 2` below it, so an underline goes at `y + lineHeight`, not at `y` (recipe `animate-text`). By hand: `splitText(...)` + one layer per piece (recipe `split-text`). A web font must be loaded before measuring.
|
|
150
154
|
|
|
151
155
|
## Assets
|
|
152
156
|
|
package/ai/reference/pitfalls.md
CHANGED
|
@@ -87,4 +87,5 @@ A good habit that catches most of these: after `init`, read `window.__logs` (see
|
|
|
87
87
|
60. **Build-ups are layers and `vol` points, not louder notes.** A track's `vol` can be `[[0, 0], [8, 0.7]]` (a fade over 8 beats); the pad's `attack` can be 2–3 s; enter instruments with rests. Per-note `!` and `,` only accent and soften.
|
|
88
88
|
61. **Music is a bed: `volume` 0.5–0.8 under speech or sfx.** At `volume: 1` it peaks at −6 dBFS (an sfx peaks at −12). A layer shorter than the music cuts it with a fade; longer is silent after it unless `loop: true`. Do not also add an mp3 bed.
|
|
89
89
|
62. **Chord names are voiced close around octave 3**: use `[c3 g3 e4]` for an exact voicing or `Am7/e` for a bass note; `swing` only moves notes that start on an off-beat eighth (`.5`) or sixteenth (`.75`); the bell and music box ring for `ring` seconds whatever the written length (shorten `ring` for fast bell lines); the synth is a handy bed, not an orchestra: keep tracks to 3–6, vary the bars, and end on the tonic.
|
|
90
|
+
63. **`animateText()` / `splitText()`: `x` and `y` are pixel numbers, and `y` is the TOP of the line.** An expression such as `x: 'GW/2'` throws (the letters are measured once, at build time): write `x: W / 2`. The pieces are centred half a line below `y`, so a line drawn under the text goes at `y + lineHeight` (about 1.2 × `fontSize`), not at `y`; look at the contact sheet, the layout check cannot see an underline through a word. (`check` writes into `check-out/` of the current folder unless you pass `--out`.)
|
|
90
91
|
|
package/ai/reference/recipes.md
CHANGED
|
@@ -589,6 +589,29 @@ new Presenter(movie, { canvas }); // arrows / Space / Enter / click / swipe
|
|
|
589
589
|
|
|
590
590
|
---
|
|
591
591
|
|
|
592
|
+
## A camera flight through a corridor (`cameraPath`, `hideBehindCamera`, a handheld shake)
|
|
593
|
+
|
|
594
|
+
`cameraPath({ points, duration, ease })` is the camera's route as keyframes: a smooth curve through `[x, y, z]` points at an even speed (an `ease` shapes it; `'power2.in'` is an accelerating rush), facing where it flies (`look: 'ahead'`, the default) or at a fixed `look: [x, y, z]`. Layers the camera passes get `hideBehindCamera: true` (hidden quietly instead of a warning). A handheld shake goes on the camera's **offsets** (`offsetX`, `offsetY`, `offsetZ`, `lookOffsetX`…): they are added to the move, so a `wiggle()` never collides with the dolly.
|
|
595
|
+
|
|
596
|
+
```js
|
|
597
|
+
// @recipe camera-fly-through
|
|
598
|
+
const gate = (i, z) => ({
|
|
599
|
+
type: 'composition', name: 'gate-' + i, threeD: true, hideBehindCamera: true, width: 360, height: 480,
|
|
600
|
+
initial: { x: 640, y: 360, z, pivotX: 180, pivotY: 240 },
|
|
601
|
+
sequences: [{ type: 'shape', shape: 'rect', width: 330, height: 450, cornerRadius: 6, initial: { x: 180, y: 240, strokeColor: '#ffe9c4', strokeWidth: 6 } }],
|
|
602
|
+
});
|
|
603
|
+
return { duration: 8, sequences: [
|
|
604
|
+
{ type: 'shape', shape: 'rect', width: 'GW', height: 'GH', initial: { x: 'GW/2', y: 'GH/2', fillColor: '#0b0a10' } },
|
|
605
|
+
...Array.from({ length: 9 }, (_, i) => gate(i, -i * 380)),
|
|
606
|
+
{ type: 'camera', keyframes: [
|
|
607
|
+
...cameraPath({ points: [[640, 360, 2200], [690, 330, 900], [640, 360, -3000]], duration: 7, ease: 'power2.in', frameRate: 30 }),
|
|
608
|
+
...wiggle({ duration: 7, freq: 9, seed: 2, props: { offsetX: { around: 0, amp: 5 }, offsetY: { around: 0, amp: 3 } } }),
|
|
609
|
+
] },
|
|
610
|
+
] };
|
|
611
|
+
```
|
|
612
|
+
|
|
613
|
+
---
|
|
614
|
+
|
|
592
615
|
## Looping motion
|
|
593
616
|
|
|
594
617
|
`repeat` (a finite count) and `yoyo` go on any keyframe. Total time = `duration × (repeat + 1)`.
|
package/ai/template.html
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
<meta charset="UTF-8" />
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
6
6
|
<title>pixi-effects video</title>
|
|
7
|
-
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/pixi-effects@0.16.
|
|
7
|
+
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/pixi-effects@0.16.2/dist/loader.css" /> <!-- the loader: HTML + CSS only, so it is on screen at once and keeps moving while the page builds the video -->
|
|
8
8
|
<style>
|
|
9
9
|
body { background: #0d1220; margin: 0; padding: 16px; }
|
|
10
10
|
.stage { position: relative; width: min(960px, 100%); } /* the player and the loader both sit inside this box */
|
|
@@ -39,10 +39,10 @@
|
|
|
39
39
|
"gsap": "https://esm.sh/gsap@3.12.5",
|
|
40
40
|
"gsap/PixiPlugin": "https://esm.sh/gsap@3.12.5/PixiPlugin",
|
|
41
41
|
"mediabunny": "https://esm.sh/mediabunny",
|
|
42
|
-
"pixi-effects": "https://cdn.jsdelivr.net/npm/pixi-effects@0.16.
|
|
43
|
-
"pixi-effects/controller": "https://cdn.jsdelivr.net/npm/pixi-effects@0.16.
|
|
44
|
-
"pixi-effects/presenter": "https://cdn.jsdelivr.net/npm/pixi-effects@0.16.
|
|
45
|
-
"pixi-effects/three": "https://cdn.jsdelivr.net/npm/pixi-effects@0.16.
|
|
42
|
+
"pixi-effects": "https://cdn.jsdelivr.net/npm/pixi-effects@0.16.2/dist/index.js",
|
|
43
|
+
"pixi-effects/controller": "https://cdn.jsdelivr.net/npm/pixi-effects@0.16.2/dist/Controller.js",
|
|
44
|
+
"pixi-effects/presenter": "https://cdn.jsdelivr.net/npm/pixi-effects@0.16.2/dist/Presenter.js",
|
|
45
|
+
"pixi-effects/three": "https://cdn.jsdelivr.net/npm/pixi-effects@0.16.2/dist/three.js",
|
|
46
46
|
"pixi-filters": "https://esm.sh/pixi-filters@6.1.5?external=pixi.js",
|
|
47
47
|
"three": "https://esm.sh/three@0.178.0"
|
|
48
48
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Filter, Container, RenderTexture, Matrix } from 'pixi.js';
|
|
2
|
-
import { S as SequenceSpec, C as CompositionShape, a0 as AudioDescriptor } from './types-
|
|
2
|
+
import { S as SequenceSpec, C as CompositionShape, a0 as AudioDescriptor } from './types-SS51lP6O.js';
|
|
3
3
|
import { gsap } from 'gsap';
|
|
4
4
|
|
|
5
5
|
/**
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Filter, Container, RenderTexture, Matrix } from 'pixi.js';
|
|
2
|
-
import { S as SequenceSpec, C as CompositionShape, a0 as AudioDescriptor } from './types-
|
|
2
|
+
import { S as SequenceSpec, C as CompositionShape, a0 as AudioDescriptor } from './types-SS51lP6O.cjs';
|
|
3
3
|
import { gsap } from 'gsap';
|
|
4
4
|
|
|
5
5
|
/**
|
|
@@ -0,0 +1,14 @@
|
|
|
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 +1 @@
|
|
|
1
|
-
{"version":3,"sources":[],"names":[],"mappings":"","file":"Composition-
|
|
1
|
+
{"version":3,"sources":[],"names":[],"mappings":"","file":"Composition-3WWNVRR5.cjs"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":[],"names":[],"mappings":"","file":"Composition-
|
|
1
|
+
{"version":3,"sources":[],"names":[],"mappings":"","file":"Composition-VJWY2NDL.js"}
|
package/dist/Controller.d.cts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { f as Movie } from './Movie-
|
|
1
|
+
import { f as Movie } from './Movie-oA0SUAyb.cjs';
|
|
2
2
|
import 'pixi.js';
|
|
3
3
|
import 'gsap';
|
|
4
|
-
import './types-
|
|
4
|
+
import './types-SS51lP6O.cjs';
|
|
5
5
|
|
|
6
6
|
/** The bar's default stylesheet. Its colours and sizes are CSS custom properties (`--mc-accent`, `--mc-fg`, `--mc-track`, `--mc-bar-bg`, `--mc-track-height`, `--mc-font`): set them in your page CSS, or with the `theme` option. */
|
|
7
7
|
declare const CONTROLLER_CSS = "\n.movie-controller-wrap { position: relative; display: inline-block; line-height: 0; }\n.movie-controller {\n position: absolute;\n pointer-events: none;\n font-family: var(--mc-font, -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif);\n display: flex;\n flex-direction: column;\n justify-content: flex-end;\n opacity: 1;\n transition: opacity 200ms ease;\n}\n.movie-controller[data-state=\"hidden\"] { opacity: 0; }\n.movie-controller > * { pointer-events: auto; }\n\n.mc-progress {\n position: relative;\n width: 100%;\n height: 16px;\n cursor: pointer;\n display: flex;\n align-items: center;\n outline: none;\n}\n.mc-progress:focus-visible {\n outline: 2px solid var(--mc-accent, #007AFF);\n outline-offset: 2px;\n}\n.mc-progress::before {\n content: \"\"; position: absolute; left: 12px; right: 12px;\n height: var(--mc-track-height, 3px); background: var(--mc-track, rgba(255,255,255,0.25));\n transition: height 120ms ease;\n}\n.mc-progress:hover::before, .mc-progress.mc-scrubbing::before { height: calc(var(--mc-track-height, 3px) + 2px); }\n.mc-stops { position: absolute; left: 12px; right: 12px; top: 0; bottom: 0; pointer-events: none; }\n.mc-stop-tick { position: absolute; top: 50%; width: 7px; height: 7px; transform: translate(-50%, -50%); border-radius: 50%; background: var(--mc-fg, #fff); box-shadow: 0 0 0 1.5px rgba(0,0,0,0.5); }\n.mc-stop-page { width: 11px; height: 11px; box-shadow: 0 0 0 2.5px var(--mc-accent, #007AFF), 0 0 0 4px rgba(0,0,0,0.35); }\n.mc-progress-fill {\n position: absolute; left: 12px; top: 50%;\n height: var(--mc-track-height, 3px);\n width: calc((100% - 24px) * var(--mc-fill, 0));\n background: var(--mc-accent, #007AFF);\n transform: translateY(-50%);\n transition: height 120ms ease;\n pointer-events: none;\n}\n.mc-progress:hover .mc-progress-fill,\n.mc-progress.mc-scrubbing .mc-progress-fill { height: calc(var(--mc-track-height, 3px) + 2px); }\n.mc-progress-thumb {\n position: absolute; top: 50%;\n left: calc(12px + (100% - 24px) * var(--mc-fill, 0));\n width: 12px; height: 12px; border-radius: 50%;\n background: var(--mc-accent, #007AFF);\n transform: translate(-50%, -50%) scale(0);\n transition: transform 120ms ease;\n pointer-events: none;\n}\n.mc-progress:hover .mc-progress-thumb,\n.mc-progress.mc-scrubbing .mc-progress-thumb { transform: translate(-50%, -50%) scale(1); }\n\n.mc-bar {\n position: relative;\n display: flex;\n align-items: center;\n gap: 12px;\n padding: 6px 12px 8px 12px;\n background: var(--mc-bar-bg, linear-gradient(to top, rgba(0,0,0,0.75) 0%, rgba(0,0,0,0) 100%));\n color: var(--mc-fg, #fff);\n line-height: 1;\n}\n.mc-btn {\n background: none; border: 0; padding: 0; margin: 0;\n width: 28px; height: 28px;\n display: inline-flex; align-items: center; justify-content: center;\n color: var(--mc-fg, #fff); opacity: 0.85; cursor: pointer;\n transition: opacity 120ms ease;\n}\n.mc-btn:hover { opacity: 1; }\n.mc-btn:disabled { opacity: 0.4; cursor: not-allowed; }\n.mc-time {\n font-size: 12px; font-family: Menlo, Monaco, monospace;\n color: var(--mc-fg, #fff); opacity: 0.85;\n min-width: 90px;\n}\n.mc-spacer { flex: 1; }\n\n.mc-volume { display: flex; align-items: center; }\n.mc-vol-slider {\n position: relative;\n width: 0;\n height: 28px;\n display: flex;\n align-items: center;\n cursor: pointer;\n margin-left: 0;\n outline: none;\n transition: width 200ms ease, margin-left 200ms ease;\n overflow: hidden;\n}\n.mc-volume:hover .mc-vol-slider,\n.mc-volume:focus-within .mc-vol-slider,\n.mc-vol-slider.mc-scrubbing {\n width: 70px;\n margin-left: 6px;\n}\n.mc-vol-slider:focus-visible { outline: 2px solid var(--mc-accent, #007AFF); outline-offset: 2px; }\n.mc-vol-slider::before {\n content: \"\"; position: absolute; left: 4px; right: 4px;\n height: 3px; background: var(--mc-track, rgba(255,255,255,0.25)); border-radius: 2px;\n}\n.mc-vol-fill {\n position: absolute; left: 4px; top: 50%;\n height: 3px;\n width: calc((100% - 8px) * var(--mc-volume, 1));\n background: var(--mc-fg, #fff);\n transform: translateY(-50%);\n border-radius: 2px;\n pointer-events: none;\n}\n.mc-vol-thumb {\n position: absolute; top: 50%;\n left: calc(4px + (100% - 8px) * var(--mc-volume, 1));\n width: 10px; height: 10px; border-radius: 50%;\n background: var(--mc-fg, #fff);\n transform: translate(-50%, -50%);\n pointer-events: none;\n}\n\n[data-mc-wrap]:fullscreen {\n width: 100vw; height: 100vh;\n display: flex; align-items: center; justify-content: center;\n background: #000;\n}\n[data-mc-wrap]:fullscreen > canvas {\n width: 100% !important;\n height: 100% !important;\n max-width: none !important;\n max-height: none !important;\n object-fit: contain;\n border-radius: 0;\n}\n\n.mc-settings-popover {\n position: absolute;\n bottom: calc(100% + 8px);\n right: 12px;\n background: rgba(20, 20, 20, 0.96);\n border-radius: 8px;\n padding: 10px 12px;\n min-width: 180px;\n color: #fff;\n font-size: 12px;\n box-shadow: 0 4px 16px rgba(0,0,0,0.4);\n display: flex;\n flex-direction: column;\n gap: 8px;\n}\n.mc-settings-popover[data-open=\"false\"] { display: none; }\n.mc-settings-row {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 12px;\n}\n.mc-settings-row > span { color: rgba(255,255,255,0.85); }\n.mc-settings-row > select {\n font: inherit;\n background: rgba(255,255,255,0.1);\n color: #fff;\n border: 1px solid rgba(255,255,255,0.18);\n border-radius: 4px;\n padding: 2px 6px;\n cursor: pointer;\n}\n.mc-export-confirm {\n margin-top: 4px;\n width: 100%;\n background: var(--mc-accent, #007AFF);\n color: #fff;\n border: 0;\n border-radius: 4px;\n padding: 6px 10px;\n font: inherit;\n font-weight: 600;\n cursor: pointer;\n transition: background 120ms ease;\n}\n.mc-export-confirm:hover { background: #0066d6; }\n.mc-export-confirm:disabled { background: rgba(255,255,255,0.18); cursor: not-allowed; }\n\n.mc-export-overlay {\n position: absolute; inset: 0;\n background: rgba(0,0,0,0.7);\n display: flex; justify-content: center; align-items: center;\n z-index: 2;\n}\n.mc-export-panel {\n background: rgba(20,20,20,0.96); color: #fff;\n border-radius: 8px; padding: 16px 20px;\n text-align: center; min-width: 200px; max-width: 80%;\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n}\n.mc-export-title { font-size: 13px; margin-bottom: 10px; opacity: 0.9; }\n.mc-export-track {\n width: 100%; height: 6px;\n background: rgba(255,255,255,0.15); border-radius: 3px;\n margin-bottom: 8px; overflow: hidden;\n}\n.mc-export-fill {\n height: 100%; width: 0%;\n background: linear-gradient(90deg, var(--mc-accent, #007AFF), var(--mc-accent, #0056CC));\n transition: width 0.3s ease; border-radius: 3px;\n}\n.mc-export-text {\n color: rgba(255,255,255,0.7); font-size: 11px;\n font-family: Menlo, Monaco, monospace;\n}\n";
|
package/dist/Controller.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { f as Movie } from './Movie-
|
|
1
|
+
import { f as Movie } from './Movie-DilBC348.js';
|
|
2
2
|
import 'pixi.js';
|
|
3
3
|
import 'gsap';
|
|
4
|
-
import './types-
|
|
4
|
+
import './types-SS51lP6O.js';
|
|
5
5
|
|
|
6
6
|
/** The bar's default stylesheet. Its colours and sizes are CSS custom properties (`--mc-accent`, `--mc-fg`, `--mc-track`, `--mc-bar-bg`, `--mc-track-height`, `--mc-font`): set them in your page CSS, or with the `theme` option. */
|
|
7
7
|
declare const CONTROLLER_CSS = "\n.movie-controller-wrap { position: relative; display: inline-block; line-height: 0; }\n.movie-controller {\n position: absolute;\n pointer-events: none;\n font-family: var(--mc-font, -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif);\n display: flex;\n flex-direction: column;\n justify-content: flex-end;\n opacity: 1;\n transition: opacity 200ms ease;\n}\n.movie-controller[data-state=\"hidden\"] { opacity: 0; }\n.movie-controller > * { pointer-events: auto; }\n\n.mc-progress {\n position: relative;\n width: 100%;\n height: 16px;\n cursor: pointer;\n display: flex;\n align-items: center;\n outline: none;\n}\n.mc-progress:focus-visible {\n outline: 2px solid var(--mc-accent, #007AFF);\n outline-offset: 2px;\n}\n.mc-progress::before {\n content: \"\"; position: absolute; left: 12px; right: 12px;\n height: var(--mc-track-height, 3px); background: var(--mc-track, rgba(255,255,255,0.25));\n transition: height 120ms ease;\n}\n.mc-progress:hover::before, .mc-progress.mc-scrubbing::before { height: calc(var(--mc-track-height, 3px) + 2px); }\n.mc-stops { position: absolute; left: 12px; right: 12px; top: 0; bottom: 0; pointer-events: none; }\n.mc-stop-tick { position: absolute; top: 50%; width: 7px; height: 7px; transform: translate(-50%, -50%); border-radius: 50%; background: var(--mc-fg, #fff); box-shadow: 0 0 0 1.5px rgba(0,0,0,0.5); }\n.mc-stop-page { width: 11px; height: 11px; box-shadow: 0 0 0 2.5px var(--mc-accent, #007AFF), 0 0 0 4px rgba(0,0,0,0.35); }\n.mc-progress-fill {\n position: absolute; left: 12px; top: 50%;\n height: var(--mc-track-height, 3px);\n width: calc((100% - 24px) * var(--mc-fill, 0));\n background: var(--mc-accent, #007AFF);\n transform: translateY(-50%);\n transition: height 120ms ease;\n pointer-events: none;\n}\n.mc-progress:hover .mc-progress-fill,\n.mc-progress.mc-scrubbing .mc-progress-fill { height: calc(var(--mc-track-height, 3px) + 2px); }\n.mc-progress-thumb {\n position: absolute; top: 50%;\n left: calc(12px + (100% - 24px) * var(--mc-fill, 0));\n width: 12px; height: 12px; border-radius: 50%;\n background: var(--mc-accent, #007AFF);\n transform: translate(-50%, -50%) scale(0);\n transition: transform 120ms ease;\n pointer-events: none;\n}\n.mc-progress:hover .mc-progress-thumb,\n.mc-progress.mc-scrubbing .mc-progress-thumb { transform: translate(-50%, -50%) scale(1); }\n\n.mc-bar {\n position: relative;\n display: flex;\n align-items: center;\n gap: 12px;\n padding: 6px 12px 8px 12px;\n background: var(--mc-bar-bg, linear-gradient(to top, rgba(0,0,0,0.75) 0%, rgba(0,0,0,0) 100%));\n color: var(--mc-fg, #fff);\n line-height: 1;\n}\n.mc-btn {\n background: none; border: 0; padding: 0; margin: 0;\n width: 28px; height: 28px;\n display: inline-flex; align-items: center; justify-content: center;\n color: var(--mc-fg, #fff); opacity: 0.85; cursor: pointer;\n transition: opacity 120ms ease;\n}\n.mc-btn:hover { opacity: 1; }\n.mc-btn:disabled { opacity: 0.4; cursor: not-allowed; }\n.mc-time {\n font-size: 12px; font-family: Menlo, Monaco, monospace;\n color: var(--mc-fg, #fff); opacity: 0.85;\n min-width: 90px;\n}\n.mc-spacer { flex: 1; }\n\n.mc-volume { display: flex; align-items: center; }\n.mc-vol-slider {\n position: relative;\n width: 0;\n height: 28px;\n display: flex;\n align-items: center;\n cursor: pointer;\n margin-left: 0;\n outline: none;\n transition: width 200ms ease, margin-left 200ms ease;\n overflow: hidden;\n}\n.mc-volume:hover .mc-vol-slider,\n.mc-volume:focus-within .mc-vol-slider,\n.mc-vol-slider.mc-scrubbing {\n width: 70px;\n margin-left: 6px;\n}\n.mc-vol-slider:focus-visible { outline: 2px solid var(--mc-accent, #007AFF); outline-offset: 2px; }\n.mc-vol-slider::before {\n content: \"\"; position: absolute; left: 4px; right: 4px;\n height: 3px; background: var(--mc-track, rgba(255,255,255,0.25)); border-radius: 2px;\n}\n.mc-vol-fill {\n position: absolute; left: 4px; top: 50%;\n height: 3px;\n width: calc((100% - 8px) * var(--mc-volume, 1));\n background: var(--mc-fg, #fff);\n transform: translateY(-50%);\n border-radius: 2px;\n pointer-events: none;\n}\n.mc-vol-thumb {\n position: absolute; top: 50%;\n left: calc(4px + (100% - 8px) * var(--mc-volume, 1));\n width: 10px; height: 10px; border-radius: 50%;\n background: var(--mc-fg, #fff);\n transform: translate(-50%, -50%);\n pointer-events: none;\n}\n\n[data-mc-wrap]:fullscreen {\n width: 100vw; height: 100vh;\n display: flex; align-items: center; justify-content: center;\n background: #000;\n}\n[data-mc-wrap]:fullscreen > canvas {\n width: 100% !important;\n height: 100% !important;\n max-width: none !important;\n max-height: none !important;\n object-fit: contain;\n border-radius: 0;\n}\n\n.mc-settings-popover {\n position: absolute;\n bottom: calc(100% + 8px);\n right: 12px;\n background: rgba(20, 20, 20, 0.96);\n border-radius: 8px;\n padding: 10px 12px;\n min-width: 180px;\n color: #fff;\n font-size: 12px;\n box-shadow: 0 4px 16px rgba(0,0,0,0.4);\n display: flex;\n flex-direction: column;\n gap: 8px;\n}\n.mc-settings-popover[data-open=\"false\"] { display: none; }\n.mc-settings-row {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 12px;\n}\n.mc-settings-row > span { color: rgba(255,255,255,0.85); }\n.mc-settings-row > select {\n font: inherit;\n background: rgba(255,255,255,0.1);\n color: #fff;\n border: 1px solid rgba(255,255,255,0.18);\n border-radius: 4px;\n padding: 2px 6px;\n cursor: pointer;\n}\n.mc-export-confirm {\n margin-top: 4px;\n width: 100%;\n background: var(--mc-accent, #007AFF);\n color: #fff;\n border: 0;\n border-radius: 4px;\n padding: 6px 10px;\n font: inherit;\n font-weight: 600;\n cursor: pointer;\n transition: background 120ms ease;\n}\n.mc-export-confirm:hover { background: #0066d6; }\n.mc-export-confirm:disabled { background: rgba(255,255,255,0.18); cursor: not-allowed; }\n\n.mc-export-overlay {\n position: absolute; inset: 0;\n background: rgba(0,0,0,0.7);\n display: flex; justify-content: center; align-items: center;\n z-index: 2;\n}\n.mc-export-panel {\n background: rgba(20,20,20,0.96); color: #fff;\n border-radius: 8px; padding: 16px 20px;\n text-align: center; min-width: 200px; max-width: 80%;\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n}\n.mc-export-title { font-size: 13px; margin-bottom: 10px; opacity: 0.9; }\n.mc-export-track {\n width: 100%; height: 6px;\n background: rgba(255,255,255,0.15); border-radius: 3px;\n margin-bottom: 8px; overflow: hidden;\n}\n.mc-export-fill {\n height: 100%; width: 0%;\n background: linear-gradient(90deg, var(--mc-accent, #007AFF), var(--mc-accent, #0056CC));\n transition: width 0.3s ease; border-radius: 3px;\n}\n.mc-export-text {\n color: rgba(255,255,255,0.7); font-size: 11px;\n font-family: Menlo, Monaco, monospace;\n}\n";
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Application } from 'pixi.js';
|
|
2
2
|
import { gsap } from 'gsap';
|
|
3
|
-
import { U as Stop, e as AssetSpec, c as CompositionSpec } from './types-
|
|
3
|
+
import { U as Stop, e as AssetSpec, c as CompositionSpec } from './types-SS51lP6O.js';
|
|
4
4
|
|
|
5
5
|
interface AudioInspectOptions {
|
|
6
6
|
/** Width of each loudness window in seconds. Default 0.1. */
|
|
@@ -457,7 +457,13 @@ declare class Movie {
|
|
|
457
457
|
*/
|
|
458
458
|
private _renderNow;
|
|
459
459
|
private _requireReady;
|
|
460
|
-
/**
|
|
460
|
+
/** True when the layers were already synced to the playhead for the frame about to be drawn (so `_renderNow` need not do it again). */
|
|
461
|
+
private _synced;
|
|
462
|
+
/**
|
|
463
|
+
* Projects every `threeD` layer for the current frame (see src/space). A threeD card is drawn into its own texture HERE, before the stage is
|
|
464
|
+
* drawn, so the shapes inside it must be brought up to the playhead first: syncing only in `_renderNow` left every card showing the state of
|
|
465
|
+
* the frame before (after a jump seek, a card whose children were animated showed a stale picture).
|
|
466
|
+
*/
|
|
461
467
|
private _updateSpace;
|
|
462
468
|
/** The motion blur an operation uses: its own option when given (`false` is off), otherwise the movie's. */
|
|
463
469
|
private _blurFor;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Application } from 'pixi.js';
|
|
2
2
|
import { gsap } from 'gsap';
|
|
3
|
-
import { U as Stop, e as AssetSpec, c as CompositionSpec } from './types-
|
|
3
|
+
import { U as Stop, e as AssetSpec, c as CompositionSpec } from './types-SS51lP6O.cjs';
|
|
4
4
|
|
|
5
5
|
interface AudioInspectOptions {
|
|
6
6
|
/** Width of each loudness window in seconds. Default 0.1. */
|
|
@@ -457,7 +457,13 @@ declare class Movie {
|
|
|
457
457
|
*/
|
|
458
458
|
private _renderNow;
|
|
459
459
|
private _requireReady;
|
|
460
|
-
/**
|
|
460
|
+
/** True when the layers were already synced to the playhead for the frame about to be drawn (so `_renderNow` need not do it again). */
|
|
461
|
+
private _synced;
|
|
462
|
+
/**
|
|
463
|
+
* Projects every `threeD` layer for the current frame (see src/space). A threeD card is drawn into its own texture HERE, before the stage is
|
|
464
|
+
* drawn, so the shapes inside it must be brought up to the playhead first: syncing only in `_renderNow` left every card showing the state of
|
|
465
|
+
* the frame before (after a jump seek, a card whose children were animated showed a stale picture).
|
|
466
|
+
*/
|
|
461
467
|
private _updateSpace;
|
|
462
468
|
/** The motion blur an operation uses: its own option when given (`false` is off), otherwise the movie's. */
|
|
463
469
|
private _blurFor;
|
package/dist/Presenter.d.cts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { f as Movie } from './Movie-
|
|
1
|
+
import { f as Movie } from './Movie-oA0SUAyb.cjs';
|
|
2
2
|
import 'pixi.js';
|
|
3
3
|
import 'gsap';
|
|
4
|
-
import './types-
|
|
4
|
+
import './types-SS51lP6O.cjs';
|
|
5
5
|
|
|
6
6
|
interface PresenterOptions {
|
|
7
7
|
canvas: HTMLCanvasElement;
|
package/dist/Presenter.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { f as Movie } from './Movie-
|
|
1
|
+
import { f as Movie } from './Movie-DilBC348.js';
|
|
2
2
|
import 'pixi.js';
|
|
3
3
|
import 'gsap';
|
|
4
|
-
import './types-
|
|
4
|
+
import './types-SS51lP6O.js';
|
|
5
5
|
|
|
6
6
|
interface PresenterOptions {
|
|
7
7
|
canvas: HTMLCanvasElement;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var
|
|
3
|
+
var chunkFJA5VUOF_cjs = require('./chunk-FJA5VUOF.cjs');
|
|
4
4
|
var chunkUQBGW5DO_cjs = require('./chunk-UQBGW5DO.cjs');
|
|
5
5
|
var pixi_js = require('pixi.js');
|
|
6
6
|
var gsap = require('gsap');
|
|
@@ -65,8 +65,8 @@ function readLayerTransform(target) {
|
|
|
65
65
|
x: c.x,
|
|
66
66
|
y: c.y,
|
|
67
67
|
z: c.z ?? 0,
|
|
68
|
-
rotationX: (c.rotationX ?? 0) *
|
|
69
|
-
rotationY: (c.rotationY ?? 0) *
|
|
68
|
+
rotationX: (c.rotationX ?? 0) * chunkFJA5VUOF_cjs.DEG,
|
|
69
|
+
rotationY: (c.rotationY ?? 0) * chunkFJA5VUOF_cjs.DEG,
|
|
70
70
|
rotationZ: c.rotation,
|
|
71
71
|
scaleX: c.scale.x,
|
|
72
72
|
scaleY: c.scale.y,
|
|
@@ -111,18 +111,19 @@ var Layer3D = class {
|
|
|
111
111
|
if (!(needW >= 1 && needH >= 1) || !Number.isFinite(bounds.x) || !Number.isFinite(bounds.y)) return this.hide();
|
|
112
112
|
const size = this.sizeFor(needW, needH);
|
|
113
113
|
const frame = { x: bounds.x - pad, y: bounds.y - pad, width: size.w, height: size.h };
|
|
114
|
-
const projected =
|
|
114
|
+
const projected = chunkFJA5VUOF_cjs.projectLayer(readLayerTransform(target), frame, basis);
|
|
115
115
|
this.depth = projected.depth;
|
|
116
116
|
if (!projected.visible) {
|
|
117
117
|
if (!this.warnedBehind) {
|
|
118
118
|
this.warnedBehind = true;
|
|
119
|
-
if (
|
|
119
|
+
if (this.seq.spec.hideBehindCamera) return this.hide();
|
|
120
|
+
if (projected.depth > chunkFJA5VUOF_cjs.NEAR) {
|
|
120
121
|
console.warn(
|
|
121
|
-
`pixi-effects: ${
|
|
122
|
+
`pixi-effects: ${chunkFJA5VUOF_cjs.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)}\xB0, rotationY ${(target.rotationY ?? 0).toFixed(0)}\xB0), 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 (the default camera sits at z = (height/2)/tan(fov/2), about 989 for 720p at fov 40).`
|
|
122
123
|
);
|
|
123
124
|
} else {
|
|
124
125
|
console.warn(
|
|
125
|
-
`pixi-effects: ${
|
|
126
|
+
`pixi-effects: ${chunkFJA5VUOF_cjs.describeLayer(this.seq.spec)} is at or behind the camera plane (z = ${(target.z ?? 0).toFixed(0)}) and is hidden. 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).`
|
|
126
127
|
);
|
|
127
128
|
}
|
|
128
129
|
}
|
|
@@ -206,12 +207,12 @@ var CAMERA_ALIASES = {
|
|
|
206
207
|
pointOfInterest: 'did you mean "lookAtX / lookAtY / lookAtZ"?',
|
|
207
208
|
lookAt: 'did you mean "lookAtX / lookAtY / lookAtZ"?'
|
|
208
209
|
};
|
|
209
|
-
var CAMERA_PROPS = ["x", "y", "z", "fov", "lookAtX", "lookAtY", "lookAtZ"];
|
|
210
|
+
var CAMERA_PROPS = ["x", "y", "z", "fov", "lookAtX", "lookAtY", "lookAtZ", "offsetX", "offsetY", "offsetZ", "lookOffsetX", "lookOffsetY", "lookOffsetZ"];
|
|
210
211
|
var NEEDS_THREE_D = ["z", "rotationX", "rotationY"];
|
|
211
212
|
var SKEW_KEYS = ["skew", "skewX", "skewY"];
|
|
212
213
|
function lintSequence(spec, warn = defaultWarn) {
|
|
213
|
-
const who =
|
|
214
|
-
const keys =
|
|
214
|
+
const who = chunkFJA5VUOF_cjs.describeLayer(spec);
|
|
215
|
+
const keys = chunkFJA5VUOF_cjs.collectPropKeys(spec);
|
|
215
216
|
const explained = /* @__PURE__ */ new Set();
|
|
216
217
|
for (const k of keys) {
|
|
217
218
|
const hint = LAYER_ALIASES[k] ?? (spec.type === "camera" ? CAMERA_ALIASES[k] : void 0);
|
|
@@ -257,7 +258,7 @@ function applyBlendMode(spec, display) {
|
|
|
257
258
|
const mode = spec.blendMode;
|
|
258
259
|
if (mode === void 0) return;
|
|
259
260
|
if (!MODES.includes(mode)) {
|
|
260
|
-
console.warn(`pixi-effects: ${
|
|
261
|
+
console.warn(`pixi-effects: ${chunkFJA5VUOF_cjs.describeLayer(spec)}: blendMode "${mode}" is not supported (use ${MODES.join(", ")})`);
|
|
261
262
|
return;
|
|
262
263
|
}
|
|
263
264
|
const filters = display.filters;
|
|
@@ -269,7 +270,7 @@ function applyBlendMode(spec, display) {
|
|
|
269
270
|
}
|
|
270
271
|
|
|
271
272
|
// src/sequences/Composition.ts
|
|
272
|
-
var CompositionSequence = class extends
|
|
273
|
+
var CompositionSequence = class extends chunkFJA5VUOF_cjs.Sequence {
|
|
273
274
|
_innerContainer = null;
|
|
274
275
|
_compositionShape = null;
|
|
275
276
|
_children = [];
|
|
@@ -303,18 +304,18 @@ var CompositionSequence = class extends chunkSPPNTV7E_cjs.Sequence {
|
|
|
303
304
|
const lateKeyframes = [];
|
|
304
305
|
for (const s of this.spec.sequences ?? []) {
|
|
305
306
|
lintSequence(s);
|
|
306
|
-
|
|
307
|
-
|
|
307
|
+
chunkFJA5VUOF_cjs.lintTiming(s, this.duration, (message, kind) => kind === "late-keyframe" ? lateKeyframes.push(message) : console.warn(message));
|
|
308
|
+
chunkFJA5VUOF_cjs.lintText(s);
|
|
308
309
|
}
|
|
309
|
-
for (const message of
|
|
310
|
-
this._children = await
|
|
310
|
+
for (const message of chunkFJA5VUOF_cjs.summarizeWarnings(lateKeyframes)) console.warn(message);
|
|
311
|
+
this._children = await chunkFJA5VUOF_cjs.buildSequenceTree(
|
|
311
312
|
this.spec.sequences ?? [],
|
|
312
313
|
this._compositionShape,
|
|
313
314
|
this.root
|
|
314
315
|
);
|
|
315
316
|
this._resolveParents();
|
|
316
317
|
for (const child of this._children) {
|
|
317
|
-
if (child instanceof
|
|
318
|
+
if (child instanceof chunkFJA5VUOF_cjs.CameraSequence) {
|
|
318
319
|
this._cameras.push(child);
|
|
319
320
|
continue;
|
|
320
321
|
}
|
|
@@ -329,7 +330,7 @@ var CompositionSequence = class extends chunkSPPNTV7E_cjs.Sequence {
|
|
|
329
330
|
this._visual.push({ seq: child, layer });
|
|
330
331
|
applyBlendMode(child.spec, layer.display);
|
|
331
332
|
if (maskSpec) {
|
|
332
|
-
console.warn(`pixi-effects: ${
|
|
333
|
+
console.warn(`pixi-effects: ${chunkFJA5VUOF_cjs.describeLayer(child.spec)}: mask is not supported on threeD layers yet; ignored`);
|
|
333
334
|
}
|
|
334
335
|
continue;
|
|
335
336
|
}
|
|
@@ -339,7 +340,7 @@ var CompositionSequence = class extends chunkSPPNTV7E_cjs.Sequence {
|
|
|
339
340
|
applyBlendMode(child.spec, child.target);
|
|
340
341
|
if (maskSpec) {
|
|
341
342
|
const lifetime = maskSpec.at === void 0 ? { at: child.at, duration: maskSpec.duration ?? child.duration } : {};
|
|
342
|
-
const built = await
|
|
343
|
+
const built = await chunkFJA5VUOF_cjs.buildSequenceTree([{ ...maskSpec, ...lifetime }], this._compositionShape, this.root);
|
|
343
344
|
const maskSeq = built[0];
|
|
344
345
|
if (maskSeq?.target) {
|
|
345
346
|
into.addChild(maskSeq.target);
|
|
@@ -389,7 +390,7 @@ var CompositionSequence = class extends chunkSPPNTV7E_cjs.Sequence {
|
|
|
389
390
|
for (let p = this._parentOf.get(seq); p; p = this._parentOf.get(p)) out.push(p);
|
|
390
391
|
return out;
|
|
391
392
|
};
|
|
392
|
-
return [...this._visual.map((v) => v.seq), ...this._nested].filter((seq) => !(seq instanceof
|
|
393
|
+
return [...this._visual.map((v) => v.seq), ...this._nested].filter((seq) => !(seq instanceof chunkFJA5VUOF_cjs.NullSequence)).map((seq) => {
|
|
393
394
|
const layer = this._visual.find((v) => v.seq === seq)?.layer ?? null;
|
|
394
395
|
return {
|
|
395
396
|
seq,
|
|
@@ -401,14 +402,14 @@ var CompositionSequence = class extends chunkSPPNTV7E_cjs.Sequence {
|
|
|
401
402
|
}
|
|
402
403
|
/** Match every `parent: 'name'` to a null layer; anything that cannot work is said out loud and drawn unparented. */
|
|
403
404
|
_resolveParents() {
|
|
404
|
-
const nulls = this._children.filter((c) => c instanceof
|
|
405
|
+
const nulls = this._children.filter((c) => c instanceof chunkFJA5VUOF_cjs.NullSequence);
|
|
405
406
|
const nullNames = nulls.map((n) => n.spec.name).filter((n) => !!n);
|
|
406
407
|
const warned = /* @__PURE__ */ new Set();
|
|
407
408
|
for (const child of this._children) {
|
|
408
409
|
const want = child.spec.parent;
|
|
409
410
|
if (want === void 0) continue;
|
|
410
|
-
const who =
|
|
411
|
-
if (child instanceof
|
|
411
|
+
const who = chunkFJA5VUOF_cjs.describeLayer(child.spec);
|
|
412
|
+
if (child instanceof chunkFJA5VUOF_cjs.CameraSequence) {
|
|
412
413
|
console.warn(`pixi-effects: ${who}: a camera cannot have a parent; ignored`);
|
|
413
414
|
continue;
|
|
414
415
|
}
|
|
@@ -422,7 +423,7 @@ var CompositionSequence = class extends chunkSPPNTV7E_cjs.Sequence {
|
|
|
422
423
|
console.warn(`pixi-effects: ${who}: parent "${want}": no layer with that name${hint ? `; did you mean "${hint}"?` : ""} (the layer is drawn without a parent)`);
|
|
423
424
|
continue;
|
|
424
425
|
}
|
|
425
|
-
const holder = same.find((c) => c instanceof
|
|
426
|
+
const holder = same.find((c) => c instanceof chunkFJA5VUOF_cjs.NullSequence);
|
|
426
427
|
if (!holder) {
|
|
427
428
|
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)`);
|
|
428
429
|
continue;
|
|
@@ -465,7 +466,7 @@ var CompositionSequence = class extends chunkSPPNTV7E_cjs.Sequence {
|
|
|
465
466
|
t,
|
|
466
467
|
compEnd
|
|
467
468
|
);
|
|
468
|
-
const basis =
|
|
469
|
+
const basis = chunkFJA5VUOF_cjs.cameraBasis(active ? active.cam.state() : chunkFJA5VUOF_cjs.homeCamera(width, height), width, height);
|
|
469
470
|
for (const layer of this._layers3d) layer.update(host, basis);
|
|
470
471
|
const order = assignDepthOrder(
|
|
471
472
|
this._visual.map((v, i) => ({ stackIndex: i, threeD: v.layer !== null, depth: v.layer?.depth ?? 0 }))
|
|
@@ -494,5 +495,5 @@ function frameOf(child) {
|
|
|
494
495
|
}
|
|
495
496
|
|
|
496
497
|
exports.CompositionSequence = CompositionSequence;
|
|
497
|
-
//# sourceMappingURL=chunk-
|
|
498
|
-
//# sourceMappingURL=chunk-
|
|
498
|
+
//# sourceMappingURL=chunk-2ROVCPSL.cjs.map
|
|
499
|
+
//# sourceMappingURL=chunk-2ROVCPSL.cjs.map
|