pixi-effects 0.3.0 → 0.5.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 +53 -0
- package/README.md +10 -6
- package/ai/SKILL.md +9 -4
- package/ai/reference/cheatsheet.md +19 -7
- package/ai/reference/pitfalls.md +32 -7
- package/ai/reference/recipes.md +117 -0
- package/ai/template.html +6 -3
- package/ai/tools/check.mjs +385 -0
- package/dist/{Base-BDw6dKRB.d.cts → Base-CPSFBkPm.d.ts} +12 -1
- package/dist/{Base-Ba4Ta4ap.d.ts → Base-DnRAxroQ.d.cts} +12 -1
- package/dist/Composition-63EXHNJJ.cjs +13 -0
- package/dist/{Composition-6FDH5OPM.cjs.map → Composition-63EXHNJJ.cjs.map} +1 -1
- package/dist/Composition-TUCRSMJU.js +4 -0
- package/dist/{Composition-FEYAUMFI.js.map → Composition-TUCRSMJU.js.map} +1 -1
- package/dist/Controller.cjs +37 -13
- package/dist/Controller.cjs.map +1 -1
- package/dist/Controller.d.cts +4 -2
- package/dist/Controller.d.ts +4 -2
- package/dist/Controller.js +37 -13
- package/dist/Controller.js.map +1 -1
- package/dist/{Movie-W8ISiEBB.d.cts → Movie-CuU_aaCm.d.cts} +56 -2
- package/dist/{Movie-DMpaT67V.d.ts → Movie-VyDiUYOj.d.ts} +56 -2
- package/dist/{chunk-ZL262ZRM.cjs → chunk-23M5QTWW.cjs} +70 -20
- package/dist/chunk-23M5QTWW.cjs.map +1 -0
- package/dist/{chunk-SIRVULFF.cjs → chunk-2RR43OIN.cjs} +256 -60
- package/dist/chunk-2RR43OIN.cjs.map +1 -0
- package/dist/{chunk-DIJG2RSF.js → chunk-5Q5XZG7Z.js} +59 -9
- package/dist/chunk-5Q5XZG7Z.js.map +1 -0
- package/dist/chunk-DKGG7WD4.js +61 -0
- package/dist/chunk-DKGG7WD4.js.map +1 -0
- package/dist/{chunk-PN5A6QA7.js → chunk-UBPZLGLU.js} +255 -61
- package/dist/chunk-UBPZLGLU.js.map +1 -0
- package/dist/chunk-UQBGW5DO.cjs +64 -0
- package/dist/chunk-UQBGW5DO.cjs.map +1 -0
- package/dist/index.cjs +336 -89
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +54 -5
- package/dist/index.d.ts +54 -5
- package/dist/index.js +320 -75
- package/dist/index.js.map +1 -1
- package/dist/sfx-MZJZXYY3.cjs +446 -0
- package/dist/sfx-MZJZXYY3.cjs.map +1 -0
- package/dist/sfx-S37DDC66.js +434 -0
- package/dist/sfx-S37DDC66.js.map +1 -0
- 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.ts → types-7VtTEKUW.d.cts} +102 -6
- package/dist/{types-3c8Vymgw.d.cts → types-7VtTEKUW.d.ts} +102 -6
- package/docs/api.md +36 -0
- package/docs/dsl.md +56 -4
- package/llms-full.txt +268 -21
- package/llms.txt +1 -1
- package/package.json +3 -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
|
@@ -174,6 +174,12 @@ interface SequenceCommon {
|
|
|
174
174
|
* Routed through PIXI's native `setMask({ inverse: true })`.
|
|
175
175
|
*/
|
|
176
176
|
maskInverted?: boolean;
|
|
177
|
+
/**
|
|
178
|
+
* How this layer blends with what is behind it. `'add'` and `'screen'` brighten (glows, light leaks,
|
|
179
|
+
* overlapping halos add up instead of covering each other), `'multiply'` darkens. Default `'normal'`.
|
|
180
|
+
* On a composition the mode is inherited by its children: each one blends with what is behind it.
|
|
181
|
+
*/
|
|
182
|
+
blendMode?: 'normal' | 'add' | 'screen' | 'multiply';
|
|
177
183
|
/**
|
|
178
184
|
* Opt this layer into 2.5D: it can then use `z`, `rotationX`, `rotationY`
|
|
179
185
|
* (in `initial` / keyframes) and is projected through the composition's
|
|
@@ -211,7 +217,8 @@ interface TextSequenceSpec extends SequenceCommon {
|
|
|
211
217
|
/** How `{value}` is printed: `decimals` (default 0) and thousands `grouping` (default false). */
|
|
212
218
|
format?: {
|
|
213
219
|
decimals?: number;
|
|
214
|
-
grouping?: boolean;
|
|
220
|
+
grouping?: boolean; /** Zero-pad the whole part to this many digits (`pad: 2` → `05`; a clock `18:42:05`). */
|
|
221
|
+
pad?: number;
|
|
215
222
|
};
|
|
216
223
|
/** Subset of PIXI v8 TextStyleOptions. String values may be exprs (e.g. fontSize: 'GW * 0.05'). */
|
|
217
224
|
style?: Record<string, PropValue | {
|
|
@@ -226,12 +233,71 @@ interface TextSequenceSpec extends SequenceCommon {
|
|
|
226
233
|
*/
|
|
227
234
|
colorSpace?: 'rgb' | 'oklab' | 'oklch';
|
|
228
235
|
}
|
|
229
|
-
|
|
236
|
+
type SfxPreset = 'click' | 'pop' | 'swoosh' | 'swipe' | 'hit' | 'riser' | 'chime' | 'beep' | 'coin' | 'glitch' | 'typewriter';
|
|
237
|
+
/** The intent-level knobs shared by presets and custom sounds. */
|
|
238
|
+
interface SfxKnobs {
|
|
239
|
+
/** Semitones up (+) or down (−); 12 = one octave. Default 0, range ±24. */
|
|
240
|
+
pitch?: number;
|
|
241
|
+
/** −1 = dark / muffled … 0 = as designed … 1 = bright / crisp. Default 0. */
|
|
242
|
+
brightness?: number;
|
|
243
|
+
/** Another take of the random parts (noise grain, glitch pattern, typewriter key). Same seed ⇒ same sound. Default 0. */
|
|
244
|
+
seed?: number;
|
|
245
|
+
}
|
|
246
|
+
/**
|
|
247
|
+
* One layer of a custom sound. Most sounds are one or two voices: noise through a moving band-pass
|
|
248
|
+
* (whooshes, air, impacts) or a tone with a pitch sweep (blips, zaps, drops).
|
|
249
|
+
*/
|
|
250
|
+
interface SfxVoice {
|
|
251
|
+
/** `'noise'` for air, hiss and impacts; a waveform for tones. */
|
|
252
|
+
wave: 'sine' | 'triangle' | 'square' | 'saw' | 'noise';
|
|
253
|
+
/** Tone frequency in Hz, or `[from, to]` to sweep (exponentially). Ignored for noise. Default 440. */
|
|
254
|
+
freq?: number | [number, number];
|
|
255
|
+
/** Keeps part of the spectrum; `freq` may sweep: `{ type: 'bandpass', freq: [500, 5000] }` = a band of noise rising. `q` default 0.7 (≥ 0.7). */
|
|
256
|
+
filter?: {
|
|
257
|
+
type: 'lowpass' | 'highpass' | 'bandpass';
|
|
258
|
+
freq: number | [number, number];
|
|
259
|
+
q?: number;
|
|
260
|
+
};
|
|
261
|
+
/** Level over the voice: `'fall'` (default) hit then decay · `'bell'` swell then fade · `'swell'` grow to the end · `'hold'` steady. */
|
|
262
|
+
envelope?: 'fall' | 'bell' | 'swell' | 'hold';
|
|
263
|
+
/** −1 left … 1 right, or `[from, to]` to move. Default 0. */
|
|
264
|
+
pan?: number | [number, number];
|
|
265
|
+
/** The part of the sound this voice plays, as fractions 0–1 of its length. Default 0 and 1. */
|
|
266
|
+
from?: number;
|
|
267
|
+
to?: number;
|
|
268
|
+
/** Level relative to the other voices. Default 1. */
|
|
269
|
+
gain?: number;
|
|
270
|
+
}
|
|
271
|
+
/** A preset with knobs, or a custom sound made of `voices` (length = the layer's duration, default 0.5 s). */
|
|
272
|
+
type SfxOptions = (SfxKnobs & {
|
|
273
|
+
preset: SfxPreset;
|
|
274
|
+
voices?: never;
|
|
275
|
+
}) | (SfxKnobs & {
|
|
276
|
+
voices: SfxVoice[];
|
|
277
|
+
preset?: never;
|
|
278
|
+
});
|
|
279
|
+
interface AudioBase extends SequenceCommon {
|
|
230
280
|
type: 'audio';
|
|
281
|
+
/** Level. 1 = the file as recorded, or the sfx preset's standard level (peaks at −12 dBFS). Animatable. */
|
|
282
|
+
volume?: number;
|
|
283
|
+
}
|
|
284
|
+
/** Plays an audio file from `assets`. `duration` defaults to the file's length (with `loop`: to the composition's end). */
|
|
285
|
+
interface AudioAssetSpec extends AudioBase {
|
|
231
286
|
asset: string;
|
|
287
|
+
sfx?: never;
|
|
288
|
+
/** Repeat the file for the whole `duration`. Without it the layer goes silent when the file ends. */
|
|
232
289
|
loop?: boolean;
|
|
233
|
-
volume?: number;
|
|
234
290
|
}
|
|
291
|
+
/**
|
|
292
|
+
* Plays a synthesised sound effect. `duration` IS the sound's length: omitted, it is the preset's own
|
|
293
|
+
* length; given, the sound is stretched or shortened to it. `at` is where the sound starts.
|
|
294
|
+
*/
|
|
295
|
+
interface AudioSfxSpec extends AudioBase {
|
|
296
|
+
sfx: SfxPreset | SfxOptions;
|
|
297
|
+
asset?: never;
|
|
298
|
+
loop?: never;
|
|
299
|
+
}
|
|
300
|
+
type AudioSequenceSpec = AudioAssetSpec | AudioSfxSpec;
|
|
235
301
|
interface CompositionSequenceSpec extends SequenceCommon {
|
|
236
302
|
type: 'composition';
|
|
237
303
|
width?: number;
|
|
@@ -287,6 +353,10 @@ interface ShapeBase extends SequenceCommon {
|
|
|
287
353
|
* sweeps; `oklab` is straight-line in the chromaticity plane.
|
|
288
354
|
*/
|
|
289
355
|
colorSpace?: 'rgb' | 'oklab' | 'oklch';
|
|
356
|
+
/** How an open stroke ends: `'butt'` (default), `'round'` (a friendly progress-ring end) or `'square'`. Top level or in `initial`. */
|
|
357
|
+
strokeCap?: 'butt' | 'round' | 'square';
|
|
358
|
+
/** How stroke corners join: `'miter'` (default; sharp points can poke out), `'round'` or `'bevel'`. Top level or in `initial`. */
|
|
359
|
+
strokeJoin?: 'miter' | 'round' | 'bevel';
|
|
290
360
|
}
|
|
291
361
|
interface RectShapeSpec extends ShapeBase {
|
|
292
362
|
shape: 'rect';
|
|
@@ -318,6 +388,22 @@ interface EllipseShapeSpec extends ShapeBase {
|
|
|
318
388
|
anchorX?: PropValue;
|
|
319
389
|
anchorY?: PropValue;
|
|
320
390
|
}
|
|
391
|
+
/**
|
|
392
|
+
* An arc around the local origin (its centre). Angles are in degrees: 0 = 3 o'clock, positive = clockwise,
|
|
393
|
+
* so a progress ring starts at `startAngle: -90` (12 o'clock) and animates `endAngle` from -90 to 270.
|
|
394
|
+
* With a stroke and no fill it is an open arc line (`strokeCap: 'round'` for rounded ends); with a fill it is a
|
|
395
|
+
* sector (a pie slice), and with `innerRadius` a ring segment (a donut slice). A sweep of 360° or more is a full circle.
|
|
396
|
+
*/
|
|
397
|
+
interface ArcShapeSpec extends ShapeBase {
|
|
398
|
+
shape: 'arc';
|
|
399
|
+
radius: PropValue;
|
|
400
|
+
/** Hole radius of a filled ring segment. Default 0 (a pie slice to the centre). Animatable. */
|
|
401
|
+
innerRadius?: PropValue;
|
|
402
|
+
/** Degrees. Default 0. Animatable. */
|
|
403
|
+
startAngle?: PropValue;
|
|
404
|
+
/** Degrees. Default 360. Animatable. */
|
|
405
|
+
endAngle?: PropValue;
|
|
406
|
+
}
|
|
321
407
|
interface LineShapeSpec extends ShapeBase {
|
|
322
408
|
shape: 'line';
|
|
323
409
|
/** Line endpoints relative to the shape's local origin. */
|
|
@@ -336,7 +422,7 @@ interface PathShapeSpec extends ShapeBase {
|
|
|
336
422
|
/** SVG path data (`d` attribute). Goes through PIXI's GraphicsContext.svg(). */
|
|
337
423
|
d: string;
|
|
338
424
|
}
|
|
339
|
-
type ShapeSequenceSpec = RectShapeSpec | CircleShapeSpec | EllipseShapeSpec | LineShapeSpec | PolygonShapeSpec | PathShapeSpec;
|
|
425
|
+
type ShapeSequenceSpec = RectShapeSpec | CircleShapeSpec | EllipseShapeSpec | ArcShapeSpec | LineShapeSpec | PolygonShapeSpec | PathShapeSpec;
|
|
340
426
|
type SequenceSpec = VideoSequenceSpec | ImageSequenceSpec | TextSequenceSpec | AudioSequenceSpec | CompositionSequenceSpec | CameraSequenceSpec | ShapeSequenceSpec;
|
|
341
427
|
/** Top-level composition (root node) — same as `CompositionSequenceSpec` minus the discriminant. */
|
|
342
428
|
interface CompositionSpec extends SequenceCommon {
|
|
@@ -353,7 +439,17 @@ interface CompositionShape {
|
|
|
353
439
|
}
|
|
354
440
|
/** Audio descriptor pushed to the mixdown queue by AudioSequence/VideoSequence. */
|
|
355
441
|
interface AudioDescriptor {
|
|
356
|
-
buffer
|
|
442
|
+
/** Decoded audio (a file or a video's soundtrack). Exactly one of `buffer` / `synth` is set. */
|
|
443
|
+
buffer?: AudioBuffer;
|
|
444
|
+
/** Stereo samples (left, right) made on demand at the mix's sample rate (a synthesised sfx). Equal keys render equal samples. */
|
|
445
|
+
synth?: {
|
|
446
|
+
key: string;
|
|
447
|
+
render: (sampleRate: number) => [Float32Array, Float32Array];
|
|
448
|
+
};
|
|
449
|
+
/** Who made it, for warnings and inspectAudio: `layer "pop-1"`. */
|
|
450
|
+
layer?: string;
|
|
451
|
+
/** What it plays: `sfx "pop"`, `asset "bgm"`, `video "green"`. */
|
|
452
|
+
source?: string;
|
|
357
453
|
loop: boolean;
|
|
358
454
|
start: number;
|
|
359
455
|
end: number;
|
|
@@ -364,4 +460,4 @@ interface AudioDescriptor {
|
|
|
364
460
|
}[];
|
|
365
461
|
}
|
|
366
462
|
|
|
367
|
-
export type {
|
|
463
|
+
export type { ArcShapeSpec as A, AudioDescriptor as B, CompositionShape as C, DipTransition as D, EllipseShapeSpec as E, FilterSpec as F, GradientSpec as G, ImageSequenceSpec as I, Keyframe as K, LineShapeSpec as L, PathShapeSpec as P, RectShapeSpec as R, SequenceSpec as S, TextSequenceSpec as T, VideoSequenceSpec as V, WipeTransition as W, ZoomTransition as Z, CameraSequenceSpec as a, SequenceCommon as b, AssetSpec as c, AudioAssetSpec as d, AudioSequenceSpec as e, AudioSfxSpec as f, ChromaKeyFilterSpec as g, CircleShapeSpec as h, CompositionSequenceSpec as i, CompositionSpec as j, CrossfadeTransition as k, CustomFilterSpec as l, DissolveTransition as m, Expr as n, IrisTransition as o, PolygonShapeSpec as p, PropValue as q, Props as r, SfxKnobs as s, SfxOptions as t, SfxPreset as u, SfxVoice as v, ShapeSequenceSpec as w, SlideTransition as x, TransitionCommon as y, TransitionSpec as z };
|
|
@@ -174,6 +174,12 @@ interface SequenceCommon {
|
|
|
174
174
|
* Routed through PIXI's native `setMask({ inverse: true })`.
|
|
175
175
|
*/
|
|
176
176
|
maskInverted?: boolean;
|
|
177
|
+
/**
|
|
178
|
+
* How this layer blends with what is behind it. `'add'` and `'screen'` brighten (glows, light leaks,
|
|
179
|
+
* overlapping halos add up instead of covering each other), `'multiply'` darkens. Default `'normal'`.
|
|
180
|
+
* On a composition the mode is inherited by its children: each one blends with what is behind it.
|
|
181
|
+
*/
|
|
182
|
+
blendMode?: 'normal' | 'add' | 'screen' | 'multiply';
|
|
177
183
|
/**
|
|
178
184
|
* Opt this layer into 2.5D: it can then use `z`, `rotationX`, `rotationY`
|
|
179
185
|
* (in `initial` / keyframes) and is projected through the composition's
|
|
@@ -211,7 +217,8 @@ interface TextSequenceSpec extends SequenceCommon {
|
|
|
211
217
|
/** How `{value}` is printed: `decimals` (default 0) and thousands `grouping` (default false). */
|
|
212
218
|
format?: {
|
|
213
219
|
decimals?: number;
|
|
214
|
-
grouping?: boolean;
|
|
220
|
+
grouping?: boolean; /** Zero-pad the whole part to this many digits (`pad: 2` → `05`; a clock `18:42:05`). */
|
|
221
|
+
pad?: number;
|
|
215
222
|
};
|
|
216
223
|
/** Subset of PIXI v8 TextStyleOptions. String values may be exprs (e.g. fontSize: 'GW * 0.05'). */
|
|
217
224
|
style?: Record<string, PropValue | {
|
|
@@ -226,12 +233,71 @@ interface TextSequenceSpec extends SequenceCommon {
|
|
|
226
233
|
*/
|
|
227
234
|
colorSpace?: 'rgb' | 'oklab' | 'oklch';
|
|
228
235
|
}
|
|
229
|
-
|
|
236
|
+
type SfxPreset = 'click' | 'pop' | 'swoosh' | 'swipe' | 'hit' | 'riser' | 'chime' | 'beep' | 'coin' | 'glitch' | 'typewriter';
|
|
237
|
+
/** The intent-level knobs shared by presets and custom sounds. */
|
|
238
|
+
interface SfxKnobs {
|
|
239
|
+
/** Semitones up (+) or down (−); 12 = one octave. Default 0, range ±24. */
|
|
240
|
+
pitch?: number;
|
|
241
|
+
/** −1 = dark / muffled … 0 = as designed … 1 = bright / crisp. Default 0. */
|
|
242
|
+
brightness?: number;
|
|
243
|
+
/** Another take of the random parts (noise grain, glitch pattern, typewriter key). Same seed ⇒ same sound. Default 0. */
|
|
244
|
+
seed?: number;
|
|
245
|
+
}
|
|
246
|
+
/**
|
|
247
|
+
* One layer of a custom sound. Most sounds are one or two voices: noise through a moving band-pass
|
|
248
|
+
* (whooshes, air, impacts) or a tone with a pitch sweep (blips, zaps, drops).
|
|
249
|
+
*/
|
|
250
|
+
interface SfxVoice {
|
|
251
|
+
/** `'noise'` for air, hiss and impacts; a waveform for tones. */
|
|
252
|
+
wave: 'sine' | 'triangle' | 'square' | 'saw' | 'noise';
|
|
253
|
+
/** Tone frequency in Hz, or `[from, to]` to sweep (exponentially). Ignored for noise. Default 440. */
|
|
254
|
+
freq?: number | [number, number];
|
|
255
|
+
/** Keeps part of the spectrum; `freq` may sweep: `{ type: 'bandpass', freq: [500, 5000] }` = a band of noise rising. `q` default 0.7 (≥ 0.7). */
|
|
256
|
+
filter?: {
|
|
257
|
+
type: 'lowpass' | 'highpass' | 'bandpass';
|
|
258
|
+
freq: number | [number, number];
|
|
259
|
+
q?: number;
|
|
260
|
+
};
|
|
261
|
+
/** Level over the voice: `'fall'` (default) hit then decay · `'bell'` swell then fade · `'swell'` grow to the end · `'hold'` steady. */
|
|
262
|
+
envelope?: 'fall' | 'bell' | 'swell' | 'hold';
|
|
263
|
+
/** −1 left … 1 right, or `[from, to]` to move. Default 0. */
|
|
264
|
+
pan?: number | [number, number];
|
|
265
|
+
/** The part of the sound this voice plays, as fractions 0–1 of its length. Default 0 and 1. */
|
|
266
|
+
from?: number;
|
|
267
|
+
to?: number;
|
|
268
|
+
/** Level relative to the other voices. Default 1. */
|
|
269
|
+
gain?: number;
|
|
270
|
+
}
|
|
271
|
+
/** A preset with knobs, or a custom sound made of `voices` (length = the layer's duration, default 0.5 s). */
|
|
272
|
+
type SfxOptions = (SfxKnobs & {
|
|
273
|
+
preset: SfxPreset;
|
|
274
|
+
voices?: never;
|
|
275
|
+
}) | (SfxKnobs & {
|
|
276
|
+
voices: SfxVoice[];
|
|
277
|
+
preset?: never;
|
|
278
|
+
});
|
|
279
|
+
interface AudioBase extends SequenceCommon {
|
|
230
280
|
type: 'audio';
|
|
281
|
+
/** Level. 1 = the file as recorded, or the sfx preset's standard level (peaks at −12 dBFS). Animatable. */
|
|
282
|
+
volume?: number;
|
|
283
|
+
}
|
|
284
|
+
/** Plays an audio file from `assets`. `duration` defaults to the file's length (with `loop`: to the composition's end). */
|
|
285
|
+
interface AudioAssetSpec extends AudioBase {
|
|
231
286
|
asset: string;
|
|
287
|
+
sfx?: never;
|
|
288
|
+
/** Repeat the file for the whole `duration`. Without it the layer goes silent when the file ends. */
|
|
232
289
|
loop?: boolean;
|
|
233
|
-
volume?: number;
|
|
234
290
|
}
|
|
291
|
+
/**
|
|
292
|
+
* Plays a synthesised sound effect. `duration` IS the sound's length: omitted, it is the preset's own
|
|
293
|
+
* length; given, the sound is stretched or shortened to it. `at` is where the sound starts.
|
|
294
|
+
*/
|
|
295
|
+
interface AudioSfxSpec extends AudioBase {
|
|
296
|
+
sfx: SfxPreset | SfxOptions;
|
|
297
|
+
asset?: never;
|
|
298
|
+
loop?: never;
|
|
299
|
+
}
|
|
300
|
+
type AudioSequenceSpec = AudioAssetSpec | AudioSfxSpec;
|
|
235
301
|
interface CompositionSequenceSpec extends SequenceCommon {
|
|
236
302
|
type: 'composition';
|
|
237
303
|
width?: number;
|
|
@@ -287,6 +353,10 @@ interface ShapeBase extends SequenceCommon {
|
|
|
287
353
|
* sweeps; `oklab` is straight-line in the chromaticity plane.
|
|
288
354
|
*/
|
|
289
355
|
colorSpace?: 'rgb' | 'oklab' | 'oklch';
|
|
356
|
+
/** How an open stroke ends: `'butt'` (default), `'round'` (a friendly progress-ring end) or `'square'`. Top level or in `initial`. */
|
|
357
|
+
strokeCap?: 'butt' | 'round' | 'square';
|
|
358
|
+
/** How stroke corners join: `'miter'` (default; sharp points can poke out), `'round'` or `'bevel'`. Top level or in `initial`. */
|
|
359
|
+
strokeJoin?: 'miter' | 'round' | 'bevel';
|
|
290
360
|
}
|
|
291
361
|
interface RectShapeSpec extends ShapeBase {
|
|
292
362
|
shape: 'rect';
|
|
@@ -318,6 +388,22 @@ interface EllipseShapeSpec extends ShapeBase {
|
|
|
318
388
|
anchorX?: PropValue;
|
|
319
389
|
anchorY?: PropValue;
|
|
320
390
|
}
|
|
391
|
+
/**
|
|
392
|
+
* An arc around the local origin (its centre). Angles are in degrees: 0 = 3 o'clock, positive = clockwise,
|
|
393
|
+
* so a progress ring starts at `startAngle: -90` (12 o'clock) and animates `endAngle` from -90 to 270.
|
|
394
|
+
* With a stroke and no fill it is an open arc line (`strokeCap: 'round'` for rounded ends); with a fill it is a
|
|
395
|
+
* sector (a pie slice), and with `innerRadius` a ring segment (a donut slice). A sweep of 360° or more is a full circle.
|
|
396
|
+
*/
|
|
397
|
+
interface ArcShapeSpec extends ShapeBase {
|
|
398
|
+
shape: 'arc';
|
|
399
|
+
radius: PropValue;
|
|
400
|
+
/** Hole radius of a filled ring segment. Default 0 (a pie slice to the centre). Animatable. */
|
|
401
|
+
innerRadius?: PropValue;
|
|
402
|
+
/** Degrees. Default 0. Animatable. */
|
|
403
|
+
startAngle?: PropValue;
|
|
404
|
+
/** Degrees. Default 360. Animatable. */
|
|
405
|
+
endAngle?: PropValue;
|
|
406
|
+
}
|
|
321
407
|
interface LineShapeSpec extends ShapeBase {
|
|
322
408
|
shape: 'line';
|
|
323
409
|
/** Line endpoints relative to the shape's local origin. */
|
|
@@ -336,7 +422,7 @@ interface PathShapeSpec extends ShapeBase {
|
|
|
336
422
|
/** SVG path data (`d` attribute). Goes through PIXI's GraphicsContext.svg(). */
|
|
337
423
|
d: string;
|
|
338
424
|
}
|
|
339
|
-
type ShapeSequenceSpec = RectShapeSpec | CircleShapeSpec | EllipseShapeSpec | LineShapeSpec | PolygonShapeSpec | PathShapeSpec;
|
|
425
|
+
type ShapeSequenceSpec = RectShapeSpec | CircleShapeSpec | EllipseShapeSpec | ArcShapeSpec | LineShapeSpec | PolygonShapeSpec | PathShapeSpec;
|
|
340
426
|
type SequenceSpec = VideoSequenceSpec | ImageSequenceSpec | TextSequenceSpec | AudioSequenceSpec | CompositionSequenceSpec | CameraSequenceSpec | ShapeSequenceSpec;
|
|
341
427
|
/** Top-level composition (root node) — same as `CompositionSequenceSpec` minus the discriminant. */
|
|
342
428
|
interface CompositionSpec extends SequenceCommon {
|
|
@@ -353,7 +439,17 @@ interface CompositionShape {
|
|
|
353
439
|
}
|
|
354
440
|
/** Audio descriptor pushed to the mixdown queue by AudioSequence/VideoSequence. */
|
|
355
441
|
interface AudioDescriptor {
|
|
356
|
-
buffer
|
|
442
|
+
/** Decoded audio (a file or a video's soundtrack). Exactly one of `buffer` / `synth` is set. */
|
|
443
|
+
buffer?: AudioBuffer;
|
|
444
|
+
/** Stereo samples (left, right) made on demand at the mix's sample rate (a synthesised sfx). Equal keys render equal samples. */
|
|
445
|
+
synth?: {
|
|
446
|
+
key: string;
|
|
447
|
+
render: (sampleRate: number) => [Float32Array, Float32Array];
|
|
448
|
+
};
|
|
449
|
+
/** Who made it, for warnings and inspectAudio: `layer "pop-1"`. */
|
|
450
|
+
layer?: string;
|
|
451
|
+
/** What it plays: `sfx "pop"`, `asset "bgm"`, `video "green"`. */
|
|
452
|
+
source?: string;
|
|
357
453
|
loop: boolean;
|
|
358
454
|
start: number;
|
|
359
455
|
end: number;
|
|
@@ -364,4 +460,4 @@ interface AudioDescriptor {
|
|
|
364
460
|
}[];
|
|
365
461
|
}
|
|
366
462
|
|
|
367
|
-
export type {
|
|
463
|
+
export type { ArcShapeSpec as A, AudioDescriptor as B, CompositionShape as C, DipTransition as D, EllipseShapeSpec as E, FilterSpec as F, GradientSpec as G, ImageSequenceSpec as I, Keyframe as K, LineShapeSpec as L, PathShapeSpec as P, RectShapeSpec as R, SequenceSpec as S, TextSequenceSpec as T, VideoSequenceSpec as V, WipeTransition as W, ZoomTransition as Z, CameraSequenceSpec as a, SequenceCommon as b, AssetSpec as c, AudioAssetSpec as d, AudioSequenceSpec as e, AudioSfxSpec as f, ChromaKeyFilterSpec as g, CircleShapeSpec as h, CompositionSequenceSpec as i, CompositionSpec as j, CrossfadeTransition as k, CustomFilterSpec as l, DissolveTransition as m, Expr as n, IrisTransition as o, PolygonShapeSpec as p, PropValue as q, Props as r, SfxKnobs as s, SfxOptions as t, SfxPreset as u, SfxVoice as v, ShapeSequenceSpec as w, SlideTransition as x, TransitionCommon as y, TransitionSpec as z };
|
package/docs/api.md
CHANGED
|
@@ -89,6 +89,21 @@ contactSheet(options?: {
|
|
|
89
89
|
|
|
90
90
|
Many frames on **one** PNG, each labelled `frame N · T s`. The cheapest way to check a whole animation by eye (include the middle of every transition and the last second). Restores the current frame afterwards.
|
|
91
91
|
|
|
92
|
+
### `measureText(text, style?): { width, height, lines }` and `splitText(text, style?, options?): TextPiece[]`
|
|
93
|
+
|
|
94
|
+
Text layout helpers, exported from `pixi-effects`. They measure with the renderer's own font metrics (the same numbers a text layer with that `style` has), so a layout built from them matches what is drawn. `style` is a text layer's `style` with numbers (no expressions). A web font must be loaded first.
|
|
95
|
+
|
|
96
|
+
```ts
|
|
97
|
+
measureText('Hello', { fontSize: 72 }); // { width: 187.4, height: 81.3, lines: 1 }
|
|
98
|
+
|
|
99
|
+
interface SplitOptions { by?: 'chars' | 'words' | 'lines'; x?: number; y?: number; align?: 'left' | 'center' | 'right' }
|
|
100
|
+
interface TextPiece { text: string; x: number; y: number; width: number; index: number; line: number }
|
|
101
|
+
splitText('Hello world', style, { by: 'words', x: 640, y: 300, align: 'center' });
|
|
102
|
+
// → [{ text: 'Hello', x, y, width, index: 0, line: 0 }, { text: 'world', ... }]
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
`x` is the line's left edge, or its centre / right edge with `align` (each line of a `\n` text is aligned on its own width); `y` is the top of the first line, and further lines go down by the line height. Whitespace takes its room but is not returned as a piece. Use one text layer per piece with the same `style` and the default top-left anchor: `{ type: 'text', text: p.text, style, initial: { x: p.x, y: p.y } }`. Kerning is kept (a piece's x is the measured width of everything before it).
|
|
106
|
+
|
|
92
107
|
### `movie.inspect(frame?, options?): Promise<InspectReport>`
|
|
93
108
|
|
|
94
109
|
Where every layer is drawn at `frame`, as JSON — for checking layout without eyes. Seeks there and stays there. `options.layers`: `'visible'` (default, only layers drawn at this frame), `'all'`, or `'none'` (just `summary` and `issues`). Faint layers (alpha < 0.3) and text whose x / y is animated (tickers) are not reported.
|
|
@@ -109,6 +124,27 @@ interface InspectReport {
|
|
|
109
124
|
}
|
|
110
125
|
```
|
|
111
126
|
|
|
127
|
+
### `movie.inspectAudio(options?): AudioReport`
|
|
128
|
+
|
|
129
|
+
The soundtrack as numbers — for checking sound without listening. Every sound is also measured on its own, so overlaps do not blur it. `options.window`: seconds per row of `windows` (default 0.1).
|
|
130
|
+
|
|
131
|
+
```ts
|
|
132
|
+
interface AudioReport {
|
|
133
|
+
duration: number; sampleRate: number;
|
|
134
|
+
peakDb: number; peakAt: number; // the mix before limiting; > 0 dBFS means it was limited
|
|
135
|
+
sources: Array<{
|
|
136
|
+
layer: string; source: string; // 'layer "hit"', 'sfx "hit"' | 'asset "bgm"' | 'video "clip"'
|
|
137
|
+
start: number; end: number; // movie time
|
|
138
|
+
peakDb: number; // the MIX while it plays
|
|
139
|
+
sound: { length: number; peakDb: number; loudestAt: number; brightnessHz: number }; // the sound alone
|
|
140
|
+
}>;
|
|
141
|
+
windows: Array<{ t: number; rmsDb: number; peakDb: number; brightnessHz: number }>;
|
|
142
|
+
issues: string[]; // limited mix, inaudible layer, sfx cut off by the end, no audio — read first
|
|
143
|
+
}
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
`render()` encodes this same mix: AAC in mp4 / mov (the encoder delay is trimmed with an edit list), Opus in webm / mkv.
|
|
147
|
+
|
|
112
148
|
### `movie.render(options?): Promise<Blob>`
|
|
113
149
|
|
|
114
150
|
Renders the entire timeline to a single video file. Pauses playback first.
|
package/docs/dsl.md
CHANGED
|
@@ -59,7 +59,7 @@ interface SequenceCommon {
|
|
|
59
59
|
| Layer | Where `x, y` land by default | To change it |
|
|
60
60
|
|---|---|---|
|
|
61
61
|
| `shape` rect / circle / ellipse | the **centre** of the shape | `anchorX` / `anchorY` (0–1). A bar growing from its base: `anchorY: 1`; left to right: `anchorX: 0` |
|
|
62
|
-
| `shape` line / polygon / path | the centre of the shape's bounds |
|
|
62
|
+
| `shape` line / polygon / path | the centre of the shape's bounds | `pivotX` / `pivotY` (in the shape's own canvas coordinates): the point that sits at `x, y` and that rotation / scale turn about. **`anchorX` / `anchorY` do nothing on these.** |
|
|
63
63
|
| `text`, `image`, `video` | the **top-left** corner | `anchorX` / `anchorY: 0.5` to centre |
|
|
64
64
|
| `composition` | its top-left corner | `pivotX` / `pivotY` = the point that sits at `x, y` and that rotation / scale turn about |
|
|
65
65
|
|
|
@@ -71,7 +71,7 @@ A layer is **not removed** when its lifespan `[at, at + duration)` ends: it is o
|
|
|
71
71
|
|
|
72
72
|
### `text`
|
|
73
73
|
|
|
74
|
-
Renders a [PIXI.Text](https://pixijs.com/8.x/guides/components/scene-objects/text/text). Animatable position, rotation, scale, opacity and `fill`. `style` accepts any PixiJS `TextStyle` field and is applied once at build time — notably `fontSize fontFamily fontWeight fill letterSpacing lineHeight align wordWrap wordWrapWidth stroke dropShadow padding` (numbers may be expressions). A `dropShadow` glow is clipped unless `padding` is at least twice its `blur`. Text content cannot change over time (
|
|
74
|
+
Renders a [PIXI.Text](https://pixijs.com/8.x/guides/components/scene-objects/text/text). Animatable position, rotation, scale, opacity and `fill`. `style` accepts any PixiJS `TextStyle` field and is applied once at build time — notably `fontSize fontFamily fontWeight fill letterSpacing lineHeight align wordWrap wordWrapWidth stroke dropShadow padding` (numbers may be expressions). A `dropShadow` glow is clipped unless `padding` is at least twice its `blur`. Text content cannot change over time, with one exception: a `{value}` placeholder in `text` prints an animatable number (`text: '{value} users'`, `initial: { value: 0 }`, a keyframe `to: { value: 2480 }`; `format: { decimals, grouping }`) — that is how counters work. Any other change of wording is a second layer (stack layers with `at` / `duration`). Multi-line text is centre-aligned unless `style.align` says otherwise. In expressions `w` / `h` are the size of the styled text, so `x: '-w'` places it just off the left edge.
|
|
75
75
|
|
|
76
76
|
```ts
|
|
77
77
|
{
|
|
@@ -116,7 +116,7 @@ A text layer has one animatable number, `value`, printed wherever the text conta
|
|
|
116
116
|
}
|
|
117
117
|
```
|
|
118
118
|
|
|
119
|
-
`format`: `decimals` (default 0)
|
|
119
|
+
`format`: `decimals` (default 0), thousands `grouping` (default false) and `pad` (zero-pad the whole part to that many digits: `pad: 2` gives `05`, so a clock is `text: '18:42:{value}'` with `format: { pad: 2 }`). Put prefixes and suffixes in the text (`'${value}'`, `'{value}%'`). If `value` is animated but the text has no `{value}`, a warning says so.
|
|
120
120
|
|
|
121
121
|
### `image`
|
|
122
122
|
|
|
@@ -179,10 +179,40 @@ Audio-only sequence. No visual. Volume is animatable via keyframes.
|
|
|
179
179
|
|
|
180
180
|
Audio is mixed during `Movie.init()` and during `Movie.render()`. Volume keyframes interpolate linearly.
|
|
181
181
|
|
|
182
|
+
#### Sound effects without files: `sfx`
|
|
183
|
+
|
|
184
|
+
```ts
|
|
185
|
+
{ type: 'audio', sfx: 'swoosh', at: 2 } // a preset at its own length
|
|
186
|
+
{ type: 'audio', sfx: { preset: 'hit', pitch: -3, brightness: -0.5 }, at: 4 } // lower and darker
|
|
187
|
+
{ type: 'audio', sfx: 'riser', at: 3, duration: 1.5, volume: 0.7 } // duration IS the sound's length (stretched)
|
|
188
|
+
{ type: 'audio', at: 6, duration: 0.3, // a custom sound
|
|
189
|
+
sfx: { voices: [{ wave: 'square', freq: [1600, 200], filter: { type: 'lowpass', freq: 3000 }, envelope: 'fall' }] } }
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
The sound is synthesised from data at init (no asset, no network), deterministic, and the same mix in playback and in the exported file (encoded there: AAC / Opus are lossy). `at` is where it starts; `volume` and volume keyframes work as for files; `loop` is not available (one layer per hit). Knobs: `pitch` (semitones, ±24), `brightness` (−1 dark … 1 bright), `seed` (another take of the random parts). `volume: 1` is the preset's standard level, peaking at −12 … −18 dBFS depending on the preset; the finished mix is soft-limited above 0.9 with a warning naming the layers. A custom sound has up to 16 `voices`: `wave` (`sine` `triangle` `square` `saw` `noise`), `freq` (Hz or `[from, to]`), `filter` (`{ type: 'lowpass' | 'highpass' | 'bandpass', freq, q }`), `envelope` (`fall` `bell` `swell` `hold`), `pan`, `from` / `to` (part of the sound, 0–1), `gain`; its default length is 0.5 s.
|
|
193
|
+
|
|
194
|
+
| Preset | Default length | Loudest at | Brightness | Use for |
|
|
195
|
+
|---|---|---|---|---|
|
|
196
|
+
| `click` | 0.04 s | start | ≈ 12780 Hz | UI click, tick |
|
|
197
|
+
| `pop` | 0.12 s | start | ≈ 390 Hz | an element appearing |
|
|
198
|
+
| `swoosh` | 0.5 s | 0.16 s | ≈ 3070 Hz | transitions, fly-pasts (moves left → right) |
|
|
199
|
+
| `swipe` | 0.22 s | start | ≈ 13190 Hz | quick UI swipe |
|
|
200
|
+
| `hit` | 0.7 s | start | ≈ 150 Hz | impact, slam |
|
|
201
|
+
| `riser` | 1 s | its end | ≈ 11850 Hz | build-up into a hit |
|
|
202
|
+
| `chime` | 1.4 s | start | ≈ 1400 Hz | success, reveal |
|
|
203
|
+
| `beep` | 0.16 s | flat | ≈ 1030 Hz | countdown, alert |
|
|
204
|
+
| `coin` | 0.4 s | 0.07 s | ≈ 1870 Hz | reward, collect |
|
|
205
|
+
| `glitch` | 0.35 s | 0.22 s | ≈ 3970 Hz | digital error |
|
|
206
|
+
| `typewriter` | 0.06 s | start | ≈ 3260 Hz | one key (vary `seed` per key) |
|
|
207
|
+
|
|
208
|
+
Brightness is the spectral centroid that `movie.inspectAudio()` reports (`sound.brightnessHz`), at 48 kHz with no knobs.
|
|
209
|
+
|
|
182
210
|
### `composition`
|
|
183
211
|
|
|
184
212
|
Nested composition. Same shape as the root spec but with `type: 'composition'` and an explicit `width`/`height`. Children animate within the local coordinate system; the composition itself can be positioned, scaled, and rotated as a unit.
|
|
185
213
|
|
|
214
|
+
**Size, clipping and culling** (measured): a 2D composition does **not** clip its children — a child far outside `width × height` is still drawn. But the rectangle `width × height` is what the renderer uses to decide the group is on screen: when that whole rectangle is off the canvas the group is skipped even if a child is placed back on screen. So keep the rectangle around what it draws (or make it canvas-sized). A `threeD: true` composition (a card) is rendered into a texture of exactly `width × height`, so it **does** clip its children there; size it for soft shadows too.
|
|
215
|
+
|
|
186
216
|
```ts
|
|
187
217
|
{
|
|
188
218
|
type: 'composition',
|
|
@@ -223,10 +253,21 @@ Every primitive draws centred on its local origin (so `anchorX`/`anchorY` and `p
|
|
|
223
253
|
| `rect` | `width`, `height` | `cornerRadius` |
|
|
224
254
|
| `circle` | `radius` | — |
|
|
225
255
|
| `ellipse` | `radiusX`, `radiusY` | — |
|
|
256
|
+
| `arc` | `radius` | `innerRadius`, `startAngle` (0), `endAngle` (360) |
|
|
226
257
|
| `line` | `from: [x,y]`, `to: [x,y]` (canvas coordinates; stroke in `initial`) | — |
|
|
227
258
|
| `polygon` | `points: [[x,y], …]` | `open` (default false) |
|
|
228
259
|
| `path` | `d` (SVG path data) | — |
|
|
229
260
|
|
|
261
|
+
**`arc`** is an arc around its local origin (the centre). Angles are in **degrees: 0° is 3 o'clock and positive is clockwise**, so a progress ring starts at `startAngle: -90` (12 o'clock) and animates `endAngle` from `-90` to `270`. With a stroke and no fill it is an open arc line (`strokeCap: 'round'` for rounded ends); with a fill it is a sector (a pie slice), and with `innerRadius` a ring segment (a donut slice). A sweep of 360° or more is a full circle; `endAngle < startAngle` sweeps counter-clockwise. `radius`, `innerRadius`, `startAngle` and `endAngle` are all animatable.
|
|
262
|
+
|
|
263
|
+
```ts
|
|
264
|
+
{ type: 'shape', shape: 'arc', radius: 120, startAngle: -90, endAngle: -90, strokeCap: 'round',
|
|
265
|
+
initial: { x: 'GW/2', y: 'GH/2', strokeColor: '#ffd166', strokeWidth: 24 },
|
|
266
|
+
keyframes: [{ at: 0, to: { endAngle: 270 }, duration: 2, ease: 'power3.out' }] } // a progress ring filling clockwise from the top
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
`strokeCap` (`'butt'`, `'round'`, `'square'`) and `strokeJoin` (`'miter'`, `'round'`, `'bevel'`) work on every shape, at the top level or in `initial`; they are not animated. Sharp corners (the apex of an M in outlined text, a thin polygon) poke out with the default `miter` join: use `'round'`.
|
|
270
|
+
|
|
230
271
|
**Style** is set on `initial` and animatable via keyframes:
|
|
231
272
|
|
|
232
273
|
| Key | Notes |
|
|
@@ -309,7 +350,7 @@ Per-shape choice of how colour keyframes are interpolated. Default `'rgb'` (line
|
|
|
309
350
|
|
|
310
351
|
**Scalar geometry is animatable.** `width`, `height`, `cornerRadius` (rect), `radius` (circle), `radiusX` / `radiusY` (ellipse) all flow through the same per-frame redraw and can be tweened via keyframes — useful for progress bars (animate `width`), pulsing icons (animate `radius`), or shape-morph callouts. Array geometry (polygon `points`, line endpoints, path `d`) is baked at build time; use `scale` / `scaleX` / `scaleY` for those.
|
|
311
352
|
|
|
312
|
-
`anchorX` / `anchorY` (default `0.5` each) control which point on the bbox sits at the local origin — `0` is left/top, `1` is right/bottom. They're animatable too. Critical for "grow from one edge" effects:
|
|
353
|
+
For rect / circle / ellipse, `anchorX` / `anchorY` (default `0.5` each; not available on line / polygon / path, which use `pivotX` / `pivotY`) control which point on the bbox sits at the local origin — `0` is left/top, `1` is right/bottom. They're animatable too. Critical for "grow from one edge" effects:
|
|
313
354
|
|
|
314
355
|
```ts
|
|
315
356
|
// Left-anchored progress bar — width animates 0 → W, left edge stays at x
|
|
@@ -546,6 +587,17 @@ When `true`, flip the mask sense: pixels INSIDE the mask shape become transparen
|
|
|
546
587
|
|
|
547
588
|
Routed through PIXI v8's native `setMask({ inverse: true })`.
|
|
548
589
|
|
|
590
|
+
#### `blendMode`
|
|
591
|
+
|
|
592
|
+
How a layer blends with what is behind it: `'normal'` (default), `'add'`, `'screen'` or `'multiply'`. `add` and `screen` brighten, so overlapping glows, halos and light leaks add up instead of covering each other; `multiply` darkens. It works on every layer type, on `threeD` layers, and on a composition (its children inherit the mode, each blending with what is behind it). Any other value warns and is ignored.
|
|
593
|
+
|
|
594
|
+
```ts
|
|
595
|
+
// A soft white glow that brightens whatever it overlaps
|
|
596
|
+
{ type: 'shape', shape: 'circle', radius: 120, blendMode: 'add',
|
|
597
|
+
fillGradient: { type: 'radial', stops: [[0, 'rgba(255,255,255,0.9)'], [1, 'rgba(255,255,255,0)']] },
|
|
598
|
+
initial: { x: 'W/2', y: 'H/2' } }
|
|
599
|
+
```
|
|
600
|
+
|
|
549
601
|
---
|
|
550
602
|
|
|
551
603
|
## Assets
|