pixi-effects 0.12.0 → 0.13.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 +12 -0
- package/README.md +5 -5
- package/ai/reference/cheatsheet.md +3 -3
- package/ai/reference/pitfalls.md +2 -2
- package/ai/template.html +5 -5
- package/ai/tools/check.mjs +1 -1
- package/dist/{Base-W3sQzGPO.d.cts → Base-C6ZnJ9pD.d.cts} +1 -1
- package/dist/{Base-CYH9vPCH.d.ts → Base-CQOlB2VH.d.ts} +1 -1
- package/dist/Controller.cjs +76 -17
- package/dist/Controller.cjs.map +1 -1
- package/dist/Controller.d.cts +15 -3
- package/dist/Controller.d.ts +15 -3
- package/dist/Controller.js +76 -17
- package/dist/Controller.js.map +1 -1
- package/dist/{Movie-CY2QPVVM.d.cts → Movie-DnR4H1OO.d.ts} +5 -2
- package/dist/{Movie-EBF6_RfX.d.ts → Movie-Tl5dRLJl.d.cts} +5 -2
- package/dist/Presenter.d.cts +2 -2
- package/dist/Presenter.d.ts +2 -2
- package/dist/index.cjs +32 -12
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +8 -5
- package/dist/index.d.ts +8 -5
- package/dist/index.js +32 -13
- package/dist/index.js.map +1 -1
- package/dist/loader.css +1 -1
- package/dist/three.d.cts +2 -2
- package/dist/three.d.ts +2 -2
- package/dist/{types-mpqQO8MJ.d.cts → types-CBSiUKau.d.cts} +18 -1
- package/dist/{types-mpqQO8MJ.d.ts → types-CBSiUKau.d.ts} +18 -1
- package/docs/api.md +4 -3
- package/docs/dsl.md +2 -2
- package/llms-full.txt +11 -10
- package/package.json +1 -1
package/dist/loader.css
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/*
|
|
2
2
|
* pixi-effects loader: one line of HTML on the page, a few lines of CSS, no JavaScript of your own.
|
|
3
3
|
*
|
|
4
|
-
* <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/pixi-effects@0.
|
|
4
|
+
* <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/pixi-effects@0.13.0/dist/loader.css">
|
|
5
5
|
* <div class="stage" style="position: relative"> <!-- any box the canvas sits in, with position: relative -->
|
|
6
6
|
* <canvas id="stage" width="1280" height="720"></canvas>
|
|
7
7
|
* <div class="pe-loader" data-label="LOADING"></div> <!-- or class="pe-loader pe-loader--bar" -->
|
package/dist/three.d.cts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { d as SequenceCommon, s as PropValue, S as SequenceSpec } from './types-
|
|
1
|
+
import { d as SequenceCommon, s as PropValue, S as SequenceSpec } from './types-CBSiUKau.cjs';
|
|
2
2
|
import { Scene, Camera, WebGLRenderer } from 'three';
|
|
3
|
-
import { S as Sequence, P as PathRouters } from './Base-
|
|
3
|
+
import { S as Sequence, P as PathRouters } from './Base-C6ZnJ9pD.cjs';
|
|
4
4
|
import 'pixi.js';
|
|
5
5
|
import 'gsap';
|
|
6
6
|
|
package/dist/three.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { d as SequenceCommon, s as PropValue, S as SequenceSpec } from './types-
|
|
1
|
+
import { d as SequenceCommon, s as PropValue, S as SequenceSpec } from './types-CBSiUKau.js';
|
|
2
2
|
import { Scene, Camera, WebGLRenderer } from 'three';
|
|
3
|
-
import { S as Sequence, P as PathRouters } from './Base-
|
|
3
|
+
import { S as Sequence, P as PathRouters } from './Base-CQOlB2VH.js';
|
|
4
4
|
import 'pixi.js';
|
|
5
5
|
import 'gsap';
|
|
6
6
|
|
|
@@ -8,6 +8,11 @@ interface StopSpec {
|
|
|
8
8
|
notes?: string;
|
|
9
9
|
/** Move on by itself after this many seconds (a kiosk, a looping demo). */
|
|
10
10
|
advance?: number;
|
|
11
|
+
/**
|
|
12
|
+
* Which picture stands for a page in a PDF (and in the page overview). `true`: this stop's picture IS the page's picture (use it when the
|
|
13
|
+
* last stop is mid-exit or a step you do not want on paper). `false`: never use this stop's picture for the PDF. Default: the page's last stop.
|
|
14
|
+
*/
|
|
15
|
+
pdf?: boolean;
|
|
11
16
|
}
|
|
12
17
|
/** A stop, normalised: its frame, and which page it belongs to. */
|
|
13
18
|
interface Stop {
|
|
@@ -22,6 +27,7 @@ interface Stop {
|
|
|
22
27
|
pageStart: boolean;
|
|
23
28
|
notes?: string;
|
|
24
29
|
advance?: number;
|
|
30
|
+
pdf?: boolean;
|
|
25
31
|
}
|
|
26
32
|
/**
|
|
27
33
|
* The stops of a composition, checked and in order. Numbers are stops; with no `page` anywhere every stop is a page of its
|
|
@@ -29,6 +35,17 @@ interface Stop {
|
|
|
29
35
|
* two stops on the same frame are one; anything outside the movie or not a number is dropped, with a warning that names it.
|
|
30
36
|
*/
|
|
31
37
|
declare function normalizeStops(raw: ReadonlyArray<number | StopSpec> | undefined, duration: number, frameRate: number): Stop[];
|
|
38
|
+
/**
|
|
39
|
+
* The stops whose pictures stand for the pages (`which: 'pages'`, default) or for every stop. A page's picture is, in order: the stop flagged
|
|
40
|
+
* `pdf: true` (the last such one, or the first with `pick: 'first'`), else the last (or first) stop not flagged `pdf: false`, else, for a page
|
|
41
|
+
* list, its last (or first) stop, and for a PDF (`pdf: true` here) nothing: the page is left out. With `which: 'stops'` a PDF leaves out the
|
|
42
|
+
* stops flagged `pdf: false`.
|
|
43
|
+
*/
|
|
44
|
+
declare function pictureStops(stops: readonly Stop[], opts?: {
|
|
45
|
+
which?: 'pages' | 'stops';
|
|
46
|
+
pick?: 'last' | 'first';
|
|
47
|
+
pdf?: boolean;
|
|
48
|
+
}): Stop[];
|
|
32
49
|
|
|
33
50
|
/**
|
|
34
51
|
* pixi-effects public DSL types.
|
|
@@ -551,4 +568,4 @@ interface AudioDescriptor {
|
|
|
551
568
|
}[];
|
|
552
569
|
}
|
|
553
570
|
|
|
554
|
-
export { type ArcShapeSpec as A, type StopSpec as B, type CompositionShape as C, type DipTransition as D, type EllipseShapeSpec as E, type FilterSpec as F, type GradientSpec as G, type TransitionCommon as H, type ImageSequenceSpec as I, normalizeStops as J, type Keyframe as K, type LineShapeSpec as L,
|
|
571
|
+
export { type ArcShapeSpec as A, type StopSpec as B, type CompositionShape as C, type DipTransition as D, type EllipseShapeSpec as E, type FilterSpec as F, type GradientSpec as G, type TransitionCommon as H, type ImageSequenceSpec as I, normalizeStops as J, type Keyframe as K, type LineShapeSpec as L, pictureStops as M, type NullSequenceSpec as N, type AudioDescriptor as O, type Props as P, type RectShapeSpec as R, type SequenceSpec as S, type TextSequenceSpec as T, type VideoSequenceSpec as V, type WipeTransition as W, type ZoomTransition as Z, type CameraSequenceSpec as a, type TransitionSpec as b, type CompositionSpec as c, type SequenceCommon as d, type AssetSpec as e, type AudioAssetSpec as f, type AudioSequenceSpec as g, type AudioSfxSpec as h, type ChromaKeyFilterSpec as i, type CircleShapeSpec as j, type CompositionSequenceSpec as k, type CrossfadeTransition as l, type CustomFilterSpec as m, type DissolveTransition as n, type Expr as o, type IrisTransition as p, type PathShapeSpec as q, type PolygonShapeSpec as r, type PropValue as s, type SfxKnobs as t, type SfxOptions as u, type SfxPreset as v, type SfxVoice as w, type ShapeSequenceSpec as x, type SlideTransition as y, type Stop as z };
|
|
@@ -8,6 +8,11 @@ interface StopSpec {
|
|
|
8
8
|
notes?: string;
|
|
9
9
|
/** Move on by itself after this many seconds (a kiosk, a looping demo). */
|
|
10
10
|
advance?: number;
|
|
11
|
+
/**
|
|
12
|
+
* Which picture stands for a page in a PDF (and in the page overview). `true`: this stop's picture IS the page's picture (use it when the
|
|
13
|
+
* last stop is mid-exit or a step you do not want on paper). `false`: never use this stop's picture for the PDF. Default: the page's last stop.
|
|
14
|
+
*/
|
|
15
|
+
pdf?: boolean;
|
|
11
16
|
}
|
|
12
17
|
/** A stop, normalised: its frame, and which page it belongs to. */
|
|
13
18
|
interface Stop {
|
|
@@ -22,6 +27,7 @@ interface Stop {
|
|
|
22
27
|
pageStart: boolean;
|
|
23
28
|
notes?: string;
|
|
24
29
|
advance?: number;
|
|
30
|
+
pdf?: boolean;
|
|
25
31
|
}
|
|
26
32
|
/**
|
|
27
33
|
* The stops of a composition, checked and in order. Numbers are stops; with no `page` anywhere every stop is a page of its
|
|
@@ -29,6 +35,17 @@ interface Stop {
|
|
|
29
35
|
* two stops on the same frame are one; anything outside the movie or not a number is dropped, with a warning that names it.
|
|
30
36
|
*/
|
|
31
37
|
declare function normalizeStops(raw: ReadonlyArray<number | StopSpec> | undefined, duration: number, frameRate: number): Stop[];
|
|
38
|
+
/**
|
|
39
|
+
* The stops whose pictures stand for the pages (`which: 'pages'`, default) or for every stop. A page's picture is, in order: the stop flagged
|
|
40
|
+
* `pdf: true` (the last such one, or the first with `pick: 'first'`), else the last (or first) stop not flagged `pdf: false`, else, for a page
|
|
41
|
+
* list, its last (or first) stop, and for a PDF (`pdf: true` here) nothing: the page is left out. With `which: 'stops'` a PDF leaves out the
|
|
42
|
+
* stops flagged `pdf: false`.
|
|
43
|
+
*/
|
|
44
|
+
declare function pictureStops(stops: readonly Stop[], opts?: {
|
|
45
|
+
which?: 'pages' | 'stops';
|
|
46
|
+
pick?: 'last' | 'first';
|
|
47
|
+
pdf?: boolean;
|
|
48
|
+
}): Stop[];
|
|
32
49
|
|
|
33
50
|
/**
|
|
34
51
|
* pixi-effects public DSL types.
|
|
@@ -551,4 +568,4 @@ interface AudioDescriptor {
|
|
|
551
568
|
}[];
|
|
552
569
|
}
|
|
553
570
|
|
|
554
|
-
export { type ArcShapeSpec as A, type StopSpec as B, type CompositionShape as C, type DipTransition as D, type EllipseShapeSpec as E, type FilterSpec as F, type GradientSpec as G, type TransitionCommon as H, type ImageSequenceSpec as I, normalizeStops as J, type Keyframe as K, type LineShapeSpec as L,
|
|
571
|
+
export { type ArcShapeSpec as A, type StopSpec as B, type CompositionShape as C, type DipTransition as D, type EllipseShapeSpec as E, type FilterSpec as F, type GradientSpec as G, type TransitionCommon as H, type ImageSequenceSpec as I, normalizeStops as J, type Keyframe as K, type LineShapeSpec as L, pictureStops as M, type NullSequenceSpec as N, type AudioDescriptor as O, type Props as P, type RectShapeSpec as R, type SequenceSpec as S, type TextSequenceSpec as T, type VideoSequenceSpec as V, type WipeTransition as W, type ZoomTransition as Z, type CameraSequenceSpec as a, type TransitionSpec as b, type CompositionSpec as c, type SequenceCommon as d, type AssetSpec as e, type AudioAssetSpec as f, type AudioSequenceSpec as g, type AudioSfxSpec as h, type ChromaKeyFilterSpec as i, type CircleShapeSpec as j, type CompositionSequenceSpec as k, type CrossfadeTransition as l, type CustomFilterSpec as m, type DissolveTransition as n, type Expr as o, type IrisTransition as p, type PathShapeSpec as q, type PolygonShapeSpec as r, type PropValue as s, type SfxKnobs as t, type SfxOptions as u, type SfxPreset as v, type SfxVoice as w, type ShapeSequenceSpec as x, type SlideTransition as y, type Stop as z };
|
package/docs/api.md
CHANGED
|
@@ -253,10 +253,10 @@ The names are the ones an HTML5 `<video>` uses, so a player written for `<video>
|
|
|
253
253
|
|
|
254
254
|
### Presentations: `stops`, `next()`, `prev()`, `Presenter`
|
|
255
255
|
|
|
256
|
-
A composition can say where a presentation pauses: `composition: { stops: [...] }`. A stop is a time in seconds or `{ at, page?, notes?, advance? }`; a stop with `page` (a name, or `true`) begins a page, the others are steps of it; with no `page` anywhere each stop is a page; the first stop always begins one. A negative `at` counts back from the end; two stops on one frame are one; a stop outside the movie is ignored with a warning.
|
|
256
|
+
A composition can say where a presentation pauses: `composition: { stops: [...] }`. A stop is a time in seconds or `{ at, page?, notes?, advance?, pdf? }`; a stop with `page` (a name, or `true`) begins a page, the others are steps of it; with no `page` anywhere each stop is a page; the first stop always begins one. A negative `at` counts back from the end; two stops on one frame are one; a stop outside the movie is ignored with a warning. `movie.play()` ignores the stops and plays to the end; the `Controller`'s play button, Space and a click on the picture play to the next stop and pause there (see `pauseAtStops`). `pdf: true` marks the stop whose picture stands for its page in a PDF (and in the page overview); `pdf: false` keeps a stop's picture out of the PDF (a last stop that is mid-exit, a step you do not want on paper). Without flags a page is pictured by its last stop.
|
|
257
257
|
|
|
258
258
|
```ts
|
|
259
|
-
movie.stops // Stop[]: { index, at, frame, page: string | null, pageIndex, pageStart, notes?, advance? }
|
|
259
|
+
movie.stops // Stop[]: { index, at, frame, page: string | null, pageIndex, pageStart, notes?, advance?, pdf? }
|
|
260
260
|
await movie.next(); // play to the next stop and pause exactly on its frame; pressed again while playing, skip to that stop at once;
|
|
261
261
|
// after the last stop play to the end. Resolves when it has landed (or stopped for another reason).
|
|
262
262
|
await movie.prev(); // jump back to the previous stop at once (no reverse playback); from the first stop, to the start
|
|
@@ -297,7 +297,7 @@ const pages = await movie.stopImages({ as: 'dataURL', type: 'image/jpeg', scale:
|
|
|
297
297
|
const pdf = await movie.exportPDF({ title: 'My talk' }); // a Blob: one PDF page per page of the deck (JPEG at the canvas size)
|
|
298
298
|
```
|
|
299
299
|
|
|
300
|
-
Both pause the movie and leave the playhead where it was; a movie with no stops returns an empty list / throws (a PDF needs pages). `exportPDF` options: `which`, `pick`, `scale`, `quality` (JPEG, default 0.92), `title`, `motionBlur`, `onImage`. A transparent background comes out black in a JPEG: give the movie a `background`. From a script, `npx pixi-effects-render my-talk.html -o my-talk.pdf` (add `--all-stops` for a page per stop).
|
|
300
|
+
Both pause the movie and leave the playhead where it was; a movie with no stops returns an empty list / throws (a PDF needs pages). The picture of a page is, in order: its last stop flagged `pdf: true`, else its last (with `pick: 'first'`, first) stop that is not `pdf: false`; a page whose stops are all `pdf: false` is left out of a PDF (a page list still shows it by its last stop); `which: 'stops'` makes a page of every stop that is not `pdf: false`. `exportPDF` options: `which`, `pick`, `scale`, `quality` (JPEG, default 0.92), `title`, `motionBlur`, `onImage`. A transparent background comes out black in a JPEG: give the movie a `background`. From a script, `npx pixi-effects-render my-talk.html -o my-talk.pdf` (add `--all-stops` for a page per stop).
|
|
301
301
|
|
|
302
302
|
### Audio-reactive: `audioEnvelope()`, `react()`, `bpmEnvelope()`
|
|
303
303
|
|
|
@@ -381,6 +381,7 @@ interface ControllerOptions {
|
|
|
381
381
|
showExportButton?: boolean; // default true; hides ⬇ + popover
|
|
382
382
|
enableKeyboardShortcuts?: boolean; // default true
|
|
383
383
|
present?: boolean; // default true: a movie with stops gets a Present button (marks on the seek bar are always shown)
|
|
384
|
+
pauseAtStops?: boolean; // default true: with stops, play / Space / click on the picture play to the next stop and pause; false plays straight through
|
|
384
385
|
clickToPlay?: boolean; // default true: a click / tap on the picture plays / pauses (like <video>); false leaves the canvas alone
|
|
385
386
|
className?: string; // default 'movie-controller'
|
|
386
387
|
theme?: ControllerTheme; // colours / thickness / font of the bar, see Theme
|
package/docs/dsl.md
CHANGED
|
@@ -826,7 +826,7 @@ initial: { x: 'GW/2', y: 'GH * 0.96', anchorX: 0.5, anchorY: 1 }
|
|
|
826
826
|
|
|
827
827
|
## Stops (presentations)
|
|
828
828
|
|
|
829
|
-
`composition.stops` says where a presentation pauses: seconds, or `{ at, page?, notes?, advance? }` (see [Presentations in the API reference](api.md#presentations-stops-next-prev-presenter)). A stop with `page` begins a page (a name, or `true`), the others are steps of it; with no `page` anywhere each stop is a page.
|
|
829
|
+
`composition.stops` says where a presentation pauses: seconds, or `{ at, page?, notes?, advance?, pdf? }` (see [Presentations in the API reference](api.md#presentations-stops-next-prev-presenter)). A stop with `page` begins a page (a name, or `true`), the others are steps of it; with no `page` anywhere each stop is a page. `movie.play()` ignores them; the `Controller` shows marks on the seek bar and its play button plays to the next stop (`pauseAtStops: false` plays through), and `movie.next()` and `Presenter` pause on them. **`pdf: true` / `pdf: false`** on a stop chooses which moment stands for a page in a PDF: `true` = this stop is the page's picture, `false` = never use it (default: the page's last stop). Use it when the last stop is mid-exit. [`deck()`](#deck) builds the whole thing from pages.
|
|
830
830
|
|
|
831
831
|
## Transitions
|
|
832
832
|
|
|
@@ -1279,7 +1279,7 @@ await movie.init({ canvas, width: 1280, height: 720, frameRate: 30, ...deck({
|
|
|
1279
1279
|
| ------ | ----- |
|
|
1280
1280
|
| `pages[]` | required: `{ name?, duration, sequences, stops?, notes?, advance?, initial?, keyframes?, filters?, … }` |
|
|
1281
1281
|
| `page.duration` | seconds; **a page should last its last stop plus the transition** (after the last stop the next press plays the rest of the page and the slide) |
|
|
1282
|
-
| `page.stops` | page-local times
|
|
1282
|
+
| `page.stops` | page-local times, or `{ at, pdf? }`: the first is the page settled, the others are steps (a bullet, a growing chart: **a step is a layer that starts at the previous stop and ends at this one**). Default: where the transition begins (the end for the last page) |
|
|
1283
1283
|
| `page.notes`, `page.advance` | notes stay on the page's first stop; `advance` (seconds) on its last |
|
|
1284
1284
|
| `transition` | `{ kind, duration, … }`: any [transition](#transitions) without `from` / `to` / `at`; the next page starts `duration` seconds early and the transition plays across the overlap. It must be shorter than every page |
|
|
1285
1285
|
| movie options | `width`, `height`, `frameRate`, `background`, `canvas`, `assets`, `poster`, `motionBlur`, `loader` pass through |
|
package/llms-full.txt
CHANGED
|
@@ -182,13 +182,13 @@ keyframes: followPath({ d: 'M 120 560 C 360 120 920 120 1160 560', duration: 4,
|
|
|
182
182
|
...particles({ count: 90, at: 2, emit: 0 /* 0 = burst; or seconds to spread births */, life: [1, 1.8], area: { x: 640, y: 330 /*, width, height */ }, angle: [0, 360] /* 0 right, 90 down, −90 up */,
|
|
183
183
|
speed: [120, 420], gravity: 240, wind: 0, drag: 1.2, sway: { amp: 30, freq: 0.6 }, size: [2, 4.5], scale: [1, 0], fade: { in: 0.2, out: 0.6 },
|
|
184
184
|
colors: ['#ffd166', '#ff6b9d'], shape: 'circle'|'rect'|'star', spin: [-200, 200], blendMode: 'add', seed: 1, name: 'burst', template: { type: 'image', asset } }) // seeded layers, paths baked into keyframes
|
|
185
|
-
// a presentation: stops pause playback (`composition.stops: [2, { at: 5, page: 'Name', notes, advance }]`; a `page` stop begins a page, the others are steps); deck() lays pages out
|
|
185
|
+
// a presentation: stops pause playback (`composition.stops: [2, { at: 5, page: 'Name', notes, advance, pdf }]`; `pdf: true` = the stop that pictures its page in a PDF, `pdf: false` = keep it out; default the page's last stop; a `page` stop begins a page, the others are steps); deck() lays pages out
|
|
186
186
|
await movie.init({ canvas, width, height, frameRate, ...deck({ transition: { kind: 'slide', direction: 'left', duration: 0.6 },
|
|
187
187
|
pages: [{ name: 'Hello', duration: 3.2 /* last stop + transition */, stops: [2.6] /* page-local */, notes, sequences: [ /* page-local time; a step = a layer starting at the previous stop */ ] }] }) });
|
|
188
188
|
new Presenter(movie, { canvas }); // import { Presenter } from 'pixi-effects/presenter': → ↓ Space Enter click = next, ← = back, Home / End, number+Enter, B / W, F, ?
|
|
189
|
-
movie.next() / prev() / goToStop(i) / goToPage(n) / movie.stops / movie.on('stop', …) // Controller
|
|
189
|
+
movie.next() / prev() / goToStop(i) / goToPage(n) / movie.stops / movie.on('stop', …) // Controller: marks on the seek bar, play / Space / click = play to the next stop (pauseAtStops: false = straight through), a Present button, PDF in the download panel
|
|
190
190
|
// Presenter extras: G = page overview (pictures), P = presenter view (a second window: live picture, next picture, notes, timer); start() makes the pictures before the audience sees anything
|
|
191
|
-
await movie.stopImages({ as: 'dataURL', scale: 0.25 }) // [{ stop, page, image }] one per page (its last stop); { which: 'stops' } one per stop
|
|
191
|
+
await movie.stopImages({ as: 'dataURL', scale: 0.25 }) // [{ stop, page, image }] one per page (its last stop); { which: 'stops' } one per stop (a page's picture honours `pdf` flags)
|
|
192
192
|
await movie.exportPDF({ title }) // Blob: a PDF page per page of the deck; CLI: pixi-effects-render talk.html -o talk.pdf (--all-stops)
|
|
193
193
|
const env = await audioEnvelope('music.mp3', { frameRate: 30 }) // BEFORE movie.init: .series.level|bass|mid|treble (0–1 per frame), .beats [s], .bpm, .at(t, band); bands: { name: [Hz, Hz] }
|
|
194
194
|
const env = bpmEnvelope(120, { duration: 12 }) // no audio file: kick / snare / hats exactly on a tempo
|
|
@@ -309,8 +309,8 @@ A good habit that catches most of these: after `init`, read `window.__logs` (see
|
|
|
309
309
|
51. **Arcs and rings are `shape: 'arc'`** (`radius`, `innerRadius`, `startAngle`, `endAngle`, degrees; 0° = 3 o'clock, clockwise). A progress ring is an arc with `startAngle: -90` and an animated `endAngle` (−90 → 270); `strokeCap: 'round'` rounds BOTH ends. Fill it for a pie slice, add `innerRadius` for a donut slice (a 120-polygon donut is no longer needed). Style (`strokeColor`, `strokeWidth`, `fillColor`) goes in `initial`; `strokeCap` / `strokeJoin` may be top level. *(fixed)*
|
|
310
310
|
52. **Measure and split text with `measureText` / `splitText`, not by hand.** `splitText(text, style, { by, x, y, align })` returns each piece's x / y / width in the renderer's own font metrics (kerning kept; verified: a whole line vs the same line in per-letter layers differs only by anti-aliasing). The pieces are for text layers with the default top-left anchor and the same `style`. Load a web font before measuring (`await document.fonts.load('24px "Name"')`) or the fallback font's widths are used. Size a pill from `measureText(...).width + padding` instead of `0.58 × fontSize × length`. *(fixed)*
|
|
311
311
|
53. **Draw-on and typewriter need no masks or per-letter layers.** `trimStart` / `trimEnd` (0–1 of the outline, animatable, every shape but `arc`) draw a stroke on: `trimEnd: 0` plus a keyframe `to: { trimEnd: 1 }`. The fill is not trimmed, so fade `fillAlpha` in afterwards; use `strokeCap: 'round'`. Text changes over time with `set: { text }` (swap at a time) and `visibleChars` (typewriter; left-align it so it grows rightward). A `to: { text }` cannot tween a string and warns. *(fixed)*
|
|
312
|
-
54. **A deck page lasts until its last stop plus the transition.** `deck()` lays pages out one after another in page-local time and a `transition` overlaps them: a page shorter than its last stop (or shorter than the transition) loses its final step or flashes. A step is a layer that starts at the previous stop (`at: <previous stop>`), so the thing that appears on a press is fully visible when the movie stops. The transition option is `kind`, not `type`.
|
|
313
|
-
55. **Presenter and Controller both listen to click and keys.** Use the `Presenter` for a talk (or let the Controller's Present button start one); do not run both on the same canvas yourself.
|
|
312
|
+
54. **A deck page lasts until its last stop plus the transition.** `deck()` lays pages out one after another in page-local time and a `transition` overlaps them: a page shorter than its last stop (or shorter than the transition) loses its final step or flashes. A step is a layer that starts at the previous stop (`at: <previous stop>`), so the thing that appears on a press is fully visible when the movie stops. The transition option is `kind`, not `type`. If the last stop is mid-exit on paper, flag the stop you want in the PDF: `{ at, pdf: true }` (in `deck()`: `stops: [1.2, { at: 3, pdf: true }]`), and `pdf: false` keeps one out.
|
|
313
|
+
55. **Presenter and Controller both listen to click and keys.** Use the `Presenter` for a talk (or let the Controller's Present button start one); do not run both on the same canvas yourself. `movie.play()` ignores stops, but the Controller's play button / Space / click-on-picture play to the next stop and pause (`pauseAtStops: false` plays through); `movie.next()` plays to the next stop (a second press skips to it), `prev()` jumps back (nothing plays in reverse).
|
|
314
314
|
56. **Analyse audio before `init`, and give it the movie's frame rate.** `const env = await audioEnvelope('music.mp3', { frameRate })` is async; `react()` is pure and bakes the keyframes, so there is no per-frame callback. A custom `bands` list replaces the defaults (include `bass` if you want it; beats are picked there). No file yet: `bpmEnvelope(120, { duration })` is a stand-in with a kick on every beat. Offset the music with `audioOffset` when the audio layer does not start at 0.
|
|
315
315
|
57. **Review a deck by its stops, not by its timeline.** `pixi-effects-check` writes `stops.png` (one picture per stop, in order) for a page with `stops`; the picture at a stop is what the audience reads while it waits.
|
|
316
316
|
|
|
@@ -1863,7 +1863,7 @@ initial: { x: 'GW/2', y: 'GH * 0.96', anchorX: 0.5, anchorY: 1 }
|
|
|
1863
1863
|
|
|
1864
1864
|
## Stops (presentations)
|
|
1865
1865
|
|
|
1866
|
-
`composition.stops` says where a presentation pauses: seconds, or `{ at, page?, notes?, advance? }` (see [Presentations in the API reference](api.md#presentations-stops-next-prev-presenter)). A stop with `page` begins a page (a name, or `true`), the others are steps of it; with no `page` anywhere each stop is a page.
|
|
1866
|
+
`composition.stops` says where a presentation pauses: seconds, or `{ at, page?, notes?, advance?, pdf? }` (see [Presentations in the API reference](api.md#presentations-stops-next-prev-presenter)). A stop with `page` begins a page (a name, or `true`), the others are steps of it; with no `page` anywhere each stop is a page. `movie.play()` ignores them; the `Controller` shows marks on the seek bar and its play button plays to the next stop (`pauseAtStops: false` plays through), and `movie.next()` and `Presenter` pause on them. **`pdf: true` / `pdf: false`** on a stop chooses which moment stands for a page in a PDF: `true` = this stop is the page's picture, `false` = never use it (default: the page's last stop). Use it when the last stop is mid-exit. [`deck()`](#deck) builds the whole thing from pages.
|
|
1867
1867
|
|
|
1868
1868
|
## Transitions
|
|
1869
1869
|
|
|
@@ -2316,7 +2316,7 @@ await movie.init({ canvas, width: 1280, height: 720, frameRate: 30, ...deck({
|
|
|
2316
2316
|
| ------ | ----- |
|
|
2317
2317
|
| `pages[]` | required: `{ name?, duration, sequences, stops?, notes?, advance?, initial?, keyframes?, filters?, … }` |
|
|
2318
2318
|
| `page.duration` | seconds; **a page should last its last stop plus the transition** (after the last stop the next press plays the rest of the page and the slide) |
|
|
2319
|
-
| `page.stops` | page-local times
|
|
2319
|
+
| `page.stops` | page-local times, or `{ at, pdf? }`: the first is the page settled, the others are steps (a bullet, a growing chart: **a step is a layer that starts at the previous stop and ends at this one**). Default: where the transition begins (the end for the last page) |
|
|
2320
2320
|
| `page.notes`, `page.advance` | notes stay on the page's first stop; `advance` (seconds) on its last |
|
|
2321
2321
|
| `transition` | `{ kind, duration, … }`: any [transition](#transitions) without `from` / `to` / `at`; the next page starts `duration` seconds early and the transition plays across the overlap. It must be shorter than every page |
|
|
2322
2322
|
| movie options | `width`, `height`, `frameRate`, `background`, `canvas`, `assets`, `poster`, `motionBlur`, `loader` pass through |
|
|
@@ -2624,10 +2624,10 @@ The names are the ones an HTML5 `<video>` uses, so a player written for `<video>
|
|
|
2624
2624
|
|
|
2625
2625
|
### Presentations: `stops`, `next()`, `prev()`, `Presenter`
|
|
2626
2626
|
|
|
2627
|
-
A composition can say where a presentation pauses: `composition: { stops: [...] }`. A stop is a time in seconds or `{ at, page?, notes?, advance? }`; a stop with `page` (a name, or `true`) begins a page, the others are steps of it; with no `page` anywhere each stop is a page; the first stop always begins one. A negative `at` counts back from the end; two stops on one frame are one; a stop outside the movie is ignored with a warning.
|
|
2627
|
+
A composition can say where a presentation pauses: `composition: { stops: [...] }`. A stop is a time in seconds or `{ at, page?, notes?, advance?, pdf? }`; a stop with `page` (a name, or `true`) begins a page, the others are steps of it; with no `page` anywhere each stop is a page; the first stop always begins one. A negative `at` counts back from the end; two stops on one frame are one; a stop outside the movie is ignored with a warning. `movie.play()` ignores the stops and plays to the end; the `Controller`'s play button, Space and a click on the picture play to the next stop and pause there (see `pauseAtStops`). `pdf: true` marks the stop whose picture stands for its page in a PDF (and in the page overview); `pdf: false` keeps a stop's picture out of the PDF (a last stop that is mid-exit, a step you do not want on paper). Without flags a page is pictured by its last stop.
|
|
2628
2628
|
|
|
2629
2629
|
```ts
|
|
2630
|
-
movie.stops // Stop[]: { index, at, frame, page: string | null, pageIndex, pageStart, notes?, advance? }
|
|
2630
|
+
movie.stops // Stop[]: { index, at, frame, page: string | null, pageIndex, pageStart, notes?, advance?, pdf? }
|
|
2631
2631
|
await movie.next(); // play to the next stop and pause exactly on its frame; pressed again while playing, skip to that stop at once;
|
|
2632
2632
|
// after the last stop play to the end. Resolves when it has landed (or stopped for another reason).
|
|
2633
2633
|
await movie.prev(); // jump back to the previous stop at once (no reverse playback); from the first stop, to the start
|
|
@@ -2668,7 +2668,7 @@ const pages = await movie.stopImages({ as: 'dataURL', type: 'image/jpeg', scale:
|
|
|
2668
2668
|
const pdf = await movie.exportPDF({ title: 'My talk' }); // a Blob: one PDF page per page of the deck (JPEG at the canvas size)
|
|
2669
2669
|
```
|
|
2670
2670
|
|
|
2671
|
-
Both pause the movie and leave the playhead where it was; a movie with no stops returns an empty list / throws (a PDF needs pages). `exportPDF` options: `which`, `pick`, `scale`, `quality` (JPEG, default 0.92), `title`, `motionBlur`, `onImage`. A transparent background comes out black in a JPEG: give the movie a `background`. From a script, `npx pixi-effects-render my-talk.html -o my-talk.pdf` (add `--all-stops` for a page per stop).
|
|
2671
|
+
Both pause the movie and leave the playhead where it was; a movie with no stops returns an empty list / throws (a PDF needs pages). The picture of a page is, in order: its last stop flagged `pdf: true`, else its last (with `pick: 'first'`, first) stop that is not `pdf: false`; a page whose stops are all `pdf: false` is left out of a PDF (a page list still shows it by its last stop); `which: 'stops'` makes a page of every stop that is not `pdf: false`. `exportPDF` options: `which`, `pick`, `scale`, `quality` (JPEG, default 0.92), `title`, `motionBlur`, `onImage`. A transparent background comes out black in a JPEG: give the movie a `background`. From a script, `npx pixi-effects-render my-talk.html -o my-talk.pdf` (add `--all-stops` for a page per stop).
|
|
2672
2672
|
|
|
2673
2673
|
### Audio-reactive: `audioEnvelope()`, `react()`, `bpmEnvelope()`
|
|
2674
2674
|
|
|
@@ -2752,6 +2752,7 @@ interface ControllerOptions {
|
|
|
2752
2752
|
showExportButton?: boolean; // default true; hides ⬇ + popover
|
|
2753
2753
|
enableKeyboardShortcuts?: boolean; // default true
|
|
2754
2754
|
present?: boolean; // default true: a movie with stops gets a Present button (marks on the seek bar are always shown)
|
|
2755
|
+
pauseAtStops?: boolean; // default true: with stops, play / Space / click on the picture play to the next stop and pause; false plays straight through
|
|
2755
2756
|
clickToPlay?: boolean; // default true: a click / tap on the picture plays / pauses (like <video>); false leaves the canvas alone
|
|
2756
2757
|
className?: string; // default 'movie-controller'
|
|
2757
2758
|
theme?: ControllerTheme; // colours / thickness / font of the bar, see Theme
|