onda-engine 0.1.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/LICENSE +106 -0
- package/LICENSE-APACHE +202 -0
- package/README.md +84 -0
- package/dist/chunk-NCNYMPIQ.js +12763 -0
- package/dist/chunk-NCNYMPIQ.js.map +1 -0
- package/dist/cinema.d.ts +580 -0
- package/dist/cinema.js +1687 -0
- package/dist/cinema.js.map +1 -0
- package/dist/components-manifest.d.ts +2 -0
- package/dist/components-manifest.js +3 -0
- package/dist/components-manifest.js.map +1 -0
- package/dist/components.d.ts +3480 -0
- package/dist/components.js +11486 -0
- package/dist/components.js.map +1 -0
- package/dist/index.d.ts +101 -0
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -0
- package/dist/manifest-7N3yu9tB.d.ts +131 -0
- package/dist/player.d.ts +177 -0
- package/dist/player.js +1749 -0
- package/dist/player.js.map +1 -0
- package/dist/react.d.ts +2141 -0
- package/dist/react.js +2052 -0
- package/dist/react.js.map +1 -0
- package/dist/render.d.ts +42 -0
- package/dist/render.js +113 -0
- package/dist/render.js.map +1 -0
- package/dist/wasm/pkg/onda_wasm.js +598 -0
- package/dist/wasm/pkg/onda_wasm_bg.wasm +0 -0
- package/dist/wasm-audio/pkg/onda_wasm_audio.js +417 -0
- package/dist/wasm-audio/pkg/onda_wasm_audio_bg.wasm +0 -0
- package/dist/wasm-vello/index.js +32 -0
- package/dist/wasm-vello/pkg/onda_wasm_vello.js +1325 -0
- package/dist/wasm-vello/pkg/onda_wasm_vello_bg.wasm +0 -0
- package/package.json +112 -0
package/dist/react.js
ADDED
|
@@ -0,0 +1,2052 @@
|
|
|
1
|
+
import * as flubberNs from 'flubber';
|
|
2
|
+
import { createContext, Children, isValidElement, createElement, Fragment, useContext } from 'react';
|
|
3
|
+
import Reconciler from 'react-reconciler';
|
|
4
|
+
import { DefaultEventPriority } from 'react-reconciler/constants.js';
|
|
5
|
+
|
|
6
|
+
// ../react/src/interpolate.ts
|
|
7
|
+
var Easing = {
|
|
8
|
+
linear: (t) => t,
|
|
9
|
+
// Remotion-named curves (drop-in for `Easing.quad`/`.cubic`/`.sin`/`.ease`).
|
|
10
|
+
quad: (t) => t * t,
|
|
11
|
+
cubic: (t) => t * t * t,
|
|
12
|
+
sin: (t) => 1 - Math.cos(t * Math.PI / 2),
|
|
13
|
+
ease: cubicBezier(0.42, 0, 1, 1),
|
|
14
|
+
easeInQuad: (t) => t * t,
|
|
15
|
+
easeOutQuad: (t) => 1 - (1 - t) * (1 - t),
|
|
16
|
+
easeInOutQuad: (t) => t < 0.5 ? 2 * t * t : 1 - (-2 * t + 2) ** 2 / 2,
|
|
17
|
+
easeInCubic: (t) => t ** 3,
|
|
18
|
+
easeOutCubic: (t) => 1 - (1 - t) ** 3,
|
|
19
|
+
easeInOutCubic: (t) => t < 0.5 ? 4 * t ** 3 : 1 - (-2 * t + 2) ** 3 / 2,
|
|
20
|
+
smoothStep: (t) => t * t * (3 - 2 * t),
|
|
21
|
+
easeInBack: (t) => 2.70158 * t ** 3 - 1.70158 * t ** 2,
|
|
22
|
+
easeOutBack: (t) => 1 + 2.70158 * (t - 1) ** 3 + 1.70158 * (t - 1) ** 2,
|
|
23
|
+
/** CSS cubic-bézier factory — Remotion's `Easing.bezier(x1,y1,x2,y2)`. */
|
|
24
|
+
bezier: (x1, y1, x2, y2) => cubicBezier(x1, y1, x2, y2)
|
|
25
|
+
};
|
|
26
|
+
function cubicBezier(x1, y1, x2, y2) {
|
|
27
|
+
const comp = (c1, c2, s) => {
|
|
28
|
+
const u = 1 - s;
|
|
29
|
+
return 3 * u * u * s * c1 + 3 * u * s * s * c2 + s * s * s;
|
|
30
|
+
};
|
|
31
|
+
const deriv = (c1, c2, s) => {
|
|
32
|
+
const u = 1 - s;
|
|
33
|
+
return 3 * u * u * c1 + 6 * u * s * (c2 - c1) + 3 * s * s * (1 - c2);
|
|
34
|
+
};
|
|
35
|
+
return (x) => {
|
|
36
|
+
let s = x;
|
|
37
|
+
for (let i = 0; i < 8; i++) {
|
|
38
|
+
const dx = comp(x1, x2, s) - x;
|
|
39
|
+
if (Math.abs(dx) < 1e-5) break;
|
|
40
|
+
const d = deriv(x1, x2, s);
|
|
41
|
+
if (Math.abs(d) < 1e-6) break;
|
|
42
|
+
s -= dx / d;
|
|
43
|
+
}
|
|
44
|
+
return comp(y1, y2, Math.min(1, Math.max(0, s)));
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
function interpolate(input, inputRange, outputRange, options = {}) {
|
|
48
|
+
if (inputRange.length < 2 || inputRange.length !== outputRange.length) {
|
|
49
|
+
throw new Error("interpolate: inputRange and outputRange must be the same length (>= 2)");
|
|
50
|
+
}
|
|
51
|
+
const { easing = Easing.linear, extrapolateLeft = "clamp", extrapolateRight = "clamp" } = options;
|
|
52
|
+
const last = inputRange.length - 1;
|
|
53
|
+
if (input <= at(inputRange, 0)) {
|
|
54
|
+
if (extrapolateLeft === "clamp") return at(outputRange, 0);
|
|
55
|
+
return segment(input, inputRange, outputRange, 0, easing);
|
|
56
|
+
}
|
|
57
|
+
if (input >= at(inputRange, last)) {
|
|
58
|
+
if (extrapolateRight === "clamp") return at(outputRange, last);
|
|
59
|
+
return segment(input, inputRange, outputRange, last - 1, easing);
|
|
60
|
+
}
|
|
61
|
+
let i = 0;
|
|
62
|
+
while (i < last - 1 && input >= at(inputRange, i + 1)) i++;
|
|
63
|
+
return segment(input, inputRange, outputRange, i, easing);
|
|
64
|
+
}
|
|
65
|
+
function at(arr, i) {
|
|
66
|
+
const value = arr[i];
|
|
67
|
+
if (value === void 0) throw new Error(`interpolate: index ${i} out of range`);
|
|
68
|
+
return value;
|
|
69
|
+
}
|
|
70
|
+
function segment(input, inputRange, outputRange, i, easing) {
|
|
71
|
+
const x0 = at(inputRange, i);
|
|
72
|
+
const x1 = at(inputRange, i + 1);
|
|
73
|
+
const y0 = at(outputRange, i);
|
|
74
|
+
const y1 = at(outputRange, i + 1);
|
|
75
|
+
const span = x1 - x0;
|
|
76
|
+
const t = span === 0 ? 0 : (input - x0) / span;
|
|
77
|
+
return y0 + (y1 - y0) * easing(t);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// ../react/src/color.ts
|
|
81
|
+
function parseColor(input) {
|
|
82
|
+
if (input === "none" || input === "transparent") return { r: 0, g: 0, b: 0, a: 0 };
|
|
83
|
+
if (typeof input !== "string") {
|
|
84
|
+
if (input == null || typeof input !== "object" || typeof input.r !== "number" || typeof input.g !== "number" || typeof input.b !== "number") {
|
|
85
|
+
throw new Error(
|
|
86
|
+
`invalid color ${JSON.stringify(input)}: expected a hex string or a {r,g,b} object with numeric 0..1 channels`
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
return { r: input.r, g: input.g, b: input.b, ...input.a !== void 0 ? { a: input.a } : {} };
|
|
90
|
+
}
|
|
91
|
+
let hex = input.trim().replace(/^#/, "");
|
|
92
|
+
if (hex.length === 3 || hex.length === 4) {
|
|
93
|
+
hex = hex.split("").map((c) => c + c).join("");
|
|
94
|
+
}
|
|
95
|
+
if (hex.length !== 6 && hex.length !== 8 || !/^[0-9a-fA-F]+$/.test(hex)) {
|
|
96
|
+
throw new Error(`invalid color '${input}': expected #rgb, #rgba, #rrggbb, or #rrggbbaa`);
|
|
97
|
+
}
|
|
98
|
+
const channel = (i) => Number.parseInt(hex.slice(i, i + 2), 16) / 255;
|
|
99
|
+
const color = { r: channel(0), g: channel(2), b: channel(4) };
|
|
100
|
+
if (hex.length === 8) color.a = channel(6);
|
|
101
|
+
return color;
|
|
102
|
+
}
|
|
103
|
+
var clamp01 = (v) => v < 0 ? 0 : v > 1 ? 1 : v;
|
|
104
|
+
var toHex = (v) => Math.round(clamp01(v) * 255).toString(16).padStart(2, "0");
|
|
105
|
+
function interpolateColors(input, inputRange, outputRange, options = {}) {
|
|
106
|
+
const colors = outputRange.map(parseColor);
|
|
107
|
+
const mix = (sel) => interpolate(input, inputRange, colors.map(sel), options);
|
|
108
|
+
const hasAlpha = colors.some((c) => c.a !== void 0 && c.a < 1);
|
|
109
|
+
const rgb = `${toHex(mix((c) => c.r))}${toHex(mix((c) => c.g))}${toHex(mix((c) => c.b))}`;
|
|
110
|
+
return `#${rgb}${hasAlpha ? toHex(mix((c) => c.a ?? 1)) : ""}`;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// ../react/src/clip.ts
|
|
114
|
+
function parseClip(input) {
|
|
115
|
+
switch (input.type) {
|
|
116
|
+
case "rect":
|
|
117
|
+
return {
|
|
118
|
+
shape: "rect",
|
|
119
|
+
size: { width: input.width, height: input.height },
|
|
120
|
+
...input.cornerRadius != null ? { corner_radius: input.cornerRadius } : {}
|
|
121
|
+
};
|
|
122
|
+
case "ellipse":
|
|
123
|
+
return { shape: "ellipse", size: { width: input.width, height: input.height } };
|
|
124
|
+
case "path":
|
|
125
|
+
return { shape: "path", data: input.d };
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
function clipRect(width, height, cornerRadius) {
|
|
129
|
+
return { type: "rect", width, height, cornerRadius };
|
|
130
|
+
}
|
|
131
|
+
function clipEllipse(width, height) {
|
|
132
|
+
return { type: "ellipse", width, height };
|
|
133
|
+
}
|
|
134
|
+
function clipPath(d) {
|
|
135
|
+
return { type: "path", d };
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// ../react/src/gradient.ts
|
|
139
|
+
function toVec2(point) {
|
|
140
|
+
return Array.isArray(point) ? { x: point[0], y: point[1] } : point;
|
|
141
|
+
}
|
|
142
|
+
function stops(input) {
|
|
143
|
+
return input.map((s) => ({ offset: s.offset, color: parseColor(s.color) }));
|
|
144
|
+
}
|
|
145
|
+
function parseGradient(input) {
|
|
146
|
+
if (input.type === "linear") {
|
|
147
|
+
return {
|
|
148
|
+
gradient: "linear",
|
|
149
|
+
start: toVec2(input.start),
|
|
150
|
+
end: toVec2(input.end),
|
|
151
|
+
stops: stops(input.stops)
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
if (input.type === "fbm") {
|
|
155
|
+
return {
|
|
156
|
+
gradient: "fbm",
|
|
157
|
+
stops: stops(input.stops),
|
|
158
|
+
scale: input.scale ?? 1,
|
|
159
|
+
time: input.time ?? 0,
|
|
160
|
+
warp: input.warp ?? 0.5
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
return {
|
|
164
|
+
gradient: "radial",
|
|
165
|
+
center: toVec2(input.center),
|
|
166
|
+
radius: input.radius,
|
|
167
|
+
stops: stops(input.stops)
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
function linearGradient(start, end, stops2) {
|
|
171
|
+
return { type: "linear", start, end, stops: stops2 };
|
|
172
|
+
}
|
|
173
|
+
function radialGradient(center, radius, stops2) {
|
|
174
|
+
return { type: "radial", center, radius, stops: stops2 };
|
|
175
|
+
}
|
|
176
|
+
function fbmGradient(stops2, opts = {}) {
|
|
177
|
+
return { type: "fbm", stops: stops2, ...opts };
|
|
178
|
+
}
|
|
179
|
+
var flubberResolved = flubberNs;
|
|
180
|
+
var flubberInterpolate = flubberResolved.interpolate ?? flubberResolved.default?.interpolate;
|
|
181
|
+
var clamp012 = (t) => t < 0 ? 0 : t > 1 ? 1 : t;
|
|
182
|
+
var cache = /* @__PURE__ */ new Map();
|
|
183
|
+
function interpolator(from, to, opts) {
|
|
184
|
+
const maxSegmentLength = opts.maxSegmentLength ?? 2;
|
|
185
|
+
const key = `${from}\0${to}\0${maxSegmentLength}`;
|
|
186
|
+
let interp = cache.get(key);
|
|
187
|
+
if (!interp) {
|
|
188
|
+
interp = flubberInterpolate(from, to, { maxSegmentLength });
|
|
189
|
+
cache.set(key, interp);
|
|
190
|
+
}
|
|
191
|
+
return interp;
|
|
192
|
+
}
|
|
193
|
+
function morphPath(from, to, t, options = {}) {
|
|
194
|
+
return interpolator(from, to, options)(clamp012(t));
|
|
195
|
+
}
|
|
196
|
+
function morphPathSequence(shapes, t, options = {}) {
|
|
197
|
+
const first = shapes[0];
|
|
198
|
+
if (first === void 0) return "";
|
|
199
|
+
if (shapes.length === 1) return first;
|
|
200
|
+
const tt = clamp012(t);
|
|
201
|
+
const segments = shapes.length - 1;
|
|
202
|
+
const scaled = tt * segments;
|
|
203
|
+
const i = Math.min(segments - 1, Math.floor(scaled));
|
|
204
|
+
return interpolator(shapes[i], shapes[i + 1], options)(scaled - i);
|
|
205
|
+
}
|
|
206
|
+
var FrameContext = createContext(null);
|
|
207
|
+
function useFrameState(hook) {
|
|
208
|
+
const state = useContext(FrameContext);
|
|
209
|
+
if (state === null) {
|
|
210
|
+
throw new Error(
|
|
211
|
+
`${hook} must be called inside a <Composition> rendered by renderFrame/renderFrames`
|
|
212
|
+
);
|
|
213
|
+
}
|
|
214
|
+
return state;
|
|
215
|
+
}
|
|
216
|
+
function useCurrentFrame() {
|
|
217
|
+
return useFrameState("useCurrentFrame").frame;
|
|
218
|
+
}
|
|
219
|
+
function useVideoConfig() {
|
|
220
|
+
const { width, height, fps, durationInFrames } = useFrameState("useVideoConfig");
|
|
221
|
+
return { width, height, fps, durationInFrames };
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
// ../react/src/components.ts
|
|
225
|
+
function Composition(props) {
|
|
226
|
+
return createElement("onda-composition", props);
|
|
227
|
+
}
|
|
228
|
+
function Group(props) {
|
|
229
|
+
return createElement("onda-group", props);
|
|
230
|
+
}
|
|
231
|
+
function Repeater({
|
|
232
|
+
count,
|
|
233
|
+
offsetX = 0,
|
|
234
|
+
offsetY = 0,
|
|
235
|
+
rotation = 0,
|
|
236
|
+
scale = 1,
|
|
237
|
+
originX = 0,
|
|
238
|
+
originY = 0,
|
|
239
|
+
startOpacity = 1,
|
|
240
|
+
endOpacity = 1,
|
|
241
|
+
children
|
|
242
|
+
}) {
|
|
243
|
+
const n = Math.max(1, Math.floor(count));
|
|
244
|
+
const opacityAt = (i) => startOpacity + (endOpacity - startOpacity) * (n > 1 ? i / (n - 1) : 0);
|
|
245
|
+
const buildLevel = (level) => {
|
|
246
|
+
if (level >= n) return null;
|
|
247
|
+
const copy = createElement(Group, { key: "copy", opacity: opacityAt(level) }, children);
|
|
248
|
+
const deeper = buildLevel(level + 1);
|
|
249
|
+
const transform = level === 0 ? {} : { x: offsetX, y: offsetY, rotation, scaleX: scale, scaleY: scale, originX, originY };
|
|
250
|
+
return createElement(Group, { key: level, ...transform }, copy, deeper);
|
|
251
|
+
};
|
|
252
|
+
return buildLevel(0) ?? createElement(Group, null);
|
|
253
|
+
}
|
|
254
|
+
function Merge(props) {
|
|
255
|
+
return createElement("onda-boolean", props);
|
|
256
|
+
}
|
|
257
|
+
function Precomp(props) {
|
|
258
|
+
const effects = [{ effect: "isolate" }, ...props.effects ?? []];
|
|
259
|
+
return createElement(Group, { ...props, effects });
|
|
260
|
+
}
|
|
261
|
+
function Flex(props) {
|
|
262
|
+
const { direction, justify, align, gap, padding, wrap, width, height, ...rest } = props;
|
|
263
|
+
const layout = { direction, justify, align, gap, padding, wrap, width, height };
|
|
264
|
+
return createElement("onda-group", { ...rest, layout });
|
|
265
|
+
}
|
|
266
|
+
function Center(props) {
|
|
267
|
+
const { children, y, height, ...rest } = props;
|
|
268
|
+
const { width } = useVideoConfig();
|
|
269
|
+
return createElement(
|
|
270
|
+
Flex,
|
|
271
|
+
{
|
|
272
|
+
...rest,
|
|
273
|
+
x: 0,
|
|
274
|
+
y: y ?? 0,
|
|
275
|
+
width,
|
|
276
|
+
...height !== void 0 ? { height } : {},
|
|
277
|
+
justify: "center",
|
|
278
|
+
align: "center"
|
|
279
|
+
},
|
|
280
|
+
children
|
|
281
|
+
);
|
|
282
|
+
}
|
|
283
|
+
function AbsoluteFill(props) {
|
|
284
|
+
const { width, height } = useVideoConfig();
|
|
285
|
+
return Flex({ direction: "column", ...props, width, height });
|
|
286
|
+
}
|
|
287
|
+
function Camera(props) {
|
|
288
|
+
const { children, focusX, focusY, zoom: zoom2 = 1, rotate = 0, viewportWidth, viewportHeight } = props;
|
|
289
|
+
const { width, height } = useVideoConfig();
|
|
290
|
+
const vw = viewportWidth ?? width;
|
|
291
|
+
const vh = viewportHeight ?? height;
|
|
292
|
+
const fx = focusX ?? vw / 2;
|
|
293
|
+
const fy = focusY ?? vh / 2;
|
|
294
|
+
return createElement(
|
|
295
|
+
Group,
|
|
296
|
+
{ x: vw / 2, y: vh / 2 },
|
|
297
|
+
createElement(
|
|
298
|
+
Group,
|
|
299
|
+
{ rotation: rotate, scaleX: zoom2, scaleY: zoom2, originX: 0, originY: 0 },
|
|
300
|
+
createElement(Group, { x: -fx, y: -fy }, children)
|
|
301
|
+
)
|
|
302
|
+
);
|
|
303
|
+
}
|
|
304
|
+
function Scene3D(props) {
|
|
305
|
+
const { camera, children, ...rest } = props;
|
|
306
|
+
return createElement("onda-group", { ...rest, camera3d: camera ?? {} }, children);
|
|
307
|
+
}
|
|
308
|
+
function Rect(props) {
|
|
309
|
+
return createElement("onda-rect", props);
|
|
310
|
+
}
|
|
311
|
+
function Ellipse(props) {
|
|
312
|
+
return createElement("onda-ellipse", props);
|
|
313
|
+
}
|
|
314
|
+
function Path(props) {
|
|
315
|
+
return createElement("onda-path", props);
|
|
316
|
+
}
|
|
317
|
+
function Text(props) {
|
|
318
|
+
return createElement("onda-text", props);
|
|
319
|
+
}
|
|
320
|
+
function Image(props) {
|
|
321
|
+
return createElement("onda-image", props);
|
|
322
|
+
}
|
|
323
|
+
function Video({ startFrom = 0, playbackRate = 1, endAt, loop, ...rest }) {
|
|
324
|
+
const frame = useCurrentFrame();
|
|
325
|
+
const { fps } = useVideoConfig();
|
|
326
|
+
let time = startFrom + frame / Math.max(1, fps) * playbackRate;
|
|
327
|
+
if (endAt != null && endAt > startFrom) {
|
|
328
|
+
const span = endAt - startFrom;
|
|
329
|
+
time = loop ? startFrom + ((time - startFrom) % span + span) % span : Math.min(time, endAt);
|
|
330
|
+
}
|
|
331
|
+
return createElement("onda-video", { ...rest, time: Math.max(0, time) });
|
|
332
|
+
}
|
|
333
|
+
function Audio(props) {
|
|
334
|
+
return createElement("onda-audio", props);
|
|
335
|
+
}
|
|
336
|
+
function Svg(props) {
|
|
337
|
+
return createElement("onda-svg", props);
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
// ../react/src/spring.ts
|
|
341
|
+
function stableStep(mass, stiffness, damping) {
|
|
342
|
+
const omega = Math.max(damping / mass, Math.sqrt(stiffness / mass), 1e-6);
|
|
343
|
+
return Math.min(1 / 60, 1.6 / omega);
|
|
344
|
+
}
|
|
345
|
+
function positionAt(timeSec, mass, stiffness, damping, dt) {
|
|
346
|
+
if (timeSec <= 0) return 0;
|
|
347
|
+
const steps = Math.max(1, Math.round(timeSec / dt));
|
|
348
|
+
let position = 0;
|
|
349
|
+
let velocity = 0;
|
|
350
|
+
for (let i = 0; i < steps; i++) {
|
|
351
|
+
const force = -stiffness * (position - 1) - damping * velocity;
|
|
352
|
+
velocity += force / mass * dt;
|
|
353
|
+
position += velocity * dt;
|
|
354
|
+
}
|
|
355
|
+
return position;
|
|
356
|
+
}
|
|
357
|
+
var settleCache = /* @__PURE__ */ new Map();
|
|
358
|
+
function measureSettleSeconds(mass, stiffness, damping, dt, threshold) {
|
|
359
|
+
const key = `${mass}|${stiffness}|${damping}|${dt}|${threshold}`;
|
|
360
|
+
const cached = settleCache.get(key);
|
|
361
|
+
if (cached !== void 0) return cached;
|
|
362
|
+
let position = 0;
|
|
363
|
+
let velocity = 0;
|
|
364
|
+
const maxSteps = 5e6;
|
|
365
|
+
let result = maxSteps * dt;
|
|
366
|
+
for (let i = 0; i < maxSteps; i++) {
|
|
367
|
+
const force = -stiffness * (position - 1) - damping * velocity;
|
|
368
|
+
velocity += force / mass * dt;
|
|
369
|
+
position += velocity * dt;
|
|
370
|
+
if (Math.abs(1 - position) < threshold && Math.abs(velocity) < threshold) {
|
|
371
|
+
result = (i + 1) * dt;
|
|
372
|
+
break;
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
settleCache.set(key, result);
|
|
376
|
+
return result;
|
|
377
|
+
}
|
|
378
|
+
function spring({
|
|
379
|
+
frame,
|
|
380
|
+
fps,
|
|
381
|
+
from = 0,
|
|
382
|
+
to = 1,
|
|
383
|
+
config = {},
|
|
384
|
+
durationInFrames,
|
|
385
|
+
durationRestThreshold = 5e-3
|
|
386
|
+
}) {
|
|
387
|
+
const { mass = 1, stiffness = 100, damping = 10 } = config;
|
|
388
|
+
let position = 0;
|
|
389
|
+
if (fps > 0 && frame > 0) {
|
|
390
|
+
const dt = stableStep(mass, stiffness, damping);
|
|
391
|
+
let timeSec = frame / fps;
|
|
392
|
+
if (durationInFrames != null && durationInFrames > 0) {
|
|
393
|
+
const naturalSec = measureSettleSeconds(mass, stiffness, damping, dt, durationRestThreshold);
|
|
394
|
+
const durationSec = durationInFrames / fps;
|
|
395
|
+
timeSec = Math.min(frame / fps * (naturalSec / durationSec), naturalSec);
|
|
396
|
+
}
|
|
397
|
+
position = positionAt(timeSec, mass, stiffness, damping, dt);
|
|
398
|
+
}
|
|
399
|
+
return from + (to - from) * position;
|
|
400
|
+
}
|
|
401
|
+
function Sequence({
|
|
402
|
+
from = 0,
|
|
403
|
+
durationInFrames,
|
|
404
|
+
children
|
|
405
|
+
}) {
|
|
406
|
+
const ctx = useContext(FrameContext);
|
|
407
|
+
if (!ctx) return null;
|
|
408
|
+
const local = ctx.frame - from;
|
|
409
|
+
const visible = local >= 0 && (durationInFrames === void 0 || local < durationInFrames);
|
|
410
|
+
if (!visible) return null;
|
|
411
|
+
const value = durationInFrames === void 0 ? { ...ctx, frame: local } : { ...ctx, frame: local, durationInFrames };
|
|
412
|
+
return createElement(FrameContext.Provider, { value }, children);
|
|
413
|
+
}
|
|
414
|
+
function Loop({ durationInFrames, children }) {
|
|
415
|
+
const ctx = useContext(FrameContext);
|
|
416
|
+
if (!ctx || durationInFrames <= 0) return null;
|
|
417
|
+
const local = (ctx.frame % durationInFrames + durationInFrames) % durationInFrames;
|
|
418
|
+
return createElement(FrameContext.Provider, { value: { ...ctx, frame: local } }, children);
|
|
419
|
+
}
|
|
420
|
+
function SeriesSequence(_props) {
|
|
421
|
+
return null;
|
|
422
|
+
}
|
|
423
|
+
function SeriesRoot({ children }) {
|
|
424
|
+
let offset = 0;
|
|
425
|
+
const items = Children.map(children, (child) => {
|
|
426
|
+
if (!isValidElement(child) || child.type !== SeriesSequence) {
|
|
427
|
+
throw new Error("<Series> children must be <Series.Sequence>");
|
|
428
|
+
}
|
|
429
|
+
const props = child.props;
|
|
430
|
+
const element = createElement(
|
|
431
|
+
Sequence,
|
|
432
|
+
{ from: offset, durationInFrames: props.durationInFrames },
|
|
433
|
+
props.children
|
|
434
|
+
);
|
|
435
|
+
offset += props.durationInFrames;
|
|
436
|
+
return element;
|
|
437
|
+
});
|
|
438
|
+
return createElement(Fragment, null, items);
|
|
439
|
+
}
|
|
440
|
+
var Series = Object.assign(SeriesRoot, { Sequence: SeriesSequence });
|
|
441
|
+
|
|
442
|
+
// ../react/src/random.ts
|
|
443
|
+
function hashSeed(seed) {
|
|
444
|
+
if (typeof seed === "number") {
|
|
445
|
+
let h2 = Math.imul(seed ^ seed >>> 16, 2246822507) >>> 0;
|
|
446
|
+
h2 = Math.imul(h2 ^ h2 >>> 13, 3266489909) >>> 0;
|
|
447
|
+
return (h2 ^ h2 >>> 16) >>> 0;
|
|
448
|
+
}
|
|
449
|
+
let h = 2166136261 >>> 0;
|
|
450
|
+
for (let i = 0; i < seed.length; i++) {
|
|
451
|
+
h ^= seed.charCodeAt(i);
|
|
452
|
+
h = Math.imul(h, 16777619) >>> 0;
|
|
453
|
+
}
|
|
454
|
+
return h >>> 0;
|
|
455
|
+
}
|
|
456
|
+
function variantSeed(seed, variant) {
|
|
457
|
+
if (!variant) return seed;
|
|
458
|
+
let h = (hashSeed(seed) ^ Math.imul(variant | 0, 2654435769)) >>> 0;
|
|
459
|
+
h = Math.imul(h ^ h >>> 13, 3266489909) >>> 0;
|
|
460
|
+
return (h ^ h >>> 16) >>> 0;
|
|
461
|
+
}
|
|
462
|
+
function random(seed) {
|
|
463
|
+
let a = hashSeed(seed);
|
|
464
|
+
a = a + 1831565813 | 0;
|
|
465
|
+
let t = Math.imul(a ^ a >>> 15, 1 | a);
|
|
466
|
+
t = t + Math.imul(t ^ t >>> 7, 61 | t) ^ t;
|
|
467
|
+
return ((t ^ t >>> 14) >>> 0) / 4294967296;
|
|
468
|
+
}
|
|
469
|
+
function hashUnit(s, ...coords) {
|
|
470
|
+
let h = (s ^ 2654435769) >>> 0;
|
|
471
|
+
for (const c of coords) {
|
|
472
|
+
h ^= c | 0;
|
|
473
|
+
h = Math.imul(h, 16777619) >>> 0;
|
|
474
|
+
h ^= h >>> 13;
|
|
475
|
+
}
|
|
476
|
+
h = Math.imul(h ^ h >>> 15, 2246822507) >>> 0;
|
|
477
|
+
return (h >>> 0) / 4294967296;
|
|
478
|
+
}
|
|
479
|
+
function fade(t) {
|
|
480
|
+
return t * t * t * (t * (t * 6 - 15) + 10);
|
|
481
|
+
}
|
|
482
|
+
function lerp(a, b, t) {
|
|
483
|
+
return a + (b - a) * t;
|
|
484
|
+
}
|
|
485
|
+
function noise2D(seed, x, y) {
|
|
486
|
+
const s = hashSeed(seed);
|
|
487
|
+
const ix = Math.floor(x);
|
|
488
|
+
const iy = Math.floor(y);
|
|
489
|
+
const fx = x - ix;
|
|
490
|
+
const fy = y - iy;
|
|
491
|
+
const u = fade(fx);
|
|
492
|
+
const v = fade(fy);
|
|
493
|
+
const n = lerp(
|
|
494
|
+
lerp(hashUnit(s, ix, iy), hashUnit(s, ix + 1, iy), u),
|
|
495
|
+
lerp(hashUnit(s, ix, iy + 1), hashUnit(s, ix + 1, iy + 1), u),
|
|
496
|
+
v
|
|
497
|
+
);
|
|
498
|
+
return n * 2 - 1;
|
|
499
|
+
}
|
|
500
|
+
function noise3D(seed, x, y, z) {
|
|
501
|
+
const s = hashSeed(seed);
|
|
502
|
+
const ix = Math.floor(x);
|
|
503
|
+
const iy = Math.floor(y);
|
|
504
|
+
const iz = Math.floor(z);
|
|
505
|
+
const u = fade(x - ix);
|
|
506
|
+
const v = fade(y - iy);
|
|
507
|
+
const w = fade(z - iz);
|
|
508
|
+
const c = (dx, dy, dz) => hashUnit(s, ix + dx, iy + dy, iz + dz);
|
|
509
|
+
const front = lerp(lerp(c(0, 0, 0), c(1, 0, 0), u), lerp(c(0, 1, 0), c(1, 1, 0), u), v);
|
|
510
|
+
const back = lerp(lerp(c(0, 0, 1), c(1, 0, 1), u), lerp(c(0, 1, 1), c(1, 1, 1), u), v);
|
|
511
|
+
return lerp(front, back, w) * 2 - 1;
|
|
512
|
+
}
|
|
513
|
+
var lerp2 = (a, b, t) => a + (b - a) * t;
|
|
514
|
+
function Particles({
|
|
515
|
+
count = 60,
|
|
516
|
+
seed = 1,
|
|
517
|
+
x = 0,
|
|
518
|
+
y = 0,
|
|
519
|
+
spawnRadius = 0,
|
|
520
|
+
speed = 4,
|
|
521
|
+
speedVariance = 0.5,
|
|
522
|
+
angle = -90,
|
|
523
|
+
spread = 360,
|
|
524
|
+
gravity = 0,
|
|
525
|
+
lifetime = 60,
|
|
526
|
+
emitOver = 0,
|
|
527
|
+
loop = false,
|
|
528
|
+
delay = 0,
|
|
529
|
+
shape = "circle",
|
|
530
|
+
size = 8,
|
|
531
|
+
opacity = [1, 0],
|
|
532
|
+
colors = ["#e85494", "#ffd36b", "#5ad1ff"],
|
|
533
|
+
spin = 0
|
|
534
|
+
}) {
|
|
535
|
+
const frame = useCurrentFrame();
|
|
536
|
+
const key = String(seed);
|
|
537
|
+
const [s0, s1] = typeof size === "number" ? [size, size] : size;
|
|
538
|
+
const particles = [];
|
|
539
|
+
for (let i = 0; i < count; i++) {
|
|
540
|
+
const r = (suffix) => random(`${key}-${i}-${suffix}`);
|
|
541
|
+
const emitFrame = delay + (emitOver > 0 ? i / count * emitOver : 0);
|
|
542
|
+
let age = frame - emitFrame;
|
|
543
|
+
if (loop && age >= 0) age %= lifetime;
|
|
544
|
+
if (age < 0 || age >= lifetime) continue;
|
|
545
|
+
const sa = r("sa") * Math.PI * 2;
|
|
546
|
+
const sr = Math.sqrt(r("sr")) * spawnRadius;
|
|
547
|
+
const ox = x + Math.cos(sa) * sr;
|
|
548
|
+
const oy = y + Math.sin(sa) * sr;
|
|
549
|
+
const dir = (angle + (r("ang") - 0.5) * spread) * Math.PI / 180;
|
|
550
|
+
const spd = speed * (1 - speedVariance * r("spd"));
|
|
551
|
+
const vx = Math.cos(dir) * spd;
|
|
552
|
+
const vy = Math.sin(dir) * spd;
|
|
553
|
+
const px = ox + vx * age;
|
|
554
|
+
const py = oy + vy * age + 0.5 * gravity * age * age;
|
|
555
|
+
const t = age / lifetime;
|
|
556
|
+
const sz = lerp2(s0, s1, t);
|
|
557
|
+
const op = lerp2(opacity[0], opacity[1], t);
|
|
558
|
+
const color = colors[Math.floor(r("col") * colors.length) % colors.length];
|
|
559
|
+
const inner = shape === "square" ? createElement(Rect, { x: -sz / 2, y: -sz / 2, width: sz, height: sz, fill: color }) : createElement(Ellipse, { x: -sz / 2, y: -sz / 2, width: sz, height: sz, fill: color });
|
|
560
|
+
particles.push(
|
|
561
|
+
createElement(
|
|
562
|
+
Group,
|
|
563
|
+
{ key: i, x: px, y: py, opacity: op, rotation: spin * t, originX: 0, originY: 0 },
|
|
564
|
+
inner
|
|
565
|
+
)
|
|
566
|
+
);
|
|
567
|
+
}
|
|
568
|
+
return createElement(Group, null, ...particles);
|
|
569
|
+
}
|
|
570
|
+
function linearTiming({ durationInFrames }) {
|
|
571
|
+
return {
|
|
572
|
+
durationInFrames,
|
|
573
|
+
getProgress: (frame) => durationInFrames <= 0 ? 1 : Math.min(1, Math.max(0, frame / durationInFrames))
|
|
574
|
+
};
|
|
575
|
+
}
|
|
576
|
+
function springTiming({
|
|
577
|
+
durationInFrames = 30,
|
|
578
|
+
config
|
|
579
|
+
} = {}) {
|
|
580
|
+
return {
|
|
581
|
+
durationInFrames,
|
|
582
|
+
getProgress: (frame, fps) => Math.max(0, spring({ frame, fps, config }))
|
|
583
|
+
};
|
|
584
|
+
}
|
|
585
|
+
function fade2() {
|
|
586
|
+
return (children, { progress, entering }) => createElement(Group, { opacity: entering ? progress : 1 - progress }, children);
|
|
587
|
+
}
|
|
588
|
+
function slide({
|
|
589
|
+
direction = "from-right"
|
|
590
|
+
} = {}) {
|
|
591
|
+
return (children, { progress, entering, width, height }) => {
|
|
592
|
+
const axisX = direction === "from-left" || direction === "from-right";
|
|
593
|
+
const span = axisX ? width : height;
|
|
594
|
+
const sign = direction === "from-right" || direction === "from-bottom" ? 1 : -1;
|
|
595
|
+
const offset = entering ? (1 - progress) * span * sign : -progress * span * sign;
|
|
596
|
+
const props = axisX ? { x: offset } : { y: offset };
|
|
597
|
+
return createElement(Group, props, children);
|
|
598
|
+
};
|
|
599
|
+
}
|
|
600
|
+
function wipe({
|
|
601
|
+
direction = "from-left"
|
|
602
|
+
} = {}) {
|
|
603
|
+
return (children, { progress, entering, width, height }) => {
|
|
604
|
+
if (!entering) {
|
|
605
|
+
const fade3 = Math.max(0, 1 - progress / 0.6);
|
|
606
|
+
return createElement(Group, { opacity: fade3 }, children);
|
|
607
|
+
}
|
|
608
|
+
const p = Math.min(1, Math.max(0, progress));
|
|
609
|
+
switch (direction) {
|
|
610
|
+
case "from-left":
|
|
611
|
+
return createElement(Group, { clip: clipRect(width * p, height) }, children);
|
|
612
|
+
case "from-top":
|
|
613
|
+
return createElement(Group, { clip: clipRect(width, height * p) }, children);
|
|
614
|
+
case "from-right": {
|
|
615
|
+
const w = width * p;
|
|
616
|
+
return createElement(
|
|
617
|
+
Group,
|
|
618
|
+
{ x: width - w },
|
|
619
|
+
createElement(Group, { x: -(width - w), clip: clipRect(w, height) }, children)
|
|
620
|
+
);
|
|
621
|
+
}
|
|
622
|
+
case "from-bottom": {
|
|
623
|
+
const h = height * p;
|
|
624
|
+
return createElement(
|
|
625
|
+
Group,
|
|
626
|
+
{ y: height - h },
|
|
627
|
+
createElement(Group, { y: -(height - h), clip: clipRect(width, h) }, children)
|
|
628
|
+
);
|
|
629
|
+
}
|
|
630
|
+
}
|
|
631
|
+
};
|
|
632
|
+
}
|
|
633
|
+
function none() {
|
|
634
|
+
return (children) => createElement(Group, {}, children);
|
|
635
|
+
}
|
|
636
|
+
function scaleAbout(children, sx, sy, px, py) {
|
|
637
|
+
return createElement(
|
|
638
|
+
Group,
|
|
639
|
+
{ x: px, y: py },
|
|
640
|
+
createElement(
|
|
641
|
+
Group,
|
|
642
|
+
{ scaleX: sx, scaleY: sy },
|
|
643
|
+
createElement(Group, { x: -px, y: -py }, children)
|
|
644
|
+
)
|
|
645
|
+
);
|
|
646
|
+
}
|
|
647
|
+
function flip() {
|
|
648
|
+
return (children, { progress, entering, width, height }) => {
|
|
649
|
+
const sx = entering ? Math.max(0, progress * 2 - 1) : Math.max(0, 1 - progress * 2);
|
|
650
|
+
return scaleAbout(children, sx, 1, width / 2, height / 2);
|
|
651
|
+
};
|
|
652
|
+
}
|
|
653
|
+
var round = (n) => Math.round(n * 1e3) / 1e3;
|
|
654
|
+
function iris() {
|
|
655
|
+
return (children, { progress, entering, width, height }) => {
|
|
656
|
+
if (!entering || progress >= 1) return createElement(Group, {}, children);
|
|
657
|
+
if (progress <= 0) return createElement(Group, { opacity: 0 }, children);
|
|
658
|
+
const cx = width / 2;
|
|
659
|
+
const cy = height / 2;
|
|
660
|
+
const r = progress * Math.hypot(cx, cy);
|
|
661
|
+
return createElement(
|
|
662
|
+
Group,
|
|
663
|
+
{ x: cx - r, y: cy - r, clip: clipEllipse(2 * r, 2 * r) },
|
|
664
|
+
createElement(Group, { x: -(cx - r), y: -(cy - r) }, children)
|
|
665
|
+
);
|
|
666
|
+
};
|
|
667
|
+
}
|
|
668
|
+
function clockWipe() {
|
|
669
|
+
return (children, { progress, entering, width, height }) => {
|
|
670
|
+
if (!entering || progress >= 1) return createElement(Group, {}, children);
|
|
671
|
+
if (progress <= 0) return createElement(Group, { opacity: 0 }, children);
|
|
672
|
+
const cx = width / 2;
|
|
673
|
+
const cy = height / 2;
|
|
674
|
+
const r = Math.hypot(cx, cy);
|
|
675
|
+
const theta = progress * Math.PI * 2;
|
|
676
|
+
const steps = Math.max(2, Math.ceil(theta / (Math.PI * 2) * 64));
|
|
677
|
+
let d = `M ${round(cx)} ${round(cy)}`;
|
|
678
|
+
for (let i = 0; i <= steps; i++) {
|
|
679
|
+
const a = theta * i / steps;
|
|
680
|
+
d += ` L ${round(cx + r * Math.sin(a))} ${round(cy - r * Math.cos(a))}`;
|
|
681
|
+
}
|
|
682
|
+
d += " Z";
|
|
683
|
+
return createElement(Group, { clip: clipPath(d) }, children);
|
|
684
|
+
};
|
|
685
|
+
}
|
|
686
|
+
var PUSH_VECTOR = {
|
|
687
|
+
left: { x: -1, y: 0 },
|
|
688
|
+
right: { x: 1, y: 0 },
|
|
689
|
+
up: { x: 0, y: -1 },
|
|
690
|
+
down: { x: 0, y: 1 }
|
|
691
|
+
};
|
|
692
|
+
function push({
|
|
693
|
+
direction = "left"
|
|
694
|
+
} = {}) {
|
|
695
|
+
return (children, { progress, entering, width, height }) => {
|
|
696
|
+
const { x, y } = PUSH_VECTOR[direction];
|
|
697
|
+
const tx = (entering ? -x * (1 - progress) : x * progress) * width;
|
|
698
|
+
const ty = (entering ? -y * (1 - progress) : y * progress) * height;
|
|
699
|
+
return createElement(Group, { x: tx, y: ty }, children);
|
|
700
|
+
};
|
|
701
|
+
}
|
|
702
|
+
function zoom({
|
|
703
|
+
direction = "in",
|
|
704
|
+
scaleAmount = 0.2
|
|
705
|
+
} = {}) {
|
|
706
|
+
return (children, { progress, entering, width, height }) => {
|
|
707
|
+
const s = scaleAmount;
|
|
708
|
+
let scale;
|
|
709
|
+
if (direction === "in") {
|
|
710
|
+
scale = entering ? 1 + s / 2 - s / 2 * progress : 1 + s * progress;
|
|
711
|
+
} else {
|
|
712
|
+
scale = entering ? 1 - s / 2 + s / 2 * progress : 1 - s * progress;
|
|
713
|
+
}
|
|
714
|
+
const opacity = entering ? progress : 1 - progress;
|
|
715
|
+
return createElement(
|
|
716
|
+
Group,
|
|
717
|
+
{ opacity },
|
|
718
|
+
scaleAbout(children, scale, scale, width / 2, height / 2)
|
|
719
|
+
);
|
|
720
|
+
};
|
|
721
|
+
}
|
|
722
|
+
function depthPush({
|
|
723
|
+
direction = "left",
|
|
724
|
+
scaleAmount = 0.08
|
|
725
|
+
} = {}) {
|
|
726
|
+
return (children, { progress, entering, width, height }) => {
|
|
727
|
+
const { x, y } = PUSH_VECTOR[direction];
|
|
728
|
+
const tx = (entering ? -x * (1 - progress) : x * progress) * width;
|
|
729
|
+
const ty = (entering ? -y * (1 - progress) : y * progress) * height;
|
|
730
|
+
const scale = entering ? 1 + scaleAmount * (1 - progress) : 1 - scaleAmount * progress;
|
|
731
|
+
return createElement(
|
|
732
|
+
Group,
|
|
733
|
+
{ x: tx, y: ty },
|
|
734
|
+
scaleAbout(children, scale, scale, width / 2, height / 2)
|
|
735
|
+
);
|
|
736
|
+
};
|
|
737
|
+
}
|
|
738
|
+
function dipToColor({ color = "#08080a" } = {}) {
|
|
739
|
+
return (children, { progress, entering, width, height }) => {
|
|
740
|
+
const sceneOpacity = entering ? Math.max(0, progress * 2 - 1) : Math.max(0, 1 - progress * 2);
|
|
741
|
+
const colorOpacity = entering ? Math.max(0, 1 - (progress - 0.5) * 2) : Math.min(1, progress * 2);
|
|
742
|
+
return createElement(
|
|
743
|
+
Group,
|
|
744
|
+
null,
|
|
745
|
+
createElement(Group, { opacity: sceneOpacity }, children),
|
|
746
|
+
createElement(
|
|
747
|
+
Group,
|
|
748
|
+
{ opacity: colorOpacity },
|
|
749
|
+
createElement(Rect, { width, height, fill: color })
|
|
750
|
+
)
|
|
751
|
+
);
|
|
752
|
+
};
|
|
753
|
+
}
|
|
754
|
+
var BLUR_RING = [
|
|
755
|
+
[0, 0],
|
|
756
|
+
[1, 0],
|
|
757
|
+
[-1, 0],
|
|
758
|
+
[0, 1],
|
|
759
|
+
[0, -1],
|
|
760
|
+
[0.7, 0.7],
|
|
761
|
+
[-0.7, 0.7],
|
|
762
|
+
[0.7, -0.7],
|
|
763
|
+
[-0.7, -0.7]
|
|
764
|
+
];
|
|
765
|
+
function blurStack(children, radius) {
|
|
766
|
+
if (radius <= 0.5) return createElement(Group, {}, children);
|
|
767
|
+
const op = 1 / BLUR_RING.length;
|
|
768
|
+
return createElement(
|
|
769
|
+
Group,
|
|
770
|
+
{},
|
|
771
|
+
...BLUR_RING.map(
|
|
772
|
+
([dx, dy], i) => createElement(Group, { key: i, x: dx * radius, y: dy * radius, opacity: op }, children)
|
|
773
|
+
)
|
|
774
|
+
);
|
|
775
|
+
}
|
|
776
|
+
function blur({ maxBlur = 24 } = {}) {
|
|
777
|
+
return (children, { progress, entering }) => {
|
|
778
|
+
const sharp = entering ? progress : 1 - progress;
|
|
779
|
+
return createElement(
|
|
780
|
+
Group,
|
|
781
|
+
{ opacity: entering ? progress : 1 - progress },
|
|
782
|
+
blurStack(children, (1 - sharp) * maxBlur)
|
|
783
|
+
);
|
|
784
|
+
};
|
|
785
|
+
}
|
|
786
|
+
function directionalSmear(children, dx, dy, radius) {
|
|
787
|
+
if (radius <= 0.5) return createElement(Group, {}, children);
|
|
788
|
+
const N = 6;
|
|
789
|
+
const op = 1 / N;
|
|
790
|
+
return createElement(
|
|
791
|
+
Group,
|
|
792
|
+
{},
|
|
793
|
+
...Array.from({ length: N }, (_, i) => {
|
|
794
|
+
const t = (i / (N - 1) - 0.5) * 2;
|
|
795
|
+
return createElement(
|
|
796
|
+
Group,
|
|
797
|
+
{ key: i, x: dx * t * radius, y: dy * t * radius, opacity: op },
|
|
798
|
+
children
|
|
799
|
+
);
|
|
800
|
+
})
|
|
801
|
+
);
|
|
802
|
+
}
|
|
803
|
+
function zoomBlur({
|
|
804
|
+
direction = "in",
|
|
805
|
+
scaleAmount = 0.35,
|
|
806
|
+
maxBlur = 28
|
|
807
|
+
} = {}) {
|
|
808
|
+
return (children, { progress, entering, width, height }) => {
|
|
809
|
+
const s = scaleAmount;
|
|
810
|
+
const dirSign = direction === "in" ? 1 : -1;
|
|
811
|
+
const scale = entering ? 1 + dirSign * s * (1 - progress) : 1 + dirSign * s * progress;
|
|
812
|
+
const sharp = entering ? progress : 1 - progress;
|
|
813
|
+
const opacity = entering ? progress : 1 - progress;
|
|
814
|
+
return createElement(
|
|
815
|
+
Group,
|
|
816
|
+
{ opacity },
|
|
817
|
+
scaleAbout(blurStack(children, (1 - sharp) * maxBlur), scale, scale, width / 2, height / 2)
|
|
818
|
+
);
|
|
819
|
+
};
|
|
820
|
+
}
|
|
821
|
+
function whipPan({
|
|
822
|
+
direction = "left",
|
|
823
|
+
maxBlur = 40
|
|
824
|
+
} = {}) {
|
|
825
|
+
return (children, { progress, entering, width, height }) => {
|
|
826
|
+
const { x, y } = PUSH_VECTOR[direction];
|
|
827
|
+
const tx = (entering ? -x * (1 - progress) : x * progress) * width;
|
|
828
|
+
const ty = (entering ? -y * (1 - progress) : y * progress) * height;
|
|
829
|
+
const sharp = entering ? progress : 1 - progress;
|
|
830
|
+
return createElement(
|
|
831
|
+
Group,
|
|
832
|
+
{ x: tx, y: ty },
|
|
833
|
+
directionalSmear(children, x, y, (1 - sharp) * maxBlur)
|
|
834
|
+
);
|
|
835
|
+
};
|
|
836
|
+
}
|
|
837
|
+
function filmBurn({ color = "#ffb070" } = {}) {
|
|
838
|
+
return (children, { progress, entering, width, height }) => {
|
|
839
|
+
const sceneOpacity = entering ? progress : 1 - progress;
|
|
840
|
+
const burn = Math.sin(Math.min(1, Math.max(0, progress)) * Math.PI);
|
|
841
|
+
const scene = createElement(Group, { opacity: sceneOpacity }, children);
|
|
842
|
+
if (!entering) return scene;
|
|
843
|
+
const leak = createElement(
|
|
844
|
+
Group,
|
|
845
|
+
{ opacity: burn * 0.85, blendMode: "screen" },
|
|
846
|
+
createElement(Rect, {
|
|
847
|
+
width,
|
|
848
|
+
height,
|
|
849
|
+
gradient: radialGradient([width * 0.72, height * 0.28], Math.max(width, height) * 0.95, [
|
|
850
|
+
{ offset: 0, color },
|
|
851
|
+
{ offset: 0.45, color: "#7a3a12" },
|
|
852
|
+
{ offset: 1, color: "#000000" }
|
|
853
|
+
])
|
|
854
|
+
})
|
|
855
|
+
);
|
|
856
|
+
return createElement(Group, null, scene, leak);
|
|
857
|
+
};
|
|
858
|
+
}
|
|
859
|
+
function lumaWipe() {
|
|
860
|
+
return (children, { progress, entering, width, height }) => {
|
|
861
|
+
if (!entering) {
|
|
862
|
+
return createElement(Group, { opacity: Math.max(0, 1 - progress / 0.7) }, children);
|
|
863
|
+
}
|
|
864
|
+
const p = Math.min(1, Math.max(0, progress));
|
|
865
|
+
const feather = 0.22;
|
|
866
|
+
const lead = p * (1 + feather);
|
|
867
|
+
const matte = createElement(Rect, {
|
|
868
|
+
width,
|
|
869
|
+
height,
|
|
870
|
+
gradient: linearGradient(
|
|
871
|
+
[0, 0],
|
|
872
|
+
[width, 0],
|
|
873
|
+
[
|
|
874
|
+
{ offset: 0, color: "#ffffff" },
|
|
875
|
+
{ offset: Math.max(0, Math.min(1, lead - feather)), color: "#ffffff" },
|
|
876
|
+
{ offset: Math.max(0, Math.min(1, lead)), color: "#000000" },
|
|
877
|
+
{ offset: 1, color: "#000000" }
|
|
878
|
+
]
|
|
879
|
+
)
|
|
880
|
+
});
|
|
881
|
+
return createElement(Group, { matte, matteMode: "luminance" }, children);
|
|
882
|
+
};
|
|
883
|
+
}
|
|
884
|
+
function chromaticAberration({
|
|
885
|
+
maxShift = 22
|
|
886
|
+
} = {}) {
|
|
887
|
+
return (children, { progress, entering }) => {
|
|
888
|
+
const sharp = entering ? progress : 1 - progress;
|
|
889
|
+
const d = (1 - sharp) * maxShift;
|
|
890
|
+
const opacity = entering ? progress : 1 - progress;
|
|
891
|
+
if (d <= 0.5) return createElement(Group, { opacity }, children);
|
|
892
|
+
return createElement(
|
|
893
|
+
Group,
|
|
894
|
+
{ opacity },
|
|
895
|
+
createElement(Group, { x: -d, opacity: 0.5 }, children),
|
|
896
|
+
createElement(Group, { x: d, opacity: 0.5 }, children),
|
|
897
|
+
createElement(Group, {}, children)
|
|
898
|
+
);
|
|
899
|
+
};
|
|
900
|
+
}
|
|
901
|
+
function devicePullback() {
|
|
902
|
+
return (children, { progress, entering, width, height }) => {
|
|
903
|
+
if (entering) {
|
|
904
|
+
const scale2 = 1.12 - 0.12 * progress;
|
|
905
|
+
return createElement(
|
|
906
|
+
Group,
|
|
907
|
+
{ opacity: progress },
|
|
908
|
+
scaleAbout(children, scale2, scale2, width / 2, height / 2)
|
|
909
|
+
);
|
|
910
|
+
}
|
|
911
|
+
const scale = 1 - 0.45 * progress;
|
|
912
|
+
return createElement(
|
|
913
|
+
Group,
|
|
914
|
+
{ opacity: 1 - progress, y: -progress * height * 0.06 },
|
|
915
|
+
scaleAbout(children, scale, scale, width / 2, height / 2)
|
|
916
|
+
);
|
|
917
|
+
};
|
|
918
|
+
}
|
|
919
|
+
function expandMorph() {
|
|
920
|
+
return (children, { progress, entering, width, height }) => {
|
|
921
|
+
if (entering) {
|
|
922
|
+
const scale2 = 0.3 + 0.7 * progress;
|
|
923
|
+
return createElement(
|
|
924
|
+
Group,
|
|
925
|
+
{ opacity: Math.min(1, progress * 1.5) },
|
|
926
|
+
scaleAbout(children, scale2, scale2, width / 2, height / 2)
|
|
927
|
+
);
|
|
928
|
+
}
|
|
929
|
+
const scale = 1 + 0.5 * progress;
|
|
930
|
+
return createElement(
|
|
931
|
+
Group,
|
|
932
|
+
{ opacity: 1 - progress },
|
|
933
|
+
scaleAbout(children, scale, scale, width / 2, height / 2)
|
|
934
|
+
);
|
|
935
|
+
};
|
|
936
|
+
}
|
|
937
|
+
function morph() {
|
|
938
|
+
return (children, { progress, entering, width, height }) => {
|
|
939
|
+
const scale = entering ? 0.94 + 0.06 * progress : 1 + 0.06 * progress;
|
|
940
|
+
return createElement(
|
|
941
|
+
Group,
|
|
942
|
+
{ opacity: entering ? progress : 1 - progress },
|
|
943
|
+
scaleAbout(children, scale, scale, width / 2, height / 2)
|
|
944
|
+
);
|
|
945
|
+
};
|
|
946
|
+
}
|
|
947
|
+
function glassWipe({
|
|
948
|
+
direction = "from-left"
|
|
949
|
+
} = {}) {
|
|
950
|
+
return (children, { progress, entering, width, height }) => {
|
|
951
|
+
if (!entering)
|
|
952
|
+
return createElement(Group, { opacity: Math.max(0, 1 - progress / 0.7) }, children);
|
|
953
|
+
const p = Math.min(1, Math.max(0, progress));
|
|
954
|
+
const frosted = blurStack(children, (1 - p) * 18);
|
|
955
|
+
switch (direction) {
|
|
956
|
+
case "from-top":
|
|
957
|
+
return createElement(Group, { clip: clipRect(width, height * p) }, frosted);
|
|
958
|
+
case "from-right": {
|
|
959
|
+
const w = width * p;
|
|
960
|
+
return createElement(
|
|
961
|
+
Group,
|
|
962
|
+
{ x: width - w },
|
|
963
|
+
createElement(Group, { x: -(width - w), clip: clipRect(w, height) }, frosted)
|
|
964
|
+
);
|
|
965
|
+
}
|
|
966
|
+
case "from-bottom": {
|
|
967
|
+
const h = height * p;
|
|
968
|
+
return createElement(
|
|
969
|
+
Group,
|
|
970
|
+
{ y: height - h },
|
|
971
|
+
createElement(Group, { y: -(height - h), clip: clipRect(width, h) }, frosted)
|
|
972
|
+
);
|
|
973
|
+
}
|
|
974
|
+
default:
|
|
975
|
+
return createElement(Group, { clip: clipRect(width * p, height) }, frosted);
|
|
976
|
+
}
|
|
977
|
+
};
|
|
978
|
+
}
|
|
979
|
+
function cellPhase(i, j) {
|
|
980
|
+
const n = Math.sin(i * 12.9898 + j * 78.233) * 43758.5453;
|
|
981
|
+
return n - Math.floor(n);
|
|
982
|
+
}
|
|
983
|
+
function gridPixelate({
|
|
984
|
+
cols = 16,
|
|
985
|
+
rows = 9
|
|
986
|
+
} = {}) {
|
|
987
|
+
return (children, { progress, entering, width, height }) => {
|
|
988
|
+
if (!entering)
|
|
989
|
+
return createElement(Group, { opacity: Math.max(0, 1 - progress / 0.7) }, children);
|
|
990
|
+
if (progress >= 1) return createElement(Group, {}, children);
|
|
991
|
+
if (progress <= 0) return createElement(Group, { opacity: 0 }, children);
|
|
992
|
+
const cw = width / cols;
|
|
993
|
+
const ch = height / rows;
|
|
994
|
+
let d = "";
|
|
995
|
+
for (let j = 0; j < rows; j++) {
|
|
996
|
+
for (let i = 0; i < cols; i++) {
|
|
997
|
+
if (cellPhase(i, j) < progress) {
|
|
998
|
+
const x = round(i * cw);
|
|
999
|
+
const y = round(j * ch);
|
|
1000
|
+
d += `M ${x} ${y} L ${round(x + cw)} ${y} L ${round(x + cw)} ${round(y + ch)} L ${x} ${round(y + ch)} Z `;
|
|
1001
|
+
}
|
|
1002
|
+
}
|
|
1003
|
+
}
|
|
1004
|
+
if (d === "") return createElement(Group, { opacity: 0 }, children);
|
|
1005
|
+
return createElement(Group, { clip: clipPath(d) }, children);
|
|
1006
|
+
};
|
|
1007
|
+
}
|
|
1008
|
+
function typeMask({ bars = 12 } = {}) {
|
|
1009
|
+
return (children, { progress, entering, width, height }) => {
|
|
1010
|
+
if (!entering)
|
|
1011
|
+
return createElement(Group, { opacity: Math.max(0, 1 - progress / 0.7) }, children);
|
|
1012
|
+
if (progress >= 1) return createElement(Group, {}, children);
|
|
1013
|
+
if (progress <= 0) return createElement(Group, { opacity: 0 }, children);
|
|
1014
|
+
const slot = width / bars;
|
|
1015
|
+
const w = round(slot * Math.min(1, progress));
|
|
1016
|
+
let d = "";
|
|
1017
|
+
for (let i = 0; i < bars; i++) {
|
|
1018
|
+
const x = round(i * slot);
|
|
1019
|
+
d += `M ${x} 0 L ${round(x + w)} 0 L ${round(x + w)} ${round(height)} L ${x} ${round(height)} Z `;
|
|
1020
|
+
}
|
|
1021
|
+
return createElement(Group, { clip: clipPath(d) }, children);
|
|
1022
|
+
};
|
|
1023
|
+
}
|
|
1024
|
+
var crossFade = fade2;
|
|
1025
|
+
function TransitionSequence(_props) {
|
|
1026
|
+
return null;
|
|
1027
|
+
}
|
|
1028
|
+
function TransitionTransition(_props) {
|
|
1029
|
+
return null;
|
|
1030
|
+
}
|
|
1031
|
+
function TransitionItem({
|
|
1032
|
+
duration,
|
|
1033
|
+
enter,
|
|
1034
|
+
exit,
|
|
1035
|
+
children
|
|
1036
|
+
}) {
|
|
1037
|
+
const frame = useCurrentFrame();
|
|
1038
|
+
const { fps, width, height } = useVideoConfig();
|
|
1039
|
+
let content = children;
|
|
1040
|
+
if (exit) {
|
|
1041
|
+
const t = exit.timing.durationInFrames;
|
|
1042
|
+
const start = duration - t;
|
|
1043
|
+
const progress = frame >= start ? exit.timing.getProgress(frame - start, fps) : 0;
|
|
1044
|
+
content = exit.presentation(content, { progress, entering: false, width, height });
|
|
1045
|
+
}
|
|
1046
|
+
if (enter) {
|
|
1047
|
+
const t = enter.timing.durationInFrames;
|
|
1048
|
+
const progress = frame < t ? enter.timing.getProgress(frame, fps) : 1;
|
|
1049
|
+
content = enter.presentation(content, { progress, entering: true, width, height });
|
|
1050
|
+
}
|
|
1051
|
+
return createElement(Group, null, content);
|
|
1052
|
+
}
|
|
1053
|
+
function TransitionSeriesRoot({ children }) {
|
|
1054
|
+
const items = Children.toArray(children).filter(isValidElement);
|
|
1055
|
+
const sequences = [];
|
|
1056
|
+
const transitions = [];
|
|
1057
|
+
for (const el of items) {
|
|
1058
|
+
if (el.type === TransitionSequence) {
|
|
1059
|
+
sequences.push(el.props);
|
|
1060
|
+
transitions.push(null);
|
|
1061
|
+
} else if (el.type === TransitionTransition) {
|
|
1062
|
+
if (sequences.length === 0 || transitions[transitions.length - 1] != null) {
|
|
1063
|
+
throw new Error("<TransitionSeries.Transition> must sit between two sequences");
|
|
1064
|
+
}
|
|
1065
|
+
transitions[transitions.length - 1] = el.props;
|
|
1066
|
+
} else {
|
|
1067
|
+
throw new Error(
|
|
1068
|
+
"<TransitionSeries> children must be <TransitionSeries.Sequence> or .Transition"
|
|
1069
|
+
);
|
|
1070
|
+
}
|
|
1071
|
+
}
|
|
1072
|
+
let offset = 0;
|
|
1073
|
+
const rendered = [];
|
|
1074
|
+
for (const [i, seq] of sequences.entries()) {
|
|
1075
|
+
const before = i > 0 ? transitions[i - 1] ?? null : null;
|
|
1076
|
+
const after = transitions[i] ?? null;
|
|
1077
|
+
const enter = before ? { presentation: before.presentation, timing: before.timing } : void 0;
|
|
1078
|
+
const exit = after ? { presentation: after.presentation, timing: after.timing } : void 0;
|
|
1079
|
+
rendered.push(
|
|
1080
|
+
createElement(
|
|
1081
|
+
Sequence,
|
|
1082
|
+
{ key: i, from: offset, durationInFrames: seq.durationInFrames },
|
|
1083
|
+
createElement(TransitionItem, {
|
|
1084
|
+
duration: seq.durationInFrames,
|
|
1085
|
+
enter,
|
|
1086
|
+
exit,
|
|
1087
|
+
// biome-ignore lint/correctness/noChildrenProp: raw createElement props object, not JSX
|
|
1088
|
+
children: seq.children
|
|
1089
|
+
})
|
|
1090
|
+
)
|
|
1091
|
+
);
|
|
1092
|
+
offset += seq.durationInFrames - (after ? after.timing.durationInFrames : 0);
|
|
1093
|
+
}
|
|
1094
|
+
return createElement(Group, null, ...rendered);
|
|
1095
|
+
}
|
|
1096
|
+
var TransitionSeries = Object.assign(TransitionSeriesRoot, {
|
|
1097
|
+
Sequence: TransitionSequence,
|
|
1098
|
+
Transition: TransitionTransition
|
|
1099
|
+
});
|
|
1100
|
+
var HOST_CONTEXT = {};
|
|
1101
|
+
function childrenToText(children) {
|
|
1102
|
+
if (children == null || children === false || children === true) return "";
|
|
1103
|
+
if (Array.isArray(children)) return children.map(childrenToText).join("");
|
|
1104
|
+
if (typeof children === "string") return children;
|
|
1105
|
+
if (typeof children === "number") return String(children);
|
|
1106
|
+
return "";
|
|
1107
|
+
}
|
|
1108
|
+
var HostTransitionContext = createContext(null);
|
|
1109
|
+
var currentUpdatePriority = DefaultEventPriority;
|
|
1110
|
+
var hostConfig = {
|
|
1111
|
+
supportsMutation: true,
|
|
1112
|
+
supportsPersistence: false,
|
|
1113
|
+
supportsHydration: false,
|
|
1114
|
+
isPrimaryRenderer: true,
|
|
1115
|
+
noTimeout: -1,
|
|
1116
|
+
scheduleTimeout: (fn, delay) => setTimeout(fn, delay),
|
|
1117
|
+
cancelTimeout: (handle) => clearTimeout(handle),
|
|
1118
|
+
createInstance(type, props) {
|
|
1119
|
+
const node = { type, props, children: [] };
|
|
1120
|
+
if (type === "onda-text") node.text = childrenToText(props.children);
|
|
1121
|
+
return node;
|
|
1122
|
+
},
|
|
1123
|
+
createTextInstance(text) {
|
|
1124
|
+
return { type: "#text", props: {}, children: [], text };
|
|
1125
|
+
},
|
|
1126
|
+
appendInitialChild(parent, child) {
|
|
1127
|
+
parent.children.push(child);
|
|
1128
|
+
},
|
|
1129
|
+
finalizeInitialChildren() {
|
|
1130
|
+
return false;
|
|
1131
|
+
},
|
|
1132
|
+
shouldSetTextContent(type) {
|
|
1133
|
+
return type === "onda-text";
|
|
1134
|
+
},
|
|
1135
|
+
getRootHostContext() {
|
|
1136
|
+
return HOST_CONTEXT;
|
|
1137
|
+
},
|
|
1138
|
+
getChildHostContext(parentContext) {
|
|
1139
|
+
return parentContext;
|
|
1140
|
+
},
|
|
1141
|
+
getPublicInstance(instance) {
|
|
1142
|
+
return instance;
|
|
1143
|
+
},
|
|
1144
|
+
prepareForCommit() {
|
|
1145
|
+
return null;
|
|
1146
|
+
},
|
|
1147
|
+
resetAfterCommit() {
|
|
1148
|
+
},
|
|
1149
|
+
preparePortalMount() {
|
|
1150
|
+
},
|
|
1151
|
+
// 0.33: `prepareUpdate` is gone; `commitUpdate` receives the new props
|
|
1152
|
+
// directly (no diff payload) and applies them.
|
|
1153
|
+
commitUpdate(instance, type, _prevProps, nextProps) {
|
|
1154
|
+
instance.props = nextProps;
|
|
1155
|
+
if (type === "onda-text") instance.text = childrenToText(nextProps.children);
|
|
1156
|
+
},
|
|
1157
|
+
commitTextUpdate(textInstance, _oldText, newText) {
|
|
1158
|
+
textInstance.text = newText;
|
|
1159
|
+
},
|
|
1160
|
+
resetTextContent() {
|
|
1161
|
+
},
|
|
1162
|
+
commitMount() {
|
|
1163
|
+
},
|
|
1164
|
+
appendChild(parent, child) {
|
|
1165
|
+
parent.children.push(child);
|
|
1166
|
+
},
|
|
1167
|
+
appendChildToContainer(container, child) {
|
|
1168
|
+
container.children.push(child);
|
|
1169
|
+
},
|
|
1170
|
+
insertBefore(parent, child, beforeChild) {
|
|
1171
|
+
const index = parent.children.indexOf(beforeChild);
|
|
1172
|
+
parent.children.splice(index < 0 ? parent.children.length : index, 0, child);
|
|
1173
|
+
},
|
|
1174
|
+
insertInContainerBefore(container, child, beforeChild) {
|
|
1175
|
+
const index = container.children.indexOf(beforeChild);
|
|
1176
|
+
container.children.splice(index < 0 ? container.children.length : index, 0, child);
|
|
1177
|
+
},
|
|
1178
|
+
removeChild(parent, child) {
|
|
1179
|
+
const index = parent.children.indexOf(child);
|
|
1180
|
+
if (index >= 0) parent.children.splice(index, 1);
|
|
1181
|
+
},
|
|
1182
|
+
removeChildFromContainer(container, child) {
|
|
1183
|
+
const index = container.children.indexOf(child);
|
|
1184
|
+
if (index >= 0) container.children.splice(index, 1);
|
|
1185
|
+
},
|
|
1186
|
+
clearContainer(container) {
|
|
1187
|
+
container.children.length = 0;
|
|
1188
|
+
},
|
|
1189
|
+
getInstanceFromNode() {
|
|
1190
|
+
return null;
|
|
1191
|
+
},
|
|
1192
|
+
beforeActiveInstanceBlur() {
|
|
1193
|
+
},
|
|
1194
|
+
afterActiveInstanceBlur() {
|
|
1195
|
+
},
|
|
1196
|
+
prepareScopeUpdate() {
|
|
1197
|
+
},
|
|
1198
|
+
getInstanceFromScope() {
|
|
1199
|
+
return null;
|
|
1200
|
+
},
|
|
1201
|
+
detachDeletedInstance() {
|
|
1202
|
+
},
|
|
1203
|
+
// ── react-reconciler 0.33 (React 19) additions ─────────────────────────────
|
|
1204
|
+
// ONDA is a static, single-pass renderer with no suspense, forms, or
|
|
1205
|
+
// transitions, so these are inert (return the "nothing pending / never
|
|
1206
|
+
// suspend / commit immediately" answers).
|
|
1207
|
+
setCurrentUpdatePriority(newPriority) {
|
|
1208
|
+
currentUpdatePriority = newPriority;
|
|
1209
|
+
},
|
|
1210
|
+
getCurrentUpdatePriority() {
|
|
1211
|
+
return currentUpdatePriority;
|
|
1212
|
+
},
|
|
1213
|
+
resolveUpdatePriority() {
|
|
1214
|
+
return DefaultEventPriority;
|
|
1215
|
+
},
|
|
1216
|
+
NotPendingTransition: null,
|
|
1217
|
+
// React's public `Context` type omits the internal fields (`_currentValue`,
|
|
1218
|
+
// `_threadCount`) the reconciler reads; the runtime object has them.
|
|
1219
|
+
// biome-ignore lint/suspicious/noExplicitAny: bridge React's public Context to the reconciler's ReactContext.
|
|
1220
|
+
HostTransitionContext,
|
|
1221
|
+
resetFormInstance() {
|
|
1222
|
+
},
|
|
1223
|
+
requestPostPaintCallback() {
|
|
1224
|
+
},
|
|
1225
|
+
shouldAttemptEagerTransition() {
|
|
1226
|
+
return false;
|
|
1227
|
+
},
|
|
1228
|
+
trackSchedulerEvent() {
|
|
1229
|
+
},
|
|
1230
|
+
resolveEventType() {
|
|
1231
|
+
return null;
|
|
1232
|
+
},
|
|
1233
|
+
resolveEventTimeStamp() {
|
|
1234
|
+
return -1;
|
|
1235
|
+
},
|
|
1236
|
+
maySuspendCommit() {
|
|
1237
|
+
return false;
|
|
1238
|
+
},
|
|
1239
|
+
preloadInstance() {
|
|
1240
|
+
return true;
|
|
1241
|
+
},
|
|
1242
|
+
startSuspendingCommit() {
|
|
1243
|
+
},
|
|
1244
|
+
suspendInstance() {
|
|
1245
|
+
},
|
|
1246
|
+
waitForCommitToBeReady() {
|
|
1247
|
+
return null;
|
|
1248
|
+
}
|
|
1249
|
+
};
|
|
1250
|
+
|
|
1251
|
+
// ../react/src/reconciler.ts
|
|
1252
|
+
var reconciler = Reconciler(hostConfig);
|
|
1253
|
+
var activeFrameState = null;
|
|
1254
|
+
var activeDof = null;
|
|
1255
|
+
function renderFrame(element, frame) {
|
|
1256
|
+
const config = videoConfig(element);
|
|
1257
|
+
const frameState = { ...config, frame };
|
|
1258
|
+
const prevFrameState = activeFrameState;
|
|
1259
|
+
activeFrameState = frameState;
|
|
1260
|
+
try {
|
|
1261
|
+
const container = { children: [] };
|
|
1262
|
+
const root = reconciler.createContainer(
|
|
1263
|
+
container,
|
|
1264
|
+
0,
|
|
1265
|
+
// LegacyRoot — renders synchronously
|
|
1266
|
+
null,
|
|
1267
|
+
// hydrationCallbacks
|
|
1268
|
+
false,
|
|
1269
|
+
// isStrictMode
|
|
1270
|
+
null,
|
|
1271
|
+
// concurrentUpdatesByDefaultOverride
|
|
1272
|
+
"",
|
|
1273
|
+
// identifierPrefix
|
|
1274
|
+
(error) => {
|
|
1275
|
+
throw error;
|
|
1276
|
+
},
|
|
1277
|
+
(error) => {
|
|
1278
|
+
throw error;
|
|
1279
|
+
},
|
|
1280
|
+
() => {
|
|
1281
|
+
},
|
|
1282
|
+
// onRecoverableError — non-fatal (e.g. hydration); ignore
|
|
1283
|
+
() => {
|
|
1284
|
+
}
|
|
1285
|
+
// onDefaultTransitionIndicator — no transitions in a static render
|
|
1286
|
+
);
|
|
1287
|
+
reconciler.updateContainerSync(
|
|
1288
|
+
createElement(FrameContext.Provider, { value: frameState }, element),
|
|
1289
|
+
root,
|
|
1290
|
+
null,
|
|
1291
|
+
null
|
|
1292
|
+
);
|
|
1293
|
+
reconciler.flushSyncWork();
|
|
1294
|
+
const top = container.children[0];
|
|
1295
|
+
if (container.children.length !== 1 || !top || top.type !== "onda-composition") {
|
|
1296
|
+
throw new Error("render: the root element must be a single <Composition>");
|
|
1297
|
+
}
|
|
1298
|
+
const scene = compositionToScene(top);
|
|
1299
|
+
reconciler.updateContainerSync(null, root, null, null);
|
|
1300
|
+
reconciler.flushSyncWork();
|
|
1301
|
+
return scene;
|
|
1302
|
+
} finally {
|
|
1303
|
+
activeFrameState = prevFrameState;
|
|
1304
|
+
}
|
|
1305
|
+
}
|
|
1306
|
+
function renderToScene(element) {
|
|
1307
|
+
return renderFrame(element, 0);
|
|
1308
|
+
}
|
|
1309
|
+
function motionBlurConfig(element) {
|
|
1310
|
+
const mb = element.props.motionBlur;
|
|
1311
|
+
if (!mb) return void 0;
|
|
1312
|
+
if (mb === true) return { shutter: 180, samples: 16 };
|
|
1313
|
+
if (typeof mb !== "object") return void 0;
|
|
1314
|
+
const o = mb;
|
|
1315
|
+
const samples = typeof o.samples === "number" ? Math.max(1, Math.round(o.samples)) : 16;
|
|
1316
|
+
if (samples < 2) return void 0;
|
|
1317
|
+
const shutter = typeof o.shutter === "number" ? Math.min(720, Math.max(1, o.shutter)) : 180;
|
|
1318
|
+
return { shutter, samples };
|
|
1319
|
+
}
|
|
1320
|
+
function outputFrameScenes(element, frame, mb) {
|
|
1321
|
+
if (!mb) return [renderFrame(element, frame)];
|
|
1322
|
+
const shutterFrames = mb.shutter / 360;
|
|
1323
|
+
const scenes = [];
|
|
1324
|
+
for (let i = 0; i < mb.samples; i++) {
|
|
1325
|
+
const t = ((i + 0.5) / mb.samples - 0.5) * shutterFrames;
|
|
1326
|
+
scenes.push(renderFrame(element, frame + t));
|
|
1327
|
+
}
|
|
1328
|
+
return scenes;
|
|
1329
|
+
}
|
|
1330
|
+
function renderFrames(element) {
|
|
1331
|
+
const { durationInFrames } = videoConfig(element);
|
|
1332
|
+
const mb = motionBlurConfig(element);
|
|
1333
|
+
const frames = [];
|
|
1334
|
+
for (let frame = 0; frame < durationInFrames; frame++) {
|
|
1335
|
+
frames.push(...outputFrameScenes(element, frame, mb));
|
|
1336
|
+
}
|
|
1337
|
+
return frames;
|
|
1338
|
+
}
|
|
1339
|
+
function renderFramesRange(element, startFrame, endFrame) {
|
|
1340
|
+
const mb = motionBlurConfig(element);
|
|
1341
|
+
const frames = [];
|
|
1342
|
+
for (let frame = startFrame; frame < endFrame; frame++) {
|
|
1343
|
+
frames.push(...outputFrameScenes(element, frame, mb));
|
|
1344
|
+
}
|
|
1345
|
+
return frames;
|
|
1346
|
+
}
|
|
1347
|
+
function renderToSceneJSON(element, space = 2) {
|
|
1348
|
+
return JSON.stringify(renderToScene(element), null, space);
|
|
1349
|
+
}
|
|
1350
|
+
function renderFramesJSON(element, space = 0) {
|
|
1351
|
+
return JSON.stringify(renderFrames(element), null, space);
|
|
1352
|
+
}
|
|
1353
|
+
function renderFrameRangeJSON(element, startFrame, endFrame, space = 0) {
|
|
1354
|
+
return JSON.stringify(renderFramesRange(element, startFrame, endFrame), null, space);
|
|
1355
|
+
}
|
|
1356
|
+
function videoConfig(element) {
|
|
1357
|
+
if (element.type !== Composition) {
|
|
1358
|
+
throw new Error("render: the root element must be a <Composition>");
|
|
1359
|
+
}
|
|
1360
|
+
const props = element.props;
|
|
1361
|
+
return {
|
|
1362
|
+
width: numberProp(props, "width", "Composition"),
|
|
1363
|
+
height: numberProp(props, "height", "Composition"),
|
|
1364
|
+
fps: numberProp(props, "fps", "Composition"),
|
|
1365
|
+
durationInFrames: numberProp(props, "durationInFrames", "Composition")
|
|
1366
|
+
};
|
|
1367
|
+
}
|
|
1368
|
+
function parseFinish(f) {
|
|
1369
|
+
const out = {};
|
|
1370
|
+
if (typeof f.exposure === "number") out.exposure = f.exposure;
|
|
1371
|
+
if (f.bloom && typeof f.bloom.sigma === "number") {
|
|
1372
|
+
out.bloom = { sigma: f.bloom.sigma };
|
|
1373
|
+
if (typeof f.bloom.threshold === "number") out.bloom.threshold = f.bloom.threshold;
|
|
1374
|
+
if (typeof f.bloom.intensity === "number") out.bloom.intensity = f.bloom.intensity;
|
|
1375
|
+
}
|
|
1376
|
+
if (typeof f.halation === "number") out.halation = f.halation;
|
|
1377
|
+
if (typeof f.temperature === "number") out.temperature = f.temperature;
|
|
1378
|
+
if (typeof f.contrast === "number") out.contrast = f.contrast;
|
|
1379
|
+
if (typeof f.saturation === "number") out.saturation = f.saturation;
|
|
1380
|
+
if (typeof f.vignette === "number") out.vignette = f.vignette;
|
|
1381
|
+
if (typeof f.grain === "number" && f.grain > 0) {
|
|
1382
|
+
out.grain = f.grain;
|
|
1383
|
+
out.grain_seed = activeFrameState?.frame ?? 0;
|
|
1384
|
+
}
|
|
1385
|
+
if (f.lut && typeof f.lut.size === "number" && f.lut.size >= 2 && Array.isArray(f.lut.table) && f.lut.table.length === f.lut.size ** 3 * 3) {
|
|
1386
|
+
out.lut = { size: f.lut.size, table: f.lut.table };
|
|
1387
|
+
}
|
|
1388
|
+
return out;
|
|
1389
|
+
}
|
|
1390
|
+
function parseDof(dof) {
|
|
1391
|
+
if (!dof || typeof dof.focus !== "number") return null;
|
|
1392
|
+
return {
|
|
1393
|
+
focus: dof.focus,
|
|
1394
|
+
aperture: typeof dof.aperture === "number" ? dof.aperture : 0.04,
|
|
1395
|
+
range: typeof dof.range === "number" ? dof.range : 0,
|
|
1396
|
+
maxBlur: typeof dof.maxBlur === "number" ? dof.maxBlur : 40
|
|
1397
|
+
};
|
|
1398
|
+
}
|
|
1399
|
+
function dofBlur(depth, dof) {
|
|
1400
|
+
const dist = Math.max(0, Math.abs(depth - dof.focus) - dof.range);
|
|
1401
|
+
return Math.min(dist * dof.aperture, dof.maxBlur);
|
|
1402
|
+
}
|
|
1403
|
+
function isVec3(v) {
|
|
1404
|
+
return Array.isArray(v) && v.length === 3 && v.every((n) => typeof n === "number");
|
|
1405
|
+
}
|
|
1406
|
+
function isVec2(v) {
|
|
1407
|
+
return Array.isArray(v) && v.length === 2 && v.every((n) => typeof n === "number");
|
|
1408
|
+
}
|
|
1409
|
+
function parseCamera3D(input) {
|
|
1410
|
+
const c = input ?? {};
|
|
1411
|
+
const out = {};
|
|
1412
|
+
if (isVec3(c.position)) out.position = c.position;
|
|
1413
|
+
if (isVec3(c.target)) out.target = c.target;
|
|
1414
|
+
if (isVec3(c.up)) out.up = c.up;
|
|
1415
|
+
if (typeof c.fov === "number") out.fov = c.fov;
|
|
1416
|
+
if (typeof c.near === "number") out.near = c.near;
|
|
1417
|
+
if (typeof c.far === "number") out.far = c.far;
|
|
1418
|
+
return out;
|
|
1419
|
+
}
|
|
1420
|
+
function parseExtrude(input) {
|
|
1421
|
+
if (typeof input === "number") return input > 0 ? { depth: input } : null;
|
|
1422
|
+
if (input && typeof input === "object") {
|
|
1423
|
+
const d = input.depth;
|
|
1424
|
+
if (typeof d === "number" && d > 0) return { depth: d };
|
|
1425
|
+
}
|
|
1426
|
+
return null;
|
|
1427
|
+
}
|
|
1428
|
+
function parseTransform3D(props) {
|
|
1429
|
+
const { position3d, rotation3d, anchor3d } = props;
|
|
1430
|
+
if (!isVec3(position3d) && !isVec3(rotation3d) && !isVec2(anchor3d)) return null;
|
|
1431
|
+
const out = {};
|
|
1432
|
+
if (isVec3(position3d)) out.position = position3d;
|
|
1433
|
+
if (isVec3(rotation3d)) out.rotation = rotation3d;
|
|
1434
|
+
if (isVec2(anchor3d)) out.anchor = anchor3d;
|
|
1435
|
+
return out;
|
|
1436
|
+
}
|
|
1437
|
+
function compositionToScene(node) {
|
|
1438
|
+
const { props } = node;
|
|
1439
|
+
activeDof = parseDof(props.dof);
|
|
1440
|
+
const children = node.children.length ? node.children.map(toNode) : null;
|
|
1441
|
+
activeDof = null;
|
|
1442
|
+
return {
|
|
1443
|
+
composition: {
|
|
1444
|
+
width: numberProp(props, "width", "Composition"),
|
|
1445
|
+
height: numberProp(props, "height", "Composition"),
|
|
1446
|
+
fps: numberProp(props, "fps", "Composition"),
|
|
1447
|
+
duration_in_frames: numberProp(props, "durationInFrames", "Composition"),
|
|
1448
|
+
// Opt-in cinematic LINEAR + ACES finishing (gpu/export only); omitted (→ gamma)
|
|
1449
|
+
// unless explicitly enabled, so existing scenes stay byte-identical.
|
|
1450
|
+
...props.linear === true ? { linear: true } : {},
|
|
1451
|
+
// Composition-level cinematic FINISH (linear-HDR chain + ACES); omitted unless set.
|
|
1452
|
+
...props.finish ? { finish: parseFinish(props.finish) } : {}
|
|
1453
|
+
},
|
|
1454
|
+
root: {
|
|
1455
|
+
kind: { type: "group" },
|
|
1456
|
+
...children ? { children } : {}
|
|
1457
|
+
}
|
|
1458
|
+
};
|
|
1459
|
+
}
|
|
1460
|
+
function elementToNode(element) {
|
|
1461
|
+
const container = { children: [] };
|
|
1462
|
+
const root = reconciler.createContainer(
|
|
1463
|
+
container,
|
|
1464
|
+
0,
|
|
1465
|
+
// LegacyRoot — renders synchronously
|
|
1466
|
+
null,
|
|
1467
|
+
false,
|
|
1468
|
+
null,
|
|
1469
|
+
"",
|
|
1470
|
+
(error) => {
|
|
1471
|
+
throw error;
|
|
1472
|
+
},
|
|
1473
|
+
(error) => {
|
|
1474
|
+
throw error;
|
|
1475
|
+
},
|
|
1476
|
+
() => {
|
|
1477
|
+
},
|
|
1478
|
+
() => {
|
|
1479
|
+
}
|
|
1480
|
+
);
|
|
1481
|
+
const wrapped = activeFrameState ? createElement(FrameContext.Provider, { value: activeFrameState }, element) : element;
|
|
1482
|
+
reconciler.updateContainerSync(wrapped, root, null, null);
|
|
1483
|
+
reconciler.flushSyncWork();
|
|
1484
|
+
const top = container.children[0];
|
|
1485
|
+
if (container.children.length !== 1 || !top) {
|
|
1486
|
+
throw new Error(
|
|
1487
|
+
"matte: the matte element must resolve to a single node (wrap many in a <Group>)"
|
|
1488
|
+
);
|
|
1489
|
+
}
|
|
1490
|
+
const node = toNode(top);
|
|
1491
|
+
reconciler.updateContainerSync(null, root, null, null);
|
|
1492
|
+
reconciler.flushSyncWork();
|
|
1493
|
+
return node;
|
|
1494
|
+
}
|
|
1495
|
+
function toNode(node) {
|
|
1496
|
+
const { props } = node;
|
|
1497
|
+
const base = {};
|
|
1498
|
+
if (typeof props.id === "number") base.id = props.id;
|
|
1499
|
+
const transform = transformOf(props);
|
|
1500
|
+
if (transform) base.transform = transform;
|
|
1501
|
+
if (typeof props.opacity === "number") base.opacity = props.opacity;
|
|
1502
|
+
if (props.clip !== void 0) base.clip = parseClip(props.clip);
|
|
1503
|
+
if (props.matte !== void 0) {
|
|
1504
|
+
base.matte = {
|
|
1505
|
+
mode: props.matteMode === "luminance" ? "luminance" : "alpha",
|
|
1506
|
+
source: elementToNode(props.matte)
|
|
1507
|
+
};
|
|
1508
|
+
}
|
|
1509
|
+
if (typeof props.blendMode === "string") base.blend = props.blendMode;
|
|
1510
|
+
const effects = Array.isArray(props.effects) ? [...props.effects] : [];
|
|
1511
|
+
if (typeof props.blur === "number" && props.blur > 0)
|
|
1512
|
+
effects.unshift({ effect: "blur", sigma: props.blur });
|
|
1513
|
+
const directionalBlur = parseDirectionalBlur(props.directionalBlur);
|
|
1514
|
+
if (directionalBlur) effects.unshift(directionalBlur);
|
|
1515
|
+
const chromaticAberration2 = parseChromaticAberration(props.chromaticAberration);
|
|
1516
|
+
if (chromaticAberration2) effects.push(chromaticAberration2);
|
|
1517
|
+
const vignette = parseVignette(props.vignette);
|
|
1518
|
+
if (vignette) effects.push(vignette);
|
|
1519
|
+
const posterize = parsePosterize(props.posterize);
|
|
1520
|
+
if (posterize) effects.push(posterize);
|
|
1521
|
+
const duotone = parseDuotone(props.duotone);
|
|
1522
|
+
if (duotone) effects.push(duotone);
|
|
1523
|
+
const chromaKey = parseChromaKey(props.chromaKey);
|
|
1524
|
+
if (chromaKey) effects.push(chromaKey);
|
|
1525
|
+
const bloom = parseBloom(props.bloom);
|
|
1526
|
+
if (bloom) effects.push(bloom);
|
|
1527
|
+
const grade = parseGrade(props.grade);
|
|
1528
|
+
if (grade) effects.push(grade);
|
|
1529
|
+
const goo = parseGoo(props.goo);
|
|
1530
|
+
if (goo) effects.push(goo);
|
|
1531
|
+
const grain = parseGrain(props.grain);
|
|
1532
|
+
if (grain) effects.push(grain);
|
|
1533
|
+
const backdropBlur = parseBackdropBlur(props.backdropBlur);
|
|
1534
|
+
if (backdropBlur) effects.push(backdropBlur);
|
|
1535
|
+
const lightWrap = parseLightWrap(props.lightWrap);
|
|
1536
|
+
if (lightWrap) effects.push(lightWrap);
|
|
1537
|
+
if (activeDof && typeof props.depth === "number") {
|
|
1538
|
+
const sigma = dofBlur(props.depth, activeDof);
|
|
1539
|
+
if (sigma > 0.4) effects.unshift({ effect: "blur", sigma });
|
|
1540
|
+
}
|
|
1541
|
+
if (effects.length) base.effects = effects;
|
|
1542
|
+
if (props.camera3d !== void 0) base.camera3d = parseCamera3D(props.camera3d);
|
|
1543
|
+
const transform3d = parseTransform3D(props);
|
|
1544
|
+
if (transform3d) base.transform3d = transform3d;
|
|
1545
|
+
const extrude = parseExtrude(props.extrude);
|
|
1546
|
+
if (extrude) base.extrude = extrude;
|
|
1547
|
+
if (props.layout !== void 0) base.layout = parseLayout(props.layout);
|
|
1548
|
+
const children = node.children.map(toNode);
|
|
1549
|
+
const withChildren = children.length ? { children } : {};
|
|
1550
|
+
switch (node.type) {
|
|
1551
|
+
case "onda-group":
|
|
1552
|
+
return { ...base, kind: { type: "group" }, ...withChildren };
|
|
1553
|
+
case "onda-rect": {
|
|
1554
|
+
const geometry = {
|
|
1555
|
+
shape: "rect",
|
|
1556
|
+
size: {
|
|
1557
|
+
width: numberProp(props, "width", "Rect"),
|
|
1558
|
+
height: numberProp(props, "height", "Rect")
|
|
1559
|
+
},
|
|
1560
|
+
...typeof props.cornerRadius === "number" ? { corner_radius: props.cornerRadius } : {}
|
|
1561
|
+
};
|
|
1562
|
+
return { ...base, kind: { type: "shape", geometry, ...fillStroke(props) }, ...withChildren };
|
|
1563
|
+
}
|
|
1564
|
+
case "onda-ellipse": {
|
|
1565
|
+
const geometry = {
|
|
1566
|
+
shape: "ellipse",
|
|
1567
|
+
size: {
|
|
1568
|
+
width: numberProp(props, "width", "Ellipse"),
|
|
1569
|
+
height: numberProp(props, "height", "Ellipse")
|
|
1570
|
+
}
|
|
1571
|
+
};
|
|
1572
|
+
return { ...base, kind: { type: "shape", geometry, ...fillStroke(props) }, ...withChildren };
|
|
1573
|
+
}
|
|
1574
|
+
case "onda-path": {
|
|
1575
|
+
const geometry = { shape: "path", data: stringProp(props, "d", "Path") };
|
|
1576
|
+
return { ...base, kind: { type: "shape", geometry, ...fillStroke(props) }, ...withChildren };
|
|
1577
|
+
}
|
|
1578
|
+
case "onda-boolean": {
|
|
1579
|
+
const op = props.op === "difference" || props.op === "intersect" || props.op === "xor" ? props.op : "union";
|
|
1580
|
+
const operands = [];
|
|
1581
|
+
for (const c of children) {
|
|
1582
|
+
if (c.kind.type === "shape") {
|
|
1583
|
+
operands.push({ geometry: c.kind.geometry, transform: c.transform ?? {} });
|
|
1584
|
+
}
|
|
1585
|
+
}
|
|
1586
|
+
const geometry = { shape: "boolean", op, operands };
|
|
1587
|
+
return { ...base, kind: { type: "shape", geometry, ...fillStroke(props) } };
|
|
1588
|
+
}
|
|
1589
|
+
case "onda-text": {
|
|
1590
|
+
const kind = {
|
|
1591
|
+
type: "text",
|
|
1592
|
+
content: node.text ?? "",
|
|
1593
|
+
...typeof props.fontSize === "number" ? { font_size: props.fontSize } : {},
|
|
1594
|
+
...props.color !== void 0 ? { color: parseColor(props.color) } : {},
|
|
1595
|
+
...typeof props.fontFamily === "string" ? { font_family: props.fontFamily } : {},
|
|
1596
|
+
...typeof props.fontWeight === "number" ? { weight: props.fontWeight } : {},
|
|
1597
|
+
...props.italic === true ? { italic: true } : {},
|
|
1598
|
+
...typeof props.letterSpacing === "number" ? { letter_spacing: props.letterSpacing } : {}
|
|
1599
|
+
};
|
|
1600
|
+
if (Array.isArray(props.runs)) {
|
|
1601
|
+
kind.runs = props.runs.map((r) => ({
|
|
1602
|
+
text: r.text,
|
|
1603
|
+
...r.color !== void 0 ? { color: parseColor(r.color) } : {},
|
|
1604
|
+
...typeof r.fontSize === "number" ? { font_size: r.fontSize } : {},
|
|
1605
|
+
...typeof r.fontFamily === "string" ? { font_family: r.fontFamily } : {},
|
|
1606
|
+
...typeof r.fontWeight === "number" ? { weight: r.fontWeight } : {},
|
|
1607
|
+
...r.italic === true ? { italic: true } : {}
|
|
1608
|
+
}));
|
|
1609
|
+
}
|
|
1610
|
+
return { ...base, kind, ...withChildren };
|
|
1611
|
+
}
|
|
1612
|
+
case "onda-image":
|
|
1613
|
+
return {
|
|
1614
|
+
...base,
|
|
1615
|
+
kind: {
|
|
1616
|
+
type: "image",
|
|
1617
|
+
src: stringProp(props, "src", "Image"),
|
|
1618
|
+
...typeof props.width === "number" ? { width: props.width } : {},
|
|
1619
|
+
...typeof props.height === "number" ? { height: props.height } : {},
|
|
1620
|
+
...props.fit === "fill" || props.fit === "cover" || props.fit === "contain" ? { fit: props.fit } : {},
|
|
1621
|
+
...typeof props.blur === "number" && props.blur > 0 ? { blur: props.blur } : {}
|
|
1622
|
+
},
|
|
1623
|
+
...withChildren
|
|
1624
|
+
};
|
|
1625
|
+
case "onda-video":
|
|
1626
|
+
return {
|
|
1627
|
+
...base,
|
|
1628
|
+
kind: {
|
|
1629
|
+
type: "video",
|
|
1630
|
+
src: stringProp(props, "src", "Video"),
|
|
1631
|
+
...typeof props.time === "number" ? { time: props.time } : {},
|
|
1632
|
+
...typeof props.width === "number" ? { width: props.width } : {},
|
|
1633
|
+
...typeof props.height === "number" ? { height: props.height } : {},
|
|
1634
|
+
...props.fit === "fill" || props.fit === "cover" || props.fit === "contain" ? { fit: props.fit } : {},
|
|
1635
|
+
...props.previewFallback === "element" || props.previewFallback === "skip" ? { previewFallback: props.previewFallback } : {}
|
|
1636
|
+
},
|
|
1637
|
+
...withChildren
|
|
1638
|
+
};
|
|
1639
|
+
case "onda-audio":
|
|
1640
|
+
return {
|
|
1641
|
+
...base,
|
|
1642
|
+
kind: {
|
|
1643
|
+
type: "audio",
|
|
1644
|
+
src: stringProp(props, "src", "Audio"),
|
|
1645
|
+
...typeof props.start === "number" ? { start: props.start } : {},
|
|
1646
|
+
...typeof props.startAt === "number" ? { start_at: props.startAt } : {},
|
|
1647
|
+
...typeof props.volume === "number" ? { volume: props.volume } : {}
|
|
1648
|
+
},
|
|
1649
|
+
...withChildren
|
|
1650
|
+
};
|
|
1651
|
+
case "onda-svg": {
|
|
1652
|
+
if (typeof props.src !== "string" && typeof props.markup !== "string") {
|
|
1653
|
+
throw new Error("<Svg> requires a 'src' or 'markup' prop");
|
|
1654
|
+
}
|
|
1655
|
+
return {
|
|
1656
|
+
...base,
|
|
1657
|
+
kind: {
|
|
1658
|
+
type: "svg",
|
|
1659
|
+
...typeof props.src === "string" ? { src: props.src } : {},
|
|
1660
|
+
...typeof props.markup === "string" ? { markup: props.markup } : {}
|
|
1661
|
+
},
|
|
1662
|
+
...withChildren
|
|
1663
|
+
};
|
|
1664
|
+
}
|
|
1665
|
+
case "#text":
|
|
1666
|
+
throw new Error(
|
|
1667
|
+
`renderToScene: raw text "${String(node.text ?? "").slice(0, 60)}" was placed inside a non-Text node. String children are only valid inside <Text>. Wrap it: h(Text, { x, y, fontSize, color }, '${String(node.text ?? "").slice(0, 30)}')`
|
|
1668
|
+
);
|
|
1669
|
+
default:
|
|
1670
|
+
throw new Error(
|
|
1671
|
+
`renderToScene: unsupported element type "${node.type}". Only ONDA primitives (Group/Rect/Ellipse/Path/Text/Image/Video/Svg) are valid. DOM elements like <div>/<span> and custom types are not rendered \u2014 use @onda-engine/react primitives only.`
|
|
1672
|
+
);
|
|
1673
|
+
}
|
|
1674
|
+
}
|
|
1675
|
+
function transformOf(props) {
|
|
1676
|
+
const transform = {};
|
|
1677
|
+
if (typeof props.x === "number" || typeof props.y === "number") {
|
|
1678
|
+
transform.translate = { x: props.x ?? 0, y: props.y ?? 0 };
|
|
1679
|
+
}
|
|
1680
|
+
if (typeof props.scaleX === "number" || typeof props.scaleY === "number") {
|
|
1681
|
+
transform.scale = { x: props.scaleX ?? 1, y: props.scaleY ?? 1 };
|
|
1682
|
+
}
|
|
1683
|
+
if (typeof props.rotation === "number") {
|
|
1684
|
+
transform.rotate = props.rotation;
|
|
1685
|
+
}
|
|
1686
|
+
if (typeof props.originX === "number" || typeof props.originY === "number") {
|
|
1687
|
+
transform.origin = { x: props.originX ?? 0, y: props.originY ?? 0 };
|
|
1688
|
+
}
|
|
1689
|
+
return transform.translate || transform.scale || transform.rotate !== void 0 || transform.origin ? transform : void 0;
|
|
1690
|
+
}
|
|
1691
|
+
var ALIGN_ALIASES = {
|
|
1692
|
+
"flex-start": "start",
|
|
1693
|
+
"flex-end": "end",
|
|
1694
|
+
left: "start",
|
|
1695
|
+
top: "start",
|
|
1696
|
+
right: "end",
|
|
1697
|
+
bottom: "end",
|
|
1698
|
+
middle: "center",
|
|
1699
|
+
"space-evenly": "space-around"
|
|
1700
|
+
};
|
|
1701
|
+
var normAlign = (v) => typeof v === "string" && v in ALIGN_ALIASES ? ALIGN_ALIASES[v] : v;
|
|
1702
|
+
function parseDirectionalBlur(input) {
|
|
1703
|
+
if (input && typeof input === "object") {
|
|
1704
|
+
const d = input;
|
|
1705
|
+
if (typeof d.sigma === "number" && d.sigma > 0) {
|
|
1706
|
+
return {
|
|
1707
|
+
effect: "directional_blur",
|
|
1708
|
+
sigma: d.sigma,
|
|
1709
|
+
angle: typeof d.angle === "number" ? d.angle : 0
|
|
1710
|
+
};
|
|
1711
|
+
}
|
|
1712
|
+
}
|
|
1713
|
+
return void 0;
|
|
1714
|
+
}
|
|
1715
|
+
function parseChromaticAberration(input) {
|
|
1716
|
+
return typeof input === "number" && input > 0 ? { effect: "chromatic_aberration", amount: input } : void 0;
|
|
1717
|
+
}
|
|
1718
|
+
function parseVignette(input) {
|
|
1719
|
+
if (typeof input === "number") {
|
|
1720
|
+
return input > 0 ? { effect: "vignette", amount: input, softness: 0.5 } : void 0;
|
|
1721
|
+
}
|
|
1722
|
+
if (input && typeof input === "object") {
|
|
1723
|
+
const v = input;
|
|
1724
|
+
if (typeof v.amount === "number" && v.amount > 0) {
|
|
1725
|
+
return {
|
|
1726
|
+
effect: "vignette",
|
|
1727
|
+
amount: v.amount,
|
|
1728
|
+
softness: typeof v.softness === "number" ? v.softness : 0.5
|
|
1729
|
+
};
|
|
1730
|
+
}
|
|
1731
|
+
}
|
|
1732
|
+
return void 0;
|
|
1733
|
+
}
|
|
1734
|
+
function parsePosterize(input) {
|
|
1735
|
+
return typeof input === "number" && input >= 2 ? { effect: "posterize", levels: input } : void 0;
|
|
1736
|
+
}
|
|
1737
|
+
function parseDuotone(input) {
|
|
1738
|
+
if (input && typeof input === "object") {
|
|
1739
|
+
const d = input;
|
|
1740
|
+
if (d.shadow !== void 0 && d.highlight !== void 0) {
|
|
1741
|
+
const s = parseColor(d.shadow);
|
|
1742
|
+
const h = parseColor(d.highlight);
|
|
1743
|
+
return { effect: "duotone", shadow: [s.r, s.g, s.b], highlight: [h.r, h.g, h.b] };
|
|
1744
|
+
}
|
|
1745
|
+
}
|
|
1746
|
+
return void 0;
|
|
1747
|
+
}
|
|
1748
|
+
function parseChromaKey(input) {
|
|
1749
|
+
if (input && typeof input === "object") {
|
|
1750
|
+
const c = input;
|
|
1751
|
+
if (c.color !== void 0) {
|
|
1752
|
+
const k = parseColor(c.color);
|
|
1753
|
+
return {
|
|
1754
|
+
effect: "chroma_key",
|
|
1755
|
+
key: [k.r, k.g, k.b],
|
|
1756
|
+
threshold: typeof c.threshold === "number" ? c.threshold : 0.4,
|
|
1757
|
+
smoothness: typeof c.smoothness === "number" ? c.smoothness : 0.1
|
|
1758
|
+
};
|
|
1759
|
+
}
|
|
1760
|
+
}
|
|
1761
|
+
return void 0;
|
|
1762
|
+
}
|
|
1763
|
+
function parseBloom(input) {
|
|
1764
|
+
if (typeof input === "number") {
|
|
1765
|
+
return input > 0 ? { effect: "bloom", threshold: 0.7, intensity: 1, sigma: input } : void 0;
|
|
1766
|
+
}
|
|
1767
|
+
if (input && typeof input === "object") {
|
|
1768
|
+
const b = input;
|
|
1769
|
+
if (typeof b.sigma === "number" && b.sigma > 0) {
|
|
1770
|
+
return {
|
|
1771
|
+
effect: "bloom",
|
|
1772
|
+
threshold: typeof b.threshold === "number" ? b.threshold : 0.7,
|
|
1773
|
+
intensity: typeof b.intensity === "number" ? b.intensity : 1,
|
|
1774
|
+
sigma: b.sigma
|
|
1775
|
+
};
|
|
1776
|
+
}
|
|
1777
|
+
}
|
|
1778
|
+
return void 0;
|
|
1779
|
+
}
|
|
1780
|
+
function parseGrade(input) {
|
|
1781
|
+
if (!input || typeof input !== "object") return void 0;
|
|
1782
|
+
const g = input;
|
|
1783
|
+
const exposure = typeof g.exposure === "number" ? g.exposure : 0;
|
|
1784
|
+
const contrast = typeof g.contrast === "number" ? g.contrast : 1;
|
|
1785
|
+
const saturation = typeof g.saturation === "number" ? g.saturation : 1;
|
|
1786
|
+
const temperature = typeof g.temperature === "number" ? g.temperature : 0;
|
|
1787
|
+
const tint = typeof g.tint === "number" ? g.tint : 0;
|
|
1788
|
+
if (exposure === 0 && contrast === 1 && saturation === 1 && temperature === 0 && tint === 0) {
|
|
1789
|
+
return void 0;
|
|
1790
|
+
}
|
|
1791
|
+
return { effect: "color_grade", exposure, contrast, saturation, temperature, tint };
|
|
1792
|
+
}
|
|
1793
|
+
function parseGoo(input) {
|
|
1794
|
+
if (typeof input === "number") {
|
|
1795
|
+
return input > 0 ? { effect: "goo", sigma: input, threshold: 0.5 } : void 0;
|
|
1796
|
+
}
|
|
1797
|
+
if (input && typeof input === "object") {
|
|
1798
|
+
const g = input;
|
|
1799
|
+
if (typeof g.sigma === "number" && g.sigma > 0) {
|
|
1800
|
+
return {
|
|
1801
|
+
effect: "goo",
|
|
1802
|
+
sigma: g.sigma,
|
|
1803
|
+
threshold: typeof g.threshold === "number" ? g.threshold : 0.5
|
|
1804
|
+
};
|
|
1805
|
+
}
|
|
1806
|
+
}
|
|
1807
|
+
return void 0;
|
|
1808
|
+
}
|
|
1809
|
+
function parseGrain(input) {
|
|
1810
|
+
if (typeof input === "number") {
|
|
1811
|
+
return input > 0 ? { effect: "grain", intensity: input, size: 1, seed: 0 } : void 0;
|
|
1812
|
+
}
|
|
1813
|
+
if (input && typeof input === "object") {
|
|
1814
|
+
const g = input;
|
|
1815
|
+
if (typeof g.intensity === "number" && g.intensity > 0) {
|
|
1816
|
+
return {
|
|
1817
|
+
effect: "grain",
|
|
1818
|
+
intensity: g.intensity,
|
|
1819
|
+
size: typeof g.size === "number" ? g.size : 1,
|
|
1820
|
+
seed: typeof g.seed === "number" ? g.seed : 0
|
|
1821
|
+
};
|
|
1822
|
+
}
|
|
1823
|
+
}
|
|
1824
|
+
return void 0;
|
|
1825
|
+
}
|
|
1826
|
+
function parseBackdropBlur(input) {
|
|
1827
|
+
const TRANSPARENT = { r: 0, g: 0, b: 0, a: 0 };
|
|
1828
|
+
if (typeof input === "number") {
|
|
1829
|
+
return input > 0 ? { effect: "backdrop_blur", sigma: input, tint: TRANSPARENT, brightness: 1, saturation: 1 } : void 0;
|
|
1830
|
+
}
|
|
1831
|
+
if (input && typeof input === "object") {
|
|
1832
|
+
const b = input;
|
|
1833
|
+
if (typeof b.sigma === "number" && b.sigma > 0) {
|
|
1834
|
+
return {
|
|
1835
|
+
effect: "backdrop_blur",
|
|
1836
|
+
sigma: b.sigma,
|
|
1837
|
+
tint: b.tint !== void 0 ? parseColor(b.tint) : TRANSPARENT,
|
|
1838
|
+
brightness: typeof b.brightness === "number" ? b.brightness : 1,
|
|
1839
|
+
saturation: typeof b.saturation === "number" ? b.saturation : 1
|
|
1840
|
+
};
|
|
1841
|
+
}
|
|
1842
|
+
}
|
|
1843
|
+
return void 0;
|
|
1844
|
+
}
|
|
1845
|
+
function parseLightWrap(input) {
|
|
1846
|
+
if (typeof input === "number") {
|
|
1847
|
+
return input > 0 ? { effect: "light_wrap", sigma: input, strength: 1 } : void 0;
|
|
1848
|
+
}
|
|
1849
|
+
if (input && typeof input === "object") {
|
|
1850
|
+
const w = input;
|
|
1851
|
+
if (typeof w.sigma === "number" && w.sigma > 0) {
|
|
1852
|
+
return {
|
|
1853
|
+
effect: "light_wrap",
|
|
1854
|
+
sigma: w.sigma,
|
|
1855
|
+
strength: typeof w.strength === "number" ? w.strength : 1
|
|
1856
|
+
};
|
|
1857
|
+
}
|
|
1858
|
+
}
|
|
1859
|
+
return void 0;
|
|
1860
|
+
}
|
|
1861
|
+
function parseLayout(layout) {
|
|
1862
|
+
const out = {};
|
|
1863
|
+
if (layout.direction !== void 0) out.direction = layout.direction;
|
|
1864
|
+
if (layout.justify !== void 0) out.justify = normAlign(layout.justify);
|
|
1865
|
+
if (layout.align !== void 0) out.align = normAlign(layout.align);
|
|
1866
|
+
if (typeof layout.gap === "number") out.gap = layout.gap;
|
|
1867
|
+
if (typeof layout.padding === "number") out.padding = layout.padding;
|
|
1868
|
+
if (layout.wrap === true) out.wrap = true;
|
|
1869
|
+
if (typeof layout.width === "number") out.width = layout.width;
|
|
1870
|
+
if (typeof layout.height === "number") out.height = layout.height;
|
|
1871
|
+
return out;
|
|
1872
|
+
}
|
|
1873
|
+
function fillStroke(props) {
|
|
1874
|
+
const out = {};
|
|
1875
|
+
if (props.fill !== void 0) out.fill = parseColor(props.fill);
|
|
1876
|
+
if (props.gradient !== void 0) out.gradient = parseGradient(props.gradient);
|
|
1877
|
+
const sh = props.shadow;
|
|
1878
|
+
if (sh && typeof sh.blur === "number") {
|
|
1879
|
+
out.shadow = {
|
|
1880
|
+
color: parseColor(sh.color),
|
|
1881
|
+
blur: sh.blur,
|
|
1882
|
+
...typeof sh.offsetX === "number" || typeof sh.offsetY === "number" ? { offset: { x: sh.offsetX ?? 0, y: sh.offsetY ?? 0 } } : {},
|
|
1883
|
+
...typeof sh.spread === "number" ? { spread: sh.spread } : {}
|
|
1884
|
+
};
|
|
1885
|
+
}
|
|
1886
|
+
if (props.stroke !== void 0) {
|
|
1887
|
+
const trim = parseTrim(props);
|
|
1888
|
+
out.stroke = {
|
|
1889
|
+
color: parseColor(props.stroke),
|
|
1890
|
+
width: typeof props.strokeWidth === "number" ? props.strokeWidth : 1,
|
|
1891
|
+
...props.strokeCap === "round" || props.strokeCap === "square" ? { cap: props.strokeCap } : {},
|
|
1892
|
+
...props.strokeJoin === "round" || props.strokeJoin === "bevel" ? { join: props.strokeJoin } : {},
|
|
1893
|
+
...Array.isArray(props.strokeDash) && props.strokeDash.length ? { dash: props.strokeDash } : {},
|
|
1894
|
+
...typeof props.strokeDashOffset === "number" ? { dash_offset: props.strokeDashOffset } : {},
|
|
1895
|
+
...trim ? { trim } : {}
|
|
1896
|
+
};
|
|
1897
|
+
}
|
|
1898
|
+
return out;
|
|
1899
|
+
}
|
|
1900
|
+
function parseTrim(props) {
|
|
1901
|
+
const { trimStart, trimEnd, trimOffset } = props;
|
|
1902
|
+
if (typeof trimStart !== "number" && typeof trimEnd !== "number" && typeof trimOffset !== "number") {
|
|
1903
|
+
return void 0;
|
|
1904
|
+
}
|
|
1905
|
+
const trim = {};
|
|
1906
|
+
if (typeof trimStart === "number") trim.start = trimStart;
|
|
1907
|
+
if (typeof trimEnd === "number") trim.end = trimEnd;
|
|
1908
|
+
if (typeof trimOffset === "number") trim.offset = trimOffset;
|
|
1909
|
+
return trim;
|
|
1910
|
+
}
|
|
1911
|
+
function numberProp(props, key, ctx) {
|
|
1912
|
+
const value = props[key];
|
|
1913
|
+
if (typeof value !== "number") throw new Error(`<${ctx}> requires a numeric '${key}' prop`);
|
|
1914
|
+
return value;
|
|
1915
|
+
}
|
|
1916
|
+
function stringProp(props, key, ctx) {
|
|
1917
|
+
const value = props[key];
|
|
1918
|
+
if (typeof value !== "string") throw new Error(`<${ctx}> requires a string '${key}' prop`);
|
|
1919
|
+
return value;
|
|
1920
|
+
}
|
|
1921
|
+
|
|
1922
|
+
// ../react/src/fonts.ts
|
|
1923
|
+
var fonts = [];
|
|
1924
|
+
var seen = /* @__PURE__ */ new Set();
|
|
1925
|
+
function signature(data) {
|
|
1926
|
+
let h = 2166136261;
|
|
1927
|
+
for (let i = 0; i < data.length; i++) {
|
|
1928
|
+
h ^= data[i];
|
|
1929
|
+
h = Math.imul(h, 16777619);
|
|
1930
|
+
}
|
|
1931
|
+
return `${data.length}:${h >>> 0}`;
|
|
1932
|
+
}
|
|
1933
|
+
function registerFont(data) {
|
|
1934
|
+
const sig = signature(data);
|
|
1935
|
+
if (seen.has(sig)) return;
|
|
1936
|
+
seen.add(sig);
|
|
1937
|
+
fonts.push(data);
|
|
1938
|
+
}
|
|
1939
|
+
function registeredFonts() {
|
|
1940
|
+
return fonts.slice();
|
|
1941
|
+
}
|
|
1942
|
+
function clearRegisteredFonts() {
|
|
1943
|
+
fonts.length = 0;
|
|
1944
|
+
seen.clear();
|
|
1945
|
+
}
|
|
1946
|
+
|
|
1947
|
+
// ../react/src/warmers.ts
|
|
1948
|
+
var warmers = [];
|
|
1949
|
+
function registerEngineWarmer(warm) {
|
|
1950
|
+
if (!warmers.includes(warm)) warmers.push(warm);
|
|
1951
|
+
}
|
|
1952
|
+
async function runEngineWarmers() {
|
|
1953
|
+
await Promise.all(warmers.map((w) => w().catch(() => {
|
|
1954
|
+
})));
|
|
1955
|
+
}
|
|
1956
|
+
//! `interpolate` — map a value through input/output ranges, the workhorse for
|
|
1957
|
+
//! frame-driven animation. Mirrors Remotion's API closely.
|
|
1958
|
+
//! Author-facing clip shapes, normalized into the engine's {@link ShapeGeometry}.
|
|
1959
|
+
//! Author-facing gradient inputs, normalized into the engine's {@link Gradient}.
|
|
1960
|
+
//! Path morphing — the Apple/ElevenLabs "magic move": one shape continuously
|
|
1961
|
+
//! morphing into another. The engine draws whatever path `d` it's handed, so this
|
|
1962
|
+
//! is a pure authoring helper (no engine change): it returns the in-between `d`
|
|
1963
|
+
//! string for a given progress `t`, which you feed to a `<Path>`.
|
|
1964
|
+
//!
|
|
1965
|
+
//! Backed by flubber, which solves the hard part — point CORRESPONDENCE between
|
|
1966
|
+
//! two arbitrary outlines (a naive `d`-string lerp tears shapes apart). The
|
|
1967
|
+
//! interpolator for a given (from → to) pair is cached, so calling `morphPath`
|
|
1968
|
+
//! every frame is cheap.
|
|
1969
|
+
//! Frame context + hooks. The engine renders a composition once per frame, with
|
|
1970
|
+
//! the current frame supplied via React context; components read it with
|
|
1971
|
+
//! {@link useCurrentFrame} and compute props from it (Remotion's model).
|
|
1972
|
+
//! The ONDA React components. Each is a thin typed wrapper that emits an
|
|
1973
|
+
//! internal host element; the reconciler maps those to scene-graph nodes.
|
|
1974
|
+
//! ```tsx
|
|
1975
|
+
//! <Composition width={1200} height={360} fps={30} durationInFrames={30}>
|
|
1976
|
+
//! <Rect width={1200} height={360} fill="#0a0d17" />
|
|
1977
|
+
//! <Text fontSize={96} color="#fff" x={96} y={110}>Hello ONDA</Text>
|
|
1978
|
+
//! </Composition>
|
|
1979
|
+
//! ```
|
|
1980
|
+
//! `spring()` — natural motion as a deterministic, frame-keyed value.
|
|
1981
|
+
//! A damped harmonic oscillator pulled from 0 toward 1, integrated with explicit
|
|
1982
|
+
//! Euler at a stable sub-frame step. Pure function of `frame` (IEEE-754 math), so
|
|
1983
|
+
//! renders are reproducible across machines. Use it like Remotion's spring —
|
|
1984
|
+
//! including `durationInFrames` to re-time how fast it settles.
|
|
1985
|
+
//! Why sub-stepping: explicit Euler diverges unless `dt < ~2/ω`, where `ω` is the
|
|
1986
|
+
//! fastest rate in the system. A heavily-overdamped config like the Onda house
|
|
1987
|
+
//! spring (damping 200) has `ω ≈ 200`, so a naive `dt = 1/fps` (≈0.033) blows up
|
|
1988
|
+
//! to NaN. We pick a stable sub-frame `dt` instead, independent of `fps`.
|
|
1989
|
+
//! Time-shifting composition primitives — `<Sequence>`, `<Series>`, `<Loop>`.
|
|
1990
|
+
//! These manipulate the frame context: children inside a `<Sequence from={N}>`
|
|
1991
|
+
//! see `useCurrentFrame()` shifted by `-N` and render only within their window.
|
|
1992
|
+
//! This is Remotion's compositional grammar, the way you assemble a timeline.
|
|
1993
|
+
//! Seeded, deterministic randomness for frame-driven motion.
|
|
1994
|
+
//! Compositions must render identically every time (same frame → same pixels),
|
|
1995
|
+
//! so `Math.random()` is off-limits. These are pure functions of their inputs:
|
|
1996
|
+
//! `random(seed)` for uniform values and `noise2D/3D` for smooth, organic motion
|
|
1997
|
+
//! (jitter, drift, wobble). Same seed + coords always yield the same result.
|
|
1998
|
+
//! THE SEEDING MODEL (the convention every seeded component follows):
|
|
1999
|
+
//! - Every component with randomness takes a `seed` prop with a fixed default —
|
|
2000
|
+
//! so an instance is deterministic out of the box and STABLE across renders,
|
|
2001
|
+
//! previews and exports (same seed → same pixels, every machine).
|
|
2002
|
+
//! - Per-element streams derive from the seed by salting (`random(seed + i *
|
|
2003
|
+
//! prime)` or `random(`${seed}-${i}-part`)`), never by consuming a stateful
|
|
2004
|
+
//! generator — order of evaluation can't change the result.
|
|
2005
|
+
//! - Want a DIFFERENT take, not a different look? Pass `variant` (an integer):
|
|
2006
|
+
//! {@link variantSeed} derives a new well-mixed seed from `(seed, variant)`,
|
|
2007
|
+
//! so alternates never require hand-edited magic seeds. `variant: 0` (or
|
|
2008
|
+
//! omitted) is the identity — existing renders are untouched.
|
|
2009
|
+
//! `<Particles>` — a deterministic particle emitter. Every particle's whole state
|
|
2010
|
+
//! (spawn point, velocity, age, size, opacity, colour, spin) is a PURE function of
|
|
2011
|
+
//! `(frame, seed, index)` via the `random(seed)` hash, so the same comp renders the
|
|
2012
|
+
//! same field on every frame and every machine — no `Math.random`, no wall-clock,
|
|
2013
|
+
//! and no engine change: each live particle is just a circle/square in one
|
|
2014
|
+
//! full-canvas `<Group>`. Frame-based units (velocity = px/frame, gravity =
|
|
2015
|
+
//! px/frame²), so it composes with the rest of the timeline.
|
|
2016
|
+
//! Transitions between sequences — `<TransitionSeries>`.
|
|
2017
|
+
//! Like `<Series>`, but consecutive sequences can *overlap* by a transition's
|
|
2018
|
+
//! duration, during which the outgoing scene animates out and the incoming one
|
|
2019
|
+
//! animates in. Built entirely on the existing primitives: a transition is just
|
|
2020
|
+
//! `opacity` (fade), `translate` (slide), or a `clip` mask (wipe) driven by the
|
|
2021
|
+
//! transition's progress — no engine support needed.
|
|
2022
|
+
//! <TransitionSeries>
|
|
2023
|
+
//! <TransitionSeries.Sequence durationInFrames={60}><A /></TransitionSeries.Sequence>
|
|
2024
|
+
//! <TransitionSeries.Transition presentation={fade()} timing={linearTiming({ durationInFrames: 20 })} />
|
|
2025
|
+
//! <TransitionSeries.Sequence durationInFrames={60}><B /></TransitionSeries.Sequence>
|
|
2026
|
+
//! </TransitionSeries>
|
|
2027
|
+
//! react-reconciler host config.
|
|
2028
|
+
//! ONDA's scene graph is a static snapshot, so this renderer just builds a
|
|
2029
|
+
//! mutable tree of [`HostNode`]s as React reconciles, which `reconciler.ts` then
|
|
2030
|
+
//! serializes once. Mutation mode; targets **react-reconciler 0.33 (React 19)**.
|
|
2031
|
+
//! Render a React element tree into an ONDA scene graph.
|
|
2032
|
+
//! Font registry — the single source for a composition's custom fonts.
|
|
2033
|
+
//! An author declares a font ONCE (via `@onda-engine/components`' `loadFont`, which loads
|
|
2034
|
+
//! it into the author-time measurement engine AND calls `registerFont` here). The
|
|
2035
|
+
//! render harness (`@onda-engine/render`) then drains this registry, materializes the
|
|
2036
|
+
//! bytes to temp files, and hands them to the `onda` CLI (`--font`) — so the font
|
|
2037
|
+
//! used to MEASURE the text (glyph placement) is the same one the renderer DRAWS,
|
|
2038
|
+
//! with no separate flag. Same hub pattern as the engine-warmer registry: bytes
|
|
2039
|
+
//! flow through `@onda-engine/react`, so neither `@onda-engine/components` nor `@onda-engine/render`
|
|
2040
|
+
//! depends on the other.
|
|
2041
|
+
//! Engine-warmer registry — a hook for packages that need to load async engine
|
|
2042
|
+
//! assets (e.g. the wasm text-measurement module) BEFORE the synchronous frame
|
|
2043
|
+
//! render, so components bake exact values instead of estimates on export.
|
|
2044
|
+
//! `@onda-engine/react` is the shared hub: `@onda-engine/components` registers its warmer on
|
|
2045
|
+
//! import (`registerEngineWarmer(preloadTextMetrics)`), and `@onda-engine/render` awaits
|
|
2046
|
+
//! `runEngineWarmers()` before `renderFramesJSON`. No package-to-package coupling,
|
|
2047
|
+
//! and it's automatic — importing the components that need warming registers it.
|
|
2048
|
+
//! `@onda-engine/react` — write React, get an ONDA scene graph.
|
|
2049
|
+
|
|
2050
|
+
export { AbsoluteFill, Audio, Camera, Center, Composition, Easing, Ellipse, Flex, Group, Image, Image as Img, Loop, Merge, Particles, Path, Precomp, Rect, Repeater, Scene3D, Sequence, Series, Svg, Text, TransitionSeries, Video, blur, chromaticAberration, clearRegisteredFonts, clipEllipse, clipPath, clipRect, clockWipe, crossFade, cubicBezier, depthPush, devicePullback, dipToColor, expandMorph, fade2 as fade, fbmGradient, filmBurn, flip, glassWipe, gridPixelate, interpolate, interpolateColors, iris, linearGradient, linearTiming, lumaWipe, morph, morphPath, morphPathSequence, motionBlurConfig, noise2D, noise3D, none, parseClip, parseColor, parseGradient, push, radialGradient, random, registerEngineWarmer, registerFont, registeredFonts, renderFrame, renderFrameRangeJSON, renderFrames, renderFramesJSON, renderFramesRange, renderToScene, renderToSceneJSON, runEngineWarmers, slide, spring, springTiming, typeMask, useCurrentFrame, useVideoConfig, variantSeed, whipPan, wipe, zoom, zoomBlur };
|
|
2051
|
+
//# sourceMappingURL=react.js.map
|
|
2052
|
+
//# sourceMappingURL=react.js.map
|