pixi-effects 0.6.0 → 0.8.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 +29 -0
- package/README.md +18 -11
- package/ai/SKILL.md +1 -1
- package/ai/reference/cheatsheet.md +9 -1
- package/ai/template.html +4 -3
- package/ai/tools/check.mjs +14 -5
- package/ai/tools/render.mjs +202 -0
- package/ai/tools/view.mjs +269 -0
- package/dist/{Base-4vNLl4tH.d.cts → Base-CSIulyIq.d.cts} +1 -1
- package/dist/{Base-CD4AoT5-.d.ts → Base-DVgwyIUj.d.ts} +1 -1
- package/dist/Composition-CM62XJKT.cjs +14 -0
- package/dist/{Composition-Y5VZSDDP.cjs.map → Composition-CM62XJKT.cjs.map} +1 -1
- package/dist/Composition-RJDSQYTV.js +5 -0
- package/dist/{Composition-7SO5WYHU.js.map → Composition-RJDSQYTV.js.map} +1 -1
- package/dist/Controller.cjs +122 -20
- package/dist/Controller.cjs.map +1 -1
- package/dist/Controller.d.cts +45 -3
- package/dist/Controller.d.ts +45 -3
- package/dist/Controller.js +122 -21
- package/dist/Controller.js.map +1 -1
- package/dist/{Movie-Ch2SUI1h.d.ts → Movie-CQOhUmko.d.cts} +75 -2
- package/dist/{Movie-CADDBfpl.d.cts → Movie-HF3pSGjQ.d.ts} +75 -2
- package/dist/{chunk-KQM2ZZMK.cjs → chunk-AGMNSJJH.cjs} +175 -29
- package/dist/chunk-AGMNSJJH.cjs.map +1 -0
- package/dist/{chunk-7IJHJXDI.js → chunk-FNKSXBKB.js} +136 -10
- package/dist/chunk-FNKSXBKB.js.map +1 -0
- package/dist/{chunk-B2FTJLNB.js → chunk-O4AJYUPK.js} +3 -3
- package/dist/{chunk-B2FTJLNB.js.map → chunk-O4AJYUPK.js.map} +1 -1
- package/dist/{chunk-4AGJF5RK.cjs → chunk-PDBDDGG5.cjs} +21 -21
- package/dist/{chunk-4AGJF5RK.cjs.map → chunk-PDBDDGG5.cjs.map} +1 -1
- package/dist/index.cjs +255 -25
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +4 -4
- package/dist/index.d.ts +4 -4
- package/dist/index.js +248 -18
- package/dist/index.js.map +1 -1
- package/dist/three.cjs +6 -5
- package/dist/three.cjs.map +1 -1
- package/dist/three.d.cts +2 -2
- package/dist/three.d.ts +2 -2
- package/dist/three.js +2 -1
- package/dist/three.js.map +1 -1
- package/dist/{types-DrW9QeKU.d.ts → types-oJijbn-K.d.cts} +14 -1
- package/dist/{types-DrW9QeKU.d.cts → types-oJijbn-K.d.ts} +14 -1
- package/docs/api.md +57 -0
- package/docs/dsl.md +60 -1
- package/llms-full.txt +127 -3
- package/package.json +7 -4
- package/dist/Composition-7SO5WYHU.js +0 -4
- package/dist/Composition-Y5VZSDDP.cjs +0 -13
- package/dist/chunk-7IJHJXDI.js.map +0 -1
- package/dist/chunk-KQM2ZZMK.cjs.map +0 -1
package/llms-full.txt
CHANGED
|
@@ -14,7 +14,7 @@ You describe a video as a tree of plain objects; the library plays it in a canva
|
|
|
14
14
|
|
|
15
15
|
1. **Start from `template.html`** (copy it; it loads the library from a CDN, collects every console warning into `window.__logs`, and exposes `window.movie`). One HTML file, plain JS. Default canvas 1280×720 @ 30 fps.
|
|
16
16
|
2. **Write the composition as data.** Anything repeated (letters, bars, particles, scenes) is a JS function or loop that returns specs — never copy-paste objects. Derive coordinates and times from constants/data, not magic numbers.
|
|
17
|
-
3. **Run the check — one command does steps 3, 4 and the export:** `node ai/tools/check.mjs my-video.html` (Node ≥ 22 and Chrome installed; no npm install; in an installed package it is `npx pixi-effects-check my-video.html`). It starts its own private Chrome (nothing is shared with other sessions), waits for `window.__ready`, and prints: console warnings, `movie.inspect` over the WHOLE timeline (grouped), `movie.inspectAudio()`, then exports an mp4 and decodes it again (video size and length, audio length, loudness per second, silence). It writes `check-out/<name>/sheet.png` — **open that picture and look at it** — plus the mp4 and `report.json`. Exit 0 = nothing to fix; exit 1 = a list of problems; fix them and run it again. Text that overlaps text is listed under `review` (not a failure: ghost layers, glow copies and per-letter boxes are often intentional; look at the sheet; `--strict` makes them fail). The manual steps below are for looking at one frame closely.
|
|
17
|
+
3. **Show the human:** `node ai/tools/view.mjs my-video.html` opens their browser on the page with a scrubbable, zoomable timeline under it (click / drag to seek, + / − or Ctrl/⌘ + wheel to zoom) (`npx pixi-effects-view …` in an installed package); it keeps running until Ctrl-C. **Deliver the file:** `node ai/tools/render.mjs my-video.html -o my-video.mp4` (`npx pixi-effects-render …` in an installed package) renders the page headless to mp4 / webm / mov / mkv and exits 0 only when the file is written; run it AFTER the check below passes. **Run the check — one command does steps 3, 4 and the export:** `node ai/tools/check.mjs my-video.html` (Node ≥ 22 and Chrome installed; no npm install; in an installed package it is `npx pixi-effects-check my-video.html`). It starts its own private Chrome (nothing is shared with other sessions), waits for `window.__ready`, and writes `timeline.html` (every layer as a bar on a time axis: ◆ keyframes, shaded transitions; hand it to the human to open) and prints: console warnings, `movie.inspect` over the WHOLE timeline (grouped), `movie.inspectAudio()`, then exports an mp4 and decodes it again (video size and length, audio length, loudness per second, silence). It writes `check-out/<name>/sheet.png` — **open that picture and look at it** — plus the mp4 and `report.json`. Exit 0 = nothing to fix; exit 1 = a list of problems; fix them and run it again. Text that overlaps text is listed under `review` (not a failure: ghost layers, glow copies and per-letter boxes are often intentional; look at the sheet; `--strict` makes them fail). The manual steps below are for looking at one frame closely.
|
|
18
18
|
**Or by hand: run it and read the logs first.** `window.__ready` must be `true` and `window.__logs` should be `[]`. A pixi-effects warning is an instruction: fix what it names.
|
|
19
19
|
4. **Look at it** — you cannot judge motion from code. The library has the tools built in:
|
|
20
20
|
- `await movie.contactSheet({ count: 6, as: 'dataURL' })` → ONE image of several labelled frames (pass `frames: [...]` / `times: [...]` to choose; include the middle of every transition and the last second). Save it and view it.
|
|
@@ -91,7 +91,7 @@ Operators `+ - * /`, parentheses, unary `-`. Functions `min max abs floor ceil r
|
|
|
91
91
|
name, at, duration,
|
|
92
92
|
initial: { ...props applied before any keyframe },
|
|
93
93
|
keyframes: [ { at, duration, ease, set | to | from | (from + to), repeat?, yoyo?, repeatDelay? } ],
|
|
94
|
-
filters: [ { type: 'chromaKey', keyColor, threshold, smoothing, spill } | { type: 'custom', name, filter: <Pixi Filter> } ],
|
|
94
|
+
filters: [ { type: 'chromaKey', keyColor, threshold, smoothing, spill } | { type: 'glow', name?, outerStrength: 3, color: '#ffd166' } /* any named filter, see below */ | { type: 'custom', name, filter: <Pixi Filter> } ],
|
|
95
95
|
mask: <a layer spec>, maskInverted, // a mask with no `at` of its own starts and ends with the layer it masks (its keyframes count from that layer's start); an explicit mask `at` is composition time
|
|
96
96
|
filterArea: { x, y, width, height } // in the layer's OWN coordinates; lets blur/glow draw past the layer's bounds
|
|
97
97
|
threeD: true // opt into 2.5D (see below)
|
|
@@ -139,6 +139,14 @@ Style (`initial` / keyframes): `fillColor fillAlpha strokeColor strokeAlpha stro
|
|
|
139
139
|
|
|
140
140
|
Add `threeD: true` to any visual layer, then use `z`, `rotationX`, `rotationY` (centre rotation with `anchorX/Y: 0.5` or `pivotX/Y`). Add a `{ type: 'camera' }` layer for a view. A `threeD` layer at `z: 0` with the default camera looks identical to a 2D one. Consecutive `threeD` layers are drawn farthest-first (equal `z` keeps array order); non-`threeD` layers ignore the camera and keep array order. Layers at/behind the camera plane are hidden. Not supported on `threeD` layers: masks, transitions, `filterArea`.
|
|
141
141
|
|
|
142
|
+
## Player
|
|
143
|
+
|
|
144
|
+
`new Controller(movie, { canvas, theme: { accent: '#ff4d6d' } })` (import from `pixi-effects/controller`; theme keys `accent foreground track barBackground trackHeight font`, or CSS `--mc-accent` …; `controller.setTheme(…)`). Your own player: `movie.play() pause() gotoFrame(f) volume muted toggleMute()`, state `currentFrame totalFrames frameRate duration isPlaying`, events `ready frame play pause ended seeking seeked volumechange error progress` (`<video>` names). `examples/15-custom-player.html` is a complete one.
|
|
145
|
+
|
|
146
|
+
## Filters by name (no import)
|
|
147
|
+
|
|
148
|
+
`filters: [{ type: 'glow', name: 'halo', outerStrength: 3, color: '#ffd166' }]` — `type` = the pixi / pixi-filters class name without `Filter`, camelCase (`glow dropShadow outline blur noise alpha adjustment hslAdjustment grayscale pixelate crt rgbSplit oldFilm glitch bulgePinch twist zoomBlur kawaseBlur radialBlur motionBlur emboss ascii colorOverlay advancedBloom bevel dot crossHatch tiltShift …`), the other keys are its options. `colorMatrix` takes `preset: 'sepia'|'grayscale'|'negative'|'polaroid'|'technicolor'|'vintage'|'kodachrome'|'browni'`. Name it to animate: `'filters.halo.outerStrength'` (scalars only; `pixelate` via `sizeX`/`sizeY`). Options: `glow` distance outerStrength innerStrength color alpha · `dropShadow` offset{x,y} blur alpha color · `outline` thickness color · `blur` strength · `adjustment` gamma contrast saturation brightness · `hslAdjustment` hue lightness saturation · `pixelate` size · `crt` curvature lineWidth noise vignetting · `rgbSplit` red/green/blue {x,y} · `oldFilm` sepia noise scratch vignetting · `glitch` slices offset · `twist` radius angle offset · `bulgePinch` center radius strength · `zoomBlur` strength center innerRadius · `colorOverlay` color alpha · `colorGradient` gradientType('linear'|'radial'|'conic') stops[{offset,color,alpha}] angle · `colorReplace` originalColor targetColor tolerance · `convolution` matrix(9) · `shockwave` / `godray` are driven by `time` (animate it) · `colorMap` / `simpleLightmap` need a texture (use `custom`). **Centres/offsets are CANVAS pixels** (default top-left corner): put them on the layer. Glow / shadow / blur draw outside the layer: widen `filterArea` (not for `grayscale` / `oldFilm`, which fill the margin black). Full table: `docs/dsl.md` → Named filters.
|
|
149
|
+
|
|
142
150
|
## Transitions (on the parent composition)
|
|
143
151
|
|
|
144
152
|
```js
|
|
@@ -1743,6 +1751,65 @@ Removes a key color from the source. Works on video, image, or composition layer
|
|
|
1743
1751
|
|
|
1744
1752
|
Animatable: `threshold`, `smoothing`, `spill`. `keyColor` is set at build time.
|
|
1745
1753
|
|
|
1754
|
+
### Named filters (`{ type: 'glow', … }`)
|
|
1755
|
+
|
|
1756
|
+
A filter by name, as plain data. `type` is the filter's class name without `Filter`, in camelCase (case does not matter), and every other key is that filter's own option. No import, and the layer stays JSON:
|
|
1757
|
+
|
|
1758
|
+
```ts
|
|
1759
|
+
{ type: 'text', text: 'NEON', style: { fontSize: 140, fill: '#fff' }, initial: { x: 'GW/2', y: 'GH/2', anchorX: 0.5, anchorY: 0.5 },
|
|
1760
|
+
filters: [{ type: 'glow', name: 'halo', outerStrength: 0, color: '#ff4d9d', distance: 20 }],
|
|
1761
|
+
keyframes: [{ at: 0, to: { 'filters.halo.outerStrength': 6 }, duration: 1 }] } // animate any scalar option by name
|
|
1762
|
+
```
|
|
1763
|
+
|
|
1764
|
+
Name a filter (`name: 'halo'`) to animate it with `'filters.halo.<option>'`. `blur`, `noise`, `alpha` and `colorMatrix` come from pixi.js. All the others come from the **`pixi-filters`** package, which is loaded the first time a layer uses one (add `"pixi-filters": "https://esm.sh/pixi-filters@6.1.5?external=pixi.js"` to the import map, or `npm i pixi-filters`; `ai/template.html` already has it). A misspelt type says what you probably meant. Rendering and exporting were verified in a real browser for 40 of them (`examples/_checks/named-filters.html` and `named-filters-2.html`); `colorMap` and `simpleLightmap` need a texture, which JSON cannot hold, so use `custom` for those two:
|
|
1765
|
+
|
|
1766
|
+
| `type` | Options (the useful ones) | What it does |
|
|
1767
|
+
|---|---|---|
|
|
1768
|
+
| `blur` | `strength`, `quality` | Gaussian blur (pixi.js) |
|
|
1769
|
+
| `noise` | `noise` (0–1), `seed` | Film grain (pixi.js) |
|
|
1770
|
+
| `alpha` | `alpha` | Whole-layer opacity (pixi.js) |
|
|
1771
|
+
| `colorMatrix` | `preset`: `'sepia' 'grayscale' 'negative' 'polaroid' 'technicolor' 'vintage' 'kodachrome' 'browni'`, or `matrix` (20 numbers) | Colour grade (pixi.js) |
|
|
1772
|
+
| `adjustment` | `gamma contrast saturation brightness red green blue alpha` (1 = unchanged) | Colour grading |
|
|
1773
|
+
| `hslAdjustment` | `hue` (degrees) `saturation lightness colorize alpha` | Hue shift |
|
|
1774
|
+
| `grayscale` | – | Black and white |
|
|
1775
|
+
| `glow` | `distance outerStrength innerStrength color alpha quality knockout` | Glow around the shape |
|
|
1776
|
+
| `dropShadow` | `offset: { x, y }` `blur alpha color shadowOnly` | Shadow |
|
|
1777
|
+
| `outline` | `thickness color alpha quality knockout` | Stroke around the shape |
|
|
1778
|
+
| `pixelate` | `size` (a number, or `[w, h]`); animate `sizeX` / `sizeY` | Mosaic |
|
|
1779
|
+
| `crt` | `curvature lineWidth lineContrast noise vignetting vignettingAlpha time` | Old monitor |
|
|
1780
|
+
| `rgbSplit` | `red green blue` (each `{ x, y }` offset) | Chromatic aberration |
|
|
1781
|
+
| `oldFilm` | `sepia noise scratch scratchDensity vignetting seed` | Aged film |
|
|
1782
|
+
| `glitch` | `slices offset direction seed average` | Digital glitch |
|
|
1783
|
+
| `bulgePinch` | `center radius strength` | Bulge (+) / pinch (−) |
|
|
1784
|
+
| `twist` | `radius angle offset: { x, y }` | Swirl |
|
|
1785
|
+
| `zoomBlur` | `strength center innerRadius radius` | Radial zoom streaks |
|
|
1786
|
+
| `kawaseBlur` | `strength quality` | Cheap, wide blur |
|
|
1787
|
+
| `radialBlur` / `motionBlur` | `angle center radius` / `velocity kernelSize` | Spin / directional blur |
|
|
1788
|
+
| `emboss` | `strength` | Relief |
|
|
1789
|
+
| `ascii` | `size color replaceColor` | Text-art |
|
|
1790
|
+
| `colorOverlay` | `color alpha` | Tint over everything |
|
|
1791
|
+
| `advancedBloom` / `bloom` | `threshold bloomScale brightness blur` | Bloom |
|
|
1792
|
+
| `bevel` | `thickness rotation lightColor lightAlpha shadowColor shadowAlpha` | Raised edge |
|
|
1793
|
+
| `dot` / `crossHatch` | `scale angle grayscale` / – | Halftone / hatching |
|
|
1794
|
+
| `godray` | `angle gain lacunarity alpha parallel time` | Light shafts (animate `time`) |
|
|
1795
|
+
| `reflection` | `mirror boundary amplitude waveLength alpha time` (ranges are `[start, end]`) | Water reflection |
|
|
1796
|
+
| `simplexNoise` | `strength scale offset` | Cloudy noise over the layer |
|
|
1797
|
+
| `colorGradient` | `gradientType: 'linear' 'radial' 'conic'` (the filter's own option is `type`, which the DSL uses), `stops: [{ offset, color, alpha }]`, `angle` | A gradient laid over the layer |
|
|
1798
|
+
| `colorReplace` / `multiColorReplace` | `originalColor targetColor tolerance` / `replacements: [[from, to], …] tolerance` | Swap colours |
|
|
1799
|
+
| `convolution` | `matrix` (9 numbers), `width height` | Sharpen, edge detect |
|
|
1800
|
+
| `tiltShift` / `tiltShiftAxis` | `blur gradientBlur start end` (canvas pixels) | Miniature blur |
|
|
1801
|
+
| `shockwave` | `center amplitude wavelength brightness speed radius time` (animate `time` 0 → 1) | A ripple from a point |
|
|
1802
|
+
| `backdropBlur` | `strength` | Blurs what is BEHIND the layer, so alone it shows nothing |
|
|
1803
|
+
| the rest of pixi-filters (`colorMap`, `simpleLightmap`) | need a texture: `{ type: 'custom' }` | see the [pixi-filters docs](https://pixijs.io/filters/docs/) |
|
|
1804
|
+
|
|
1805
|
+
Gotchas, all measured:
|
|
1806
|
+
|
|
1807
|
+
- **Centres and offsets are in canvas pixels, not the layer's:** `twist.offset`, `bulgePinch.center`, `zoomBlur.center`, `radialBlur.center`, `shockwave.center` default to the canvas's top-left corner, so a layer in the middle looks unaffected. Put the centre on the layer: `{ type: 'twist', offset: { x: 640, y: 360 } }` for a layer centred on a 1280×720 canvas.
|
|
1808
|
+
- **A filter works inside the layer's bounds.** Glow, dropShadow, outline and blur draw outside it and are clipped unless you widen the area (`filterArea: { x: -24, y: -24, width: w + 48, height: h + 48 }`, in the layer's own coordinates). Do not widen it for `grayscale`, `oldFilm` or other filters that paint the whole area: they fill the extra margin with black.
|
|
1809
|
+
- **Animate scalars by name** (`'filters.halo.outerStrength'`). Options that are points (`rgbSplit.red`, `dropShadow.offset`) do not tween as a whole; animate `'filters.<name>.red.x'` if the filter exposes it, or swap the filter. `pixelate` animates through `sizeX` / `sizeY`, not `size`.
|
|
1810
|
+
- `shockwave` and `godray` are driven by `time` (a still frame at `time: 0` looks like nothing): animate it. `radialBlur`, `shockwave` and `tiltShift` positions are canvas pixels too.
|
|
1811
|
+
- Some filters take their value as one argument; the DSL hides that (`{ type: 'pixelate', size: 8 }`, `{ type: 'emboss', strength: 6 }`).
|
|
1812
|
+
|
|
1746
1813
|
### `custom`
|
|
1747
1814
|
|
|
1748
1815
|
Escape hatch for any PIXI `Filter` instance — including PIXI's own built-ins (`BlurFilter`, `ColorMatrixFilter`, `NoiseFilter`, etc.), [pixi-filters](https://github.com/pixijs/filters), community packages, or your own `Filter` subclass. The instance is used as-is; animation works the same way as for `chromaKey` as long as the filter has writable scalar properties at the addressed paths.
|
|
@@ -1776,7 +1843,7 @@ Notes:
|
|
|
1776
1843
|
Notes:
|
|
1777
1844
|
|
|
1778
1845
|
- `filter` must be a PIXI `Filter` instance (constructor must have run on the consumer side). Plain object literals throw.
|
|
1779
|
-
- `
|
|
1846
|
+
- Use `custom` for a filter that has no name above (a community filter, or your own `Filter`). For everything in pixi.js and `pixi-filters`, prefer the named form.
|
|
1780
1847
|
- Without a `name`, the filter still applies but cannot be addressed via `filters.<name>.<prop>` keyframe paths.
|
|
1781
1848
|
|
|
1782
1849
|
---
|
|
@@ -2064,10 +2131,18 @@ interface RenderOptions {
|
|
|
2064
2131
|
}
|
|
2065
2132
|
```
|
|
2066
2133
|
|
|
2134
|
+
**Audio codec fallback:** unless you name `audio.codec`, the first codec this browser can actually encode is used (mp4: `aac`, then `opus`, `mp3`, `flac`; webm: `opus`, `vorbis`). Chrome on Linux has no AAC encoder, so there an mp4 with sound carries Opus and a warning says so (before, the export failed with a message about encoder configurations). A codec you name is never swapped: if it cannot be encoded the error says which one would work.
|
|
2135
|
+
|
|
2067
2136
|
Returns a `Blob` whose `type` is the container's MIME (e.g. `video/mp4`). Emits `'progress'` repeatedly during the render.
|
|
2068
2137
|
|
|
2069
2138
|
The renderer also forces a keyframe every ~2 seconds so the resulting file scrubs efficiently in standard players.
|
|
2070
2139
|
|
|
2140
|
+
### `movie.timelineData(): TimelineData` and `movie.timelineChart(options?): string`
|
|
2141
|
+
|
|
2142
|
+
The whole movie as rows on a time axis, for a person to read at a glance. `timelineData()` is the data: `{ duration, rows, transitions }`, a row per layer with `name`, `type`, `depth` (1 inside a composition), absolute `start` / `end` in seconds, `keys` (when each keyframe starts) and, for a family of generated layers, `parts` (one span per layer). Layers of one type whose names differ only by numbers (`ring10-0`, `ring9-1`, `pop-3` …), four or more, become ONE row (`ring#-# ×19`), so a movie with 700 generated layers is a screenful. `timelineChart({ title })` returns one self-contained HTML page (an inline SVG, no scripts): bars coloured by type, ◆ at each keyframe start, a shaded band per transition, a tooltip per bar. `pixi-effects-check` writes it next to the contact sheet as `timeline.html`.
|
|
2143
|
+
|
|
2144
|
+
`movie.timelineSvg()` is the chart alone (one `<svg>`, geometry in `data-*` attributes, each row `<g class="row" data-start>`): `pixi-effects-view` puts a playhead on it and turns a click into a seek.
|
|
2145
|
+
|
|
2071
2146
|
### `movie.destroy(): Promise<void>`
|
|
2072
2147
|
|
|
2073
2148
|
Pauses playback, destroys the underlying PIXI Application, releases audio buffers and AudioContext, and marks the instance unusable.
|
|
@@ -2086,6 +2161,12 @@ movie.off(event, fn): this
|
|
|
2086
2161
|
| `'play'` | none | when `play()` actually transitions from paused |
|
|
2087
2162
|
| `'pause'` | none | when `pause()` actually transitions from playing |
|
|
2088
2163
|
| `'progress'`| `{ progress: number; frame: number; totalFrames: number }` | during `render()`, once per encoded frame |
|
|
2164
|
+
| `'seeking'` / `'seeked'` | `{ frame, totalFrames }` (the frame it lands on) | a `gotoFrame()` to another frame starts / has finished. NOT emitted for playback ticks, `render()`, `snapshot()` or `contactSheet()` |
|
|
2165
|
+
| `'ended'` | none | playback ran off the end (after `'pause'`); pausing by hand does not emit it |
|
|
2166
|
+
| `'volumechange'` | `{ volume: number; muted: boolean }` | `volume` or `muted` really changed (clamping counts) |
|
|
2167
|
+
| `'error'` | `{ where: 'init' \| 'render' \| 'playback'; message: string; error: unknown }` | `init()` / `render()` failed (the call still rejects), or a frame failed during playback |
|
|
2168
|
+
|
|
2169
|
+
The names are the ones an HTML5 `<video>` uses, so a player written for `<video>` ports over.
|
|
2089
2170
|
|
|
2090
2171
|
`progress` is `0..100` (rounded integer percent).
|
|
2091
2172
|
|
|
@@ -2111,6 +2192,24 @@ Flips `muted` and returns the new value.
|
|
|
2111
2192
|
|
|
2112
2193
|
---
|
|
2113
2194
|
|
|
2195
|
+
## Building your own player
|
|
2196
|
+
|
|
2197
|
+
`Controller` is one optional player; everything it does is public on the movie, so a player of your own is plain HTML + CSS + a few handlers (`examples/15-custom-player.html` is a complete one):
|
|
2198
|
+
|
|
2199
|
+
```js
|
|
2200
|
+
const el = id => document.getElementById(id);
|
|
2201
|
+
movie.on('ready', () => { el('seek').max = movie.totalFrames; });
|
|
2202
|
+
movie.on('frame', ({ frame }) => { el('seek').value = frame; el('time').textContent = (frame / movie.frameRate).toFixed(1) + ' s'; });
|
|
2203
|
+
movie.on('play', () => { el('play').textContent = '❚❚'; });
|
|
2204
|
+
movie.on('pause', () => { el('play').textContent = '▶'; });
|
|
2205
|
+
movie.on('ended', () => { el('play').textContent = '↻'; }); // play() at the end starts again from 0
|
|
2206
|
+
movie.on('volumechange', ({ volume, muted }) => { el('vol').value = muted ? 0 : volume; });
|
|
2207
|
+
el('play').onclick = () => (movie.isPlaying ? movie.pause() : movie.play());
|
|
2208
|
+
el('seek').oninput = e => movie.gotoFrame(+e.target.value);
|
|
2209
|
+
```
|
|
2210
|
+
|
|
2211
|
+
What you have: `play()`, `pause()`, `gotoFrame(frame)`, `volume`, `muted` / `toggleMute()`; the state `currentFrame`, `totalFrames`, `frameRate`, `duration`, `isPlaying`, `isReady`; the events above; `render()` for a download button; `snapshot()` for a thumbnail; `timelineData()` / `timelineSvg()` for a timeline like `pixi-effects-view`'s. Keep the movie's `canvas` wherever your layout wants it (the page's CSS sizes it). To only change how `Controller` looks, see **Theme** below.
|
|
2212
|
+
|
|
2114
2213
|
## `Controller`
|
|
2115
2214
|
|
|
2116
2215
|
Imported from `pixi-effects/controller`.
|
|
@@ -2142,6 +2241,7 @@ interface ControllerOptions {
|
|
|
2142
2241
|
showExportButton?: boolean; // default true; hides ⬇ + popover
|
|
2143
2242
|
enableKeyboardShortcuts?: boolean; // default true
|
|
2144
2243
|
className?: string; // default 'movie-controller'
|
|
2244
|
+
theme?: ControllerTheme; // colours / thickness / font of the bar, see Theme
|
|
2145
2245
|
}
|
|
2146
2246
|
```
|
|
2147
2247
|
|
|
@@ -2150,6 +2250,30 @@ Mounting strategy:
|
|
|
2150
2250
|
- If `canvas.parentElement` already has a non-static `position`, the controller is appended directly into it.
|
|
2151
2251
|
- Otherwise the canvas is wrapped in a `<div class="movie-controller-wrap">` (with `position: relative`). The wrapper is removed on `destroy()`.
|
|
2152
2252
|
|
|
2253
|
+
### Theme: change the colours of the bar
|
|
2254
|
+
|
|
2255
|
+
The bar is blue by default. Every colour and the track thickness are CSS custom properties with the old values as defaults, so there are two ways to change them:
|
|
2256
|
+
|
|
2257
|
+
```js
|
|
2258
|
+
new Controller(movie, { canvas, theme: { accent: '#ff4d6d', foreground: '#ffe9a8', trackHeight: 5 } }); // in code
|
|
2259
|
+
controller.setTheme({ accent: '#7bd88f' }); // later, live; only the keys you give change
|
|
2260
|
+
controller.setTheme({ accent: null }); // null = back to the default
|
|
2261
|
+
```
|
|
2262
|
+
```css
|
|
2263
|
+
:root { --mc-accent: #ff4d6d; } /* or in your page CSS: the canvas's parent, or :root */
|
|
2264
|
+
```
|
|
2265
|
+
|
|
2266
|
+
| `theme` key | CSS property | What | Default |
|
|
2267
|
+
|---|---|---|---|
|
|
2268
|
+
| `accent` | `--mc-accent` | progress bar, its thumb, focus rings, the export button | `#007AFF` |
|
|
2269
|
+
| `foreground` | `--mc-fg` | icons, the time, the volume slider | `#fff` |
|
|
2270
|
+
| `track` | `--mc-track` | the unfilled part of the progress / volume bars | `rgba(255,255,255,0.25)` |
|
|
2271
|
+
| `barBackground` | `--mc-bar-bg` | the background behind the buttons (any CSS background, e.g. a gradient) | black fading to transparent upward |
|
|
2272
|
+
| `trackHeight` | `--mc-track-height` | thickness of the progress bar (a number is px) | `3px` |
|
|
2273
|
+
| `font` | `--mc-font` | `font-family` of the bar | system UI font |
|
|
2274
|
+
|
|
2275
|
+
A misspelt key warns with the valid ones. The default stylesheet is exported as `CONTROLLER_CSS` (from `pixi-effects/controller`) if you want to copy and restyle more.
|
|
2276
|
+
|
|
2153
2277
|
### `controller.destroy(): void`
|
|
2154
2278
|
|
|
2155
2279
|
Idempotent. Removes:
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pixi-effects",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.0",
|
|
4
4
|
"description": "Declarative composition and video rendering for the web. Built on PixiJS v8.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
"main": "./dist/index.cjs",
|
|
21
21
|
"module": "./dist/index.js",
|
|
22
22
|
"types": "./dist/index.d.ts",
|
|
23
|
-
"bin": { "pixi-effects-check": "ai/tools/check.mjs" },
|
|
23
|
+
"bin": { "pixi-effects-check": "ai/tools/check.mjs", "pixi-effects-render": "ai/tools/render.mjs", "pixi-effects-view": "ai/tools/view.mjs" },
|
|
24
24
|
"files": ["dist", "ai", "docs/dsl.md", "docs/api.md", "llms.txt", "llms-full.txt", "CHANGELOG.md", "README.md", "LICENSE"],
|
|
25
25
|
"keywords": [
|
|
26
26
|
"pixi", "pixijs", "video", "composition", "compositing", "rendering",
|
|
@@ -49,10 +49,12 @@
|
|
|
49
49
|
"peerDependencies": {
|
|
50
50
|
"gsap": "^3.12.0",
|
|
51
51
|
"pixi.js": "^8.0.0",
|
|
52
|
-
"three": ">=0.150.0"
|
|
52
|
+
"three": ">=0.150.0",
|
|
53
|
+
"pixi-filters": "^6.1.0"
|
|
53
54
|
},
|
|
54
55
|
"peerDependenciesMeta": {
|
|
55
|
-
"three": { "optional": true }
|
|
56
|
+
"three": { "optional": true },
|
|
57
|
+
"pixi-filters": { "optional": true }
|
|
56
58
|
},
|
|
57
59
|
"devDependencies": {
|
|
58
60
|
"tsup": "^8.0.0",
|
|
@@ -61,6 +63,7 @@
|
|
|
61
63
|
"happy-dom": "^15.0.0",
|
|
62
64
|
"gsap": "^3.12.0",
|
|
63
65
|
"pixi.js": "^8.22.0",
|
|
66
|
+
"pixi-filters": "^6.1.5",
|
|
64
67
|
"three": "^0.185.1",
|
|
65
68
|
"@types/three": "^0.185.0"
|
|
66
69
|
},
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
var chunk4AGJF5RK_cjs = require('./chunk-4AGJF5RK.cjs');
|
|
4
|
-
require('./chunk-KQM2ZZMK.cjs');
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
Object.defineProperty(exports, "CompositionSequence", {
|
|
9
|
-
enumerable: true,
|
|
10
|
-
get: function () { return chunk4AGJF5RK_cjs.CompositionSequence; }
|
|
11
|
-
});
|
|
12
|
-
//# sourceMappingURL=Composition-Y5VZSDDP.cjs.map
|
|
13
|
-
//# sourceMappingURL=Composition-Y5VZSDDP.cjs.map
|