@pixerate/flair 0.15.0 → 0.16.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 +10 -0
- package/README.md +22 -1
- package/dist/{chunk-SHHAIWUR.js → chunk-UNLCGDCB.js} +10 -10
- package/dist/cli.js +5 -5
- package/dist/editor/index.js +3 -3
- package/dist/index.js +74 -74
- package/dist/react/index.js +1 -1
- package/dist/svelte/index.d.ts +135 -0
- package/dist/svelte/index.js +190 -0
- package/dist/svelte/index.js.map +1 -0
- package/package.json +13 -3
- /package/dist/{chunk-SHHAIWUR.js.map → chunk-UNLCGDCB.js.map} +0 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
# @pixerate/flair
|
|
2
2
|
|
|
3
|
+
## 0.16.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 55fbb80: New `@pixerate/flair/svelte` entry point, with `svelte` (>= 5) as an optional peer dependency (its types only; nothing to compile):
|
|
8
|
+
|
|
9
|
+
- `use:vectorEffect={{ id: "drip-text", values: { word: "Slop" }, loop: false }}` plays a catalog vector effect (or `{ motion }`, any `SvgMotion`) in an element: it makes an `SvgPlayer` on mount, remakes it when the params change (values by content, `speed` without remaking) and disposes it on destroy. Works as an attachment through `fromAction`.
|
|
10
|
+
- `vectorEffectStill(params)` returns `{ markup, style }` for server rendering: `<span use:vectorEffect={params} style={still.style}>{@html still.markup}</span>` shows the last frame, sized from the viewBox, until the player takes over. The still stays under `prefers-reduced-motion` (`reducedMotion` overrides).
|
|
11
|
+
- Sized like the React components: a block with the viewBox's aspect ratio, or with `inline` (or an effect's `inline` control) an `inline-block` 0.72em high.
|
|
12
|
+
|
|
3
13
|
## 0.15.0
|
|
4
14
|
|
|
5
15
|
### Minor Changes
|
package/README.md
CHANGED
|
@@ -45,6 +45,7 @@ Flair gives products a library of good-looking, configurable visual effects (her
|
|
|
45
45
|
| `@pixerate/flair/catalog` | `EFFECTS`, `getEffect`, `findEffects`, `effectDefaults`, `renderVectorEffect`, `vectorControls`, GLSL helpers, sample images, `textImage` | None |
|
|
46
46
|
| `@pixerate/flair/vector` | `SvgMotion`, `svgFrame`, `svgView`, `SvgPlayer`, `svgFrameMarkup`, `svgViewBox`, `animateCount`, `formatCount`, `morphPaths`, `morphSequence`, `svgMotionIssues` | None |
|
|
47
47
|
| `@pixerate/flair/react` | `<VectorEffect>`, `<SvgPlayerView>`: vector effects as React components, server-rendered as a still | `react` ≥ 18 (optional peer) |
|
|
48
|
+
| `@pixerate/flair/svelte` | `vectorEffect` action, `vectorEffectStill`: vector effects in Svelte, server-rendered as a still | `svelte` ≥ 5 (optional peer; types only) |
|
|
48
49
|
| `@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) |
|
|
49
50
|
| `@pixerate/flair/node` | `exportMotionGraphic`: renders a shader to GIF/MP4 through headless Chrome and ffmpeg | Node.js |
|
|
50
51
|
|
|
@@ -167,7 +168,27 @@ import { VectorEffect } from "@pixerate/flair/react";
|
|
|
167
168
|
- **Size**: a block as wide as its container, with the viewBox's aspect ratio. With `inline` (or an effect whose `inline` control is on, like drip text's) it is an `inline-block` 0.72em high, sitting on the baseline like a capital letter; pass a CSS length to `inline` for another height.
|
|
168
169
|
- **Props**: player options (`loop`, `autoplay`, `speed`, `hold`, `trigger`, `steps`, `pauseWhenHidden`, `playOnEnter`, `clock` …), `values` and `preset` for an effect, `onPlayer` for the player itself, and any `<span>` attribute (`elementId` sets the host's `id` on `<VectorEffect>`). Values are compared by content, so an inline object does not restart it; pass `<SvgPlayerView>` a memoised `motion`.
|
|
169
170
|
|
|
170
|
-
Without React, `svgFrameMarkup(motion, { at: "last" })` (from `@pixerate/flair/vector`) gives the same still as safe `<svg>` markup with no DOM, for any server or framework.
|
|
171
|
+
Without React or Svelte, `svgFrameMarkup(motion, { at: "last" })` (from `@pixerate/flair/vector`) gives the same still as safe `<svg>` markup with no DOM, for any server or framework.
|
|
172
|
+
|
|
173
|
+
### Svelte
|
|
174
|
+
|
|
175
|
+
`@pixerate/flair/svelte` does the same for Svelte 5, as plain TypeScript (no `.svelte` files, so nothing to compile). The `vectorEffect` action plays an effect by `id` (or any `motion`) in its element, and `vectorEffectStill` gives the server-rendered still:
|
|
176
|
+
|
|
177
|
+
```svelte
|
|
178
|
+
<script>
|
|
179
|
+
import { vectorEffect, vectorEffectStill } from "@pixerate/flair/svelte";
|
|
180
|
+
|
|
181
|
+
let { word = "Slop" } = $props();
|
|
182
|
+
const params = $derived({ id: "drip-text", values: { word, inline: true, font: "inherit" }, loop: false });
|
|
183
|
+
const still = $derived(vectorEffectStill(params));
|
|
184
|
+
</script>
|
|
185
|
+
|
|
186
|
+
<h1>Stop the <span use:vectorEffect={params} style={still.style}>{@html still.markup}</span></h1>
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
- **Params**: the React props, as one object: `id` with `values` and `preset`, or `motion`; player options; `poster`, `reducedMotion`, `inline`, `onPlayer`. A change remakes the player (values by content; `speed` without remaking), and it is disposed when the element goes.
|
|
190
|
+
- **Still**: `markup` is the still's layer and `style` the host's size from the viewBox, with the same rules as React. The action hides the still while the player runs and keeps it under `prefers-reduced-motion`. Without `vectorEffectStill` it makes its own still and sizes the element on the client.
|
|
191
|
+
- **Attachments**: `{@attach fromAction(vectorEffect, () => params)}`, with `fromAction` from `svelte/attachments`.
|
|
171
192
|
|
|
172
193
|
`shader-text` fills letters from a text image: set its `uText` control to `textImage("Your title", { weight: 900 })`, a transparent SVG `data:` URL of any Unicode text.
|
|
173
194
|
|
|
@@ -1,3 +1,12 @@
|
|
|
1
|
+
import {
|
|
2
|
+
CAMERA_MS,
|
|
3
|
+
CAMERA_STEP_MS,
|
|
4
|
+
EFFECT_TYPES,
|
|
5
|
+
MAX_TILT,
|
|
6
|
+
cameraAt,
|
|
7
|
+
cameraTrack,
|
|
8
|
+
sceneStage
|
|
9
|
+
} from "./chunk-WAMA45G3.js";
|
|
1
10
|
import {
|
|
2
11
|
HOVER_SECONDS,
|
|
3
12
|
MAX_CLICKS,
|
|
@@ -18,15 +27,6 @@ import {
|
|
|
18
27
|
imageUrls,
|
|
19
28
|
parseControls
|
|
20
29
|
} from "./chunk-WC3VADZC.js";
|
|
21
|
-
import {
|
|
22
|
-
CAMERA_MS,
|
|
23
|
-
CAMERA_STEP_MS,
|
|
24
|
-
EFFECT_TYPES,
|
|
25
|
-
MAX_TILT,
|
|
26
|
-
cameraAt,
|
|
27
|
-
cameraTrack,
|
|
28
|
-
sceneStage
|
|
29
|
-
} from "./chunk-WAMA45G3.js";
|
|
30
30
|
|
|
31
31
|
// src/shader/renderer.ts
|
|
32
32
|
var ShaderRenderer = class {
|
|
@@ -1617,4 +1617,4 @@ export {
|
|
|
1617
1617
|
validateScene,
|
|
1618
1618
|
standaloneSceneHtml
|
|
1619
1619
|
};
|
|
1620
|
-
//# sourceMappingURL=chunk-
|
|
1620
|
+
//# sourceMappingURL=chunk-UNLCGDCB.js.map
|
package/dist/cli.js
CHANGED
|
@@ -3,16 +3,16 @@ import {
|
|
|
3
3
|
standaloneHtml,
|
|
4
4
|
standaloneSceneHtml,
|
|
5
5
|
validateScene
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-UNLCGDCB.js";
|
|
7
|
+
import {
|
|
8
|
+
simulateScene
|
|
9
|
+
} from "./chunk-WAMA45G3.js";
|
|
7
10
|
import "./chunk-YLJWC4N2.js";
|
|
8
11
|
import "./chunk-EHDJ3CPJ.js";
|
|
12
|
+
import "./chunk-WMZQCUFJ.js";
|
|
9
13
|
import {
|
|
10
14
|
parseControls
|
|
11
15
|
} from "./chunk-WC3VADZC.js";
|
|
12
|
-
import {
|
|
13
|
-
simulateScene
|
|
14
|
-
} from "./chunk-WAMA45G3.js";
|
|
15
|
-
import "./chunk-WMZQCUFJ.js";
|
|
16
16
|
|
|
17
17
|
// src/cli.ts
|
|
18
18
|
import { Command } from "commander";
|
package/dist/editor/index.js
CHANGED
|
@@ -16,14 +16,14 @@ import {
|
|
|
16
16
|
interpolateOklab
|
|
17
17
|
} from "../chunk-YLJWC4N2.js";
|
|
18
18
|
import "../chunk-EHDJ3CPJ.js";
|
|
19
|
+
import {
|
|
20
|
+
realClock
|
|
21
|
+
} from "../chunk-WMZQCUFJ.js";
|
|
19
22
|
import {
|
|
20
23
|
checkControlValue,
|
|
21
24
|
controlValue,
|
|
22
25
|
parseControls
|
|
23
26
|
} from "../chunk-WC3VADZC.js";
|
|
24
|
-
import {
|
|
25
|
-
realClock
|
|
26
|
-
} from "../chunk-WMZQCUFJ.js";
|
|
27
27
|
|
|
28
28
|
// src/editor/timeline.ts
|
|
29
29
|
import {
|
package/dist/index.js
CHANGED
|
@@ -1,3 +1,13 @@
|
|
|
1
|
+
import {
|
|
2
|
+
calculateShotTimings,
|
|
3
|
+
createLottieEase,
|
|
4
|
+
exportCmx3600Edl,
|
|
5
|
+
exportFcp7Xml,
|
|
6
|
+
exportLottie,
|
|
7
|
+
exportMotn,
|
|
8
|
+
framesToTimecode,
|
|
9
|
+
timecodeToFrames
|
|
10
|
+
} from "./chunk-GTIAOYOK.js";
|
|
1
11
|
import {
|
|
2
12
|
computeBoxFit,
|
|
3
13
|
formatUnit,
|
|
@@ -49,80 +59,7 @@ import {
|
|
|
49
59
|
standaloneHtml,
|
|
50
60
|
standaloneSceneHtml,
|
|
51
61
|
validateScene
|
|
52
|
-
} from "./chunk-
|
|
53
|
-
import {
|
|
54
|
-
BUILTIN_UNIFORMS,
|
|
55
|
-
HOVER_SECONDS,
|
|
56
|
-
ImageLoadError,
|
|
57
|
-
MAX_CLICKS,
|
|
58
|
-
PointerTracker,
|
|
59
|
-
SVG_EDGE,
|
|
60
|
-
ShaderCompileError,
|
|
61
|
-
TextureCache,
|
|
62
|
-
VERTEX_SOURCE,
|
|
63
|
-
applyAscCdl,
|
|
64
|
-
applyLiftGammaGain,
|
|
65
|
-
applyTextureSettings,
|
|
66
|
-
applyVibrance,
|
|
67
|
-
clicksUniform,
|
|
68
|
-
compileShader,
|
|
69
|
-
convertColorSpace,
|
|
70
|
-
createContext,
|
|
71
|
-
drawFrame,
|
|
72
|
-
formatCompileError,
|
|
73
|
-
getColorGradingGLSL,
|
|
74
|
-
getOklabGLSL,
|
|
75
|
-
getWhiteBalanceGains,
|
|
76
|
-
interpolateOklab,
|
|
77
|
-
interpolateOklch,
|
|
78
|
-
isSvgUrl,
|
|
79
|
-
linearRgbToSrgb,
|
|
80
|
-
linearSrgbToOklab,
|
|
81
|
-
linearToSrgb,
|
|
82
|
-
loadImage,
|
|
83
|
-
oklabToLinearSrgb,
|
|
84
|
-
oklabToOklch,
|
|
85
|
-
oklabToSrgb,
|
|
86
|
-
oklchToOklab,
|
|
87
|
-
parseCompileLog,
|
|
88
|
-
preloadImages,
|
|
89
|
-
releaseImages,
|
|
90
|
-
resetGlCaches,
|
|
91
|
-
sizeSvg,
|
|
92
|
-
srgbToLinear,
|
|
93
|
-
srgbToLinearRgb,
|
|
94
|
-
srgbToOklab,
|
|
95
|
-
wrapSource
|
|
96
|
-
} from "./chunk-YLJWC4N2.js";
|
|
97
|
-
import {
|
|
98
|
-
ALL_TRANSITION_TYPES,
|
|
99
|
-
blendTransitionPixel,
|
|
100
|
-
getTransitionGLSL,
|
|
101
|
-
sampleTransition
|
|
102
|
-
} from "./chunk-EHDJ3CPJ.js";
|
|
103
|
-
import {
|
|
104
|
-
checkControlValue,
|
|
105
|
-
coerceControlValue,
|
|
106
|
-
componentCount,
|
|
107
|
-
controlValue,
|
|
108
|
-
describeValue,
|
|
109
|
-
hexToRgb,
|
|
110
|
-
humanise,
|
|
111
|
-
imageUrls,
|
|
112
|
-
isImageUrl,
|
|
113
|
-
parseControls,
|
|
114
|
-
rgbToHex
|
|
115
|
-
} from "./chunk-WC3VADZC.js";
|
|
116
|
-
import {
|
|
117
|
-
calculateShotTimings,
|
|
118
|
-
createLottieEase,
|
|
119
|
-
exportCmx3600Edl,
|
|
120
|
-
exportFcp7Xml,
|
|
121
|
-
exportLottie,
|
|
122
|
-
exportMotn,
|
|
123
|
-
framesToTimecode,
|
|
124
|
-
timecodeToFrames
|
|
125
|
-
} from "./chunk-GTIAOYOK.js";
|
|
62
|
+
} from "./chunk-UNLCGDCB.js";
|
|
126
63
|
import {
|
|
127
64
|
CAMERA_MS,
|
|
128
65
|
CAMERA_STEP_MS,
|
|
@@ -180,12 +117,75 @@ import {
|
|
|
180
117
|
viewAt,
|
|
181
118
|
visibleRect
|
|
182
119
|
} from "./chunk-WAMA45G3.js";
|
|
120
|
+
import {
|
|
121
|
+
BUILTIN_UNIFORMS,
|
|
122
|
+
HOVER_SECONDS,
|
|
123
|
+
ImageLoadError,
|
|
124
|
+
MAX_CLICKS,
|
|
125
|
+
PointerTracker,
|
|
126
|
+
SVG_EDGE,
|
|
127
|
+
ShaderCompileError,
|
|
128
|
+
TextureCache,
|
|
129
|
+
VERTEX_SOURCE,
|
|
130
|
+
applyAscCdl,
|
|
131
|
+
applyLiftGammaGain,
|
|
132
|
+
applyTextureSettings,
|
|
133
|
+
applyVibrance,
|
|
134
|
+
clicksUniform,
|
|
135
|
+
compileShader,
|
|
136
|
+
convertColorSpace,
|
|
137
|
+
createContext,
|
|
138
|
+
drawFrame,
|
|
139
|
+
formatCompileError,
|
|
140
|
+
getColorGradingGLSL,
|
|
141
|
+
getOklabGLSL,
|
|
142
|
+
getWhiteBalanceGains,
|
|
143
|
+
interpolateOklab,
|
|
144
|
+
interpolateOklch,
|
|
145
|
+
isSvgUrl,
|
|
146
|
+
linearRgbToSrgb,
|
|
147
|
+
linearSrgbToOklab,
|
|
148
|
+
linearToSrgb,
|
|
149
|
+
loadImage,
|
|
150
|
+
oklabToLinearSrgb,
|
|
151
|
+
oklabToOklch,
|
|
152
|
+
oklabToSrgb,
|
|
153
|
+
oklchToOklab,
|
|
154
|
+
parseCompileLog,
|
|
155
|
+
preloadImages,
|
|
156
|
+
releaseImages,
|
|
157
|
+
resetGlCaches,
|
|
158
|
+
sizeSvg,
|
|
159
|
+
srgbToLinear,
|
|
160
|
+
srgbToLinearRgb,
|
|
161
|
+
srgbToOklab,
|
|
162
|
+
wrapSource
|
|
163
|
+
} from "./chunk-YLJWC4N2.js";
|
|
164
|
+
import {
|
|
165
|
+
ALL_TRANSITION_TYPES,
|
|
166
|
+
blendTransitionPixel,
|
|
167
|
+
getTransitionGLSL,
|
|
168
|
+
sampleTransition
|
|
169
|
+
} from "./chunk-EHDJ3CPJ.js";
|
|
183
170
|
import {
|
|
184
171
|
ManualClock,
|
|
185
172
|
jitter,
|
|
186
173
|
pageHidden,
|
|
187
174
|
realClock
|
|
188
175
|
} from "./chunk-WMZQCUFJ.js";
|
|
176
|
+
import {
|
|
177
|
+
checkControlValue,
|
|
178
|
+
coerceControlValue,
|
|
179
|
+
componentCount,
|
|
180
|
+
controlValue,
|
|
181
|
+
describeValue,
|
|
182
|
+
hexToRgb,
|
|
183
|
+
humanise,
|
|
184
|
+
imageUrls,
|
|
185
|
+
isImageUrl,
|
|
186
|
+
parseControls,
|
|
187
|
+
rgbToHex
|
|
188
|
+
} from "./chunk-WC3VADZC.js";
|
|
189
189
|
|
|
190
190
|
// src/time/retiming.ts
|
|
191
191
|
function loopTime(t, duration, start = 0) {
|
package/dist/react/index.js
CHANGED
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import { ActionReturn } from 'svelte/action';
|
|
2
|
+
import { V as VectorEffect } from '../types-9J6NWaN3.js';
|
|
3
|
+
import { C as ControlValue } from '../controls-Syr6drzO.js';
|
|
4
|
+
import { a as SvgTrigger, C as Clock, b as SvgPlayer, S as SvgMotion } from '../svg-XugR-6Ec.js';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Svelte 5 bindings for Flair's vector effects, written as plain TypeScript so
|
|
8
|
+
* they need no Svelte compiler: the `vectorEffect` action plays a catalog
|
|
9
|
+
* effect (or any {@link SvgMotion}) in an element, and `vectorEffectStill`
|
|
10
|
+
* gives the markup and style of its still for server rendering.
|
|
11
|
+
*
|
|
12
|
+
* ```svelte
|
|
13
|
+
* <script>
|
|
14
|
+
* import { vectorEffect, vectorEffectStill } from "@pixerate/flair/svelte";
|
|
15
|
+
* const params = { id: "drip-text", values: { word: "Slop" }, loop: false };
|
|
16
|
+
* const still = vectorEffectStill(params);
|
|
17
|
+
* </script>
|
|
18
|
+
*
|
|
19
|
+
* <span use:vectorEffect={params} style={still.style}>{@html still.markup}</span>
|
|
20
|
+
* ```
|
|
21
|
+
*
|
|
22
|
+
* The still is in place from the first paint; the live `SvgPlayer` takes over
|
|
23
|
+
* after mount and is disposed on destroy. Under `prefers-reduced-motion:
|
|
24
|
+
* reduce` the still stays.
|
|
25
|
+
*
|
|
26
|
+
* Needs Svelte 5 (an optional peer dependency of the package); only its types
|
|
27
|
+
* are imported.
|
|
28
|
+
*
|
|
29
|
+
* @packageDocumentation
|
|
30
|
+
*/
|
|
31
|
+
|
|
32
|
+
/** When to leave the still in place of the animation. */
|
|
33
|
+
type ReducedMotion = "user" | "always" | "never";
|
|
34
|
+
/** Options shared by an effect by id and a motion of your own. */
|
|
35
|
+
interface SvgPlayerParams {
|
|
36
|
+
/** Start again at the end (default `true`); `false` plays once and stays on the last frame. */
|
|
37
|
+
loop?: boolean;
|
|
38
|
+
/** Start playing at once (default `true`). */
|
|
39
|
+
autoplay?: boolean;
|
|
40
|
+
/** Playback speed (default 1); changed without remaking the player. */
|
|
41
|
+
speed?: number;
|
|
42
|
+
/** Ms to rest on the last frame before looping (default 600, or the effect's). */
|
|
43
|
+
hold?: number;
|
|
44
|
+
/** Loop length in ms (default: the picture's own, plus `hold`). */
|
|
45
|
+
duration?: number;
|
|
46
|
+
/** Step start times in ms, for pictures that change on steps. */
|
|
47
|
+
steps?: readonly number[];
|
|
48
|
+
/** Plays in answer to the pointer (see `SvgTrigger`; default the effect's). */
|
|
49
|
+
trigger?: SvgTrigger;
|
|
50
|
+
/** Pause while scrolled out of view. */
|
|
51
|
+
pauseWhenHidden?: boolean;
|
|
52
|
+
/** Wait to start until scrolled into view. */
|
|
53
|
+
playOnEnter?: boolean;
|
|
54
|
+
/** Hide the picture while paused or reset. */
|
|
55
|
+
hideWhenPaused?: boolean;
|
|
56
|
+
/** Time source (default animation frames); a `ManualClock` steps frames yourself. */
|
|
57
|
+
clock?: Clock;
|
|
58
|
+
/** Called with the id of the innermost element with an id that is clicked. */
|
|
59
|
+
onPick?: (id: string) => void;
|
|
60
|
+
/** Called with the player once it is made, and `undefined` when it is disposed. */
|
|
61
|
+
onPlayer?: (player: SvgPlayer | undefined) => void;
|
|
62
|
+
/**
|
|
63
|
+
* The still shown before the player starts and instead of it under reduced
|
|
64
|
+
* motion: `"last"` (default; the rest frame, `"first"` for a hover or press
|
|
65
|
+
* trigger), `"first"` or a time in ms.
|
|
66
|
+
*/
|
|
67
|
+
poster?: "first" | "last" | number;
|
|
68
|
+
/**
|
|
69
|
+
* `"user"` (default) shows the still when the visitor prefers reduced
|
|
70
|
+
* motion, `"always"` always does, `"never"` always plays.
|
|
71
|
+
*/
|
|
72
|
+
reducedMotion?: ReducedMotion;
|
|
73
|
+
/**
|
|
74
|
+
* Size it as a word in a line of text: `display: inline-block` with the
|
|
75
|
+
* viewBox's aspect ratio and `height` 0.72em (the cap height of a heavy
|
|
76
|
+
* sans; pass a CSS length for another). Default: the effect's `inline`
|
|
77
|
+
* control. Otherwise it is a block as wide as its container with the
|
|
78
|
+
* viewBox's aspect ratio.
|
|
79
|
+
*/
|
|
80
|
+
inline?: boolean | string;
|
|
81
|
+
}
|
|
82
|
+
/** A catalog vector effect by id (or the effect itself), with its values. */
|
|
83
|
+
interface VectorEffectByIdParams extends SvgPlayerParams {
|
|
84
|
+
/** A vector effect's catalog id (`"drip-text"`), or the effect itself. */
|
|
85
|
+
id: string | VectorEffect;
|
|
86
|
+
/** Control values; missing ones take the preset's, then the defaults. Compared by content. */
|
|
87
|
+
values?: Record<string, ControlValue>;
|
|
88
|
+
/** A preset's name to start from. */
|
|
89
|
+
preset?: string;
|
|
90
|
+
motion?: never;
|
|
91
|
+
}
|
|
92
|
+
/** Any {@link SvgMotion}; a new object restarts the player. */
|
|
93
|
+
interface SvgMotionParams extends SvgPlayerParams {
|
|
94
|
+
/** The picture and its animations. */
|
|
95
|
+
motion: SvgMotion;
|
|
96
|
+
id?: never;
|
|
97
|
+
values?: never;
|
|
98
|
+
preset?: never;
|
|
99
|
+
}
|
|
100
|
+
/** What `vectorEffect` and `vectorEffectStill` take: an effect by `id`, or a `motion`. */
|
|
101
|
+
type VectorEffectParams = VectorEffectByIdParams | SvgMotionParams;
|
|
102
|
+
/** A still of the picture, for `{@html markup}` inside an element with `style`. */
|
|
103
|
+
interface VectorEffectStill {
|
|
104
|
+
/** The still's layer: safe `<svg>` markup (see `svgFrameMarkup`) in an absolutely placed `<span>`. */
|
|
105
|
+
markup: string;
|
|
106
|
+
/** The host's inline style: its position and size from the viewBox. */
|
|
107
|
+
style: string;
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* The still of an effect or motion, for server rendering: put `markup` in the
|
|
111
|
+
* element that has the `vectorEffect` action, with `style` as its style, so
|
|
112
|
+
* the page shows the picture before the player starts (and instead of it
|
|
113
|
+
* under reduced motion), in the room the player will take.
|
|
114
|
+
* Throws when `id` is not a vector effect.
|
|
115
|
+
*/
|
|
116
|
+
declare function vectorEffectStill(params: VectorEffectParams): VectorEffectStill;
|
|
117
|
+
/**
|
|
118
|
+
* A Svelte action that plays a vector effect in its element:
|
|
119
|
+
* `<span use:vectorEffect={{ id: "drip-text", values: { word: "Slop" }, loop: false }} />`.
|
|
120
|
+
*
|
|
121
|
+
* It makes an `SvgPlayer` in a layer of its own, remakes it when the params
|
|
122
|
+
* change (values by content; `speed` without remaking) and disposes it on
|
|
123
|
+
* destroy. A still from {@link vectorEffectStill} in the element is hidden
|
|
124
|
+
* while the player runs and shown again after; without one, the action makes
|
|
125
|
+
* its own (so reduced motion still shows the picture) and sizes the element
|
|
126
|
+
* from the viewBox, leaving alone any of `position`, `display`, `height` and
|
|
127
|
+
* `aspect-ratio` the element's style already sets.
|
|
128
|
+
*
|
|
129
|
+
* For an attachment, `{@attach fromAction(vectorEffect, () => params)}`
|
|
130
|
+
* (`fromAction` is from `svelte/attachments`). Throws when `id` is not a
|
|
131
|
+
* vector effect.
|
|
132
|
+
*/
|
|
133
|
+
declare function vectorEffect(node: HTMLElement, initial: VectorEffectParams): ActionReturn<VectorEffectParams>;
|
|
134
|
+
|
|
135
|
+
export { type ReducedMotion, SvgMotion, type SvgMotionParams, SvgPlayer, type SvgPlayerParams, SvgTrigger, type VectorEffectByIdParams, type VectorEffectParams, type VectorEffectStill, vectorEffect, vectorEffectStill };
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
import {
|
|
2
|
+
effectDefaults,
|
|
3
|
+
getEffect,
|
|
4
|
+
renderVectorEffect
|
|
5
|
+
} from "../chunk-KWNFZ5NC.js";
|
|
6
|
+
import {
|
|
7
|
+
svgFrameMarkup,
|
|
8
|
+
svgViewBox
|
|
9
|
+
} from "../chunk-DVVQRZJ5.js";
|
|
10
|
+
import {
|
|
11
|
+
SvgPlayer
|
|
12
|
+
} from "../chunk-4RGHZWQC.js";
|
|
13
|
+
import "../chunk-OHW7UG6D.js";
|
|
14
|
+
import "../chunk-EHDJ3CPJ.js";
|
|
15
|
+
import "../chunk-WMZQCUFJ.js";
|
|
16
|
+
import "../chunk-WC3VADZC.js";
|
|
17
|
+
|
|
18
|
+
// src/svelte/index.ts
|
|
19
|
+
var CAP_HEIGHT = "0.72em";
|
|
20
|
+
var STILL_ATTR = "data-flair-still";
|
|
21
|
+
var LAYER = "position:absolute;inset:0;display:block";
|
|
22
|
+
function resolve(params, cache) {
|
|
23
|
+
if (params.motion) return { motion: params.motion, hold: params.hold, trigger: params.trigger, inline: params.inline };
|
|
24
|
+
const { id, values, preset } = params;
|
|
25
|
+
const effect = typeof id === "string" ? getEffect(id) : id;
|
|
26
|
+
if (!effect || effect.kind !== "vector")
|
|
27
|
+
throw new Error(
|
|
28
|
+
`"${typeof id === "string" ? id : effect?.id ?? id}" is not a vector effect; pick one from findEffects({ kind: "vector" }), or pass a motion`
|
|
29
|
+
);
|
|
30
|
+
const key = JSON.stringify([preset ?? null, values ?? {}]);
|
|
31
|
+
let motion;
|
|
32
|
+
if (cache?.current && cache.current.effect === effect && cache.current.key === key) motion = cache.current.motion;
|
|
33
|
+
else {
|
|
34
|
+
motion = renderVectorEffect(effect, effectDefaults(effect, { preset, values }));
|
|
35
|
+
if (cache) cache.current = { key, effect, motion };
|
|
36
|
+
}
|
|
37
|
+
return {
|
|
38
|
+
motion,
|
|
39
|
+
hold: params.hold ?? effect.hold,
|
|
40
|
+
trigger: params.trigger ?? effect.trigger,
|
|
41
|
+
inline: params.inline ?? values?.inline === true
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
function hostStyle(svg, inline) {
|
|
45
|
+
const box = svgViewBox(svg);
|
|
46
|
+
const style = [
|
|
47
|
+
["position", "relative"],
|
|
48
|
+
["display", inline ? "inline-block" : "block"]
|
|
49
|
+
];
|
|
50
|
+
if (inline) style.push(["height", typeof inline === "string" ? inline : CAP_HEIGHT]);
|
|
51
|
+
if (box) style.push(["aspect-ratio", `${box[2]} / ${box[3]}`]);
|
|
52
|
+
return style;
|
|
53
|
+
}
|
|
54
|
+
function stillMarkup(r, params) {
|
|
55
|
+
const at = params.poster ?? (r.trigger === "hover" || r.trigger === "press" ? "first" : "last");
|
|
56
|
+
return svgFrameMarkup(r.motion, { at, steps: params.steps }) ?? "";
|
|
57
|
+
}
|
|
58
|
+
function vectorEffectStill(params) {
|
|
59
|
+
const r = resolve(params);
|
|
60
|
+
return {
|
|
61
|
+
markup: `<span ${STILL_ATTR} aria-hidden="true" style="${LAYER}">${stillMarkup(r, params)}</span>`,
|
|
62
|
+
style: hostStyle(r.motion.svg, r.inline).map(([k, v]) => `${k}: ${v}`).join("; ")
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
function prefersReducedMotion() {
|
|
66
|
+
return typeof matchMedia === "function" && matchMedia("(prefers-reduced-motion: reduce)").matches;
|
|
67
|
+
}
|
|
68
|
+
function playerKey(r, p) {
|
|
69
|
+
return [
|
|
70
|
+
r.motion,
|
|
71
|
+
p.loop,
|
|
72
|
+
p.autoplay,
|
|
73
|
+
r.hold,
|
|
74
|
+
p.duration,
|
|
75
|
+
p.steps?.join(","),
|
|
76
|
+
r.trigger,
|
|
77
|
+
p.pauseWhenHidden,
|
|
78
|
+
p.playOnEnter,
|
|
79
|
+
p.hideWhenPaused,
|
|
80
|
+
p.clock,
|
|
81
|
+
p.reducedMotion ?? "user"
|
|
82
|
+
];
|
|
83
|
+
}
|
|
84
|
+
function vectorEffect(node, initial) {
|
|
85
|
+
const cache = {};
|
|
86
|
+
const doc = node.ownerDocument;
|
|
87
|
+
const mount = doc.createElement("span");
|
|
88
|
+
mount.style.cssText = LAYER;
|
|
89
|
+
node.appendChild(mount);
|
|
90
|
+
let params = initial;
|
|
91
|
+
let resolved = resolve(params, cache);
|
|
92
|
+
let player;
|
|
93
|
+
let key = [];
|
|
94
|
+
let ownStill;
|
|
95
|
+
const sized = /* @__PURE__ */ new Set();
|
|
96
|
+
const stills = () => node.querySelectorAll(`:scope > [${STILL_ATTR}]`);
|
|
97
|
+
const showStills = (show) => {
|
|
98
|
+
for (const s of stills()) s.style.display = show ? "block" : "none";
|
|
99
|
+
};
|
|
100
|
+
const reattach = () => {
|
|
101
|
+
if (mount.parentNode !== node) node.appendChild(mount);
|
|
102
|
+
if (player) showStills(false);
|
|
103
|
+
};
|
|
104
|
+
const observer = typeof MutationObserver === "function" ? new MutationObserver(reattach) : void 0;
|
|
105
|
+
observer?.observe(node, { childList: true });
|
|
106
|
+
const size = () => {
|
|
107
|
+
const style = hostStyle(resolved.motion.svg, resolved.inline);
|
|
108
|
+
for (const [prop, value] of style) {
|
|
109
|
+
if (!sized.has(prop) && node.style.getPropertyValue(prop)) continue;
|
|
110
|
+
node.style.setProperty(prop, value);
|
|
111
|
+
sized.add(prop);
|
|
112
|
+
}
|
|
113
|
+
for (const prop of sized)
|
|
114
|
+
if (!style.some(([p]) => p === prop)) {
|
|
115
|
+
node.style.removeProperty(prop);
|
|
116
|
+
sized.delete(prop);
|
|
117
|
+
}
|
|
118
|
+
};
|
|
119
|
+
if (stills().length === 0) {
|
|
120
|
+
ownStill = doc.createElement("span");
|
|
121
|
+
ownStill.setAttribute(STILL_ATTR, "");
|
|
122
|
+
ownStill.setAttribute("aria-hidden", "true");
|
|
123
|
+
ownStill.style.cssText = LAYER;
|
|
124
|
+
node.insertBefore(ownStill, mount);
|
|
125
|
+
size();
|
|
126
|
+
}
|
|
127
|
+
const stop = () => {
|
|
128
|
+
if (!player) return;
|
|
129
|
+
player.dispose();
|
|
130
|
+
player = void 0;
|
|
131
|
+
showStills(true);
|
|
132
|
+
params.onPlayer?.(void 0);
|
|
133
|
+
};
|
|
134
|
+
const start = () => {
|
|
135
|
+
const reduced = params.reducedMotion ?? "user";
|
|
136
|
+
if (reduced === "always" || reduced === "user" && prefersReducedMotion()) return;
|
|
137
|
+
player = new SvgPlayer(mount, resolved.motion, {
|
|
138
|
+
loop: params.loop,
|
|
139
|
+
autoplay: params.autoplay,
|
|
140
|
+
speed: params.speed,
|
|
141
|
+
hold: resolved.hold,
|
|
142
|
+
duration: params.duration,
|
|
143
|
+
steps: params.steps,
|
|
144
|
+
trigger: resolved.trigger,
|
|
145
|
+
pauseWhenHidden: params.pauseWhenHidden,
|
|
146
|
+
playOnEnter: params.playOnEnter,
|
|
147
|
+
hideWhenPaused: params.hideWhenPaused,
|
|
148
|
+
clock: params.clock,
|
|
149
|
+
// The latest params' callback, without remaking the player.
|
|
150
|
+
onPick: (id) => params.onPick?.(id)
|
|
151
|
+
});
|
|
152
|
+
showStills(false);
|
|
153
|
+
params.onPlayer?.(player);
|
|
154
|
+
};
|
|
155
|
+
const apply = () => {
|
|
156
|
+
reattach();
|
|
157
|
+
if (ownStill) {
|
|
158
|
+
ownStill.innerHTML = stillMarkup(resolved, params);
|
|
159
|
+
size();
|
|
160
|
+
}
|
|
161
|
+
const next = playerKey(resolved, params);
|
|
162
|
+
if (player && next.every((v, i) => v === key[i])) {
|
|
163
|
+
player.setSpeed(params.speed ?? 1);
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
key = next;
|
|
167
|
+
stop();
|
|
168
|
+
start();
|
|
169
|
+
};
|
|
170
|
+
apply();
|
|
171
|
+
return {
|
|
172
|
+
update(next) {
|
|
173
|
+
params = next;
|
|
174
|
+
resolved = resolve(params, cache);
|
|
175
|
+
apply();
|
|
176
|
+
},
|
|
177
|
+
destroy() {
|
|
178
|
+
observer?.disconnect();
|
|
179
|
+
stop();
|
|
180
|
+
mount.remove();
|
|
181
|
+
ownStill?.remove();
|
|
182
|
+
for (const prop of sized) node.style.removeProperty(prop);
|
|
183
|
+
}
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
export {
|
|
187
|
+
vectorEffect,
|
|
188
|
+
vectorEffectStill
|
|
189
|
+
};
|
|
190
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/svelte/index.ts"],"sourcesContent":["/**\n * Svelte 5 bindings for Flair's vector effects, written as plain TypeScript so\n * they need no Svelte compiler: the `vectorEffect` action plays a catalog\n * effect (or any {@link SvgMotion}) in an element, and `vectorEffectStill`\n * gives the markup and style of its still for server rendering.\n *\n * ```svelte\n * <script>\n * import { vectorEffect, vectorEffectStill } from \"@pixerate/flair/svelte\";\n * const params = { id: \"drip-text\", values: { word: \"Slop\" }, loop: false };\n * const still = vectorEffectStill(params);\n * </script>\n *\n * <span use:vectorEffect={params} style={still.style}>{@html still.markup}</span>\n * ```\n *\n * The still is in place from the first paint; the live `SvgPlayer` takes over\n * after mount and is disposed on destroy. Under `prefers-reduced-motion:\n * reduce` the still stays.\n *\n * Needs Svelte 5 (an optional peer dependency of the package); only its types\n * are imported.\n *\n * @packageDocumentation\n */\n\nimport type { ActionReturn } from \"svelte/action\";\nimport { getEffect, effectDefaults } from \"../catalog/index.js\";\nimport { renderVectorEffect, type VectorEffect as VectorEffectDefinition } from \"../catalog/types.js\";\nimport type { ControlValue } from \"../shader/controls.js\";\nimport type { Clock } from \"../time/clock.js\";\nimport type { SvgMotion } from \"../vector/motion.js\";\nimport { svgFrameMarkup, svgViewBox } from \"../vector/static.js\";\nimport { SvgPlayer, type SvgTrigger } from \"../vector/svg.js\";\n\n/** Cap height of a heavy sans, as a share of its font size: what an inline word is framed to. */\nconst CAP_HEIGHT = \"0.72em\";\n\n/** Marks the still's layer, so the action can find it (and hide it while the player runs). */\nconst STILL_ATTR = \"data-flair-still\";\n\nconst LAYER = \"position:absolute;inset:0;display:block\";\n\n/** When to leave the still in place of the animation. */\nexport type ReducedMotion = \"user\" | \"always\" | \"never\";\n\n/** Options shared by an effect by id and a motion of your own. */\nexport interface SvgPlayerParams {\n /** Start again at the end (default `true`); `false` plays once and stays on the last frame. */\n loop?: boolean;\n /** Start playing at once (default `true`). */\n autoplay?: boolean;\n /** Playback speed (default 1); changed without remaking the player. */\n speed?: number;\n /** Ms to rest on the last frame before looping (default 600, or the effect's). */\n hold?: number;\n /** Loop length in ms (default: the picture's own, plus `hold`). */\n duration?: number;\n /** Step start times in ms, for pictures that change on steps. */\n steps?: readonly number[];\n /** Plays in answer to the pointer (see `SvgTrigger`; default the effect's). */\n trigger?: SvgTrigger;\n /** Pause while scrolled out of view. */\n pauseWhenHidden?: boolean;\n /** Wait to start until scrolled into view. */\n playOnEnter?: boolean;\n /** Hide the picture while paused or reset. */\n hideWhenPaused?: boolean;\n /** Time source (default animation frames); a `ManualClock` steps frames yourself. */\n clock?: Clock;\n /** Called with the id of the innermost element with an id that is clicked. */\n onPick?: (id: string) => void;\n /** Called with the player once it is made, and `undefined` when it is disposed. */\n onPlayer?: (player: SvgPlayer | undefined) => void;\n /**\n * The still shown before the player starts and instead of it under reduced\n * motion: `\"last\"` (default; the rest frame, `\"first\"` for a hover or press\n * trigger), `\"first\"` or a time in ms.\n */\n poster?: \"first\" | \"last\" | number;\n /**\n * `\"user\"` (default) shows the still when the visitor prefers reduced\n * motion, `\"always\"` always does, `\"never\"` always plays.\n */\n reducedMotion?: ReducedMotion;\n /**\n * Size it as a word in a line of text: `display: inline-block` with the\n * viewBox's aspect ratio and `height` 0.72em (the cap height of a heavy\n * sans; pass a CSS length for another). Default: the effect's `inline`\n * control. Otherwise it is a block as wide as its container with the\n * viewBox's aspect ratio.\n */\n inline?: boolean | string;\n}\n\n/** A catalog vector effect by id (or the effect itself), with its values. */\nexport interface VectorEffectByIdParams extends SvgPlayerParams {\n /** A vector effect's catalog id (`\"drip-text\"`), or the effect itself. */\n id: string | VectorEffectDefinition;\n /** Control values; missing ones take the preset's, then the defaults. Compared by content. */\n values?: Record<string, ControlValue>;\n /** A preset's name to start from. */\n preset?: string;\n motion?: never;\n}\n\n/** Any {@link SvgMotion}; a new object restarts the player. */\nexport interface SvgMotionParams extends SvgPlayerParams {\n /** The picture and its animations. */\n motion: SvgMotion;\n id?: never;\n values?: never;\n preset?: never;\n}\n\n/** What `vectorEffect` and `vectorEffectStill` take: an effect by `id`, or a `motion`. */\nexport type VectorEffectParams = VectorEffectByIdParams | SvgMotionParams;\n\n/** A still of the picture, for `{@html markup}` inside an element with `style`. */\nexport interface VectorEffectStill {\n /** The still's layer: safe `<svg>` markup (see `svgFrameMarkup`) in an absolutely placed `<span>`. */\n markup: string;\n /** The host's inline style: its position and size from the viewBox. */\n style: string;\n}\n\ninterface Resolved {\n motion: SvgMotion;\n hold?: number;\n trigger?: SvgTrigger;\n inline?: boolean | string;\n}\n\n/** Remembers the last motion made from an effect, so values compared by content do not remake it. */\ntype MotionCache = { key: string; effect: VectorEffectDefinition; motion: SvgMotion };\n\nfunction resolve(params: VectorEffectParams, cache?: { current?: MotionCache }): Resolved {\n if (params.motion) return { motion: params.motion, hold: params.hold, trigger: params.trigger, inline: params.inline };\n const { id, values, preset } = params as VectorEffectByIdParams;\n const effect = typeof id === \"string\" ? getEffect(id) : id;\n if (!effect || effect.kind !== \"vector\")\n throw new Error(\n `\"${typeof id === \"string\" ? id : (effect?.id ?? id)}\" is not a vector effect; pick one from findEffects({ kind: \"vector\" }), or pass a motion`,\n );\n const key = JSON.stringify([preset ?? null, values ?? {}]);\n let motion: SvgMotion;\n if (cache?.current && cache.current.effect === effect && cache.current.key === key) motion = cache.current.motion;\n else {\n motion = renderVectorEffect(effect, effectDefaults(effect, { preset, values }));\n if (cache) cache.current = { key, effect, motion };\n }\n return {\n motion,\n hold: params.hold ?? effect.hold,\n trigger: params.trigger ?? effect.trigger,\n inline: params.inline ?? values?.inline === true,\n };\n}\n\n/** The host's sizing from the picture's viewBox, so the still and the player take the same room. */\nfunction hostStyle(svg: string, inline: boolean | string | undefined): [string, string][] {\n const box = svgViewBox(svg);\n const style: [string, string][] = [\n [\"position\", \"relative\"],\n [\"display\", inline ? \"inline-block\" : \"block\"],\n ];\n if (inline) style.push([\"height\", typeof inline === \"string\" ? inline : CAP_HEIGHT]);\n if (box) style.push([\"aspect-ratio\", `${box[2]} / ${box[3]}`]);\n return style;\n}\n\nfunction stillMarkup(r: Resolved, params: SvgPlayerParams): string {\n const at = params.poster ?? (r.trigger === \"hover\" || r.trigger === \"press\" ? \"first\" : \"last\");\n return svgFrameMarkup(r.motion, { at, steps: params.steps }) ?? \"\";\n}\n\n/**\n * The still of an effect or motion, for server rendering: put `markup` in the\n * element that has the `vectorEffect` action, with `style` as its style, so\n * the page shows the picture before the player starts (and instead of it\n * under reduced motion), in the room the player will take.\n * Throws when `id` is not a vector effect.\n */\nexport function vectorEffectStill(params: VectorEffectParams): VectorEffectStill {\n const r = resolve(params);\n return {\n markup: `<span ${STILL_ATTR} aria-hidden=\"true\" style=\"${LAYER}\">${stillMarkup(r, params)}</span>`,\n style: hostStyle(r.motion.svg, r.inline)\n .map(([k, v]) => `${k}: ${v}`)\n .join(\"; \"),\n };\n}\n\nfunction prefersReducedMotion(): boolean {\n return typeof matchMedia === \"function\" && matchMedia(\"(prefers-reduced-motion: reduce)\").matches;\n}\n\n/** The options whose change remakes the player (`speed` is set on it instead). */\nfunction playerKey(r: Resolved, p: SvgPlayerParams): unknown[] {\n return [\n r.motion,\n p.loop,\n p.autoplay,\n r.hold,\n p.duration,\n p.steps?.join(\",\"),\n r.trigger,\n p.pauseWhenHidden,\n p.playOnEnter,\n p.hideWhenPaused,\n p.clock,\n p.reducedMotion ?? \"user\",\n ];\n}\n\n/**\n * A Svelte action that plays a vector effect in its element:\n * `<span use:vectorEffect={{ id: \"drip-text\", values: { word: \"Slop\" }, loop: false }} />`.\n *\n * It makes an `SvgPlayer` in a layer of its own, remakes it when the params\n * change (values by content; `speed` without remaking) and disposes it on\n * destroy. A still from {@link vectorEffectStill} in the element is hidden\n * while the player runs and shown again after; without one, the action makes\n * its own (so reduced motion still shows the picture) and sizes the element\n * from the viewBox, leaving alone any of `position`, `display`, `height` and\n * `aspect-ratio` the element's style already sets.\n *\n * For an attachment, `{@attach fromAction(vectorEffect, () => params)}`\n * (`fromAction` is from `svelte/attachments`). Throws when `id` is not a\n * vector effect.\n */\nexport function vectorEffect(node: HTMLElement, initial: VectorEffectParams): ActionReturn<VectorEffectParams> {\n const cache: { current?: MotionCache } = {};\n const doc = node.ownerDocument;\n const mount = doc.createElement(\"span\");\n mount.style.cssText = LAYER;\n node.appendChild(mount);\n\n let params = initial;\n let resolved = resolve(params, cache);\n let player: SvgPlayer | undefined;\n let key: unknown[] = [];\n /** The still the action made itself, when the element had none. */\n let ownStill: HTMLElement | undefined;\n /** Sizing properties the action set (the element's own style had them empty). */\n const sized = new Set<string>();\n\n const stills = () => node.querySelectorAll<HTMLElement>(`:scope > [${STILL_ATTR}]`);\n const showStills = (show: boolean) => {\n for (const s of stills()) s.style.display = show ? \"block\" : \"none\";\n };\n // When the params change Svelte replaces a server still (`{@html}`), and when\n // it is the element's only child, by setting `innerHTML`, which takes the\n // player's layer out too: put the layer back, and keep a new still hidden.\n const reattach = () => {\n if (mount.parentNode !== node) node.appendChild(mount);\n if (player) showStills(false);\n };\n const observer = typeof MutationObserver === \"function\" ? new MutationObserver(reattach) : undefined;\n observer?.observe(node, { childList: true });\n\n const size = () => {\n const style = hostStyle(resolved.motion.svg, resolved.inline);\n for (const [prop, value] of style) {\n if (!sized.has(prop) && node.style.getPropertyValue(prop)) continue;\n node.style.setProperty(prop, value);\n sized.add(prop);\n }\n // A property the new sizing drops (`height` when no longer inline) is cleared.\n for (const prop of sized)\n if (!style.some(([p]) => p === prop)) {\n node.style.removeProperty(prop);\n sized.delete(prop);\n }\n };\n\n if (stills().length === 0) {\n ownStill = doc.createElement(\"span\");\n ownStill.setAttribute(STILL_ATTR, \"\");\n ownStill.setAttribute(\"aria-hidden\", \"true\");\n ownStill.style.cssText = LAYER;\n node.insertBefore(ownStill, mount);\n size();\n }\n\n const stop = () => {\n if (!player) return;\n player.dispose();\n player = undefined;\n showStills(true);\n params.onPlayer?.(undefined);\n };\n\n const start = () => {\n const reduced = params.reducedMotion ?? \"user\";\n if (reduced === \"always\" || (reduced === \"user\" && prefersReducedMotion())) return;\n player = new SvgPlayer(mount, resolved.motion, {\n loop: params.loop,\n autoplay: params.autoplay,\n speed: params.speed,\n hold: resolved.hold,\n duration: params.duration,\n steps: params.steps,\n trigger: resolved.trigger,\n pauseWhenHidden: params.pauseWhenHidden,\n playOnEnter: params.playOnEnter,\n hideWhenPaused: params.hideWhenPaused,\n clock: params.clock,\n // The latest params' callback, without remaking the player.\n onPick: (id) => params.onPick?.(id),\n });\n showStills(false);\n params.onPlayer?.(player);\n };\n\n const apply = () => {\n reattach();\n if (ownStill) {\n ownStill.innerHTML = stillMarkup(resolved, params);\n size();\n }\n const next = playerKey(resolved, params);\n if (player && next.every((v, i) => v === key[i])) {\n player.setSpeed(params.speed ?? 1);\n return;\n }\n key = next;\n stop();\n start();\n };\n\n apply();\n\n return {\n update(next) {\n params = next;\n resolved = resolve(params, cache);\n apply();\n },\n destroy() {\n observer?.disconnect();\n stop();\n mount.remove();\n ownStill?.remove();\n for (const prop of sized) node.style.removeProperty(prop);\n },\n };\n}\n\nexport type { SvgPlayer, SvgMotion, SvgTrigger };\n"],"mappings":"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","names":[]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pixerate/flair",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.16.0",
|
|
4
4
|
"description": "Web visual effects and motion graphics: WebGL2 shader effects, animated vector effects, a catalog of ready-made effects, and a UI-agnostic, agent-ready effect editor.",
|
|
5
5
|
"author": "Pixerate",
|
|
6
6
|
"license": "MIT",
|
|
@@ -42,6 +42,10 @@
|
|
|
42
42
|
"types": "./dist/react/index.d.ts",
|
|
43
43
|
"import": "./dist/react/index.js"
|
|
44
44
|
},
|
|
45
|
+
"./svelte": {
|
|
46
|
+
"types": "./dist/svelte/index.d.ts",
|
|
47
|
+
"import": "./dist/svelte/index.js"
|
|
48
|
+
},
|
|
45
49
|
"./node": {
|
|
46
50
|
"types": "./dist/node.d.ts",
|
|
47
51
|
"import": "./dist/node.js"
|
|
@@ -62,7 +66,8 @@
|
|
|
62
66
|
"generative",
|
|
63
67
|
"editor",
|
|
64
68
|
"ai-agents",
|
|
65
|
-
"react"
|
|
69
|
+
"react",
|
|
70
|
+
"svelte"
|
|
66
71
|
],
|
|
67
72
|
"dependencies": {
|
|
68
73
|
"commander": "^12.0.0",
|
|
@@ -70,7 +75,8 @@
|
|
|
70
75
|
},
|
|
71
76
|
"peerDependencies": {
|
|
72
77
|
"@pixerate/editor": ">=0.29.0 <1",
|
|
73
|
-
"react": ">=18"
|
|
78
|
+
"react": ">=18",
|
|
79
|
+
"svelte": ">=5"
|
|
74
80
|
},
|
|
75
81
|
"peerDependenciesMeta": {
|
|
76
82
|
"@pixerate/editor": {
|
|
@@ -78,6 +84,9 @@
|
|
|
78
84
|
},
|
|
79
85
|
"react": {
|
|
80
86
|
"optional": true
|
|
87
|
+
},
|
|
88
|
+
"svelte": {
|
|
89
|
+
"optional": true
|
|
81
90
|
}
|
|
82
91
|
},
|
|
83
92
|
"devDependencies": {
|
|
@@ -89,6 +98,7 @@
|
|
|
89
98
|
"jsdom": "^30.1.3",
|
|
90
99
|
"react": "^19.3.1",
|
|
91
100
|
"react-dom": "^19.3.1",
|
|
101
|
+
"svelte": "^5.57.2",
|
|
92
102
|
"tsup": "^8.5",
|
|
93
103
|
"typescript": "^5.9",
|
|
94
104
|
"vitest": "^4.1.11"
|
|
File without changes
|