@pixerate/flair 0.22.0 → 0.24.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 +20 -0
- package/README.md +151 -6
- package/dist/catalog/index.d.ts +91 -4
- package/dist/catalog/index.js +7 -1
- package/dist/{chunk-3RKDS64M.js → chunk-OQ4EWZ3B.js} +849 -11
- package/dist/chunk-OQ4EWZ3B.js.map +1 -0
- package/dist/editor/index.d.ts +2 -2
- package/dist/editor/index.js +31 -11
- package/dist/editor/index.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/react/index.d.ts +2 -2
- package/dist/react/index.js +1 -1
- package/dist/svelte/index.d.ts +2 -2
- package/dist/svelte/index.js +1 -1
- package/dist/{types-Cy5EihZ8.d.ts → types-DUOMxZaq.d.ts} +3 -3
- package/dist/vector/index.d.ts +2 -2
- package/package.json +5 -1
- package/dist/chunk-3RKDS64M.js.map +0 -1
- package/dist/{svg-BEOJriIW.d.ts → svg-BhRboN9q.d.ts} +46 -46
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,25 @@
|
|
|
1
1
|
# @pixerate/flair
|
|
2
2
|
|
|
3
|
+
## 0.24.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- e909f4e: Catalog: a new **lighting** category, 10 effects that relight a picture or video rather than draw light as a background. The shaders read the picture's brightness as a height map (a Sobel of fine detail plus two broader scales from its mip levels) and shade the normals it gives.
|
|
8
|
+
|
|
9
|
+
- **relight**: a directional light with azimuth, elevation, colour, ambient and a soft specular.
|
|
10
|
+
- **point light**: a torch over a darkened picture, following the pointer while hovered and drifting otherwise.
|
|
11
|
+
- **studio lighting**: three-point lighting on a subject, warm key, cool fill and a rim along its silhouette (alpha, or the corner colour keyed out), on a studio backdrop.
|
|
12
|
+
- **time of day**: dawn, noon, golden hour, blue hour and night on an hour control or animated, with lights glowing from the brightest spots at night.
|
|
13
|
+
- **gobo projection**: window panes, foliage, stars, venetian slats or your own text (a gobo image) projected onto a picture, softly focused and drifting.
|
|
14
|
+
- **neon spill**, **candlelight**, **pool caustics** and **party lights** (beams, sweeping pools, mirror ball and strobe).
|
|
15
|
+
- **lamp switch** (vector, click): a pendant lamp whose pull cord switches a warm light cone on and off.
|
|
16
|
+
|
|
17
|
+
## 0.23.0
|
|
18
|
+
|
|
19
|
+
### Minor Changes
|
|
20
|
+
|
|
21
|
+
- 69cdb60: `effectInputs(effect)` says what an effect takes and how it answers the pointer: what it `accepts` (image, video, svg, text, mask, path), its image controls and whether each wants a picture, a mask or text, its text controls (copy, font, path, glyphs), its video use, its `interactions` (hover, press, click, trail, pointer) and whether it is a transparent overlay. It is worked out from the effect's controls, source and fields, so it never drifts. `findEffects` and the `catalog.search` command filter on `accepts`, `interaction` and `transparent`, and the search lists each effect's inputs. `silhouette-morph` now says `video: "none"` (it takes silhouettes, not photos), and the fireworks, explosion and bullet-time effects are tagged `interactive`.
|
|
22
|
+
|
|
3
23
|
## 0.22.0
|
|
4
24
|
|
|
5
25
|
### Minor Changes
|
package/README.md
CHANGED
|
@@ -19,7 +19,7 @@ Flair gives products a library of good-looking, configurable visual effects (her
|
|
|
19
19
|
|
|
20
20
|
### Core Capabilities
|
|
21
21
|
|
|
22
|
-
- 🎨 **Effect catalog (`@pixerate/flair/catalog`)**:
|
|
22
|
+
- 🎨 **Effect catalog (`@pixerate/flair/catalog`)**: 297 effects, each with controls and presets. There are 214 WebGL2 shaders (gradients, organic textures and growth, patterns, light, lighting that relights pictures, space, retro and pixel art, image treatments, transparent overlays, text fills, image morphs, pointer interactions, pyrotechnics, time warps, melting, electricity, splatters, bubbles, stickers, pointer trails, particles, debris, impacts, comic looks, atmosphere, glows, shadows, distortions, transitions) and 83 animated SVG effects (loaders, status icons, animated titles, shape morphs, UI micro-interactions, pixel sprites, stickers, cartoon effects, glowing and lifting UI, a lamp switch, charts, counters, node graphs). [Browse them live](https://pixerate.github.io/flair/).
|
|
23
23
|
- ⚡ **Shader engine**:
|
|
24
24
|
- Shadertoy-style GLSL ES 3.00 fragment shaders, with controls declared in the source (`// @control`), including image inputs.
|
|
25
25
|
- Compile errors come back with line numbers that match the source.
|
|
@@ -43,11 +43,12 @@ Flair gives products a library of good-looking, configurable visual effects (her
|
|
|
43
43
|
| :--- | :--- | :--- |
|
|
44
44
|
| `@pixerate/flair` | Shader engine, vector motion, time and easing, colour, blending, transitions, scenes, interchange exporters | None (browser-safe) |
|
|
45
45
|
| `@pixerate/flair/catalog` | `EFFECTS`, `getEffect`, `findEffects` (also by `video` use), `effectDefaults`, `renderVectorEffect`, `vectorControls`, GLSL helpers, sample images, `textImage` | None |
|
|
46
|
-
| `@pixerate/flair/vector` | `SvgMotion`, `svgFrame`, `svgView`, `SvgPlayer`, `svgFrameMarkup`, `svgViewBox`, `animateCount`, `formatCount`, `morphPaths`, `morphSequence`, `svgMotionIssues` | None |
|
|
47
|
-
| `@pixerate/flair/
|
|
48
|
-
| `@pixerate/flair/
|
|
46
|
+
| `@pixerate/flair/vector` | `SvgMotion`, `svgFrame`, `svgView`, `SvgPlayer`, `svgFrameMarkup`, `svgViewBox`, `animateCount`, `formatCount`, `morphPaths`, `morphSequence`, `svgAnimatedMarkup` (an animated `.svg` file), `svgMotionIssues` | None |
|
|
47
|
+
| `@pixerate/flair/embed` | `mountEffect`, `subjectValues`: any catalog effect on a page, applied to a picture, video, SVG, words or a path; `recordEffect`: MP4, WebM, GIF or PNG frames in the browser | None |
|
|
48
|
+
| `@pixerate/flair/react` | `<FlairEffect>`: any catalog effect applied to a subject; `<VectorEffect>`, `<SvgPlayerView>`: vector effects as React components, server-rendered as a still | `react` ≥ 18 (optional peer) |
|
|
49
|
+
| `@pixerate/flair/svelte` | `flairEffect` action: any catalog effect applied to a subject; `vectorEffect` action, `vectorEffectStill`: vector effects in Svelte, server-rendered as a still | `svelte` ≥ 5 (optional peer; types only) |
|
|
49
50
|
| `@pixerate/flair/editor` | `EffectEditor`, effect documents, keyframe tracks, renderer bindings, parameter-panel binding, agent tools and instructions | `@pixerate/editor` ≥ 0.29 (optional peer; only its dependency-free `/history`, `/commands`, `/tools`, `/animation` and `/parameters` subpaths) |
|
|
50
|
-
| `@pixerate/flair/node` | `exportMotionGraphic`:
|
|
51
|
+
| `@pixerate/flair/node` | `exportEffect` and `exportMotionGraphic`: render a catalog effect (on a picture, video, SVG, words or a path) or a shader to MP4, WebM, MOV, GIF, PNG frames or animated SVG, through headless Chrome (and ffmpeg for video) | Node.js |
|
|
51
52
|
|
|
52
53
|
The `flair` CLI inspects shaders, writes standalone pages and directs scenes (see [CLI](#-cli)).
|
|
53
54
|
|
|
@@ -58,7 +59,7 @@ The `flair` CLI inspects shaders, writes standalone pages and directs scenes (se
|
|
|
58
59
|
```mermaid
|
|
59
60
|
graph TD
|
|
60
61
|
subgraph Effects["Effects (no dependencies)"]
|
|
61
|
-
Catalog["catalog:
|
|
62
|
+
Catalog["catalog: 297 effects + presets"]
|
|
62
63
|
Shader["shader engine: controls, compile, render, GIF, HTML"]
|
|
63
64
|
Vector["vector motion: SvgMotion, SvgPlayer"]
|
|
64
65
|
Time["time: clocks, easing, keyframes, noise"]
|
|
@@ -150,8 +151,90 @@ new SvgPlayer(host, motion, { loop: false });
|
|
|
150
151
|
|
|
151
152
|
Find effects with `findEffects({ text: "glow", category: "light", kind: "shader" })`. Each effect has an `id`, `name`, `description`, `category`, `tags` and optional `presets`. Image effects default to built-in sample pictures, so they look right before you set `uImage` to your own URL.
|
|
152
153
|
|
|
154
|
+
#### What an effect takes
|
|
155
|
+
|
|
156
|
+
`effectInputs(effect)` says what you can give an effect and how it answers the pointer, worked out from the effect itself:
|
|
157
|
+
|
|
158
|
+
```ts
|
|
159
|
+
import { effectInputs, findEffects, getEffect, textImage } from "@pixerate/flair/catalog";
|
|
160
|
+
|
|
161
|
+
effectInputs(getEffect("image-duotone")!);
|
|
162
|
+
// { accepts: ["image", "video", "svg"], images: [{ name: "uImage", wants: "picture" }], video: "filter", interactions: [], transparent: false, … }
|
|
163
|
+
effectInputs(getEffect("typewriter")!);
|
|
164
|
+
// { accepts: ["text"], texts: [{ name: "text", holds: "copy" }, { name: "font", holds: "font" }], interactions: [], … }
|
|
165
|
+
effectInputs(getEffect("click-ripple")!).interactions; // ["click"]
|
|
166
|
+
|
|
167
|
+
findEffects({ accepts: ["video"] }); // effects that run on video
|
|
168
|
+
findEffects({ accepts: ["text"], kind: "vector" }); // animated titles and labels
|
|
169
|
+
findEffects({ accepts: ["image"], interaction: ["hover"] }); // pictures that react to hovering
|
|
170
|
+
findEffects({ transparent: true }); // overlays to lay over a page, picture or video
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
- `accepts`: `image` (any picture URL), `video` (a live `<video>`, with `setLiveSource`), `svg` (an SVG URL, drawn with its transparency), `text` (words for a text control, or for a text mask through `textImage("…")`), `mask` (a shape with transparency whose silhouette the effect uses: an icon, a logo, a word), `path` (SVG path data for a shape control).
|
|
174
|
+
- `images`: each image control's name, and whether it `wants` a `picture`, a `mask` or `text`.
|
|
175
|
+
- `texts`: each text control's name, and whether it `holds` `copy`, a `font`, a `path` or `glyphs`.
|
|
176
|
+
- `interactions`: `hover`, `press`, `click`, `trail` (follows where the pointer has been) or `pointer` (follows where it is), from the pointer built-ins a shader reads or a vector effect's `trigger`.
|
|
177
|
+
- `transparent`, `video` and `trigger`, as on the effect.
|
|
178
|
+
|
|
179
|
+
`findEffects` filters on `accepts` (all of them), `interaction` (any of them; `[]` for effects that don't answer the pointer) and `transparent`, and the `catalog.search` command takes the same filters and lists each effect's `accepts` and `interactions`. The `interactive` tag is kept in step with `interactions`.
|
|
180
|
+
|
|
181
|
+
#### Put an effect on a page
|
|
182
|
+
|
|
183
|
+
`@pixerate/flair/embed` pairs a catalog effect with what it takes and runs it, shader or vector, in one call. `mountEffect(host, effect, { subject })` takes a subject:
|
|
184
|
+
- `{ image }`: a picture's URL.
|
|
185
|
+
- `{ video }`: a playing `<video>`, shown live.
|
|
186
|
+
- `{ svg }`: an SVG, as markup or a URL.
|
|
187
|
+
- `{ text }`: words, typed into a vector effect, or drawn with `textImage` for a shader's text or mask.
|
|
188
|
+
- `{ path }`: path data.
|
|
189
|
+
|
|
190
|
+
It puts the subject in the right control (see `effectInputs`) and throws a sentence if the effect can't take it.
|
|
191
|
+
|
|
192
|
+
```ts
|
|
193
|
+
import { mountEffect } from "@pixerate/flair/embed";
|
|
194
|
+
|
|
195
|
+
mountEffect(el, "image-duotone", { subject: { image: "/team.jpg" }, preset: "Spotify" });
|
|
196
|
+
mountEffect(el, "crt", { subject: { video: document.querySelector("video")! } });
|
|
197
|
+
mountEffect(el, "neon-outline", { subject: { svg: logoMarkup } });
|
|
198
|
+
mountEffect(el, "typewriter", { subject: { text: "Hello there" } });
|
|
199
|
+
mountEffect(el, "transition-wipe", { subject: [{ image: "/a.jpg" }, { image: "/b.jpg" }] });
|
|
200
|
+
const m = mountEffect(el, "click-ripple"); // overlays get a transparent canvas, interactive effects answer the pointer
|
|
201
|
+
m.setValues({ uSpeed: 2 });
|
|
202
|
+
m.dispose();
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
`subjectValues(effect, subject)` gives the control values (and live sources) without mounting anything, for `ShaderRenderer`, `ShaderFrames` or `renderVectorEffect`.
|
|
206
|
+
|
|
207
|
+
In React, `<FlairEffect effect="image-duotone" subject={{ image: "/team.jpg" }} style={{ aspectRatio: "16 / 9" }} />` (from `@pixerate/flair/react`) does the same. In Svelte, use `<div use:flairEffect={{ effect, subject }}></div>` (from `@pixerate/flair/svelte`). Either way the effect fills its element, so size it with CSS.
|
|
208
|
+
|
|
153
209
|
Effects that show a picture (image, transition, morph, time-warp and the like) take transparent pictures too, such as SVG icons (Lucide and similar sets draw black `currentColor` strokes on a clear background). They lay the picture over a backdrop: `uBackdrop` (white by default) shows through its clear parts. Set `uBackdropAlpha` to 0 to keep the picture's alpha instead, and render with `{ alpha: true }` to lay the result over the page. `SAMPLE_IMAGES.icon` is a Lucide-style sample icon. SVGs are rasterised at 2048 px on the long edge, so icons stay sharp.
|
|
154
210
|
|
|
211
|
+
#### Recording an effect
|
|
212
|
+
|
|
213
|
+
`recordEffect(effect, options)` (from `@pixerate/flair/embed`) records a catalog effect, applied to a subject, to a file in the browser, with no server and no extra dependencies:
|
|
214
|
+
|
|
215
|
+
```ts
|
|
216
|
+
import { recordEffect } from "@pixerate/flair/embed";
|
|
217
|
+
|
|
218
|
+
const mp4 = await recordEffect("image-duotone", { subject: { image: "/team.jpg" }, preset: "Spotify" });
|
|
219
|
+
const webm = await recordEffect("crt", { subject: { video }, format: "webm", duration: 5 });
|
|
220
|
+
const gif = await recordEffect("typewriter", { subject: { text: "Hello there" }, format: "gif", width: 640, height: 360, fps: 25 });
|
|
221
|
+
const overlay = await recordEffect("click-ripple", {
|
|
222
|
+
format: "webm",
|
|
223
|
+
alpha: true,
|
|
224
|
+
pointer: { clicks: [{ x: 640, y: 360, time: 0.2 }] }, // or (t) => ({ pointer: [x, y, hover, press] })
|
|
225
|
+
onProgress: (f) => (bar.value = f),
|
|
226
|
+
});
|
|
227
|
+
download(mp4); // a Blob
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
- **Formats.** `mp4` (H.264) or `webm` (VP9, else VP8) are encoded with WebCodecs and written by small built-in muxers: a faststart MP4 (`moov` before `mdat`) and a WebM with no `Cues`. `gif` uses gifenc. `png` gives a ZIP of numbered frames (`frame-0001.png`, …), so every format is one `Blob`. The default is `mp4` where the browser encodes H.264, else `webm`. When the browser can't make a format, it rejects with a sentence that names the formats that would work.
|
|
231
|
+
- **Exact frames.** Frame `i` is drawn at `start + i / fps` off screen, so the result is the same whatever the page is doing. Shaders render with `ShaderFrames`. A `<video>` subject is paused and sought to each frame's time, then put back. A canvas subject is drawn as it is, so draw it for each frame in `beforeFrame(time)`. Vector effects are drawn with `svgView` and rasterised.
|
|
232
|
+
- **Defaults.** 1280 × 720 at 30 fps. The length is a shader's `loop`, or a vector effect's picture plus its `hold`, else 4 s. The bitrate is `width × height × fps × 0.15`. Video sizes round up to even numbers. `recordingPlan(effect, options)` tells you what a recording will be (format, size, frames, encoder) before any frame is drawn.
|
|
233
|
+
- **Interaction.** Shaders that answer the pointer play their idle demo unless `pointer` scripts it, with the same `pointer`, `clicks` and `trail` that `ShaderFrames.render` takes. Click-triggered vector effects take `steps` (ms).
|
|
234
|
+
- **Transparency.** `alpha` defaults to the effect's `transparent`. It is kept in `webm` (VP9/VP8 with an alpha channel, encoded as a second stream and stored as `BlockAdditions`, as Chrome does), in `png`, and in `gif` (one see-through colour, so edges are hard). An `mp4` has no alpha, so asking for both is refused. When alpha is off, transparent pixels are laid over `background` (black for shaders, white for vector effects).
|
|
235
|
+
|
|
236
|
+
Limits: video needs WebCodecs (Chrome and Edge 94+, Safari 16.4+ for H.264, and Firefox 130+). Safari doesn't play alpha WebM, so use PNG frames there. Everything is held in memory until the end, so record long or 4K clips in parts. Vector effects are rasterised once per frame, which is slower than shaders. A picture from another site needs CORS, as on screen.
|
|
237
|
+
|
|
155
238
|
#### Effects on video
|
|
156
239
|
|
|
157
240
|
Image controls can show a playing `<video>` (or a canvas) instead of a picture. `setLiveSource(name, video)` puts it in the control named `name`. The video's current frame is uploaded each time it changes, and a paused preview still follows the video. The renderer never plays, seeks or loops the video itself. The control's value keeps its URL, so documents, presets and agents still see a plain string.
|
|
@@ -181,6 +264,24 @@ Overlay shaders (`category: "overlay"`) have `transparent: true`: they write cov
|
|
|
181
264
|
|
|
182
265
|
To morph between any two SVG shapes, `morphPaths(from, to)` (from `@pixerate/flair/vector`) returns two paths with one structure, so `attrs: { d: [a, b] }` blends them point by point: curves and arcs are read, pieces are paired by position and size, and a piece with no partner grows from or shrinks to a point (draw them with `fill-rule="evenodd"`). `morphSequence` does the same for three or more shapes. `feature-morph` morphs two photos from five matching points, and `silhouette-morph` morphs any two silhouettes or logos (or words, through `textImage`).
|
|
183
266
|
|
|
267
|
+
#### An animated SVG file
|
|
268
|
+
|
|
269
|
+
`svgAnimatedMarkup(motion, options)` (from `@pixerate/flair/vector`) writes any `SvgMotion` as one self-contained `.svg` that plays and loops on its own, with no script and no player: put it in an `<img src>`, open it as a file, or inline it. The changes are written as SMIL, so it needs no DOM to make:
|
|
270
|
+
|
|
271
|
+
```ts
|
|
272
|
+
import { effectDefaults, getEffect, renderVectorEffect, type VectorEffect } from "@pixerate/flair/catalog";
|
|
273
|
+
import { svgAnimatedMarkup } from "@pixerate/flair/vector";
|
|
274
|
+
|
|
275
|
+
const effect = getEffect("stat-counter") as VectorEffect;
|
|
276
|
+
const svg = svgAnimatedMarkup(renderVectorEffect(effect, effectDefaults(effect)), { hold: effect.hold })!;
|
|
277
|
+
img.src = `data:image/svg+xml,${encodeURIComponent(svg)}`; // or save it as stat-counter.svg
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
- **Options**: `hold` (ms on the last frame, default 600), `duration` (the loop, default the picture's length plus `hold`), `loop: false` to play once and stay on the last frame, `steps` (ms) for click-triggered effects, `fps` (default 30) for what is sampled, and `idSuffix`.
|
|
281
|
+
- **How it is written**: each attribute change is one `<animate>` (a `transform` is one `<animateTransform>` per function), and eases are exact `keySplines`. `draw` animates `stroke-dashoffset`, `along` becomes `<animateMotion>` on the path, `view` animates the `viewBox` (keeping `data-fixed` and `data-nonscaling` elements as the player does), and `solo` toggles `display`. Counts and written text are one `<tspan>` per text, shown in turn, sampled at `fps`. Every element starts on the first frame, so it still shows that where SMIL does not run.
|
|
282
|
+
- **Limits**: a count that changes faster than `fps` can be up to half a sample out. A change cut short by the next one, or a `matrix` transform, is sampled. A `view` that frames an element by id (it needs layout) is left out, as in `svgFrameMarkup`. Attributes SMIL cannot blend change in steps. Ids the picture refers to (gradients, filters, paths followed) get a suffix, a hash of the motion by default, so two files inlined in one page don't clash. `#id` selectors in a `<style>` are not renamed. Interaction triggers (hover, press, click) cannot be written as SMIL, so a click-triggered effect plays its `steps` on a timer.
|
|
283
|
+
- **Size**: catalog effects come out at 1 to 105 KB (`drip-text` is the largest).
|
|
284
|
+
|
|
184
285
|
### React
|
|
185
286
|
|
|
186
287
|
`@pixerate/flair/react` wraps the player, so a page needs no ref, effect or dispose of its own. `<VectorEffect>` plays a catalog vector effect by id, `<SvgPlayerView>` any `SvgMotion`:
|
|
@@ -357,6 +458,7 @@ The exporters carry the camera too:
|
|
|
357
458
|
| organic | liquid marble, smoke, lava lamp, plasma, caustics, flames, reaction diffusion, cell division, coral growth, veins, ferrofluid, jellyfish, fur, blob morph |
|
|
358
459
|
| pattern | dot wave, Truchet flow, hex pulse, living cells, moiré rings, kaleidoscope, stripes, neon circuit |
|
|
359
460
|
| light / space | bokeh, god rays, sparkles, neon ripple, light ribbons, warp starfield, nebula, event horizon |
|
|
461
|
+
| lighting | relight, point light, studio lighting, time of day, gobo projection, neon spill, candlelight, pool caustics, party lights, lamp switch |
|
|
360
462
|
| retro | synthwave, digital rain, dithered gradient, TV static, 8-bit, Game Boy screen, pixel fire, pixel landscape, OutRun road, palette cycle, raster bars, tape loading, pixel water, pixel text, coin spin, space invader, heart health bar, chomp loader |
|
|
361
463
|
| image | ripple, glitch, duotone, halftone, pixel reveal, Ken Burns, ASCII art, shatter, melt, risograph, thermal, edge glow, tilt-shift, slit-scan, mosaic flip, chromatic lens, oil paint, CRT screen, film look |
|
|
362
464
|
| overlay | rain on glass, snowfall, lens flare, vignette & grain, scanline sweep, spotlight, embers, shine sweep, frost creep, viewfinder |
|
|
@@ -394,8 +496,51 @@ npx flair html ./effect.frag --out page.html --open
|
|
|
394
496
|
npx flair scene validate ./scene.json # layout, framing and timing checks
|
|
395
497
|
npx flair scene review ./scene.json # fast headless simulation + review
|
|
396
498
|
npx flair scene lottie ./scene.json out.json # also: fcp7, edl, motn
|
|
499
|
+
|
|
500
|
+
# Catalog effects to files
|
|
501
|
+
npx flair effects --accepts video # pick an effect: what each takes (--interaction click, --category image, --kind, --search, --json)
|
|
502
|
+
npx flair render image-duotone --image team.jpg --preset Spotify -o team.mp4
|
|
503
|
+
npx flair render crt --video clip.mp4 -o crt.mp4
|
|
504
|
+
npx flair render click-ripple --alpha -o ripple.webm
|
|
505
|
+
npx flair render shader-text --text "FLAIR" --set uSpeed=2 --size 1280x720 -o title.mov
|
|
506
|
+
npx flair render typewriter --text "Hello" -o hello.gif
|
|
507
|
+
npx flair render toggle-switch -o toggle.svg # vector effects: an animated SVG, no browser needed
|
|
508
|
+
npx flair render aurora --duration 4 -o frames/ # PNG frames (or -o out_%04d.png)
|
|
509
|
+
|
|
510
|
+
# A shader file of your own
|
|
511
|
+
npx flair export ./effect.frag -o out.mp4 -w 1280 -h 720 -d 5 --values '{"speed":2}'
|
|
397
512
|
```
|
|
398
513
|
|
|
514
|
+
#### Rendering in Node
|
|
515
|
+
|
|
516
|
+
`flair render` and `flair export` render in headless Chrome, so a shader draws exactly as `ShaderRenderer` draws it: the same GLSL wrapper and built-ins (`iPointer`, `iClicks`, `iTrail`), image controls (SVGs sized up first), `int` and `bool` controls. The output format comes from the extension:
|
|
517
|
+
|
|
518
|
+
| Output | Notes |
|
|
519
|
+
| --- | --- |
|
|
520
|
+
| `.mp4` | H.264, yuv420p, BT.709. Always opaque (`--alpha` is refused). |
|
|
521
|
+
| `.webm` | VP9; `--alpha` keeps transparency (yuva420p). |
|
|
522
|
+
| `.mov` | ProRes 4444; `--alpha` keeps transparency. |
|
|
523
|
+
| `.gif` | 256 colours a frame; `--alpha` gives one-bit transparency. No ffmpeg needed. |
|
|
524
|
+
| `.png` | Frames: a folder (`-o frames/`) or a pattern (`-o out_%04d.png`). No ffmpeg needed. |
|
|
525
|
+
| `.svg` | Vector effects only: one animated SVG (`svgAnimatedMarkup`), with no browser at all. |
|
|
526
|
+
|
|
527
|
+
Subjects: `--image` (a file or URL), `--svg` (a file, URL or markup), `--text` (with `--font`, `--weight`), `--path` (path data) and `--video` (a file, decoded with ffmpeg to one frame per output frame, looping a short clip; the render lasts as long as the clip, up to 10 s, unless `--duration` says). Repeat a flag for effects that take two pictures, such as transitions. `--set name=value` sets a control (numbers, `true`/`false`, colours like `#ff0080`, or comma-separated vectors), over `--preset`.
|
|
528
|
+
|
|
529
|
+
Without `--alpha`, overlays are drawn over `--background` (black for shaders, white for vector effects). Effects that answer the pointer get a scripted one that hovers, wanders and clicks (`--no-pointer` leaves it still); click-triggered vector effects are clicked through their steps, and hover or press ones play through and back.
|
|
530
|
+
|
|
531
|
+
Chrome is found in the usual places, in Playwright's downloads, or at `CHROME_PATH`; ffmpeg on the `PATH` or at `FFMPEG_PATH`. The same from code:
|
|
532
|
+
|
|
533
|
+
```ts
|
|
534
|
+
import { exportEffect, exportMotionGraphic } from "@pixerate/flair/node";
|
|
535
|
+
|
|
536
|
+
await exportEffect("image-duotone", { subject: { image: "team.jpg" }, preset: "Spotify", outputPath: "team.mp4" });
|
|
537
|
+
await exportEffect("click-ripple", { alpha: true, outputPath: "ripple.webm" });
|
|
538
|
+
await exportEffect("toggle-switch", { outputPath: "toggle.svg" });
|
|
539
|
+
await exportMotionGraphic({ source, values: { speed: 2 }, outputPath: "out.gif", duration: 2 });
|
|
540
|
+
```
|
|
541
|
+
|
|
542
|
+
Limits: vector frames are drawn with `svgFrameMarkup`, so a change `along` a path and a `view` that frames an element by id stay where the markup puts them; GIF frame delays are whole hundredths of a second (30 fps plays at 33.3).
|
|
543
|
+
|
|
399
544
|
---
|
|
400
545
|
|
|
401
546
|
## 🧪 Interactive Browser Demo
|
package/dist/catalog/index.d.ts
CHANGED
|
@@ -1,7 +1,76 @@
|
|
|
1
1
|
import { a as ControlDef, C as ControlValue } from '../controls-Ec6P0Vpy.js';
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
2
|
+
import { a as SvgTrigger } from '../svg-BhRboN9q.js';
|
|
3
|
+
import { a as VideoUse, E as EffectDefinition, b as EffectCategory, S as ShaderEffect } from '../types-DUOMxZaq.js';
|
|
4
|
+
export { C as CURRENT_COLOR, c as EFFECT_CATEGORIES, d as EffectPreset, e as VECTOR_CONTROL_TYPES, f as VIDEO_USES, g as VectorControl, V as VectorEffect, h as colorControl, i as isCurrentColor, n as numberControl, r as renderVectorEffect, v as vectorControlDef, j as vectorControlIssues, k as vectorControls, l as vectorEffectControls } from '../types-DUOMxZaq.js';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* What an effect takes and how it answers the pointer, worked out from the
|
|
8
|
+
* effect itself (its controls, its `video` use, the pointer built-ins its
|
|
9
|
+
* source reads, its `trigger`), so a consumer can pair a picture, a video,
|
|
10
|
+
* an SVG, a word or a path with effects that suit it.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Something you can give an effect:
|
|
15
|
+
* - `image`: a photo or picture (any URL or `data:` image) for an image control.
|
|
16
|
+
* - `video`: a live `<video>` (or canvas) for an image control, with `setLiveSource`.
|
|
17
|
+
* - `svg`: an SVG file or `data:` URL for an image control (drawn as a picture, keeping its transparency).
|
|
18
|
+
* - `text`: words, for a text control, or for a text mask through `textImage`.
|
|
19
|
+
* - `mask`: a shape with transparency (an icon, a logo, `textImage`) whose silhouette the effect uses.
|
|
20
|
+
* - `path`: SVG path data (`d`), for a shape control.
|
|
21
|
+
*/
|
|
22
|
+
type EffectInput = "image" | "video" | "svg" | "text" | "mask" | "path";
|
|
23
|
+
/** Every {@link EffectInput}. */
|
|
24
|
+
declare const EFFECT_INPUTS: readonly EffectInput[];
|
|
25
|
+
/**
|
|
26
|
+
* How an effect answers the pointer:
|
|
27
|
+
* - `hover`: changes while the pointer is over it.
|
|
28
|
+
* - `press`: changes while the pointer is held down (or dragged).
|
|
29
|
+
* - `click`: answers each click or tap.
|
|
30
|
+
* - `trail`: follows where the pointer has just been.
|
|
31
|
+
* - `pointer`: follows where the pointer is.
|
|
32
|
+
*/
|
|
33
|
+
type EffectInteraction = "hover" | "press" | "click" | "trail" | "pointer";
|
|
34
|
+
/** Every {@link EffectInteraction}. */
|
|
35
|
+
declare const EFFECT_INTERACTIONS: readonly EffectInteraction[];
|
|
36
|
+
/** A shader's image control and what it wants. */
|
|
37
|
+
interface ImageSlot {
|
|
38
|
+
/** The control's name (a `sampler2D` uniform): set it with `setValues` or `setLiveSource`. */
|
|
39
|
+
name: string;
|
|
40
|
+
label: string;
|
|
41
|
+
/**
|
|
42
|
+
* What it is for: `picture` (a photo, video or SVG shown as it is), `mask`
|
|
43
|
+
* (a shape whose transparency the effect uses: an icon, a logo, a
|
|
44
|
+
* silhouette), or `text` (a word drawn by `textImage`).
|
|
45
|
+
*/
|
|
46
|
+
wants: "picture" | "mask" | "text";
|
|
47
|
+
}
|
|
48
|
+
/** A vector effect's text control and what it holds. */
|
|
49
|
+
interface TextSlot {
|
|
50
|
+
/** The control's name: set it with `renderVectorEffect(effect, { [name]: … })`. */
|
|
51
|
+
name: string;
|
|
52
|
+
label: string;
|
|
53
|
+
/** `copy` (words shown), `font` (a CSS font family), `path` (SVG path data) or `glyphs` (characters to draw from). */
|
|
54
|
+
holds: "copy" | "font" | "path" | "glyphs";
|
|
55
|
+
default: string;
|
|
56
|
+
}
|
|
57
|
+
/** What an effect takes and how it answers the pointer (see {@link effectInputs}). */
|
|
58
|
+
interface EffectInputs {
|
|
59
|
+
/** Everything it can be given, from its image and text controls. Empty: it makes its picture from nothing. */
|
|
60
|
+
accepts: EffectInput[];
|
|
61
|
+
/** Its image controls (shader effects). */
|
|
62
|
+
images: ImageSlot[];
|
|
63
|
+
/** Its text controls (vector effects). */
|
|
64
|
+
texts: TextSlot[];
|
|
65
|
+
/** How it suits video, for shaders with an image control (see `VideoUse`). */
|
|
66
|
+
video?: VideoUse;
|
|
67
|
+
/** How it answers the pointer; empty when it does not. */
|
|
68
|
+
interactions: EffectInteraction[];
|
|
69
|
+
/** A vector effect's trigger: what makes it play. */
|
|
70
|
+
trigger?: SvgTrigger;
|
|
71
|
+
/** Draws over other content (render it with `alpha: true`, over a page or picture). */
|
|
72
|
+
transparent: boolean;
|
|
73
|
+
}
|
|
5
74
|
|
|
6
75
|
/**
|
|
7
76
|
* Sample pictures for the catalog's image effects: small SVGs as `data:` URLs,
|
|
@@ -71,6 +140,7 @@ declare function glsl(...parts: string[]): string;
|
|
|
71
140
|
* - Video: shader effects with image controls say how they suit video
|
|
72
141
|
* (`video`, see `VideoUse`); `findEffects({ video: ["filter"] })` finds them.
|
|
73
142
|
* - Controls: `effectControls`, `effectDefaults`.
|
|
143
|
+
* - Inputs: `effectInputs` (what an effect takes, how it answers the pointer).
|
|
74
144
|
* - Vector effects: their pictures declare their controls (`vectorControls`),
|
|
75
145
|
* and `renderVectorEffect` fills them in.
|
|
76
146
|
*
|
|
@@ -83,6 +153,17 @@ declare const SHADER_EFFECTS: readonly ShaderEffect[];
|
|
|
83
153
|
declare const EFFECTS: readonly EffectDefinition[];
|
|
84
154
|
/** The effect with this id, or undefined. */
|
|
85
155
|
declare function getEffect(id: string): EffectDefinition | undefined;
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* What an effect takes and how it answers the pointer: what you can give it
|
|
159
|
+
* (`accepts`: image, video, svg, text, mask, path), its image controls and
|
|
160
|
+
* whether each wants a picture, a mask or text, its text controls (copy,
|
|
161
|
+
* font, path, glyphs), how it suits video, how it answers the pointer
|
|
162
|
+
* (`interactions`: hover, press, click, trail, pointer) and whether it draws
|
|
163
|
+
* over other content. Worked out from the effect itself, so it always agrees
|
|
164
|
+
* with its controls and source.
|
|
165
|
+
*/
|
|
166
|
+
declare function effectInputs(effect: EffectDefinition): EffectInputs;
|
|
86
167
|
interface EffectQuery {
|
|
87
168
|
/** Words to look for in the name, description, tags and id (all must match). */
|
|
88
169
|
text?: string;
|
|
@@ -92,6 +173,12 @@ interface EffectQuery {
|
|
|
92
173
|
tags?: readonly string[];
|
|
93
174
|
/** Effects that suit video in one of these ways (shader effects with image controls only). */
|
|
94
175
|
video?: readonly VideoUse[];
|
|
176
|
+
/** Effects that can be given every one of these (see {@link effectInputs}): `["image"]`, `["text"]`, `["svg"]`… */
|
|
177
|
+
accepts?: readonly EffectInput[];
|
|
178
|
+
/** Effects that answer the pointer in at least one of these ways; `[]` for effects that don't answer it at all. */
|
|
179
|
+
interaction?: readonly EffectInteraction[];
|
|
180
|
+
/** Overlays (`true`), which draw over other content, or effects that fill their frame (`false`). */
|
|
181
|
+
transparent?: boolean;
|
|
95
182
|
}
|
|
96
183
|
/** Effects matching a query, in catalog order. */
|
|
97
184
|
declare function findEffects(query?: EffectQuery): EffectDefinition[];
|
|
@@ -103,4 +190,4 @@ declare function effectDefaults(effect: EffectDefinition, options?: {
|
|
|
103
190
|
values?: Record<string, ControlValue>;
|
|
104
191
|
}): Record<string, ControlValue>;
|
|
105
192
|
|
|
106
|
-
export { EFFECTS, EffectCategory, EffectDefinition, type EffectQuery, FBM, HASH, NOISE, OKLAB_MIX, PALETTE, ROTATE, SAMPLE_IMAGES, SHADER_EFFECTS, ShaderEffect, VideoUse, effectControls, effectDefaults, findEffects, getEffect, glsl, svgDataUrl, textImage };
|
|
193
|
+
export { EFFECTS, EFFECT_INPUTS, EFFECT_INTERACTIONS, EffectCategory, EffectDefinition, type EffectInput, type EffectInputs, type EffectInteraction, type EffectQuery, FBM, HASH, type ImageSlot, NOISE, OKLAB_MIX, PALETTE, ROTATE, SAMPLE_IMAGES, SHADER_EFFECTS, ShaderEffect, type TextSlot, VideoUse, effectControls, effectDefaults, effectInputs, findEffects, getEffect, glsl, svgDataUrl, textImage };
|
package/dist/catalog/index.js
CHANGED
|
@@ -2,6 +2,8 @@ import {
|
|
|
2
2
|
CURRENT_COLOR,
|
|
3
3
|
EFFECTS,
|
|
4
4
|
EFFECT_CATEGORIES,
|
|
5
|
+
EFFECT_INPUTS,
|
|
6
|
+
EFFECT_INTERACTIONS,
|
|
5
7
|
FBM,
|
|
6
8
|
HASH,
|
|
7
9
|
NOISE,
|
|
@@ -15,6 +17,7 @@ import {
|
|
|
15
17
|
colorControl,
|
|
16
18
|
effectControls,
|
|
17
19
|
effectDefaults,
|
|
20
|
+
effectInputs,
|
|
18
21
|
findEffects,
|
|
19
22
|
getEffect,
|
|
20
23
|
glsl,
|
|
@@ -27,7 +30,7 @@ import {
|
|
|
27
30
|
vectorControlIssues,
|
|
28
31
|
vectorControls,
|
|
29
32
|
vectorEffectControls
|
|
30
|
-
} from "../chunk-
|
|
33
|
+
} from "../chunk-OQ4EWZ3B.js";
|
|
31
34
|
import "../chunk-OHW7UG6D.js";
|
|
32
35
|
import "../chunk-EHDJ3CPJ.js";
|
|
33
36
|
import "../chunk-VYOMXE6I.js";
|
|
@@ -35,6 +38,8 @@ export {
|
|
|
35
38
|
CURRENT_COLOR,
|
|
36
39
|
EFFECTS,
|
|
37
40
|
EFFECT_CATEGORIES,
|
|
41
|
+
EFFECT_INPUTS,
|
|
42
|
+
EFFECT_INTERACTIONS,
|
|
38
43
|
FBM,
|
|
39
44
|
HASH,
|
|
40
45
|
NOISE,
|
|
@@ -48,6 +53,7 @@ export {
|
|
|
48
53
|
colorControl,
|
|
49
54
|
effectControls,
|
|
50
55
|
effectDefaults,
|
|
56
|
+
effectInputs,
|
|
51
57
|
findEffects,
|
|
52
58
|
getEffect,
|
|
53
59
|
glsl,
|