pixi-effects 0.45.0 → 0.46.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 +5 -0
- package/README.md +5 -5
- package/ai/chat-template.html +3 -3
- package/dist/Composition-NSY7A32Y.js +7 -0
- package/dist/{Composition-YGBQX3HZ.js.map → Composition-NSY7A32Y.js.map} +1 -1
- package/dist/Composition-PME3P5LP.cjs +16 -0
- package/dist/{Composition-N6BOROJ2.cjs.map → Composition-PME3P5LP.cjs.map} +1 -1
- package/dist/Controller.d.cts +2 -2
- package/dist/Controller.d.ts +2 -2
- package/dist/{Movie-C-d4zN-N.d.cts → Movie-BqrAWN0g.d.cts} +6 -1
- package/dist/{Movie-lMSS9JwX.d.ts → Movie-sV8nJr_f.d.ts} +6 -1
- package/dist/{Spans-XTIWYSNO.cjs → Spans-2TZCNREQ.cjs} +9 -9
- package/dist/{Spans-XTIWYSNO.cjs.map → Spans-2TZCNREQ.cjs.map} +1 -1
- package/dist/{Spans-6L2ZVECX.js → Spans-TESJPQKX.js} +4 -4
- package/dist/{Spans-6L2ZVECX.js.map → Spans-TESJPQKX.js.map} +1 -1
- package/dist/{chunk-Z6JQO7GX.js → chunk-2E74SP63.js} +47 -13
- package/dist/chunk-2E74SP63.js.map +1 -0
- package/dist/{chunk-3IO5RWE4.cjs → chunk-7M63Y6MO.cjs} +85 -51
- package/dist/chunk-7M63Y6MO.cjs.map +1 -0
- package/dist/{chunk-3HZV3UHU.cjs → chunk-DKCMJILW.cjs} +67 -65
- package/dist/chunk-DKCMJILW.cjs.map +1 -0
- package/dist/{chunk-4QVQ5UJJ.js → chunk-DM2OLQOJ.js} +5 -6
- package/dist/chunk-DM2OLQOJ.js.map +1 -0
- package/dist/{chunk-ICI6ACHY.js → chunk-FPV3NPYC.js} +8 -6
- package/dist/chunk-FPV3NPYC.js.map +1 -0
- package/dist/{chunk-X62H3RBG.cjs → chunk-LALEBV63.cjs} +5 -6
- package/dist/chunk-LALEBV63.cjs.map +1 -0
- package/dist/index.cjs +33 -33
- package/dist/index.d.cts +3 -3
- package/dist/index.d.ts +3 -3
- package/dist/index.js +3 -3
- package/dist/loader.css +1 -1
- package/dist/present.cjs +3 -3
- package/dist/present.d.cts +2 -2
- package/dist/present.d.ts +2 -2
- package/dist/present.js +3 -3
- package/dist/three.cjs +194 -15
- package/dist/three.cjs.map +1 -1
- package/dist/three.d.cts +75 -4
- package/dist/three.d.ts +75 -4
- package/dist/three.js +191 -13
- package/dist/three.js.map +1 -1
- package/dist/{types-C5xo0fzb.d.ts → types-CTSrPvY3.d.cts} +64 -58
- package/dist/{types-C5xo0fzb.d.cts → types-CTSrPvY3.d.ts} +64 -58
- package/docs/api.md +1 -0
- package/docs/dsl.md +24 -0
- package/llms-full.txt +61 -2
- package/package.json +1 -1
- package/skills/pixi-effects/SKILL.md +1 -1
- package/skills/pixi-effects/reference/cheatsheet.md +1 -1
- package/skills/pixi-effects/reference/recipes.md +34 -0
- package/skills/pixi-effects/template.html +6 -6
- package/dist/Composition-N6BOROJ2.cjs +0 -16
- package/dist/Composition-YGBQX3HZ.js +0 -7
- package/dist/chunk-3HZV3UHU.cjs.map +0 -1
- package/dist/chunk-3IO5RWE4.cjs.map +0 -1
- package/dist/chunk-4QVQ5UJJ.js.map +0 -1
- package/dist/chunk-ICI6ACHY.js.map +0 -1
- package/dist/chunk-X62H3RBG.cjs.map +0 -1
- package/dist/chunk-Z6JQO7GX.js.map +0 -1
|
@@ -1,4 +1,61 @@
|
|
|
1
|
-
import
|
|
1
|
+
import * as pixi_js from 'pixi.js';
|
|
2
|
+
import { Container, TextureSource } from 'pixi.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Resolves a routed keyframe path to a concrete tween target.
|
|
6
|
+
* `path` is everything after the registered prefix + dot (for
|
|
7
|
+
* `three.cube.rotation.y` under prefix `three`, path = `cube.rotation.y`).
|
|
8
|
+
* Return null to skip the key (the router is expected to have warned).
|
|
9
|
+
*/
|
|
10
|
+
type PathRouter = (path: string) => {
|
|
11
|
+
target: object;
|
|
12
|
+
prop: string;
|
|
13
|
+
} | null;
|
|
14
|
+
type PathRouters = Record<string, PathRouter>;
|
|
15
|
+
|
|
16
|
+
/** Variables available to expression evaluation. */
|
|
17
|
+
interface Scope {
|
|
18
|
+
w: number;
|
|
19
|
+
h: number;
|
|
20
|
+
W: number;
|
|
21
|
+
H: number;
|
|
22
|
+
GW: number;
|
|
23
|
+
GH: number;
|
|
24
|
+
contain: number;
|
|
25
|
+
cover: number;
|
|
26
|
+
t: number;
|
|
27
|
+
d: number;
|
|
28
|
+
T: number;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
declare abstract class Sequence {
|
|
32
|
+
spec: SequenceSpec;
|
|
33
|
+
parent: CompositionShape | null;
|
|
34
|
+
root: CompositionShape;
|
|
35
|
+
target: Container | null;
|
|
36
|
+
intrinsicWidth: number;
|
|
37
|
+
intrinsicHeight: number;
|
|
38
|
+
at: number;
|
|
39
|
+
duration: number | undefined;
|
|
40
|
+
constructor(spec: SequenceSpec, parent: CompositionShape | null, root: CompositionShape);
|
|
41
|
+
abstract build(): Promise<void>;
|
|
42
|
+
buildFilters(): void;
|
|
43
|
+
scope(): Scope;
|
|
44
|
+
/**
|
|
45
|
+
* Prefix routers contributed to the keyframe pipeline (e.g. `three.` in
|
|
46
|
+
* pixi-effects/three). Base sequences route nothing extra.
|
|
47
|
+
*/
|
|
48
|
+
protected pathRouters(): PathRouters;
|
|
49
|
+
/**
|
|
50
|
+
* The `initial` and `keyframes` that go onto the display object. A layer whose `time` runs a clock (video, composition) takes `time`
|
|
51
|
+
* out of them: it is not a property of the display object.
|
|
52
|
+
*/
|
|
53
|
+
protected displayProps(): {
|
|
54
|
+
initial: SequenceSpec['initial'];
|
|
55
|
+
keyframes: SequenceSpec['keyframes'];
|
|
56
|
+
};
|
|
57
|
+
destroy(): void;
|
|
58
|
+
}
|
|
2
59
|
|
|
3
60
|
/** What an author writes in `composition.stops`: a time in seconds, or an object. */
|
|
4
61
|
interface StopSpec {
|
|
@@ -120,62 +177,6 @@ interface SharedGpu {
|
|
|
120
177
|
adopt(source: TextureSource, texture: unknown): void;
|
|
121
178
|
}
|
|
122
179
|
|
|
123
|
-
/**
|
|
124
|
-
* Resolves a routed keyframe path to a concrete tween target.
|
|
125
|
-
* `path` is everything after the registered prefix + dot (for
|
|
126
|
-
* `three.cube.rotation.y` under prefix `three`, path = `cube.rotation.y`).
|
|
127
|
-
* Return null to skip the key (the router is expected to have warned).
|
|
128
|
-
*/
|
|
129
|
-
type PathRouter = (path: string) => {
|
|
130
|
-
target: object;
|
|
131
|
-
prop: string;
|
|
132
|
-
} | null;
|
|
133
|
-
type PathRouters = Record<string, PathRouter>;
|
|
134
|
-
|
|
135
|
-
/** Variables available to expression evaluation. */
|
|
136
|
-
interface Scope {
|
|
137
|
-
w: number;
|
|
138
|
-
h: number;
|
|
139
|
-
W: number;
|
|
140
|
-
H: number;
|
|
141
|
-
GW: number;
|
|
142
|
-
GH: number;
|
|
143
|
-
contain: number;
|
|
144
|
-
cover: number;
|
|
145
|
-
t: number;
|
|
146
|
-
d: number;
|
|
147
|
-
T: number;
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
declare abstract class Sequence {
|
|
151
|
-
spec: SequenceSpec;
|
|
152
|
-
parent: CompositionShape | null;
|
|
153
|
-
root: CompositionShape;
|
|
154
|
-
target: Container | null;
|
|
155
|
-
intrinsicWidth: number;
|
|
156
|
-
intrinsicHeight: number;
|
|
157
|
-
at: number;
|
|
158
|
-
duration: number | undefined;
|
|
159
|
-
constructor(spec: SequenceSpec, parent: CompositionShape | null, root: CompositionShape);
|
|
160
|
-
abstract build(): Promise<void>;
|
|
161
|
-
buildFilters(): void;
|
|
162
|
-
scope(): Scope;
|
|
163
|
-
/**
|
|
164
|
-
* Prefix routers contributed to the keyframe pipeline (e.g. `three.` in
|
|
165
|
-
* pixi-effects/three). Base sequences route nothing extra.
|
|
166
|
-
*/
|
|
167
|
-
protected pathRouters(): PathRouters;
|
|
168
|
-
/**
|
|
169
|
-
* The `initial` and `keyframes` that go onto the display object. A layer whose `time` runs a clock (video, composition) takes `time`
|
|
170
|
-
* out of them: it is not a property of the display object.
|
|
171
|
-
*/
|
|
172
|
-
protected displayProps(): {
|
|
173
|
-
initial: SequenceSpec['initial'];
|
|
174
|
-
keyframes: SequenceSpec['keyframes'];
|
|
175
|
-
};
|
|
176
|
-
destroy(): void;
|
|
177
|
-
}
|
|
178
|
-
|
|
179
180
|
/**
|
|
180
181
|
* Keeping a layer's expensive state across a rebuild (`setVars`, `resize`, `rebuild`): the new tree is built while the old one still plays, and a layer that cannot be told apart from
|
|
181
182
|
* one of the old tree (same name, same setup, same size and options) may take over that layer's scene and renderer instead of building its own. Today only the `three` layer does:
|
|
@@ -949,6 +950,11 @@ interface CompositionShape {
|
|
|
949
950
|
gpu?: SharedGpu;
|
|
950
951
|
/** The layers of the tree that a rebuild replaces, that a new layer of the same kind may take the state of instead of building its own (`three` layers: see core/reuse.ts). Root shape only. */
|
|
951
952
|
reuse?: ReusePool;
|
|
953
|
+
/** A layer of this composition by its `name`, among those already built (the ones listed before the asker): a `three` layer shows one of them on a mesh (`threeLayerTexture`). */
|
|
954
|
+
findLayer?: (name: string) => Sequence | null;
|
|
955
|
+
layerNames?: () => string[];
|
|
956
|
+
/** PixiJS's renderer, on the root shape only: what draws a layer into a texture for `threeLayerTexture`. */
|
|
957
|
+
pixiRenderer?: pixi_js.Renderer;
|
|
952
958
|
}
|
|
953
959
|
/** The composition's camera and the lights alive at one moment (what its `camera` and `light` layers say, after their keyframes). */
|
|
954
960
|
interface SpaceNow {
|
|
@@ -957,4 +963,4 @@ interface SpaceNow {
|
|
|
957
963
|
fog: FogState | null;
|
|
958
964
|
}
|
|
959
965
|
|
|
960
|
-
export { type SfxPreset as $, type AssetSpec as A, type BlendModeName as B, type CompositionSpec as C, type DipTransition as D, type EllipseShapeSpec as E, type FilterSpec as F, type GradientSpec as G, type MaskRefSpec as H, type ImageSequenceSpec as I, type MatteRefSpec as J, type Keyframe as K, type LightSequenceSpec as L, type MusicOptions as M, type MusicDrum as N, type MusicDrums as O, type Props as P, type MusicInstrument as Q, type MusicLevel as R, type SequenceSpec as S, type TransitionSpec as T, type MusicTrack as U, type NullSequenceSpec as V, type PathShapeSpec as W, type PolygonShapeSpec as X, type RectShapeSpec as Y, type SfxKnobs as Z, type SfxOptions as _, type Stop as a, type SfxVoice as a0, type ShaderSequenceSpec as a1, type ShapeSequenceSpec as a2, type SlideTransition as a3, type StopSpec as a4, type TextSpan as a5, type TransitionCommon as a6, type VideoSequenceSpec as a7, type WipeTransition as a8, type ZoomTransition as a9,
|
|
966
|
+
export { type SfxPreset as $, type AssetSpec as A, type BlendModeName as B, type CompositionSpec as C, type DipTransition as D, type EllipseShapeSpec as E, type FilterSpec as F, type GradientSpec as G, type MaskRefSpec as H, type ImageSequenceSpec as I, type MatteRefSpec as J, type Keyframe as K, type LightSequenceSpec as L, type MusicOptions as M, type MusicDrum as N, type MusicDrums as O, type Props as P, type MusicInstrument as Q, type MusicLevel as R, type SequenceSpec as S, type TransitionSpec as T, type MusicTrack as U, type NullSequenceSpec as V, type PathShapeSpec as W, type PolygonShapeSpec as X, type RectShapeSpec as Y, type SfxKnobs as Z, type SfxOptions as _, type Stop as a, type SfxVoice as a0, type ShaderSequenceSpec as a1, type ShapeSequenceSpec as a2, type SlideTransition as a3, type StopSpec as a4, type TextSpan as a5, type TransitionCommon as a6, type VideoSequenceSpec as a7, type WipeTransition as a8, type ZoomTransition as a9, Sequence as b, type SpaceNow as c, type SequenceCommon as d, type PropValue as e, type PathRouters as f, type CompositionShape as g, type CameraSequenceSpec as h, type TextSequenceSpec as i, type ArcShapeSpec as j, type AudioAssetSpec as k, type AudioMusicSpec as l, type AudioSequenceSpec as m, type AudioSfxSpec as n, type CanvasInfo as o, type CanvasSequenceSpec as p, type ChromaKeyFilterSpec as q, type CircleShapeSpec as r, type CompositionSequenceSpec as s, type CrossfadeTransition as t, type CustomFilterSpec as u, type DissolveTransition as v, type Expr as w, type IrisTransition as x, type LineShapeSpec as y, type LumaTransition as z };
|
|
@@ -1,4 +1,61 @@
|
|
|
1
|
-
import
|
|
1
|
+
import * as pixi_js from 'pixi.js';
|
|
2
|
+
import { Container, TextureSource } from 'pixi.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Resolves a routed keyframe path to a concrete tween target.
|
|
6
|
+
* `path` is everything after the registered prefix + dot (for
|
|
7
|
+
* `three.cube.rotation.y` under prefix `three`, path = `cube.rotation.y`).
|
|
8
|
+
* Return null to skip the key (the router is expected to have warned).
|
|
9
|
+
*/
|
|
10
|
+
type PathRouter = (path: string) => {
|
|
11
|
+
target: object;
|
|
12
|
+
prop: string;
|
|
13
|
+
} | null;
|
|
14
|
+
type PathRouters = Record<string, PathRouter>;
|
|
15
|
+
|
|
16
|
+
/** Variables available to expression evaluation. */
|
|
17
|
+
interface Scope {
|
|
18
|
+
w: number;
|
|
19
|
+
h: number;
|
|
20
|
+
W: number;
|
|
21
|
+
H: number;
|
|
22
|
+
GW: number;
|
|
23
|
+
GH: number;
|
|
24
|
+
contain: number;
|
|
25
|
+
cover: number;
|
|
26
|
+
t: number;
|
|
27
|
+
d: number;
|
|
28
|
+
T: number;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
declare abstract class Sequence {
|
|
32
|
+
spec: SequenceSpec;
|
|
33
|
+
parent: CompositionShape | null;
|
|
34
|
+
root: CompositionShape;
|
|
35
|
+
target: Container | null;
|
|
36
|
+
intrinsicWidth: number;
|
|
37
|
+
intrinsicHeight: number;
|
|
38
|
+
at: number;
|
|
39
|
+
duration: number | undefined;
|
|
40
|
+
constructor(spec: SequenceSpec, parent: CompositionShape | null, root: CompositionShape);
|
|
41
|
+
abstract build(): Promise<void>;
|
|
42
|
+
buildFilters(): void;
|
|
43
|
+
scope(): Scope;
|
|
44
|
+
/**
|
|
45
|
+
* Prefix routers contributed to the keyframe pipeline (e.g. `three.` in
|
|
46
|
+
* pixi-effects/three). Base sequences route nothing extra.
|
|
47
|
+
*/
|
|
48
|
+
protected pathRouters(): PathRouters;
|
|
49
|
+
/**
|
|
50
|
+
* The `initial` and `keyframes` that go onto the display object. A layer whose `time` runs a clock (video, composition) takes `time`
|
|
51
|
+
* out of them: it is not a property of the display object.
|
|
52
|
+
*/
|
|
53
|
+
protected displayProps(): {
|
|
54
|
+
initial: SequenceSpec['initial'];
|
|
55
|
+
keyframes: SequenceSpec['keyframes'];
|
|
56
|
+
};
|
|
57
|
+
destroy(): void;
|
|
58
|
+
}
|
|
2
59
|
|
|
3
60
|
/** What an author writes in `composition.stops`: a time in seconds, or an object. */
|
|
4
61
|
interface StopSpec {
|
|
@@ -120,62 +177,6 @@ interface SharedGpu {
|
|
|
120
177
|
adopt(source: TextureSource, texture: unknown): void;
|
|
121
178
|
}
|
|
122
179
|
|
|
123
|
-
/**
|
|
124
|
-
* Resolves a routed keyframe path to a concrete tween target.
|
|
125
|
-
* `path` is everything after the registered prefix + dot (for
|
|
126
|
-
* `three.cube.rotation.y` under prefix `three`, path = `cube.rotation.y`).
|
|
127
|
-
* Return null to skip the key (the router is expected to have warned).
|
|
128
|
-
*/
|
|
129
|
-
type PathRouter = (path: string) => {
|
|
130
|
-
target: object;
|
|
131
|
-
prop: string;
|
|
132
|
-
} | null;
|
|
133
|
-
type PathRouters = Record<string, PathRouter>;
|
|
134
|
-
|
|
135
|
-
/** Variables available to expression evaluation. */
|
|
136
|
-
interface Scope {
|
|
137
|
-
w: number;
|
|
138
|
-
h: number;
|
|
139
|
-
W: number;
|
|
140
|
-
H: number;
|
|
141
|
-
GW: number;
|
|
142
|
-
GH: number;
|
|
143
|
-
contain: number;
|
|
144
|
-
cover: number;
|
|
145
|
-
t: number;
|
|
146
|
-
d: number;
|
|
147
|
-
T: number;
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
declare abstract class Sequence {
|
|
151
|
-
spec: SequenceSpec;
|
|
152
|
-
parent: CompositionShape | null;
|
|
153
|
-
root: CompositionShape;
|
|
154
|
-
target: Container | null;
|
|
155
|
-
intrinsicWidth: number;
|
|
156
|
-
intrinsicHeight: number;
|
|
157
|
-
at: number;
|
|
158
|
-
duration: number | undefined;
|
|
159
|
-
constructor(spec: SequenceSpec, parent: CompositionShape | null, root: CompositionShape);
|
|
160
|
-
abstract build(): Promise<void>;
|
|
161
|
-
buildFilters(): void;
|
|
162
|
-
scope(): Scope;
|
|
163
|
-
/**
|
|
164
|
-
* Prefix routers contributed to the keyframe pipeline (e.g. `three.` in
|
|
165
|
-
* pixi-effects/three). Base sequences route nothing extra.
|
|
166
|
-
*/
|
|
167
|
-
protected pathRouters(): PathRouters;
|
|
168
|
-
/**
|
|
169
|
-
* The `initial` and `keyframes` that go onto the display object. A layer whose `time` runs a clock (video, composition) takes `time`
|
|
170
|
-
* out of them: it is not a property of the display object.
|
|
171
|
-
*/
|
|
172
|
-
protected displayProps(): {
|
|
173
|
-
initial: SequenceSpec['initial'];
|
|
174
|
-
keyframes: SequenceSpec['keyframes'];
|
|
175
|
-
};
|
|
176
|
-
destroy(): void;
|
|
177
|
-
}
|
|
178
|
-
|
|
179
180
|
/**
|
|
180
181
|
* Keeping a layer's expensive state across a rebuild (`setVars`, `resize`, `rebuild`): the new tree is built while the old one still plays, and a layer that cannot be told apart from
|
|
181
182
|
* one of the old tree (same name, same setup, same size and options) may take over that layer's scene and renderer instead of building its own. Today only the `three` layer does:
|
|
@@ -949,6 +950,11 @@ interface CompositionShape {
|
|
|
949
950
|
gpu?: SharedGpu;
|
|
950
951
|
/** The layers of the tree that a rebuild replaces, that a new layer of the same kind may take the state of instead of building its own (`three` layers: see core/reuse.ts). Root shape only. */
|
|
951
952
|
reuse?: ReusePool;
|
|
953
|
+
/** A layer of this composition by its `name`, among those already built (the ones listed before the asker): a `three` layer shows one of them on a mesh (`threeLayerTexture`). */
|
|
954
|
+
findLayer?: (name: string) => Sequence | null;
|
|
955
|
+
layerNames?: () => string[];
|
|
956
|
+
/** PixiJS's renderer, on the root shape only: what draws a layer into a texture for `threeLayerTexture`. */
|
|
957
|
+
pixiRenderer?: pixi_js.Renderer;
|
|
952
958
|
}
|
|
953
959
|
/** The composition's camera and the lights alive at one moment (what its `camera` and `light` layers say, after their keyframes). */
|
|
954
960
|
interface SpaceNow {
|
|
@@ -957,4 +963,4 @@ interface SpaceNow {
|
|
|
957
963
|
fog: FogState | null;
|
|
958
964
|
}
|
|
959
965
|
|
|
960
|
-
export { type SfxPreset as $, type AssetSpec as A, type BlendModeName as B, type CompositionSpec as C, type DipTransition as D, type EllipseShapeSpec as E, type FilterSpec as F, type GradientSpec as G, type MaskRefSpec as H, type ImageSequenceSpec as I, type MatteRefSpec as J, type Keyframe as K, type LightSequenceSpec as L, type MusicOptions as M, type MusicDrum as N, type MusicDrums as O, type Props as P, type MusicInstrument as Q, type MusicLevel as R, type SequenceSpec as S, type TransitionSpec as T, type MusicTrack as U, type NullSequenceSpec as V, type PathShapeSpec as W, type PolygonShapeSpec as X, type RectShapeSpec as Y, type SfxKnobs as Z, type SfxOptions as _, type Stop as a, type SfxVoice as a0, type ShaderSequenceSpec as a1, type ShapeSequenceSpec as a2, type SlideTransition as a3, type StopSpec as a4, type TextSpan as a5, type TransitionCommon as a6, type VideoSequenceSpec as a7, type WipeTransition as a8, type ZoomTransition as a9,
|
|
966
|
+
export { type SfxPreset as $, type AssetSpec as A, type BlendModeName as B, type CompositionSpec as C, type DipTransition as D, type EllipseShapeSpec as E, type FilterSpec as F, type GradientSpec as G, type MaskRefSpec as H, type ImageSequenceSpec as I, type MatteRefSpec as J, type Keyframe as K, type LightSequenceSpec as L, type MusicOptions as M, type MusicDrum as N, type MusicDrums as O, type Props as P, type MusicInstrument as Q, type MusicLevel as R, type SequenceSpec as S, type TransitionSpec as T, type MusicTrack as U, type NullSequenceSpec as V, type PathShapeSpec as W, type PolygonShapeSpec as X, type RectShapeSpec as Y, type SfxKnobs as Z, type SfxOptions as _, type Stop as a, type SfxVoice as a0, type ShaderSequenceSpec as a1, type ShapeSequenceSpec as a2, type SlideTransition as a3, type StopSpec as a4, type TextSpan as a5, type TransitionCommon as a6, type VideoSequenceSpec as a7, type WipeTransition as a8, type ZoomTransition as a9, Sequence as b, type SpaceNow as c, type SequenceCommon as d, type PropValue as e, type PathRouters as f, type CompositionShape as g, type CameraSequenceSpec as h, type TextSequenceSpec as i, type ArcShapeSpec as j, type AudioAssetSpec as k, type AudioMusicSpec as l, type AudioSequenceSpec as m, type AudioSfxSpec as n, type CanvasInfo as o, type CanvasSequenceSpec as p, type ChromaKeyFilterSpec as q, type CircleShapeSpec as r, type CompositionSequenceSpec as s, type CrossfadeTransition as t, type CustomFilterSpec as u, type DissolveTransition as v, type Expr as w, type IrisTransition as x, type LineShapeSpec as y, type LumaTransition as z };
|
package/docs/api.md
CHANGED
|
@@ -177,6 +177,7 @@ interface PickHit {
|
|
|
177
177
|
kind: 'flat' | 'card' | 'three'; // a 2D layer, a threeD layer (its projected card), a three layer
|
|
178
178
|
point: { x: number; y: number }; // the point asked about
|
|
179
179
|
local?: { x: number; y: number }; // a flat layer: the point in the layer's own pixels
|
|
180
|
+
via?: { path: string; object: string }; // the layer is shown on a mesh of a `three` layer (threeLayerTexture) and the point is on that mesh: the three layer and the object it came through
|
|
180
181
|
three?: { object: string; point: [number, number, number]; distance: number; object3d: unknown; // a three layer: what the ray met first
|
|
181
182
|
grab?: (plane?) => { move: (x, y) => { point, delta } | null } }; // drag it: see below
|
|
182
183
|
}
|
package/docs/dsl.md
CHANGED
|
@@ -850,6 +850,30 @@ three({ type: 'three', width: 'GW', height: 'GH',
|
|
|
850
850
|
- **`threeFrameCamera(camera, object, { direction, margin })`** puts a `PerspectiveCamera` where the whole of `object` (its box now, with its scale and place) fits the frame, looking at its centre, from a three-quarter view (`direction` default `[0.45, 0.3, 1]`) with 10 % room (`margin` 1.1): the smallest distance at which the 8 corners of the box fit both the vertical and the horizontal field of view (a bounding sphere would leave a long model small and head-on). It uses the camera's own `fov` and `aspect` and sets `near` / `far`. The box is the bind pose: a clip that travels far needs a camera of your own.
|
|
851
851
|
- With `followCamera` the composition's camera is the layer's camera (do not call `threeFrameCamera`); with `followLights` its lights light the model. See the recipes `gltf-in-space` and `gltf-shadow-room`. `orbit()` and `cameraPath()` drive the composition's `camera` layer, so they carry a following scene too.
|
|
852
852
|
|
|
853
|
+
### A 2D layer on a mesh: `threeLayerTexture`
|
|
854
|
+
|
|
855
|
+
`threeLayerTexture(ctx, 'screen')` makes a live 2D layer (a `composition`) the picture on a mesh of a `three` layer: a phone or laptop screen with an app running, a television, a billboard, a label on a bottle, a card with moving type. The 2D layer stays an ordinary layer (the timeline drives it, its own `at` / `duration` / keyframes / filters apply) but is no longer drawn on the stage: just before the three layer draws, PixiJS draws it into a texture and the mesh shows that texture.
|
|
856
|
+
|
|
857
|
+
```js
|
|
858
|
+
import { threeLayerTexture } from 'pixi-effects/three';
|
|
859
|
+
// in the composition, the 2D layer FIRST, then the three layer
|
|
860
|
+
{ type: 'composition', name: 'screen', width: 480, height: 270, duration: 8, sequences: [ /* a background rect, your UI */ ] },
|
|
861
|
+
three({ type: 'three', width: 'GW', height: 'GH',
|
|
862
|
+
setup: async (ctx) => {
|
|
863
|
+
const screen = await threeLayerTexture(ctx, 'screen', { resolution: 2 }); // { material, texture, width, height, dispose }
|
|
864
|
+
ctx.scene.add(new THREE.Mesh(new THREE.PlaneGeometry(1.6, 0.9), screen.material));
|
|
865
|
+
} })
|
|
866
|
+
```
|
|
867
|
+
|
|
868
|
+
- **The rules.** The layer must be a `composition` with a `width` and `height`, in the same composition as the three layer and **listed before it** (a layer built later cannot be found: the error lists the names that are there). `await` the call in `setup`. `resolution` (default 1) is the pixels of the texture per pixel of the layer: 2 for a screen that is seen large. `transparent: true` lets what is behind the mesh show through the empty and half-transparent parts of the composition (a sticker, a label with rounded corners; the material blends and does not write depth); default `false`, an opaque screen. The layer's own place, scale and rotation do not matter: its content is drawn from (0, 0) to (width, height); its alpha and filters do.
|
|
869
|
+
- **The material.** `screen.material` is unlit (a screen shines by itself; scene lights do not shade it) and carries the colour decoding; put it on a mesh, or on one face of a box (`[dark, dark, dark, dark, screen.material, dark]`). Use `screen.texture` yourself and its colours may be wrong. Without `transparent`, give the composition a background rect: what it leaves empty is black on the mesh.
|
|
870
|
+
- **Two routes, the same colours** (measured: the background, a moving dot and a growing bar equal the 2D layer, channel for channel, on both). When the three layer draws on PixiJS's own GPU device (`renderer: 'webgpu'` in a WebGPU movie, the default) the texture is PixiJS's own GPU texture used through three's `ExternalTexture`: no copy, 0.3 ms a frame, and the page's import map needs `"three/webgpu"` and `"three/tsl"` next to `"three"`. Otherwise (the layer has a canvas of its own with `shareGpu: false`, a `renderer: 'webgl'` layer, or PixiJS on WebGL) the pixels are read back into a canvas: 1.6 ms a frame at 480 x 270, 4 ms at 1280 x 720.
|
|
871
|
+
- **A function of the time.** The 2D layer is drawn when the three layer draws, after the timeline has put every layer at the frame: a seek, an export and a repeat give the same picture (`check` draws the layer twice, in two orders, as for any three layer). A layer that has not started yet, or has ended, shows nothing on the mesh.
|
|
872
|
+
- **A rebuild** (`setVars`, `resize`) keeps the three layer and shows the layer of the new tree (the text that changed shows on the mesh); a layer that changed size is drawn stretched to the texture.
|
|
873
|
+
- **Seen small or at a slant** the texture is filtered, not aliased (1-pixel stripes on a plane a quarter of the picture come out as one even grey, not black and white stripes; four taps along a slant). The mip levels are PixiJS's own on the shared GPU, averaged in the stored sRGB values, so very fine bright detail seen very small is a little darker there than on a read-back canvas, whose mipmaps three makes in linear light (stripes average to 128 and 188).
|
|
874
|
+
- **A click reaches the screen.** `movie.pick(x, y)` on a point of the mesh answers for the layers INSIDE the 2D layer, at the point of its picture under the pointer: the top hit is `screen/button` (a flat hit with `local` in the layer's own pixels), with `via: { path: 'phone', object: 'face' }` naming the three layer and the mesh it came through; under it come the screen's other layers, then the mesh itself. `only` / `ignore` see through the mesh (`only: ['screen']` is the screen's layers wherever they are shown), `movie.hover` and the click recipes work on a phone's screen as they do on a flat layer, and a layer that is not alive does not answer. A page asks the layer by its own name, so the button is one `pick` away: `if (movie.pick(...movie.canvasPoint(event), { only: ['button'] })) …`.
|
|
875
|
+
- **Not in this version:** several meshes showing the same layer are answered through the first one the ray meets (that is the right one), but a layer shown on a mesh seen through another transparent mesh is not looked behind.
|
|
876
|
+
|
|
853
877
|
### Keyframe paths: `three.<name>.<path>`
|
|
854
878
|
|
|
855
879
|
Objects returned from `setup`'s `{ objects }` are addressable from keyframes as `three.<name>.<path>`, using the same `from`/`to`/`set` vocabulary as every other prop:
|
package/llms-full.txt
CHANGED
|
@@ -6,7 +6,7 @@ Concatenation of the skill, cheatsheet, pitfalls, recipes and the DSL / API / me
|
|
|
6
6
|
|
|
7
7
|
<!-- ===== skills/pixi-effects/SKILL.md ===== -->
|
|
8
8
|
|
|
9
|
-
> Written for pixi-effects 0.
|
|
9
|
+
> Written for pixi-effects 0.46.0. Update this skill with `npx skills update`; the newest docs are at https://yjmtmtk.github.io/pixi-effects/
|
|
10
10
|
|
|
11
11
|
# pixi-effects — write a video as data
|
|
12
12
|
|
|
@@ -209,7 +209,7 @@ Style (`initial` / keyframes): `fillColor fillAlpha strokeColor strokeAlpha stro
|
|
|
209
209
|
|
|
210
210
|
**camera** (2.5D) — props in `initial`/keyframes: `x y z lookAtX lookAtY lookAtZ fov focus aperture` (defaults: centred, looking at the z = 0 plane, `fov` 40, `z` auto = `(H/2)/tan(fov/2)` ≈ 989 at 720p). **Depth of field:** `focus` = the sharp plane (a `threeD` layer's name = its first z, or a z number), `aperture` = the lens diameter in px (30 default, 0 off); writing either blurs the other `threeD` layers by their distance from the focus (a rack focus is a keyframe on `focus`; recipe `rack-focus`; `inspect` reports `depthBlur`, and `check --at 2.4` prints each layer's blur). A dolly with no typed distance: leave `z` automatic and animate `offsetZ`. 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`.
|
|
211
211
|
|
|
212
|
-
**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`. **In the composition's space:** `followCamera: true` (the layer's PerspectiveCamera is the composition's `camera`; the scene is in composition px with y flipped: the composition's `(x, y, z)` is `(x, -y, z)`; give the layer the composition's size), `followLights: true` (the `light` layers become three lights) and `followFog: true` (the camera's fog becomes the scene's `Fog`: close to, not equal to, the card fog); a light's `castsShadows` casts in three (mark your meshes `castShadow` / `receiveShadow`); `ctx.space` has the raw numbers. **glTF:** `import { threeGltf, threeFrameCamera } from 'pixi-effects/three'`: `const m = await threeGltf(url, { animation: 'Run' | 0 | false, speed, offset, loop })` in `setup` (`ctx.scene.add(m.object)`; `m.fit(px)` scales the longest side to px; `m.clips` names the clips), `update: (t) => m.pose(t)` (a function of t: never `mixer.update(dt)`), `threeFrameCamera(ctx.camera, m.object)` frames the whole model from a three-quarter view. Needs `"three/addons/"` in the import map. Recipe `gltf-in-space`.
|
|
212
|
+
**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`. **In the composition's space:** `followCamera: true` (the layer's PerspectiveCamera is the composition's `camera`; the scene is in composition px with y flipped: the composition's `(x, y, z)` is `(x, -y, z)`; give the layer the composition's size), `followLights: true` (the `light` layers become three lights) and `followFog: true` (the camera's fog becomes the scene's `Fog`: close to, not equal to, the card fog); a light's `castsShadows` casts in three (mark your meshes `castShadow` / `receiveShadow`); `ctx.space` has the raw numbers. **glTF:** `import { threeGltf, threeFrameCamera } from 'pixi-effects/three'`: `const m = await threeGltf(url, { animation: 'Run' | 0 | false, speed, offset, loop })` in `setup` (`ctx.scene.add(m.object)`; `m.fit(px)` scales the longest side to px; `m.clips` names the clips), `update: (t) => m.pose(t)` (a function of t: never `mixer.update(dt)`), `threeFrameCamera(ctx.camera, m.object)` frames the whole model from a three-quarter view. Needs `"three/addons/"` in the import map. Recipe `gltf-in-space`. **A 2D layer on a mesh** (a phone screen, a television): `const screen = await threeLayerTexture(ctx, 'screen')` in `setup`, `screen.material` on a mesh; `screen` is a `composition` listed BEFORE the three layer (recipe `three-screen`).
|
|
213
213
|
|
|
214
214
|
## Camera flight and a camera shake
|
|
215
215
|
|
|
@@ -1856,6 +1856,40 @@ return [
|
|
|
1856
1856
|
|
|
1857
1857
|
A moving, shadow-casting model is held to "a function of the time" by `check`: place it from `t` in `update` (as above) and let `threeGltf` load it (it draws a skinned mesh always: three culls it by a bounding sphere that does not follow it).
|
|
1858
1858
|
|
|
1859
|
+
## An app or video on a phone, a television, a billboard (`threeLayerTexture`)
|
|
1860
|
+
|
|
1861
|
+
A 2D composition is the screen; a `three` layer holds the object it is on. The composition keeps animating (its own keyframes, text, charts, a counter) and the mesh shows it live, turning with the object. Put the composition BEFORE the three layer, give it a background rect, and `await threeLayerTexture(ctx, 'screen')` in `setup`: `screen.material` goes on the face of the mesh. The page's import map needs `"three/webgpu"` and `"three/tsl"` next to `"three"` (the template's has them when it has `three`).
|
|
1862
|
+
|
|
1863
|
+
```js
|
|
1864
|
+
// @docs-only three-screen
|
|
1865
|
+
// import { registerThree, threeLayerTexture } from 'pixi-effects/three'; registerThree(); (before movie.init)
|
|
1866
|
+
// composition 1280 x 720, 8 s
|
|
1867
|
+
let phone;
|
|
1868
|
+
return [
|
|
1869
|
+
{ type: 'shape', shape: 'rect', width: 'GW', height: 'GH', initial: { x: 'GW/2', y: 'GH/2', fillColor: '#e9e4dc' } },
|
|
1870
|
+
{ type: 'composition', name: 'screen', width: 390, height: 844, duration: 8, sequences: [ // the app: any 2D layers; this is what the phone shows
|
|
1871
|
+
{ type: 'shape', shape: 'rect', width: 390, height: 844, initial: { x: 195, y: 422, fillColor: '#101a33' } },
|
|
1872
|
+
{ type: 'text', text: 'Hello', style: { fontSize: 72, fill: '#ffffff', fontWeight: '800' }, initial: { x: 195, y: 160, anchorX: 0.5, anchorY: 0.5 },
|
|
1873
|
+
keyframes: [{ at: 0.4, from: { alpha: 0, y: 200 }, to: { alpha: 1, y: 160 }, duration: 0.8, ease: 'power3.out' }] },
|
|
1874
|
+
] },
|
|
1875
|
+
{ type: 'three', name: 'phone', width: 'GW', height: 'GH',
|
|
1876
|
+
setup: async (ctx) => {
|
|
1877
|
+
const screen = await threeLayerTexture(ctx, 'screen', { resolution: 2 }); // a screen that is seen large: 2 x the pixels
|
|
1878
|
+
const body = new THREE.Mesh(new THREE.BoxGeometry(1.0, 2.1, 0.09), new THREE.MeshStandardMaterial({ color: 0x15171c, metalness: 0.6, roughness: 0.35 }));
|
|
1879
|
+
const face = new THREE.Mesh(new THREE.PlaneGeometry(0.92, 2.0), screen.material); // the layer's colours, as they are in 2D
|
|
1880
|
+
face.position.z = 0.046; // just in front of the body
|
|
1881
|
+
phone = new THREE.Group(); phone.add(body, face);
|
|
1882
|
+
ctx.scene.add(phone, new THREE.AmbientLight(0xffffff, 0.8), new THREE.DirectionalLight(0xffffff, 2.2));
|
|
1883
|
+
ctx.camera.position.set(0, 0, 2.9);
|
|
1884
|
+
},
|
|
1885
|
+
update: (t) => { phone.rotation.y = 0.6 * Math.sin(t * 0.7); } }, // a function of t, like any three layer
|
|
1886
|
+
];
|
|
1887
|
+
```
|
|
1888
|
+
|
|
1889
|
+
A tap on the phone's screen reaches the app: `movie.pick(...movie.canvasPoint(event))` answers with the layer inside the screen (`screen/button`, with `via` naming the phone), so the click and hover recipes work on a mesh as on a flat layer.
|
|
1890
|
+
|
|
1891
|
+
Rules: the layer must be a `composition` with a `width` and `height`; a mistake in the name lists the names that are there; the material is unlit (a screen shines by itself) so keep the scene's lights for the body; empty parts of the composition are black on the mesh unless you pass `{ transparent: true }` (a sticker or a rounded label). The full rules, the two routes (no copy on the shared GPU, a read-back otherwise) and what is not in this version: `pixi-effects help threeLayerTexture`.
|
|
1892
|
+
|
|
1859
1893
|
## Click a layer: what is under the pointer (`movie.pick`)
|
|
1860
1894
|
|
|
1861
1895
|
`movie.pick(x, y)` answers "what is drawn under this point, at the frame on screen" with no hit-testing code of yours: a flat layer by its own shape, a `threeD` card by its projected quad, a `three` layer by a ray (the hit says which object). `movie.canvasPoint(event)` turns a pointer event into canvas pixels, whatever size the canvas is shown at. Name the layers you want to react to: `only` / `ignore` take layer names (or paths; a group's name stands for everything in it). A `three` layer covers its whole picture but only what the ray meets is a hit: elsewhere the layers below are hit (the title, the background).
|
|
@@ -3125,6 +3159,30 @@ three({ type: 'three', width: 'GW', height: 'GH',
|
|
|
3125
3159
|
- **`threeFrameCamera(camera, object, { direction, margin })`** puts a `PerspectiveCamera` where the whole of `object` (its box now, with its scale and place) fits the frame, looking at its centre, from a three-quarter view (`direction` default `[0.45, 0.3, 1]`) with 10 % room (`margin` 1.1): the smallest distance at which the 8 corners of the box fit both the vertical and the horizontal field of view (a bounding sphere would leave a long model small and head-on). It uses the camera's own `fov` and `aspect` and sets `near` / `far`. The box is the bind pose: a clip that travels far needs a camera of your own.
|
|
3126
3160
|
- With `followCamera` the composition's camera is the layer's camera (do not call `threeFrameCamera`); with `followLights` its lights light the model. See the recipes `gltf-in-space` and `gltf-shadow-room`. `orbit()` and `cameraPath()` drive the composition's `camera` layer, so they carry a following scene too.
|
|
3127
3161
|
|
|
3162
|
+
### A 2D layer on a mesh: `threeLayerTexture`
|
|
3163
|
+
|
|
3164
|
+
`threeLayerTexture(ctx, 'screen')` makes a live 2D layer (a `composition`) the picture on a mesh of a `three` layer: a phone or laptop screen with an app running, a television, a billboard, a label on a bottle, a card with moving type. The 2D layer stays an ordinary layer (the timeline drives it, its own `at` / `duration` / keyframes / filters apply) but is no longer drawn on the stage: just before the three layer draws, PixiJS draws it into a texture and the mesh shows that texture.
|
|
3165
|
+
|
|
3166
|
+
```js
|
|
3167
|
+
import { threeLayerTexture } from 'pixi-effects/three';
|
|
3168
|
+
// in the composition, the 2D layer FIRST, then the three layer
|
|
3169
|
+
{ type: 'composition', name: 'screen', width: 480, height: 270, duration: 8, sequences: [ /* a background rect, your UI */ ] },
|
|
3170
|
+
three({ type: 'three', width: 'GW', height: 'GH',
|
|
3171
|
+
setup: async (ctx) => {
|
|
3172
|
+
const screen = await threeLayerTexture(ctx, 'screen', { resolution: 2 }); // { material, texture, width, height, dispose }
|
|
3173
|
+
ctx.scene.add(new THREE.Mesh(new THREE.PlaneGeometry(1.6, 0.9), screen.material));
|
|
3174
|
+
} })
|
|
3175
|
+
```
|
|
3176
|
+
|
|
3177
|
+
- **The rules.** The layer must be a `composition` with a `width` and `height`, in the same composition as the three layer and **listed before it** (a layer built later cannot be found: the error lists the names that are there). `await` the call in `setup`. `resolution` (default 1) is the pixels of the texture per pixel of the layer: 2 for a screen that is seen large. `transparent: true` lets what is behind the mesh show through the empty and half-transparent parts of the composition (a sticker, a label with rounded corners; the material blends and does not write depth); default `false`, an opaque screen. The layer's own place, scale and rotation do not matter: its content is drawn from (0, 0) to (width, height); its alpha and filters do.
|
|
3178
|
+
- **The material.** `screen.material` is unlit (a screen shines by itself; scene lights do not shade it) and carries the colour decoding; put it on a mesh, or on one face of a box (`[dark, dark, dark, dark, screen.material, dark]`). Use `screen.texture` yourself and its colours may be wrong. Without `transparent`, give the composition a background rect: what it leaves empty is black on the mesh.
|
|
3179
|
+
- **Two routes, the same colours** (measured: the background, a moving dot and a growing bar equal the 2D layer, channel for channel, on both). When the three layer draws on PixiJS's own GPU device (`renderer: 'webgpu'` in a WebGPU movie, the default) the texture is PixiJS's own GPU texture used through three's `ExternalTexture`: no copy, 0.3 ms a frame, and the page's import map needs `"three/webgpu"` and `"three/tsl"` next to `"three"`. Otherwise (the layer has a canvas of its own with `shareGpu: false`, a `renderer: 'webgl'` layer, or PixiJS on WebGL) the pixels are read back into a canvas: 1.6 ms a frame at 480 x 270, 4 ms at 1280 x 720.
|
|
3180
|
+
- **A function of the time.** The 2D layer is drawn when the three layer draws, after the timeline has put every layer at the frame: a seek, an export and a repeat give the same picture (`check` draws the layer twice, in two orders, as for any three layer). A layer that has not started yet, or has ended, shows nothing on the mesh.
|
|
3181
|
+
- **A rebuild** (`setVars`, `resize`) keeps the three layer and shows the layer of the new tree (the text that changed shows on the mesh); a layer that changed size is drawn stretched to the texture.
|
|
3182
|
+
- **Seen small or at a slant** the texture is filtered, not aliased (1-pixel stripes on a plane a quarter of the picture come out as one even grey, not black and white stripes; four taps along a slant). The mip levels are PixiJS's own on the shared GPU, averaged in the stored sRGB values, so very fine bright detail seen very small is a little darker there than on a read-back canvas, whose mipmaps three makes in linear light (stripes average to 128 and 188).
|
|
3183
|
+
- **A click reaches the screen.** `movie.pick(x, y)` on a point of the mesh answers for the layers INSIDE the 2D layer, at the point of its picture under the pointer: the top hit is `screen/button` (a flat hit with `local` in the layer's own pixels), with `via: { path: 'phone', object: 'face' }` naming the three layer and the mesh it came through; under it come the screen's other layers, then the mesh itself. `only` / `ignore` see through the mesh (`only: ['screen']` is the screen's layers wherever they are shown), `movie.hover` and the click recipes work on a phone's screen as they do on a flat layer, and a layer that is not alive does not answer. A page asks the layer by its own name, so the button is one `pick` away: `if (movie.pick(...movie.canvasPoint(event), { only: ['button'] })) …`.
|
|
3184
|
+
- **Not in this version:** several meshes showing the same layer are answered through the first one the ray meets (that is the right one), but a layer shown on a mesh seen through another transparent mesh is not looked behind.
|
|
3185
|
+
|
|
3128
3186
|
### Keyframe paths: `three.<name>.<path>`
|
|
3129
3187
|
|
|
3130
3188
|
Objects returned from `setup`'s `{ objects }` are addressable from keyframes as `three.<name>.<path>`, using the same `from`/`to`/`set` vocabulary as every other prop:
|
|
@@ -4286,6 +4344,7 @@ interface PickHit {
|
|
|
4286
4344
|
kind: 'flat' | 'card' | 'three'; // a 2D layer, a threeD layer (its projected card), a three layer
|
|
4287
4345
|
point: { x: number; y: number }; // the point asked about
|
|
4288
4346
|
local?: { x: number; y: number }; // a flat layer: the point in the layer's own pixels
|
|
4347
|
+
via?: { path: string; object: string }; // the layer is shown on a mesh of a `three` layer (threeLayerTexture) and the point is on that mesh: the three layer and the object it came through
|
|
4289
4348
|
three?: { object: string; point: [number, number, number]; distance: number; object3d: unknown; // a three layer: what the ray met first
|
|
4290
4349
|
grab?: (plane?) => { move: (x, y) => { point, delta } | null } }; // drag it: see below
|
|
4291
4350
|
}
|
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@ name: pixi-effects
|
|
|
3
3
|
description: Use when asked to make, animate, preview or export a video, motion graphics, an animated title, a lower third, kinetic type, a promo, a slideshow with transitions, an animated chart or data story, a social clip, a 2.5D / depth scene, or music and sound for a video, written as code in JavaScript or HTML. pixi-effects is a JS library where a video is a plain-object composition (text, shape, image, video, audio and file-free sfx / music, camera, light and shader layers with keyframes) that plays in the browser, is checked by a tool the agent can run, and exports MP4 / WebM / MOV with no server. Not for a project that already uses another video tool (Remotion, Motion Canvas, ffmpeg); follow that tool instead.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
> Written for pixi-effects 0.
|
|
6
|
+
> Written for pixi-effects 0.46.0. Update this skill with `npx skills update`; the newest docs are at https://yjmtmtk.github.io/pixi-effects/
|
|
7
7
|
|
|
8
8
|
# pixi-effects — write a video as data
|
|
9
9
|
|
|
@@ -87,7 +87,7 @@ Style (`initial` / keyframes): `fillColor fillAlpha strokeColor strokeAlpha stro
|
|
|
87
87
|
|
|
88
88
|
**camera** (2.5D) — props in `initial`/keyframes: `x y z lookAtX lookAtY lookAtZ fov focus aperture` (defaults: centred, looking at the z = 0 plane, `fov` 40, `z` auto = `(H/2)/tan(fov/2)` ≈ 989 at 720p). **Depth of field:** `focus` = the sharp plane (a `threeD` layer's name = its first z, or a z number), `aperture` = the lens diameter in px (30 default, 0 off); writing either blurs the other `threeD` layers by their distance from the focus (a rack focus is a keyframe on `focus`; recipe `rack-focus`; `inspect` reports `depthBlur`, and `check --at 2.4` prints each layer's blur). A dolly with no typed distance: leave `z` automatic and animate `offsetZ`. 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`.
|
|
89
89
|
|
|
90
|
-
**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`. **In the composition's space:** `followCamera: true` (the layer's PerspectiveCamera is the composition's `camera`; the scene is in composition px with y flipped: the composition's `(x, y, z)` is `(x, -y, z)`; give the layer the composition's size), `followLights: true` (the `light` layers become three lights) and `followFog: true` (the camera's fog becomes the scene's `Fog`: close to, not equal to, the card fog); a light's `castsShadows` casts in three (mark your meshes `castShadow` / `receiveShadow`); `ctx.space` has the raw numbers. **glTF:** `import { threeGltf, threeFrameCamera } from 'pixi-effects/three'`: `const m = await threeGltf(url, { animation: 'Run' | 0 | false, speed, offset, loop })` in `setup` (`ctx.scene.add(m.object)`; `m.fit(px)` scales the longest side to px; `m.clips` names the clips), `update: (t) => m.pose(t)` (a function of t: never `mixer.update(dt)`), `threeFrameCamera(ctx.camera, m.object)` frames the whole model from a three-quarter view. Needs `"three/addons/"` in the import map. Recipe `gltf-in-space`.
|
|
90
|
+
**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`. **In the composition's space:** `followCamera: true` (the layer's PerspectiveCamera is the composition's `camera`; the scene is in composition px with y flipped: the composition's `(x, y, z)` is `(x, -y, z)`; give the layer the composition's size), `followLights: true` (the `light` layers become three lights) and `followFog: true` (the camera's fog becomes the scene's `Fog`: close to, not equal to, the card fog); a light's `castsShadows` casts in three (mark your meshes `castShadow` / `receiveShadow`); `ctx.space` has the raw numbers. **glTF:** `import { threeGltf, threeFrameCamera } from 'pixi-effects/three'`: `const m = await threeGltf(url, { animation: 'Run' | 0 | false, speed, offset, loop })` in `setup` (`ctx.scene.add(m.object)`; `m.fit(px)` scales the longest side to px; `m.clips` names the clips), `update: (t) => m.pose(t)` (a function of t: never `mixer.update(dt)`), `threeFrameCamera(ctx.camera, m.object)` frames the whole model from a three-quarter view. Needs `"three/addons/"` in the import map. Recipe `gltf-in-space`. **A 2D layer on a mesh** (a phone screen, a television): `const screen = await threeLayerTexture(ctx, 'screen')` in `setup`, `screen.material` on a mesh; `screen` is a `composition` listed BEFORE the three layer (recipe `three-screen`).
|
|
91
91
|
|
|
92
92
|
## Camera flight and a camera shake
|
|
93
93
|
|
|
@@ -1386,6 +1386,40 @@ return [
|
|
|
1386
1386
|
|
|
1387
1387
|
A moving, shadow-casting model is held to "a function of the time" by `check`: place it from `t` in `update` (as above) and let `threeGltf` load it (it draws a skinned mesh always: three culls it by a bounding sphere that does not follow it).
|
|
1388
1388
|
|
|
1389
|
+
## An app or video on a phone, a television, a billboard (`threeLayerTexture`)
|
|
1390
|
+
|
|
1391
|
+
A 2D composition is the screen; a `three` layer holds the object it is on. The composition keeps animating (its own keyframes, text, charts, a counter) and the mesh shows it live, turning with the object. Put the composition BEFORE the three layer, give it a background rect, and `await threeLayerTexture(ctx, 'screen')` in `setup`: `screen.material` goes on the face of the mesh. The page's import map needs `"three/webgpu"` and `"three/tsl"` next to `"three"` (the template's has them when it has `three`).
|
|
1392
|
+
|
|
1393
|
+
```js
|
|
1394
|
+
// @docs-only three-screen
|
|
1395
|
+
// import { registerThree, threeLayerTexture } from 'pixi-effects/three'; registerThree(); (before movie.init)
|
|
1396
|
+
// composition 1280 x 720, 8 s
|
|
1397
|
+
let phone;
|
|
1398
|
+
return [
|
|
1399
|
+
{ type: 'shape', shape: 'rect', width: 'GW', height: 'GH', initial: { x: 'GW/2', y: 'GH/2', fillColor: '#e9e4dc' } },
|
|
1400
|
+
{ type: 'composition', name: 'screen', width: 390, height: 844, duration: 8, sequences: [ // the app: any 2D layers; this is what the phone shows
|
|
1401
|
+
{ type: 'shape', shape: 'rect', width: 390, height: 844, initial: { x: 195, y: 422, fillColor: '#101a33' } },
|
|
1402
|
+
{ type: 'text', text: 'Hello', style: { fontSize: 72, fill: '#ffffff', fontWeight: '800' }, initial: { x: 195, y: 160, anchorX: 0.5, anchorY: 0.5 },
|
|
1403
|
+
keyframes: [{ at: 0.4, from: { alpha: 0, y: 200 }, to: { alpha: 1, y: 160 }, duration: 0.8, ease: 'power3.out' }] },
|
|
1404
|
+
] },
|
|
1405
|
+
{ type: 'three', name: 'phone', width: 'GW', height: 'GH',
|
|
1406
|
+
setup: async (ctx) => {
|
|
1407
|
+
const screen = await threeLayerTexture(ctx, 'screen', { resolution: 2 }); // a screen that is seen large: 2 x the pixels
|
|
1408
|
+
const body = new THREE.Mesh(new THREE.BoxGeometry(1.0, 2.1, 0.09), new THREE.MeshStandardMaterial({ color: 0x15171c, metalness: 0.6, roughness: 0.35 }));
|
|
1409
|
+
const face = new THREE.Mesh(new THREE.PlaneGeometry(0.92, 2.0), screen.material); // the layer's colours, as they are in 2D
|
|
1410
|
+
face.position.z = 0.046; // just in front of the body
|
|
1411
|
+
phone = new THREE.Group(); phone.add(body, face);
|
|
1412
|
+
ctx.scene.add(phone, new THREE.AmbientLight(0xffffff, 0.8), new THREE.DirectionalLight(0xffffff, 2.2));
|
|
1413
|
+
ctx.camera.position.set(0, 0, 2.9);
|
|
1414
|
+
},
|
|
1415
|
+
update: (t) => { phone.rotation.y = 0.6 * Math.sin(t * 0.7); } }, // a function of t, like any three layer
|
|
1416
|
+
];
|
|
1417
|
+
```
|
|
1418
|
+
|
|
1419
|
+
A tap on the phone's screen reaches the app: `movie.pick(...movie.canvasPoint(event))` answers with the layer inside the screen (`screen/button`, with `via` naming the phone), so the click and hover recipes work on a mesh as on a flat layer.
|
|
1420
|
+
|
|
1421
|
+
Rules: the layer must be a `composition` with a `width` and `height`; a mistake in the name lists the names that are there; the material is unlit (a screen shines by itself) so keep the scene's lights for the body; empty parts of the composition are black on the mesh unless you pass `{ transparent: true }` (a sticker or a rounded label). The full rules, the two routes (no copy on the shared GPU, a read-back otherwise) and what is not in this version: `pixi-effects help threeLayerTexture`.
|
|
1422
|
+
|
|
1389
1423
|
## Click a layer: what is under the pointer (`movie.pick`)
|
|
1390
1424
|
|
|
1391
1425
|
`movie.pick(x, y)` answers "what is drawn under this point, at the frame on screen" with no hit-testing code of yours: a flat layer by its own shape, a `threeD` card by its projected quad, a `three` layer by a ray (the hit says which object). `movie.canvasPoint(event)` turns a pointer event into canvas pixels, whatever size the canvas is shown at. Name the layers you want to react to: `only` / `ignore` take layer names (or paths; a group's name stands for everything in it). A `three` layer covers its whole picture but only what the ray meets is a hit: elsewhere the layers below are hit (the title, the background).
|
|
@@ -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.
|
|
7
|
+
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/pixi-effects@0.46.0/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 */
|
|
@@ -37,17 +37,17 @@
|
|
|
37
37
|
"imports": {
|
|
38
38
|
"pixi.js": "https://esm.sh/pixi.js@8.22.0?bundle-deps",
|
|
39
39
|
"mediabunny": "https://esm.sh/mediabunny",
|
|
40
|
-
"pixi-effects": "https://cdn.jsdelivr.net/npm/pixi-effects@0.
|
|
41
|
-
"pixi-effects/controller": "https://cdn.jsdelivr.net/npm/pixi-effects@0.
|
|
42
|
-
"pixi-effects/present": "https://cdn.jsdelivr.net/npm/pixi-effects@0.
|
|
43
|
-
"pixi-effects/three": "https://cdn.jsdelivr.net/npm/pixi-effects@0.
|
|
40
|
+
"pixi-effects": "https://cdn.jsdelivr.net/npm/pixi-effects@0.46.0/dist/index.js",
|
|
41
|
+
"pixi-effects/controller": "https://cdn.jsdelivr.net/npm/pixi-effects@0.46.0/dist/Controller.js",
|
|
42
|
+
"pixi-effects/present": "https://cdn.jsdelivr.net/npm/pixi-effects@0.46.0/dist/present.js",
|
|
43
|
+
"pixi-effects/three": "https://cdn.jsdelivr.net/npm/pixi-effects@0.46.0/dist/three.js",
|
|
44
44
|
"pixi-filters": "https://esm.sh/pixi-filters@6.1.5?external=pixi.js",
|
|
45
45
|
"three": "https://esm.sh/three@0.178.0",
|
|
46
46
|
"three/addons/": "https://cdn.jsdelivr.net/npm/three@0.178.0/examples/jsm/"
|
|
47
47
|
}
|
|
48
48
|
}
|
|
49
49
|
</script>
|
|
50
|
-
<link rel="modulepreload" href="https://cdn.jsdelivr.net/npm/pixi-effects@0.
|
|
50
|
+
<link rel="modulepreload" href="https://cdn.jsdelivr.net/npm/pixi-effects@0.46.0/dist/index.js" /> <!-- after the import map: the library downloads while the page is parsed -->
|
|
51
51
|
|
|
52
52
|
<script type="module">
|
|
53
53
|
import { Movie, kenBurns, withFade } from 'pixi-effects';
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
var chunk3HZV3UHU_cjs = require('./chunk-3HZV3UHU.cjs');
|
|
4
|
-
require('./chunk-X62H3RBG.cjs');
|
|
5
|
-
require('./chunk-JN7JHNZR.cjs');
|
|
6
|
-
require('./chunk-3SAS3ZE5.cjs');
|
|
7
|
-
require('./chunk-TWZNOUM7.cjs');
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
Object.defineProperty(exports, "CompositionSequence", {
|
|
12
|
-
enumerable: true,
|
|
13
|
-
get: function () { return chunk3HZV3UHU_cjs.CompositionSequence; }
|
|
14
|
-
});
|
|
15
|
-
//# sourceMappingURL=Composition-N6BOROJ2.cjs.map
|
|
16
|
-
//# sourceMappingURL=Composition-N6BOROJ2.cjs.map
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
export { CompositionSequence } from './chunk-ICI6ACHY.js';
|
|
2
|
-
import './chunk-4QVQ5UJJ.js';
|
|
3
|
-
import './chunk-VNP7QCJE.js';
|
|
4
|
-
import './chunk-32MMNDDN.js';
|
|
5
|
-
import './chunk-BXOCEZ4U.js';
|
|
6
|
-
//# sourceMappingURL=Composition-YGBQX3HZ.js.map
|
|
7
|
-
//# sourceMappingURL=Composition-YGBQX3HZ.js.map
|