pixi-effects 0.1.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.
@@ -0,0 +1,364 @@
1
+ import { Application } from 'pixi.js';
2
+ import { gsap } from 'gsap';
3
+
4
+ /**
5
+ * pixi-effects public DSL types.
6
+ *
7
+ * The composition spec users pass to `Movie.init({ composition })` is
8
+ * shaped by `CompositionSpec`. Every `SequenceSpec` is a discriminated
9
+ * union over `type`, so editor autocomplete narrows correctly.
10
+ */
11
+ /** A string is treated as an arithmetic expression evaluated against the scope (see src/expr/Parser.ts). */
12
+ type Expr = string;
13
+ /** Any prop value: a number, an Expr string, or any string passed through verbatim (e.g. color hex, font names). */
14
+ type PropValue = number | string;
15
+ /** Generic prop bag (key → value). */
16
+ type Props = Record<string, PropValue>;
17
+ /** A single keyframe entry. Either `set`, `to`, `from`, or `from`+`to` is meaningful per kind. */
18
+ interface Keyframe {
19
+ at?: number;
20
+ duration?: number;
21
+ ease?: string;
22
+ set?: Props;
23
+ to?: Props;
24
+ from?: Props;
25
+ }
26
+ interface AssetSpec {
27
+ name: string;
28
+ src: string;
29
+ }
30
+ /**
31
+ * Escape hatch for arbitrary PIXI filters (e.g. anything from `pixi-filters`,
32
+ * a custom user-built `Filter` subclass, or a community filter package).
33
+ *
34
+ * The instance is used as-is; animation paths (`filters.<name>.<prop>`) work
35
+ * as long as the filter has a writable property at that path.
36
+ *
37
+ * Example:
38
+ * ```ts
39
+ * import { GlowFilter } from 'pixi-filters';
40
+ *
41
+ * filters: [
42
+ * { type: 'custom', name: 'glow', filter: new GlowFilter({ outerStrength: 2 }) },
43
+ * ],
44
+ * keyframes: [
45
+ * { at: 1, to: { 'filters.glow.outerStrength': 5 }, duration: 0.5 },
46
+ * ],
47
+ * ```
48
+ */
49
+ interface CustomFilterSpec {
50
+ type: 'custom';
51
+ name?: string;
52
+ /** A PIXI `Filter` instance. Imported here as `unknown` to avoid a hard `pixi.js` type dep on consumers reading this file purely as types. */
53
+ filter: unknown;
54
+ }
55
+ interface ChromaKeyFilterSpec {
56
+ type: 'chromaKey';
57
+ name?: string;
58
+ keyColor?: string | [number, number, number];
59
+ threshold?: number;
60
+ smoothing?: number;
61
+ spill?: number;
62
+ }
63
+ type FilterSpec = ChromaKeyFilterSpec | CustomFilterSpec;
64
+ interface TransitionCommon {
65
+ /** Sibling sequence's `name` — the outgoing scene. */
66
+ from: string;
67
+ /** Sibling sequence's `name` — the incoming scene. Must be declared after `from` in the parent's `sequences[]`. */
68
+ to: string;
69
+ /** Start time (parent-relative seconds). Same `at` semantics as Keyframe. */
70
+ at: number;
71
+ /** Length of the transition in seconds. Must be > 0. */
72
+ duration: number;
73
+ /** GSAP easing name. Default `'none'` (linear). */
74
+ ease?: string;
75
+ }
76
+ interface CrossfadeTransition extends TransitionCommon {
77
+ kind: 'crossfade';
78
+ }
79
+ interface WipeTransition extends TransitionCommon {
80
+ kind: 'wipe';
81
+ direction: 'left' | 'right' | 'up' | 'down';
82
+ /** 0..1 edge softness. Default 0.02. */
83
+ smoothing?: number;
84
+ }
85
+ interface IrisTransition extends TransitionCommon {
86
+ kind: 'iris';
87
+ /** `'in'` (default) = B opens up from a point. `'out'` = A closes down to a point. */
88
+ mode?: 'in' | 'out';
89
+ smoothing?: number;
90
+ }
91
+ interface SlideTransition extends TransitionCommon {
92
+ kind: 'slide';
93
+ /** Direction of motion: `'left'` = both sequences slide leftward (B enters from the right). */
94
+ direction: 'left' | 'right' | 'up' | 'down';
95
+ }
96
+ /**
97
+ * "Dip through": A fades out across the first half of the window, B fades in
98
+ * across the second half. The visible color during the dip is whatever sits
99
+ * behind A and B (canvas background, or any persistent layer beneath them).
100
+ */
101
+ interface DipTransition extends TransitionCommon {
102
+ kind: 'dip';
103
+ }
104
+ interface ZoomTransition extends TransitionCommon {
105
+ kind: 'zoom';
106
+ /**
107
+ * `'in'` (default): B opens up — starts at `fromScale` and zooms to 1.
108
+ * `'out'`: A closes — zooms from 1 to `fromScale` and fades.
109
+ */
110
+ mode?: 'in' | 'out';
111
+ /** Starting scale of the zoomed sequence. Default 4 (B starts 4x size). */
112
+ fromScale?: number;
113
+ }
114
+ interface DissolveTransition extends TransitionCommon {
115
+ kind: 'dissolve';
116
+ /** Pattern frequency. Higher = finer grain. Default 30. */
117
+ scale?: number;
118
+ /** Pattern offset for reproducibly varying the dissolve shape. Default 0. */
119
+ seed?: number;
120
+ /** 0..1 edge softness within each chunk. Default 0.05. */
121
+ smoothing?: number;
122
+ }
123
+ type TransitionSpec = CrossfadeTransition | WipeTransition | IrisTransition | SlideTransition | DipTransition | ZoomTransition | DissolveTransition;
124
+ interface SequenceCommon {
125
+ name?: string;
126
+ at?: number;
127
+ duration?: number;
128
+ initial?: Props;
129
+ keyframes?: Keyframe[];
130
+ filters?: FilterSpec[];
131
+ /**
132
+ * Override PIXI's auto-computed filter region. By default, filters apply
133
+ * only inside the target's bounding box. Setting this to a parent-relative
134
+ * rectangle lets a filter (e.g. a wipe / iris transition) draw across an
135
+ * area larger than the sprite — useful when the sprite content is small
136
+ * but the visual effect should cover the whole composition.
137
+ */
138
+ filterArea?: {
139
+ x: number;
140
+ y: number;
141
+ width: number;
142
+ height: number;
143
+ };
144
+ /**
145
+ * Inline mask spec. The mask sequence is built as a hidden sibling and
146
+ * wired via `target.mask` — it shapes which pixels of this sequence are
147
+ * visible. Mask coordinates live in the same space as this sequence
148
+ * (i.e. relative to the same parent composition).
149
+ *
150
+ * The mask is itself a sequence, so it can have its own `initial` and
151
+ * `keyframes` — useful for reveal animations (a circle growing from
152
+ * `radius: 0` to full-size). It defaults to running for this sequence's
153
+ * full lifetime.
154
+ *
155
+ * Any sequence type works as a mask; shapes are the natural choice for
156
+ * geometric reveals.
157
+ */
158
+ mask?: SequenceSpec;
159
+ /**
160
+ * When true, the mask is inverted: pixels inside the mask shape become
161
+ * transparent and pixels outside become visible. Useful for "knockout"
162
+ * effects (cut a hole through an image / panel). Default false.
163
+ *
164
+ * Routed through PIXI's native `setMask({ inverse: true })`.
165
+ */
166
+ maskInverted?: boolean;
167
+ }
168
+ interface VideoSequenceSpec extends SequenceCommon {
169
+ type: 'video';
170
+ asset: string;
171
+ loop?: boolean;
172
+ audio?: boolean;
173
+ volume?: number;
174
+ }
175
+ interface ImageSequenceSpec extends SequenceCommon {
176
+ type: 'image';
177
+ asset: string;
178
+ /**
179
+ * Colour space used to interpolate `tint` keyframes. Default `'rgb'`
180
+ * (linear sRGB lerp via PIXI's tint pipeline). Set to `'oklab'` /
181
+ * `'oklch'` for perceptually uniform interpolation — same semantics
182
+ * as on `shape`.
183
+ */
184
+ colorSpace?: 'rgb' | 'oklab' | 'oklch';
185
+ }
186
+ interface TextSequenceSpec extends SequenceCommon {
187
+ type: 'text';
188
+ text?: string;
189
+ /** Subset of PIXI v8 TextStyleOptions. String values may be exprs (e.g. fontSize: 'GW * 0.05'). */
190
+ style?: Record<string, PropValue | {
191
+ color?: PropValue;
192
+ width?: PropValue;
193
+ }>;
194
+ /**
195
+ * Colour space used to interpolate `fill` keyframes. Default `'rgb'`.
196
+ * Set to `'oklab'` / `'oklch'` for perceptually uniform interpolation
197
+ * — same semantics as on `shape`. The text's fill is re-rasterised on
198
+ * every frame the tween is active.
199
+ */
200
+ colorSpace?: 'rgb' | 'oklab' | 'oklch';
201
+ }
202
+ interface AudioSequenceSpec extends SequenceCommon {
203
+ type: 'audio';
204
+ asset: string;
205
+ loop?: boolean;
206
+ volume?: number;
207
+ }
208
+ interface CompositionSequenceSpec extends SequenceCommon {
209
+ type: 'composition';
210
+ width?: number;
211
+ height?: number;
212
+ sequences?: SequenceSpec[];
213
+ transitions?: TransitionSpec[];
214
+ }
215
+ interface ShapeBase extends SequenceCommon {
216
+ type: 'shape';
217
+ /**
218
+ * Colour space used to interpolate `fillColor` / `strokeColor` keyframes.
219
+ *
220
+ * - `'rgb'` (default): linear RGB tween via `gsap.utils.interpolate`.
221
+ * Fast, but a red → green ramp passes through muddy brown / olive
222
+ * greys at the midpoint because intermediate sRGB values are
223
+ * perceptually unbalanced.
224
+ * - `'oklab'` / `'oklch'`: perceptually uniform colour spaces. Hue and
225
+ * chroma stay vibrant through the transition. `oklch` interpolates
226
+ * hue along the shorter angular path, giving smooth rainbow-like
227
+ * sweeps; `oklab` is straight-line in the chromaticity plane.
228
+ */
229
+ colorSpace?: 'rgb' | 'oklab' | 'oklch';
230
+ }
231
+ interface RectShapeSpec extends ShapeBase {
232
+ shape: 'rect';
233
+ width: PropValue;
234
+ height: PropValue;
235
+ /** Rounded corner radius. Default 0 (sharp). */
236
+ cornerRadius?: PropValue;
237
+ /**
238
+ * Where the local origin sits relative to the rect's bounding box.
239
+ * `0` = left / top edge, `0.5` = centre (default), `1` = right / bottom
240
+ * edge. Animating `width` with `anchorX: 0` makes the bar grow
241
+ * rightward (progress-bar style) without the left edge drifting.
242
+ */
243
+ anchorX?: PropValue;
244
+ anchorY?: PropValue;
245
+ }
246
+ interface CircleShapeSpec extends ShapeBase {
247
+ shape: 'circle';
248
+ radius: PropValue;
249
+ /** See RectShapeSpec.anchorX. Default 0.5 (centre). */
250
+ anchorX?: PropValue;
251
+ anchorY?: PropValue;
252
+ }
253
+ interface EllipseShapeSpec extends ShapeBase {
254
+ shape: 'ellipse';
255
+ radiusX: PropValue;
256
+ radiusY: PropValue;
257
+ /** See RectShapeSpec.anchorX. Default 0.5 (centre). */
258
+ anchorX?: PropValue;
259
+ anchorY?: PropValue;
260
+ }
261
+ interface LineShapeSpec extends ShapeBase {
262
+ shape: 'line';
263
+ /** Line endpoints relative to the shape's local origin. */
264
+ from: [PropValue, PropValue];
265
+ to: [PropValue, PropValue];
266
+ }
267
+ interface PolygonShapeSpec extends ShapeBase {
268
+ shape: 'polygon';
269
+ /** Vertices in local space. The path is auto-closed. */
270
+ points: Array<[PropValue, PropValue]>;
271
+ /** When true, draw an open polyline instead of a closed polygon. Default false. */
272
+ open?: boolean;
273
+ }
274
+ interface PathShapeSpec extends ShapeBase {
275
+ shape: 'path';
276
+ /** SVG path data (`d` attribute). Goes through PIXI's GraphicsContext.svg(). */
277
+ d: string;
278
+ }
279
+ type ShapeSequenceSpec = RectShapeSpec | CircleShapeSpec | EllipseShapeSpec | LineShapeSpec | PolygonShapeSpec | PathShapeSpec;
280
+ type SequenceSpec = VideoSequenceSpec | ImageSequenceSpec | TextSequenceSpec | AudioSequenceSpec | CompositionSequenceSpec | ShapeSequenceSpec;
281
+ /** Top-level composition (root node) — same as `CompositionSequenceSpec` minus the discriminant. */
282
+ interface CompositionSpec extends SequenceCommon {
283
+ width?: number;
284
+ height?: number;
285
+ sequences?: SequenceSpec[];
286
+ transitions?: TransitionSpec[];
287
+ }
288
+
289
+ interface MovieOptions {
290
+ width?: number;
291
+ height?: number;
292
+ duration?: number;
293
+ frameRate?: number;
294
+ background?: string;
295
+ canvas?: HTMLCanvasElement;
296
+ assets?: AssetSpec[];
297
+ composition?: CompositionSpec;
298
+ }
299
+ interface RenderOptions {
300
+ format?: 'mp4' | 'mov' | 'webm' | 'mkv';
301
+ video?: {
302
+ codec?: string;
303
+ bitrate?: 'very-low' | 'low' | 'medium' | 'high' | 'very-high';
304
+ };
305
+ audio?: {
306
+ codec?: string;
307
+ bitrate?: 'very-low' | 'low' | 'medium' | 'high' | 'very-high';
308
+ };
309
+ }
310
+ interface FrameEvent {
311
+ frame: number;
312
+ totalFrames: number;
313
+ }
314
+ interface ProgressEvent {
315
+ progress: number;
316
+ frame: number;
317
+ totalFrames: number;
318
+ }
319
+ type Listener = (...args: any[]) => void;
320
+ declare class Movie {
321
+ private _events;
322
+ private _initState;
323
+ app: Application | null;
324
+ timeline: ReturnType<typeof gsap.timeline> | null;
325
+ audioBuffer: AudioBuffer | null;
326
+ audioSource: AudioBufferSourceNode | null;
327
+ gainNode: GainNode | null;
328
+ private _volume;
329
+ private _muted;
330
+ isPlaying: boolean;
331
+ currentFrame: number;
332
+ totalFrames: number;
333
+ width: number;
334
+ height: number;
335
+ duration: number;
336
+ frameRate: number;
337
+ background: string;
338
+ private _audioContext;
339
+ private _rootSequence;
340
+ private _rootContainer;
341
+ private _raf;
342
+ on(event: 'ready', fn: () => void): this;
343
+ on(event: 'frame', fn: (e: FrameEvent) => void): this;
344
+ on(event: 'progress', fn: (e: ProgressEvent) => void): this;
345
+ on(event: 'pause', fn: () => void): this;
346
+ off(event: string, fn: Listener): this;
347
+ emit(event: string, ...args: unknown[]): void;
348
+ get isReady(): boolean;
349
+ private _ensureAudioContext;
350
+ init(options?: MovieOptions): Promise<void>;
351
+ gotoFrame(frame: number, force?: boolean): Promise<void>;
352
+ private _awaitVideoFrames;
353
+ play(): void;
354
+ pause(): void;
355
+ set volume(v: number);
356
+ get volume(): number;
357
+ set muted(v: boolean);
358
+ get muted(): boolean;
359
+ toggleMute(): boolean;
360
+ render(options?: RenderOptions): Promise<Blob>;
361
+ destroy(): Promise<void>;
362
+ }
363
+
364
+ export { type AssetSpec as A, type ChromaKeyFilterSpec as C, type DipTransition as D, type EllipseShapeSpec as E, type FilterSpec as F, type ImageSequenceSpec as I, type Keyframe as K, type LineShapeSpec as L, Movie as M, type PathShapeSpec as P, type RectShapeSpec as R, type SequenceCommon as S, type TextSequenceSpec as T, type VideoSequenceSpec as V, type WipeTransition as W, type ZoomTransition as Z, type AudioSequenceSpec as a, type CircleShapeSpec as b, type CompositionSequenceSpec as c, type CompositionSpec as d, type CrossfadeTransition as e, type CustomFilterSpec as f, type DissolveTransition as g, type Expr as h, type FrameEvent as i, type IrisTransition as j, type MovieOptions as k, type PolygonShapeSpec as l, type ProgressEvent as m, type PropValue as n, type Props as o, type RenderOptions as p, type SequenceSpec as q, type ShapeSequenceSpec as r, type SlideTransition as s, type TransitionCommon as t, type TransitionSpec as u };