pixi-effects 0.3.0 → 0.4.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 +24 -0
- package/README.md +6 -6
- package/ai/SKILL.md +3 -1
- package/ai/reference/cheatsheet.md +6 -5
- package/ai/reference/pitfalls.md +24 -3
- package/ai/template.html +3 -3
- package/dist/{Base-Ba4Ta4ap.d.ts → Base-5R7C5pHR.d.cts} +7 -1
- package/dist/{Base-BDw6dKRB.d.cts → Base-DhY9oMxr.d.ts} +7 -1
- package/dist/Composition-D7RCAGEA.cjs +13 -0
- package/dist/{Composition-6FDH5OPM.cjs.map → Composition-D7RCAGEA.cjs.map} +1 -1
- package/dist/Composition-RGTRZ2JX.js +4 -0
- package/dist/{Composition-FEYAUMFI.js.map → Composition-RGTRZ2JX.js.map} +1 -1
- package/dist/Controller.d.cts +2 -2
- package/dist/Controller.d.ts +2 -2
- package/dist/{Movie-W8ISiEBB.d.cts → Movie-CghFXprP.d.cts} +1 -1
- package/dist/{Movie-DMpaT67V.d.ts → Movie-DRZtIGU7.d.ts} +1 -1
- package/dist/{chunk-SIRVULFF.cjs → chunk-6BQ6IEW2.cjs} +119 -36
- package/dist/chunk-6BQ6IEW2.cjs.map +1 -0
- package/dist/{chunk-DIJG2RSF.js → chunk-A574IA4F.js} +44 -6
- package/dist/chunk-A574IA4F.js.map +1 -0
- package/dist/{chunk-ZL262ZRM.cjs → chunk-QGBOC5YL.cjs} +56 -18
- package/dist/chunk-QGBOC5YL.cjs.map +1 -0
- package/dist/{chunk-PN5A6QA7.js → chunk-ZQX7WIYL.js} +119 -37
- package/dist/chunk-ZQX7WIYL.js.map +1 -0
- package/dist/index.cjs +58 -16
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +4 -4
- package/dist/index.d.ts +4 -4
- package/dist/index.js +50 -8
- package/dist/index.js.map +1 -1
- package/dist/three.cjs +5 -5
- package/dist/three.d.cts +2 -2
- package/dist/three.d.ts +2 -2
- package/dist/three.js +1 -1
- package/dist/{types-3c8Vymgw.d.cts → types-CRE9WKd4.d.cts} +6 -0
- package/dist/{types-3c8Vymgw.d.ts → types-CRE9WKd4.d.ts} +6 -0
- package/docs/dsl.md +12 -1
- package/llms-full.txt +45 -10
- package/package.json +2 -1
- package/dist/Composition-6FDH5OPM.cjs +0 -13
- package/dist/Composition-FEYAUMFI.js +0 -4
- package/dist/chunk-DIJG2RSF.js.map +0 -1
- package/dist/chunk-PN5A6QA7.js.map +0 -1
- package/dist/chunk-SIRVULFF.cjs.map +0 -1
- package/dist/chunk-ZL262ZRM.cjs.map +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,29 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.4.0
|
|
4
|
+
|
|
5
|
+
**Changed**
|
|
6
|
+
|
|
7
|
+
- A mask with no `at` of its own now starts and ends with the layer it masks, so its keyframes are measured from that layer's start (as `docs/dsl.md` always said: "shares its lifetime"). An explicit mask `at` is still composition time. **Migration:** a mask reveal that used `at: 0` on a layer that appears later now plays when the layer appears (before, it had finished long before). Give the mask an explicit `at` to keep the old timing.
|
|
8
|
+
- An `audio` layer with `loop: true` and no `duration` lasts until the composition ends (it used to stop after one clip length). Without `loop` and without `duration` it lasts as long as its clip, so a one-shot sound effect needs no `duration`.
|
|
9
|
+
|
|
10
|
+
**Fixed**
|
|
11
|
+
|
|
12
|
+
- **Volume keyframes** behaved unlike every other keyframe: a `to` ramped from the end of the previous keyframe, so the documented fade-out `{ at: -2, to: { volume: 0 }, duration: 2 }` faded the music across the whole piece, and `set` ramped towards its time instead of jumping. Each change now holds the current value until its own start, and keyframes apply in time order.
|
|
13
|
+
- A `set` keyframe (text `{value}` counter and fill, image tint, shape style) stayed applied after seeking back before it.
|
|
14
|
+
- **A shape could be missing after a jump seek** (contact sheet, snapshot, scrubbing): geometry was drawn only while rendering, after the culler had measured the previous frame's bounds, so a rect growing from `width: 0` at the left edge was culled. Shapes now redraw before the cull.
|
|
15
|
+
- `threeD` layers whose size changes (a growing shape, text) no longer make Pixi warn "destroyed while still bound": the render texture is resized in place.
|
|
16
|
+
- Text `style` words (`stroke: { join: 'round' }`, named colours, `textBaseline`, `whiteSpace`) were parsed as expressions and warned.
|
|
17
|
+
- `movie.inspect` judges overlaps on what is visible (cut to the canvas and the layer's mask), so a ticker parked off the canvas is not reported, and does not report the two scenes of a running transition.
|
|
18
|
+
- The same "keyframe starts after the layer ends" warning, printed once per layer by a loop, is summarised after three.
|
|
19
|
+
- `docs/dsl.md` contradicted itself on whether text content can change (it can, through `{value}`).
|
|
20
|
+
|
|
21
|
+
**Added**
|
|
22
|
+
|
|
23
|
+
- `blendMode: 'normal' | 'add' | 'screen' | 'multiply'` on every layer (a composition's children inherit it): glows and light leaks that add up.
|
|
24
|
+
- A new `sfx-swoosh.mp3` in `examples/_assets` (an airy band-pass noise sweep, 0.55 s, stereo) replacing the old one.
|
|
25
|
+
- `examples/gallery`: 30 portfolio pieces written as plain data by three AI models, with a showcase page, and the stumble notes that fed these fixes. `ai/reference/pitfalls.md` items 32–48.
|
|
26
|
+
|
|
3
27
|
## 0.3.0
|
|
4
28
|
|
|
5
29
|
**Breaking**
|
package/README.md
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# pixi-effects
|
|
2
2
|
|
|
3
|
-
> **Status**: experimental — current release `0.
|
|
3
|
+
> **Status**: experimental — current release `0.4.0`. The API may still change between minor versions (see [CHANGELOG](./CHANGELOG.md): `0.4.0` makes a mask share its layer's lifetime, `0.3.0` made keyframe `at` relative to the layer).
|
|
4
4
|
|
|
5
|
-
**[Live demos →](https://yjmtmtk.github.io/pixi-effects/)** · 12 numbered examples + an in-browser playground.
|
|
5
|
+
**[Gallery →](https://yjmtmtk.github.io/pixi-effects/examples/gallery/)** · 30 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
|
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/).
|
|
8
8
|
|
|
@@ -35,8 +35,8 @@ Drop the imports into an [importmap](https://developer.mozilla.org/docs/Web/HTML
|
|
|
35
35
|
"gsap": "https://esm.sh/gsap@3.12.5",
|
|
36
36
|
"gsap/PixiPlugin": "https://esm.sh/gsap@3.12.5/PixiPlugin",
|
|
37
37
|
"mediabunny": "https://esm.sh/mediabunny",
|
|
38
|
-
"pixi-effects": "https://cdn.jsdelivr.net/npm/pixi-effects@0.
|
|
39
|
-
"pixi-effects/controller": "https://cdn.jsdelivr.net/npm/pixi-effects@0.
|
|
38
|
+
"pixi-effects": "https://cdn.jsdelivr.net/npm/pixi-effects@0.4.0/dist/index.js",
|
|
39
|
+
"pixi-effects/controller": "https://cdn.jsdelivr.net/npm/pixi-effects@0.4.0/dist/Controller.js"
|
|
40
40
|
}
|
|
41
41
|
}
|
|
42
42
|
</script>
|
|
@@ -47,7 +47,7 @@ Drop the imports into an [importmap](https://developer.mozilla.org/docs/Web/HTML
|
|
|
47
47
|
</script>
|
|
48
48
|
```
|
|
49
49
|
|
|
50
|
-
Load the `dist/` files **as they are** (jsDelivr's `/npm/…/dist/…`, or unpkg's `https://unpkg.com/pixi-effects@0.
|
|
50
|
+
Load the `dist/` files **as they are** (jsDelivr's `/npm/…/dist/…`, or unpkg's `https://unpkg.com/pixi-effects@0.4.0/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.
|
|
51
51
|
|
|
52
52
|
> **Using three.js?** Add two more entries (`three` and `pixi-effects/three`) to this importmap — see [Adding three.js](#adding-threejs-optional) below.
|
|
53
53
|
>
|
|
@@ -63,7 +63,7 @@ Add two more entries to the importmap above: three.js itself, and the `pixi-effe
|
|
|
63
63
|
"imports": {
|
|
64
64
|
"...": "(everything from the importmap above)",
|
|
65
65
|
"three": "https://esm.sh/three@0.178.0",
|
|
66
|
-
"pixi-effects/three": "https://cdn.jsdelivr.net/npm/pixi-effects@0.
|
|
66
|
+
"pixi-effects/three": "https://cdn.jsdelivr.net/npm/pixi-effects@0.4.0/dist/three.js"
|
|
67
67
|
}
|
|
68
68
|
}
|
|
69
69
|
</script>
|
package/ai/SKILL.md
CHANGED
|
@@ -28,7 +28,9 @@ You describe a video as a tree of plain objects; the library plays it in a canva
|
|
|
28
28
|
- **`audio` shorter than its layer goes silent** — add `loop: true`.
|
|
29
29
|
- **Masks live in the parent's coordinates and don't follow the masked layer.** **`filterArea` is in the layer's own coordinates.**
|
|
30
30
|
- **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.
|
|
31
|
-
- **Built in:** `fillGradient` (linear/radial, alpha stops → vignettes), `{value}` counters, `repeat`/`yoyo`, `orbit()`. **Not in the DSL (hand-roll, recipes exist or loops are easy):** other per-frame curves, per-letter text animators (one layer per letter),
|
|
31
|
+
- **Built in:** `fillGradient` (linear/radial, alpha stops → vignettes), `{value}` counters, `repeat`/`yoyo`, `orbit()`. **Not in the DSL (hand-roll, recipes exist or loops are easy):** arc / progress-ring and stroke draw-on, text that changes over time (other than `{value}`), other per-frame curves and jitter, per-letter text animators (one layer per letter), particle emitters, group/parent layers.
|
|
32
|
+
- **Blend modes:** `blendMode: 'add' | 'screen' | 'multiply'` on any layer (glows, light leaks).
|
|
33
|
+
- **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.
|
|
32
34
|
|
|
33
35
|
## Layout sanity (the cheap bugs a screenshot catches)
|
|
34
36
|
|
|
@@ -36,24 +36,25 @@ name, at, duration,
|
|
|
36
36
|
initial: { ...props applied before any keyframe },
|
|
37
37
|
keyframes: [ { at, duration, ease, set | to | from | (from + to), repeat?, yoyo?, repeatDelay? } ],
|
|
38
38
|
filters: [ { type: 'chromaKey', keyColor, threshold, smoothing, spill } | { type: 'custom', name, filter: <Pixi Filter> } ],
|
|
39
|
-
mask: <a layer spec>, maskInverted,
|
|
39
|
+
mask: <a layer spec>, maskInverted, // a mask with no `at` of its own starts and ends with the layer it masks (its keyframes count from that layer's start); an explicit mask `at` is composition time
|
|
40
40
|
filterArea: { x, y, width, height } // in the layer's OWN coordinates; lets blur/glow draw past the layer's bounds
|
|
41
41
|
threeD: true // opt into 2.5D (see below)
|
|
42
|
+
blendMode: 'normal' | 'add' | 'screen' | 'multiply' // add / screen brighten (glows, light leaks: overlapping halos add up); on a composition its children inherit the mode
|
|
42
43
|
```
|
|
43
44
|
|
|
44
45
|
Animatable props (`initial` / keyframes): `x y alpha rotation scale scaleX scaleY pivotX pivotY anchorX anchorY skewX skewY tint width height visible autoAlpha`, plus for shapes their style/geometry, for text `fill`, for audio `volume`, for 3D `z rotationX rotationY`, filter params as `'filters.<name>.<param>'`, three objects as `'three.<obj>.<path>'`.
|
|
45
46
|
|
|
46
|
-
Keyframe kinds: `set` (jump), `to` (animate to), `from` (animate from), `from`+`to`. `repeat` (finite extra plays), `yoyo`, `repeatDelay` loop any of them (endless repeats are not allowed). `ease` = any GSAP ease (`'none'`, `'power2.out'`, `'expo.out'`, `'back.out(1.7)'`, `'elastic.out(1,0.5)'`, `'sine.inOut'`, `'bounce.out'`…). Default ease is linear.
|
|
47
|
+
Keyframe kinds: `set` (jump at `at`; undone when you seek back), `to` (animate to), `from` (animate from), `from`+`to`. `from` / `from+to` show their start value from the layer's start, so a delayed fade-in needs no `initial.alpha`. `repeat` (finite extra plays), `yoyo`, `repeatDelay` loop any of them (endless repeats are not allowed). `ease` = any GSAP ease (`'none'`, `'power2.out'`, `'expo.out'`, `'back.out(1.7)'`, `'elastic.out(1,0.5)'`, `'sine.inOut'`, `'bounce.out'`…). Default ease is linear.
|
|
47
48
|
|
|
48
49
|
## Layer types
|
|
49
50
|
|
|
50
|
-
**text** — `text`, `style` (any PixiJS TextStyle field: `fontSize fontFamily fontWeight fill letterSpacing lineHeight align wordWrap wordWrapWidth stroke dropShadow padding`; expressions OK for numbers), `colorSpace`. Default anchor is **top-left**; use `anchorX/anchorY: 0.5` to centre on `x,y`. A text layer has one animatable number, `value`, printed where the text contains `{value}` (`text: '{value} users'`, `initial: { value: 0 }`, keyframe `to: { value: 2480 }`; `format: { decimals, grouping }`) — that is how counters work. Other content cannot change over time.
|
|
51
|
+
**text** — `text`, `style` (any PixiJS TextStyle field: `fontSize fontFamily fontWeight fill letterSpacing lineHeight align wordWrap wordWrapWidth stroke dropShadow padding`; expressions OK for numbers), `colorSpace`. Default anchor is **top-left**; use `anchorX/anchorY: 0.5` to centre on `x,y`. A text layer has one animatable number, `value`, printed where the text contains `{value}` (`text: '{value} users'`, `initial: { value: 0 }`, keyframe `to: { value: 2480 }`; `format: { decimals, grouping }`) — that is how counters work. Other content cannot change over time. Multi-line text is centre-aligned by default (`style.align: 'left'` to change).
|
|
51
52
|
|
|
52
53
|
**image** — `asset`, `tint`, `colorSpace: 'rgb'|'oklab'|'oklch'`. Default anchor top-left; natural size = `w`,`h`.
|
|
53
54
|
|
|
54
55
|
**video** — `asset`, `loop`, `audio`, `volume`; `initial: { scale: 'cover' }`.
|
|
55
56
|
|
|
56
|
-
**audio** — `asset`, `loop`, `volume`; fade with volume keyframes. **
|
|
57
|
+
**audio** — `asset`, `loop`, `volume`; fade with volume keyframes (`{ at: -2, to: { volume: 0 }, duration: 2 }` fades the last 2 s). **Omit `duration` for a one-shot sound effect: it lasts exactly as long as its clip.** With `loop: true` and no `duration` it lasts until the composition ends. An explicit `duration` longer than the clip goes silent after the clip unless `loop: true`.
|
|
57
58
|
|
|
58
59
|
**shape** — `shape: 'rect' | 'circle' | 'ellipse' | 'line' | 'polygon' | 'path'`:
|
|
59
60
|
|
|
@@ -68,7 +69,7 @@ Keyframe kinds: `set` (jump), `to` (animate to), `from` (animate from), `from`+`
|
|
|
68
69
|
|
|
69
70
|
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.
|
|
70
71
|
|
|
71
|
-
**composition** — `width height duration sequences transitions`. With `threeD: true` it is a **card**: its children are drawn into one texture and move / rotate in depth together. Children use the composition's local coordinates and times. Position/rotate/scale it as a unit; to rotate/scale about its centre set `pivotX/pivotY` to the centre and `x/y` to where that point should sit.
|
|
72
|
+
**composition** — `width height duration sequences transitions`. With `threeD: true` it is a **card**: its children are drawn into one texture (content outside its `width × height` is clipped: size it for a soft shadow too) and move / rotate in depth together. Children use the composition's local coordinates and times. Position/rotate/scale it as a unit; to rotate/scale about its centre set `pivotX/pivotY` to the centre and `x/y` to where that point should sit.
|
|
72
73
|
|
|
73
74
|
**camera** (2.5D) — props in `initial`/keyframes: `x y z lookAtX lookAtY lookAtZ fov` (defaults: centred, looking at the z = 0 plane, `fov` 40, `z` auto = `(H/2)/tan(fov/2)` ≈ 989 at 720p). Never on the camera object itself. An orbit is `orbit()`; by hand: `x = cx + R·sin θ`, `z = R·cos θ`, `R = (H/2)/tan(fov/2)`, `lookAt` = the centre at `z = 0`.
|
|
74
75
|
|
package/ai/reference/pitfalls.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# pixi-effects pitfalls — what actually went wrong
|
|
2
2
|
|
|
3
|
-
Collected by having fresh AI sessions build
|
|
3
|
+
Collected by having fresh AI sessions build pieces from the docs alone: first six (kinetic type, news lower-third, slideshow, 2.5D showcase, three.js title, bar chart), then a portfolio of 16 more (items 32 and up), made by three different models. Each entry is a real stumble. **Status**: `fixed` = the library now handles or warns; `docs` = nothing can catch it, so remember it; `hand-roll` = the DSL has no feature, see the workaround.
|
|
4
4
|
|
|
5
5
|
A good habit that catches most of these: after `init`, read `window.__logs` (see `ai/template.html`) — every pixi-effects warning says what to change.
|
|
6
6
|
|
|
@@ -10,7 +10,7 @@ A good habit that catches most of these: after `init`, read `window.__logs` (see
|
|
|
10
10
|
2. **A layer's own `at` and a transition's `at` are composition time** (seconds from the parent composition's start). Only keyframes are layer-local.
|
|
11
11
|
3. **Layers are hidden, not removed, after `[at, at+duration)`** and keep their last values. Z-order is array order (later = on top). To switch scenes, stack scenes with `at`/`duration`, each starting with an opaque full-screen rect. *(docs)*
|
|
12
12
|
4. **Transitions need overlapping lifespans.** Both layers must be alive for the whole window: scene `i` starts at `i·(L−T)`, the transition's `at` is the next scene's start, total = `n·(L−T)+T`. A scene that ends while the transition is still running leaves a gap. *(docs)*
|
|
13
|
-
5. **Audio
|
|
13
|
+
5. **Audio length.** Omit `duration` on a one-shot sound effect: the layer lasts exactly as long as its clip (pop 0.13 s, click 0.1 s, chime 0.6 s, swoosh 0.55 s; nothing to measure). `loop: true` with no `duration` lasts until the composition ends. An explicit `duration` longer than the clip goes silent after the clip unless `loop: true` (`bgm.mp3` is 6 s) and warns. Volume fades work like any keyframe: `volume: 0` + `{ at: 0, to: { volume: v }, duration: 2 }` + `{ at: -2, to: { volume: 0 }, duration: 2 }` (the last 2 s only; `set` is a jump). *(fixed: volume keyframes used to ramp from the previous keyframe's end, so the music faded across the whole piece)*
|
|
14
14
|
6. **Snap times to the frame grid (`Math.round(t*30)/30`) when two things must stay locked** (a bar and its label). Drive both from one JS easing function with per-frame `set` keyframes rather than mixing a tween with computed positions. *(docs)*
|
|
15
15
|
7. **Pick eases on purpose.** `expo.in` stays tiny until the very end (a "flood" circle never reaches the corners — use a bigger radius and `power2.in`). A zoom transition with `power2.out` looks like a cut because the incoming scene is nearly opaque after 40 %. Default is linear. *(docs)*
|
|
16
16
|
8. **`render()` runs in roughly real time** (6 s of 720p ≈ 6 s). Listen to `movie.on('progress', e => e.progress)` (0–100). *(docs)*
|
|
@@ -20,7 +20,7 @@ A good habit that catches most of these: after `init`, read `window.__logs` (see
|
|
|
20
20
|
9. **Defaults differ by type.** rect/circle/ellipse are centred on `x,y`; text and image are top-left; a composition has no anchor — use `pivotX/pivotY` and set `x,y` to where the pivot should land. For a bar growing from its base use `anchorY: 1` (or `anchorX: 0`). *(docs)*
|
|
21
21
|
10. **Shape geometry may be at the top level or in `initial`** (`width height radius anchorX …`). *(fixed: `initial` used to be silently ignored)*
|
|
22
22
|
11. **Angles are degrees** — `rotation`, `skew*`, `rotationX/Y` — and `rotation: -0.2` is almost no rotation. *(docs)*
|
|
23
|
-
12. **Text**: anchor 0.5 centres the line *box*, not the cap height (
|
|
23
|
+
12. **Text**: anchor 0.5 centres the line *box*, not the cap height (an underline under serif or italic text needs ~`0.9·size` below the centre: `0.46·size` touches the descenders of a g or y). Arial Black is ~0.8·size wide per glyph, monospace ~0.6·size; leave ~7 % for hold-scale. `style` accepts any PixiJS TextStyle field: `letterSpacing`, `wordWrap` + `wordWrapWidth`, `lineHeight`, `stroke`, `dropShadow`, `padding`. A glow (`dropShadow` with `blur`) is clipped unless `padding ≥ 2·blur`. *(docs)*
|
|
24
24
|
13. **`w` / `h` in expressions** are the layer's own size. For text they now describe the styled text (`x: '-w'` scrolls a marquee fully out). *(fixed)*
|
|
25
25
|
14. **Masks live in the parent's coordinate space and do not follow the masked layer.** Reveal by growing the mask's `width` from a fixed edge (`anchorX: 0`); exit by tweening `x` and `width` together; slide the content separately. *(docs)*
|
|
26
26
|
15. **`line` / `polygon` / `path` use plain canvas coordinates** when you give no `x,y` (they used to be drawn around (0,0)); with `x,y` those place the shape's centre. Stroke colour and width go in `initial`. A background pill must be sized from its text (~0.58 × fontSize per glyph + padding): `inspect` does not check text against shapes. *(fixed)*
|
|
@@ -52,3 +52,24 @@ A good habit that catches most of these: after `init`, read `window.__logs` (see
|
|
|
52
52
|
30b. **Unknown option names now warn** (`contactSheet({ cols: 2 })` → did you mean `columns`; `init({ fps })` → `frameRate`). Layer names show up in `inspect` paths, so give layers a `name`. `inspect` ignores faint (alpha < 0.3) layers and text whose x / y is animated (tickers), so a scrolling marquee is not reported as cut off. Pixi may print `[BindGroup] … destroyed while still bound` warnings at `movie.destroy()`: harmless (playback and seeking never warn). *(docs)*
|
|
53
53
|
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)*
|
|
54
54
|
31. **Check audio without rendering by decoding the render**: `const b = await movie.render({format:'mp4'})` then `new OfflineAudioContext(...).decodeAudioData(await b.arrayBuffer())` and measure RMS over time. `movie.audioBuffer` holds the mix. *(docs)*
|
|
55
|
+
|
|
56
|
+
## Round 2 — found by building 16 portfolio pieces (examples/gallery)
|
|
57
|
+
|
|
58
|
+
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.
|
|
59
|
+
33. **A mask starts and ends with the layer it masks** *(fixed)*, so its keyframes count from that layer's start. A mask with an explicit `at` keeps composition time. Before, a mask `at: 0` reveal had finished before a layer that appeared at 8 s, and the layer stayed invisible without any warning.
|
|
60
|
+
34. **`from` and `from+to` show their start value from the LAYER's start**, not from the keyframe's `at` (GSAP behaviour): a delayed fade-in `{ at: 2, from: { alpha: 0 }, to: { alpha: 1 } }` needs no `initial.alpha`. To keep the current value until `at` and then jump, use `set`. *(docs)*
|
|
61
|
+
35. **One `Movie` per page.** Creating a second Movie (for an isolated repro) and destroying it breaks the first (`null.clear`). Reload instead. *(docs)*
|
|
62
|
+
36. **Multi-line text is centre-aligned by default** (`style.align: 'left'` for left-aligned paragraphs). *(docs)*
|
|
63
|
+
37. **A `threeD` composition (a card) clips its children at its own width × height.** Size the card for its soft shadow too. A growing `threeD` shape or text is fine: its texture is resized in place (it used to warn "destroyed while still bound"). *(docs / fixed)*
|
|
64
|
+
38. **Under an orbit the side the camera swings toward becomes the NEAR side**: a "far" card at `z: −200` can be drawn bigger than 1× on that side and run off the frame (`inspect` only checks text). Pull those cards inward / lower `z`, and check a frame at the extreme of the sweep. After `orbit()` ends the default camera returns, so to hold the final pose add a camera layer that starts there. Near particles balloon under a dolly zoom: cap particle `z` at about +150, or one becomes a fake full stop. *(docs)*
|
|
65
|
+
39. **`fillGradient` stops span the whole unmasked shape**, not the visible part: under a mask, place the stops for the visible fraction (e.g. 0.64→1). A translucent shape over near-black reads as grey: put the alpha in the gradient stops. *(docs)*
|
|
66
|
+
40. **Rotate a polygon / path about a chosen point** by wrapping it in a `composition` with `pivotX/pivotY` (its centre is otherwise the bounds centre). *(docs)*
|
|
67
|
+
41. **Glow a whole set of layers** by putting them in one `composition` with a blur filter and `filterArea` (only a composition carries a filter for its children): wide enough for the blur, and the set is built once, so the extra copy is just a second group. *(docs)*
|
|
68
|
+
42. **Transition looks:** the default `dissolve` reads as a hard black-and-white camouflage and `scale: 220` as static; `scale: 90, smoothing: 0.5` is a soft film dissolve. The default edge softness of `iris` (0.02) and `wipe` blurs flat graphics at 720p: `smoothing: 0.004`–`0.01` gives a clean edge. *(docs)*
|
|
69
|
+
43. **Layers built in a loop need their z-order planned**: anything pushed to the array after the loop is drawn over what the loop made (rings crossed over a header). Keyframes on different properties of one layer may overlap in time. Keep a subject in the upper ~60 % of a source image if a caption will sit under it (`inspect` does not look at picture content). *(docs)*
|
|
70
|
+
44. **`{value}` has no number padding**: a clock or timecode cannot show `:05`. Keep the value in a range that always has the same digit count, or split digits into two counters. *(hand-roll)*
|
|
71
|
+
45. **The same mistake in a loop warns once**: the warning "keyframe starts after the layer ends" is summarised after three layers. The usual cause is an off-by-one (`f <= frames` instead of `f < frames`) that puts the last sample exactly at the layer's end. *(fixed)*
|
|
72
|
+
46. **Still not in the DSL (hand-roll; round-two notes have workarounds):** arc / progress-ring primitive and stroke draw-on / trim path (use a growing rect mask for a one-direction reveal, tick marks, or pre-drawn arcs shown one per frame), text content that changes over time (only `{value}`), per-frame jitter (film grain = seeded `set` keyframes on an oversized image), particle emitters, per-letter / per-line text split (measure with `canvas.measureText` using Pixi's font string), shared masks, `zIndex`.
|
|
73
|
+
46b. **Blend modes exist now** (`blendMode: 'add' | 'screen' | 'multiply'` on any layer, threeD layers and compositions included; a composition's children inherit it). Additive white cores no longer read grey on a coloured background. *(fixed)*
|
|
74
|
+
47. **Browser tooling:** the `agent-browser` daemon is shared, so a `contactSheet` can come back from another session: after saving an image, look at it and confirm it is YOUR piece. In zsh a variable holding `agent-browser --session x` is not split into words (use a small wrapper script). `inspect` now clips overlaps to the visible area and ignores the two scenes of a running transition; it still does not compare text with picture content or shapes.
|
|
75
|
+
48. **A composition's `alpha` is applied to each child separately, not to the flattened group** (measured: two overlapping opaque circles in a composition at `alpha: 0.5` show the lower one through the upper one in the overlap). Fading a group that has overlapping children therefore shows the overlap; fade the children themselves, or keep overlapping parts out of the group. Expressions have no way to read a sibling's size: measure text with `canvas.measureText` (the font string Pixi uses) and compute pill widths in JS. *(docs)*
|
package/ai/template.html
CHANGED
|
@@ -33,9 +33,9 @@
|
|
|
33
33
|
"gsap": "https://esm.sh/gsap@3.12.5",
|
|
34
34
|
"gsap/PixiPlugin": "https://esm.sh/gsap@3.12.5/PixiPlugin",
|
|
35
35
|
"mediabunny": "https://esm.sh/mediabunny",
|
|
36
|
-
"pixi-effects": "https://cdn.jsdelivr.net/npm/pixi-effects@0.
|
|
37
|
-
"pixi-effects/controller": "https://cdn.jsdelivr.net/npm/pixi-effects@0.
|
|
38
|
-
"pixi-effects/three": "https://cdn.jsdelivr.net/npm/pixi-effects@0.
|
|
36
|
+
"pixi-effects": "https://cdn.jsdelivr.net/npm/pixi-effects@0.4.0/dist/index.js",
|
|
37
|
+
"pixi-effects/controller": "https://cdn.jsdelivr.net/npm/pixi-effects@0.4.0/dist/Controller.js",
|
|
38
|
+
"pixi-effects/three": "https://cdn.jsdelivr.net/npm/pixi-effects@0.4.0/dist/three.js",
|
|
39
39
|
"three": "https://esm.sh/three@0.178.0"
|
|
40
40
|
}
|
|
41
41
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Filter, Container, RenderTexture, Matrix } from 'pixi.js';
|
|
2
|
-
import { S as SequenceSpec, C as CompositionShape, t as AudioDescriptor } from './types-
|
|
2
|
+
import { S as SequenceSpec, C as CompositionShape, t as AudioDescriptor } from './types-CRE9WKd4.cjs';
|
|
3
3
|
import { gsap } from 'gsap';
|
|
4
4
|
|
|
5
5
|
/**
|
|
@@ -86,6 +86,12 @@ declare abstract class Sequence {
|
|
|
86
86
|
* project their `threeD` layers; everything else has nothing to do.
|
|
87
87
|
*/
|
|
88
88
|
updateSpace(_t: number, _host: SpaceHost): void;
|
|
89
|
+
/**
|
|
90
|
+
* Runs once per rendered frame, after the timeline seek and before the cull. Layers whose drawn
|
|
91
|
+
* geometry follows tweened state (shapes) redraw here, so the culler measures where the layer is NOW
|
|
92
|
+
* and not where it was drawn at the previous render.
|
|
93
|
+
*/
|
|
94
|
+
syncFrame(): void;
|
|
89
95
|
destroy(): void;
|
|
90
96
|
}
|
|
91
97
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Filter, Container, RenderTexture, Matrix } from 'pixi.js';
|
|
2
|
-
import { S as SequenceSpec, C as CompositionShape, t as AudioDescriptor } from './types-
|
|
2
|
+
import { S as SequenceSpec, C as CompositionShape, t as AudioDescriptor } from './types-CRE9WKd4.js';
|
|
3
3
|
import { gsap } from 'gsap';
|
|
4
4
|
|
|
5
5
|
/**
|
|
@@ -86,6 +86,12 @@ declare abstract class Sequence {
|
|
|
86
86
|
* project their `threeD` layers; everything else has nothing to do.
|
|
87
87
|
*/
|
|
88
88
|
updateSpace(_t: number, _host: SpaceHost): void;
|
|
89
|
+
/**
|
|
90
|
+
* Runs once per rendered frame, after the timeline seek and before the cull. Layers whose drawn
|
|
91
|
+
* geometry follows tweened state (shapes) redraw here, so the culler measures where the layer is NOW
|
|
92
|
+
* and not where it was drawn at the previous render.
|
|
93
|
+
*/
|
|
94
|
+
syncFrame(): void;
|
|
89
95
|
destroy(): void;
|
|
90
96
|
}
|
|
91
97
|
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var chunkQGBOC5YL_cjs = require('./chunk-QGBOC5YL.cjs');
|
|
4
|
+
require('./chunk-6BQ6IEW2.cjs');
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
Object.defineProperty(exports, "CompositionSequence", {
|
|
9
|
+
enumerable: true,
|
|
10
|
+
get: function () { return chunkQGBOC5YL_cjs.CompositionSequence; }
|
|
11
|
+
});
|
|
12
|
+
//# sourceMappingURL=Composition-D7RCAGEA.cjs.map
|
|
13
|
+
//# sourceMappingURL=Composition-D7RCAGEA.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":[],"names":[],"mappings":"","file":"Composition-
|
|
1
|
+
{"version":3,"sources":[],"names":[],"mappings":"","file":"Composition-D7RCAGEA.cjs"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":[],"names":[],"mappings":"","file":"Composition-
|
|
1
|
+
{"version":3,"sources":[],"names":[],"mappings":"","file":"Composition-RGTRZ2JX.js"}
|
package/dist/Controller.d.cts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { M as Movie } from './Movie-
|
|
1
|
+
import { M as Movie } from './Movie-CghFXprP.cjs';
|
|
2
2
|
import 'pixi.js';
|
|
3
3
|
import 'gsap';
|
|
4
|
-
import './types-
|
|
4
|
+
import './types-CRE9WKd4.cjs';
|
|
5
5
|
|
|
6
6
|
declare function formatTime(seconds: number): string;
|
|
7
7
|
declare function frameToPercent(frame: number, totalFrames: number): number;
|
package/dist/Controller.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { M as Movie } from './Movie-
|
|
1
|
+
import { M as Movie } from './Movie-DRZtIGU7.js';
|
|
2
2
|
import 'pixi.js';
|
|
3
3
|
import 'gsap';
|
|
4
|
-
import './types-
|
|
4
|
+
import './types-CRE9WKd4.js';
|
|
5
5
|
|
|
6
6
|
declare function formatTime(seconds: number): string;
|
|
7
7
|
declare function frameToPercent(frame: number, totalFrames: number): number;
|
|
@@ -798,6 +798,14 @@ var Sequence = class {
|
|
|
798
798
|
*/
|
|
799
799
|
updateSpace(_t, _host) {
|
|
800
800
|
}
|
|
801
|
+
/**
|
|
802
|
+
* Runs once per rendered frame, after the timeline seek and before the cull. Layers whose drawn
|
|
803
|
+
* geometry follows tweened state (shapes) redraw here, so the culler measures where the layer is NOW
|
|
804
|
+
* and not where it was drawn at the previous render.
|
|
805
|
+
*/
|
|
806
|
+
syncFrame() {
|
|
807
|
+
this.maskSequence?.syncFrame();
|
|
808
|
+
}
|
|
801
809
|
destroy() {
|
|
802
810
|
this.maskSequence?.destroy();
|
|
803
811
|
this.maskSequence = null;
|
|
@@ -834,11 +842,16 @@ function lintTiming(spec, parentDuration, warn = defaultWarn) {
|
|
|
834
842
|
const kat = kf.at ?? 0;
|
|
835
843
|
if (kat >= duration) {
|
|
836
844
|
warn(
|
|
837
|
-
`pixi-effects: ${who}: keyframes[${i}] starts at ${kat}s, after the layer ends (duration ${duration}s), so it never plays. Keyframe \`at\` is measured from the start of this layer, not from the start of the composition
|
|
845
|
+
`pixi-effects: ${who}: keyframes[${i}] starts at ${kat}s, after the layer ends (duration ${duration}s), so it never plays. Keyframe \`at\` is measured from the start of this layer, not from the start of the composition.`,
|
|
846
|
+
"late-keyframe"
|
|
838
847
|
);
|
|
839
848
|
}
|
|
840
849
|
});
|
|
841
850
|
}
|
|
851
|
+
function summarizeWarnings(messages, shown = 3) {
|
|
852
|
+
if (messages.length <= shown) return messages;
|
|
853
|
+
return [...messages.slice(0, shown), `pixi-effects: ${messages.length - shown} more layers with the same problem (the same loop or helper is probably building them): fix the ones above and these go away.`];
|
|
854
|
+
}
|
|
842
855
|
function lintText(spec, warn = defaultWarn) {
|
|
843
856
|
if (spec.type !== "text") return;
|
|
844
857
|
if (collectPropKeys(spec).has("value") && !(spec.text ?? "").includes("{value}")) {
|
|
@@ -1013,6 +1026,27 @@ var CameraSequence = class extends Sequence {
|
|
|
1013
1026
|
}
|
|
1014
1027
|
};
|
|
1015
1028
|
|
|
1029
|
+
// src/core/revertibleSet.ts
|
|
1030
|
+
function revertibleSet(timeline, at, read, write, value) {
|
|
1031
|
+
let p = 0;
|
|
1032
|
+
let before;
|
|
1033
|
+
const proxy = {
|
|
1034
|
+
get p() {
|
|
1035
|
+
return p;
|
|
1036
|
+
},
|
|
1037
|
+
set p(next) {
|
|
1038
|
+
if (next >= 1 && p < 1) {
|
|
1039
|
+
before = read();
|
|
1040
|
+
write(value);
|
|
1041
|
+
} else if (next < 1 && p >= 1) {
|
|
1042
|
+
write(before);
|
|
1043
|
+
}
|
|
1044
|
+
p = next;
|
|
1045
|
+
}
|
|
1046
|
+
};
|
|
1047
|
+
timeline.set(proxy, { p: 1 }, at);
|
|
1048
|
+
}
|
|
1049
|
+
|
|
1016
1050
|
// src/expr/colorInterp.ts
|
|
1017
1051
|
function buildColorInterp(from, to, space) {
|
|
1018
1052
|
const fromRGBA = parseToRGBA(from);
|
|
@@ -1203,9 +1237,10 @@ var ImageSequence = class extends Sequence {
|
|
|
1203
1237
|
const toTint = kf.to?.tint;
|
|
1204
1238
|
const setTint = kf.set?.tint;
|
|
1205
1239
|
if (setTint !== void 0) {
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1240
|
+
const sprite = this.target;
|
|
1241
|
+
revertibleSet(timeline, at, () => sprite.tint, (c) => {
|
|
1242
|
+
sprite.tint = c;
|
|
1243
|
+
}, setTint);
|
|
1209
1244
|
}
|
|
1210
1245
|
if (toTint !== void 0) {
|
|
1211
1246
|
tweenColor(
|
|
@@ -1252,7 +1287,21 @@ function stripKeysInKeyframe(kf, keys) {
|
|
|
1252
1287
|
from: stripKeys(kf.from, keys)
|
|
1253
1288
|
};
|
|
1254
1289
|
}
|
|
1255
|
-
var STYLE_OPAQUE_KEYS = [
|
|
1290
|
+
var STYLE_OPAQUE_KEYS = [
|
|
1291
|
+
"fontFamily",
|
|
1292
|
+
"fill",
|
|
1293
|
+
"align",
|
|
1294
|
+
"fontStyle",
|
|
1295
|
+
"fontWeight",
|
|
1296
|
+
"fontVariant",
|
|
1297
|
+
"textBaseline",
|
|
1298
|
+
"whiteSpace",
|
|
1299
|
+
"lineJoin",
|
|
1300
|
+
"join",
|
|
1301
|
+
"cap",
|
|
1302
|
+
"color",
|
|
1303
|
+
"stroke"
|
|
1304
|
+
];
|
|
1256
1305
|
function formatValue(v, f) {
|
|
1257
1306
|
const fixed = v.toFixed(Math.max(0, Math.floor(f?.decimals ?? 0)));
|
|
1258
1307
|
if (!f?.grouping) return fixed;
|
|
@@ -1367,10 +1416,10 @@ function bindValueKeyframes(timeline, state, refresh, keyframes, parentDuration,
|
|
|
1367
1416
|
const loop = loopVars(kf);
|
|
1368
1417
|
if (setV !== void 0) {
|
|
1369
1418
|
const v = resolveNumber(setV, scope);
|
|
1370
|
-
timeline.
|
|
1371
|
-
state.value =
|
|
1419
|
+
revertibleSet(timeline, at, () => state.value, (n) => {
|
|
1420
|
+
state.value = n;
|
|
1372
1421
|
refresh();
|
|
1373
|
-
},
|
|
1422
|
+
}, v);
|
|
1374
1423
|
}
|
|
1375
1424
|
if (toV !== void 0 && fromV !== void 0) {
|
|
1376
1425
|
timeline.fromTo(
|
|
@@ -1399,10 +1448,10 @@ function bindFillKeyframes(timeline, text, keyframes, parentDuration, offset, co
|
|
|
1399
1448
|
const fromFill = pickFill(kf.from);
|
|
1400
1449
|
const toFill = pickFill(kf.to);
|
|
1401
1450
|
if (setFill !== void 0) {
|
|
1402
|
-
timeline.
|
|
1403
|
-
state.fill =
|
|
1451
|
+
revertibleSet(timeline, at, () => state.fill, (c) => {
|
|
1452
|
+
state.fill = c;
|
|
1404
1453
|
writeFill();
|
|
1405
|
-
},
|
|
1454
|
+
}, setFill);
|
|
1406
1455
|
}
|
|
1407
1456
|
if (toFill !== void 0) {
|
|
1408
1457
|
tweenColor(
|
|
@@ -1433,7 +1482,8 @@ var AudioSequence = class extends Sequence {
|
|
|
1433
1482
|
const data = await pixi_js.Assets.get(this.spec.asset);
|
|
1434
1483
|
this._audioBuffer = data.audioBuffer;
|
|
1435
1484
|
if (this.duration === void 0) {
|
|
1436
|
-
|
|
1485
|
+
const rest = Math.max(0, (this.parent?.duration ?? this.root.duration) - this.at);
|
|
1486
|
+
this.duration = this.spec.duration ?? (this.spec.loop ? rest : data.duration) ?? this.root.duration;
|
|
1437
1487
|
}
|
|
1438
1488
|
if (!this.spec.loop && data.duration !== void 0 && this.duration > data.duration + 0.05) {
|
|
1439
1489
|
console.warn(
|
|
@@ -1449,21 +1499,32 @@ var AudioSequence = class extends Sequence {
|
|
|
1449
1499
|
const initialVolume = this.spec.volume ?? 1;
|
|
1450
1500
|
const volumeKeyframes = [];
|
|
1451
1501
|
const dur = this.duration;
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1502
|
+
const t0 = baseTime + this.at;
|
|
1503
|
+
const keyframes = (this.spec.keyframes ?? []).map((raw) => normalizeKeyframe(raw, dur)).filter((kf) => [kf.set, kf.to, kf.from].some((p) => p && "volume" in p)).sort((x, y) => x.at - y.at);
|
|
1504
|
+
let current = initialVolume;
|
|
1505
|
+
const point = (time, value) => {
|
|
1506
|
+
volumeKeyframes.push({ time, value });
|
|
1507
|
+
};
|
|
1508
|
+
for (const kf of keyframes) {
|
|
1509
|
+
const start = t0 + kf.at;
|
|
1510
|
+
const end = start + kf.duration;
|
|
1511
|
+
const set = kf.set?.volume;
|
|
1512
|
+
const to = kf.to?.volume;
|
|
1513
|
+
const from = kf.from?.volume;
|
|
1514
|
+
point(start, current);
|
|
1515
|
+
if (kf.kind === "set" && set !== void 0) {
|
|
1516
|
+
point(start, set);
|
|
1517
|
+
current = set;
|
|
1518
|
+
} else if (kf.kind === "to" && to !== void 0) {
|
|
1519
|
+
point(end, to);
|
|
1520
|
+
current = to;
|
|
1521
|
+
} else if (kf.kind === "fromTo" && to !== void 0) {
|
|
1522
|
+
point(start, from ?? current);
|
|
1523
|
+
point(end, to);
|
|
1524
|
+
current = to;
|
|
1525
|
+
} else if (kf.kind === "from" && from !== void 0) {
|
|
1526
|
+
point(start, from);
|
|
1527
|
+
point(end, current);
|
|
1467
1528
|
}
|
|
1468
1529
|
}
|
|
1469
1530
|
out.push({
|
|
@@ -1761,12 +1822,32 @@ var ShapeSequence = class extends Sequence {
|
|
|
1761
1822
|
if (init.y === void 0) graphics.y = cy;
|
|
1762
1823
|
}
|
|
1763
1824
|
graphics.onRender = () => {
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
|
|
1825
|
+
if (this._fresh) {
|
|
1826
|
+
this._fresh = false;
|
|
1827
|
+
return;
|
|
1828
|
+
}
|
|
1829
|
+
this._redraw();
|
|
1767
1830
|
};
|
|
1768
1831
|
this.buildFilters();
|
|
1769
1832
|
}
|
|
1833
|
+
_fresh = false;
|
|
1834
|
+
_redraw() {
|
|
1835
|
+
const graphics = this.target;
|
|
1836
|
+
graphics.clear();
|
|
1837
|
+
this._drawGeometry(graphics);
|
|
1838
|
+
applyState(graphics, this._state);
|
|
1839
|
+
}
|
|
1840
|
+
/**
|
|
1841
|
+
* Draw from the tweened `_state` before the culler runs. The culler measures the geometry as last
|
|
1842
|
+
* drawn; a layer drawn only inside `onRender` is culled on stale bounds (a rect growing from
|
|
1843
|
+
* `width: 0` at the left edge was skipped after a jump seek, and one frame late in playback).
|
|
1844
|
+
*/
|
|
1845
|
+
syncFrame() {
|
|
1846
|
+
super.syncFrame();
|
|
1847
|
+
if (!this.target?.renderable) return;
|
|
1848
|
+
this._redraw();
|
|
1849
|
+
this._fresh = true;
|
|
1850
|
+
}
|
|
1770
1851
|
bindTimeline(timeline, offset = 0) {
|
|
1771
1852
|
if (!this.target) return;
|
|
1772
1853
|
const scope = this.scope();
|
|
@@ -1930,9 +2011,10 @@ function bindLiveKeyframes(timeline, state, liveKeys, keyframes, parentDuration,
|
|
|
1930
2011
|
if (live) {
|
|
1931
2012
|
for (const [k, v] of Object.entries(live)) {
|
|
1932
2013
|
const resolved = resolveLiveValue(k, v, scope);
|
|
1933
|
-
|
|
1934
|
-
|
|
1935
|
-
|
|
2014
|
+
const live2 = state;
|
|
2015
|
+
revertibleSet(timeline, at, () => live2[k], (v2) => {
|
|
2016
|
+
live2[k] = v2;
|
|
2017
|
+
}, resolved);
|
|
1936
2018
|
}
|
|
1937
2019
|
}
|
|
1938
2020
|
}
|
|
@@ -1991,7 +2073,7 @@ function numOrZero(v, scope) {
|
|
|
1991
2073
|
var _CompositionSequence = null;
|
|
1992
2074
|
async function getCompositionSequence() {
|
|
1993
2075
|
if (!_CompositionSequence) {
|
|
1994
|
-
_CompositionSequence = (await import('./Composition-
|
|
2076
|
+
_CompositionSequence = (await import('./Composition-D7RCAGEA.cjs')).CompositionSequence;
|
|
1995
2077
|
}
|
|
1996
2078
|
return _CompositionSequence;
|
|
1997
2079
|
}
|
|
@@ -2045,5 +2127,6 @@ exports.lintTiming = lintTiming;
|
|
|
2045
2127
|
exports.projectLayer = projectLayer;
|
|
2046
2128
|
exports.registerSequenceType = registerSequenceType;
|
|
2047
2129
|
exports.resolveAt = resolveAt;
|
|
2048
|
-
|
|
2049
|
-
//# sourceMappingURL=chunk-
|
|
2130
|
+
exports.summarizeWarnings = summarizeWarnings;
|
|
2131
|
+
//# sourceMappingURL=chunk-6BQ6IEW2.cjs.map
|
|
2132
|
+
//# sourceMappingURL=chunk-6BQ6IEW2.cjs.map
|