reframe-video 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 +24 -0
- package/README.md +77 -0
- package/assets/fonts/inter-400.woff2 +0 -0
- package/assets/fonts/inter-700.woff2 +0 -0
- package/assets/fonts/inter-800.woff2 +0 -0
- package/assets/sfx/LICENSE.md +12 -0
- package/assets/sfx/click_002.ogg +0 -0
- package/assets/sfx/click_003.ogg +0 -0
- package/assets/sfx/click_004.ogg +0 -0
- package/assets/sfx/confirmation_001.ogg +0 -0
- package/assets/sfx/keypress-001.wav +0 -0
- package/assets/sfx/keypress-004.wav +0 -0
- package/assets/sfx/keypress-007.wav +0 -0
- package/assets/sfx/keypress-010.wav +0 -0
- package/assets/sfx/keypress-014.wav +0 -0
- package/dist/analyze.js +344 -0
- package/dist/bin.js +1677 -0
- package/dist/browserEntry.js +532 -0
- package/dist/cli.js +1205 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +889 -0
- package/dist/renderer-canvas.js +89 -0
- package/dist/types/audio.d.ts +53 -0
- package/dist/types/behaviors.d.ts +7 -0
- package/dist/types/compile.d.ts +38 -0
- package/dist/types/compose.d.ts +64 -0
- package/dist/types/dsl.d.ts +66 -0
- package/dist/types/evaluate.d.ts +59 -0
- package/dist/types/index.d.ts +9 -0
- package/dist/types/interpolate.d.ts +12 -0
- package/dist/types/ir.d.ts +213 -0
- package/dist/types/validate.d.ts +12 -0
- package/guides/edsl-guide.md +202 -0
- package/guides/regen-contract.md +18 -0
- package/package.json +55 -0
- package/preview/index.html +60 -0
- package/preview/src/main.ts +162 -0
- package/preview/src/panel.ts +347 -0
- package/preview/src/store.ts +220 -0
- package/preview/src/virtual.d.ts +4 -0
- package/preview/vite.config.ts +52 -0
|
@@ -0,0 +1,532 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
(() => {
|
|
3
|
+
// ../core/src/ir.ts
|
|
4
|
+
var DEFAULT_TO_DURATION = 0.5;
|
|
5
|
+
var DEFAULT_TWEEN_DURATION = 0.5;
|
|
6
|
+
|
|
7
|
+
// ../core/src/compile.ts
|
|
8
|
+
var key = (target, prop) => `${target}.${prop}`;
|
|
9
|
+
function compileScene(ir) {
|
|
10
|
+
const nodeById = /* @__PURE__ */ new Map();
|
|
11
|
+
const nodeOrder = [];
|
|
12
|
+
const collect = (nodes) => {
|
|
13
|
+
for (const node of nodes) {
|
|
14
|
+
nodeById.set(node.id, node);
|
|
15
|
+
nodeOrder.push(node.id);
|
|
16
|
+
if (node.type === "group") collect(node.children);
|
|
17
|
+
}
|
|
18
|
+
};
|
|
19
|
+
collect(ir.nodes);
|
|
20
|
+
const initialValues = /* @__PURE__ */ new Map();
|
|
21
|
+
for (const [id, node] of nodeById) {
|
|
22
|
+
for (const [prop, value] of Object.entries(node.props)) {
|
|
23
|
+
if (typeof value === "number" || typeof value === "string") {
|
|
24
|
+
initialValues.set(key(id, prop), value);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
if (ir.initial !== void 0) {
|
|
29
|
+
const override = ir.states?.[ir.initial] ?? {};
|
|
30
|
+
for (const [id, props] of Object.entries(override)) {
|
|
31
|
+
for (const [prop, value] of Object.entries(props)) {
|
|
32
|
+
initialValues.set(key(id, prop), value);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
const segments = /* @__PURE__ */ new Map();
|
|
37
|
+
const current = new Map(initialValues);
|
|
38
|
+
const pushSegment = (seg) => {
|
|
39
|
+
const k = key(seg.target, seg.prop);
|
|
40
|
+
let list = segments.get(k);
|
|
41
|
+
if (!list) segments.set(k, list = []);
|
|
42
|
+
list.push(seg);
|
|
43
|
+
current.set(k, seg.to);
|
|
44
|
+
};
|
|
45
|
+
const currentValue = (target, prop) => {
|
|
46
|
+
const v = current.get(key(target, prop));
|
|
47
|
+
if (v !== void 0) return v;
|
|
48
|
+
if (prop === "opacity" || prop === "scale" || prop === "progress") return 1;
|
|
49
|
+
if (prop === "rotation") return 0;
|
|
50
|
+
throw new Error(`cannot animate "${prop}" of "${target}": no base value to start from`);
|
|
51
|
+
};
|
|
52
|
+
const labelTimes = /* @__PURE__ */ new Map();
|
|
53
|
+
const walk = (tl, start) => {
|
|
54
|
+
const end = walkInner(tl, start);
|
|
55
|
+
if ("label" in tl && tl.label !== void 0) labelTimes.set(tl.label, { t0: start, t1: end });
|
|
56
|
+
return end;
|
|
57
|
+
};
|
|
58
|
+
const walkInner = (tl, start) => {
|
|
59
|
+
switch (tl.kind) {
|
|
60
|
+
case "seq": {
|
|
61
|
+
let t = start;
|
|
62
|
+
for (const child of tl.children) t = walk(child, t);
|
|
63
|
+
return t;
|
|
64
|
+
}
|
|
65
|
+
case "par": {
|
|
66
|
+
let end = start;
|
|
67
|
+
for (const child of tl.children) end = Math.max(end, walk(child, start));
|
|
68
|
+
return end;
|
|
69
|
+
}
|
|
70
|
+
case "stagger": {
|
|
71
|
+
let end = start;
|
|
72
|
+
tl.children.forEach((child, i) => {
|
|
73
|
+
end = Math.max(end, walk(child, start + i * tl.interval));
|
|
74
|
+
});
|
|
75
|
+
return end;
|
|
76
|
+
}
|
|
77
|
+
case "wait":
|
|
78
|
+
return start + tl.duration;
|
|
79
|
+
case "tween": {
|
|
80
|
+
const duration = tl.duration ?? DEFAULT_TWEEN_DURATION;
|
|
81
|
+
for (const [prop, toValue] of Object.entries(tl.props)) {
|
|
82
|
+
pushSegment({
|
|
83
|
+
target: tl.target,
|
|
84
|
+
prop,
|
|
85
|
+
t0: start,
|
|
86
|
+
t1: start + duration,
|
|
87
|
+
from: currentValue(tl.target, prop),
|
|
88
|
+
to: toValue,
|
|
89
|
+
...tl.ease !== void 0 && { ease: tl.ease }
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
return start + duration;
|
|
93
|
+
}
|
|
94
|
+
case "to": {
|
|
95
|
+
const override = ir.states?.[tl.state] ?? {};
|
|
96
|
+
const duration = tl.duration ?? DEFAULT_TO_DURATION;
|
|
97
|
+
const staggerInterval = tl.stagger ?? 0;
|
|
98
|
+
const targets = nodeOrder.filter(
|
|
99
|
+
(id) => id in override && (tl.filter === void 0 || tl.filter.includes(id))
|
|
100
|
+
);
|
|
101
|
+
targets.forEach((id, i) => {
|
|
102
|
+
const t0 = start + i * staggerInterval;
|
|
103
|
+
for (const [prop, toValue] of Object.entries(override[id])) {
|
|
104
|
+
pushSegment({
|
|
105
|
+
target: id,
|
|
106
|
+
prop,
|
|
107
|
+
t0,
|
|
108
|
+
t1: t0 + duration,
|
|
109
|
+
from: currentValue(id, prop),
|
|
110
|
+
to: toValue,
|
|
111
|
+
...tl.ease !== void 0 && { ease: tl.ease }
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
});
|
|
115
|
+
const last = Math.max(0, targets.length - 1);
|
|
116
|
+
return start + duration + last * staggerInterval;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
};
|
|
120
|
+
const inferredEnd = ir.timeline ? walk(ir.timeline, 0) : 0;
|
|
121
|
+
for (const list of segments.values()) list.sort((a, b) => a.t0 - b.t0);
|
|
122
|
+
return {
|
|
123
|
+
ir,
|
|
124
|
+
duration: ir.duration ?? inferredEnd,
|
|
125
|
+
segments,
|
|
126
|
+
initialValues,
|
|
127
|
+
nodeById,
|
|
128
|
+
nodeOrder,
|
|
129
|
+
labelTimes
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// ../core/src/validate.ts
|
|
134
|
+
var COMMON_PROPS = ["x", "y", "opacity", "rotation", "scale", "anchor"];
|
|
135
|
+
var PROPS_BY_TYPE = {
|
|
136
|
+
rect: [...COMMON_PROPS, "width", "height", "fill", "stroke", "strokeWidth", "radius"],
|
|
137
|
+
ellipse: [...COMMON_PROPS, "width", "height", "fill", "stroke", "strokeWidth"],
|
|
138
|
+
line: ["x1", "y1", "x2", "y2", "stroke", "strokeWidth", "opacity", "progress"],
|
|
139
|
+
text: [...COMMON_PROPS, "content", "contentDecimals", "fontFamily", "fontSize", "fontWeight", "fill", "letterSpacing"],
|
|
140
|
+
group: COMMON_PROPS
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
// ../core/src/behaviors.ts
|
|
144
|
+
function sampleBehavior(b, t) {
|
|
145
|
+
switch (b.name) {
|
|
146
|
+
case "oscillate": {
|
|
147
|
+
const { amplitude, frequency, phase = 0 } = b.params;
|
|
148
|
+
return amplitude * Math.sin(2 * Math.PI * frequency * t + phase);
|
|
149
|
+
}
|
|
150
|
+
case "wiggle": {
|
|
151
|
+
const { amplitude, frequency, seed } = b.params;
|
|
152
|
+
return amplitude * valueNoise(t * frequency, seed);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
function valueNoise(x, seed) {
|
|
157
|
+
const i = Math.floor(x);
|
|
158
|
+
const f = x - i;
|
|
159
|
+
const u = f * f * (3 - 2 * f);
|
|
160
|
+
const a = hash01(i, seed) * 2 - 1;
|
|
161
|
+
const b = hash01(i + 1, seed) * 2 - 1;
|
|
162
|
+
return a + (b - a) * u;
|
|
163
|
+
}
|
|
164
|
+
function hash01(n, seed) {
|
|
165
|
+
let h = n * 374761393 + seed * 668265263 | 0;
|
|
166
|
+
h = h ^ h >>> 13 | 0;
|
|
167
|
+
h = Math.imul(h, 1274126177);
|
|
168
|
+
h = (h ^ h >>> 16) >>> 0;
|
|
169
|
+
return h / 4294967295;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// ../core/src/interpolate.ts
|
|
173
|
+
var EASE_TABLE = {
|
|
174
|
+
linear: (u) => u,
|
|
175
|
+
easeInQuad: (u) => u * u,
|
|
176
|
+
easeOutQuad: (u) => 1 - (1 - u) * (1 - u),
|
|
177
|
+
easeInOutQuad: (u) => u < 0.5 ? 2 * u * u : 1 - (-2 * u + 2) ** 2 / 2,
|
|
178
|
+
easeInCubic: (u) => u ** 3,
|
|
179
|
+
easeOutCubic: (u) => 1 - (1 - u) ** 3,
|
|
180
|
+
easeInOutCubic: (u) => u < 0.5 ? 4 * u ** 3 : 1 - (-2 * u + 2) ** 3 / 2,
|
|
181
|
+
easeInQuart: (u) => u ** 4,
|
|
182
|
+
easeOutQuart: (u) => 1 - (1 - u) ** 4,
|
|
183
|
+
easeInOutQuart: (u) => u < 0.5 ? 8 * u ** 4 : 1 - (-2 * u + 2) ** 4 / 2,
|
|
184
|
+
easeInExpo: (u) => u === 0 ? 0 : 2 ** (10 * u - 10),
|
|
185
|
+
easeOutExpo: (u) => u === 1 ? 1 : 1 - 2 ** (-10 * u),
|
|
186
|
+
easeInOutExpo: (u) => u === 0 ? 0 : u === 1 ? 1 : u < 0.5 ? 2 ** (20 * u - 10) / 2 : (2 - 2 ** (-20 * u + 10)) / 2
|
|
187
|
+
};
|
|
188
|
+
var EASE_NAMES = Object.keys(EASE_TABLE);
|
|
189
|
+
function resolveEase(ease) {
|
|
190
|
+
if (ease === void 0) return EASE_TABLE.linear;
|
|
191
|
+
if (typeof ease === "string") {
|
|
192
|
+
const fn = EASE_TABLE[ease];
|
|
193
|
+
if (!fn) throw new Error(`unknown ease "${ease}" \u2014 valid: ${Object.keys(EASE_TABLE).join(", ")}`);
|
|
194
|
+
return fn;
|
|
195
|
+
}
|
|
196
|
+
return cubicBezierEase(...ease.cubicBezier);
|
|
197
|
+
}
|
|
198
|
+
function cubicBezierEase(x1, y1, x2, y2) {
|
|
199
|
+
const bez = (a, b) => (t) => 3 * a * t * (1 - t) ** 2 + 3 * b * t * t * (1 - t) + t ** 3;
|
|
200
|
+
const bx = bez(x1, x2);
|
|
201
|
+
const by = bez(y1, y2);
|
|
202
|
+
const dbx = (t) => 3 * x1 * (1 - t) * (1 - 3 * t) + 3 * x2 * t * (2 - 3 * t) + 3 * t * t;
|
|
203
|
+
return (u) => {
|
|
204
|
+
if (u <= 0) return 0;
|
|
205
|
+
if (u >= 1) return 1;
|
|
206
|
+
let t = u;
|
|
207
|
+
for (let i = 0; i < 8; i++) {
|
|
208
|
+
const err = bx(t) - u;
|
|
209
|
+
if (Math.abs(err) < 1e-6) return by(t);
|
|
210
|
+
const d = dbx(t);
|
|
211
|
+
if (Math.abs(d) < 1e-6) break;
|
|
212
|
+
t -= err / d;
|
|
213
|
+
}
|
|
214
|
+
let lo = 0;
|
|
215
|
+
let hi = 1;
|
|
216
|
+
t = u;
|
|
217
|
+
while (hi - lo > 1e-6) {
|
|
218
|
+
if (bx(t) < u) lo = t;
|
|
219
|
+
else hi = t;
|
|
220
|
+
t = (lo + hi) / 2;
|
|
221
|
+
}
|
|
222
|
+
return by(t);
|
|
223
|
+
};
|
|
224
|
+
}
|
|
225
|
+
var HEX_COLOR = /^#(?:[0-9a-fA-F]{3,4}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$/;
|
|
226
|
+
function isColor(v) {
|
|
227
|
+
return typeof v === "string" && HEX_COLOR.test(v);
|
|
228
|
+
}
|
|
229
|
+
function parseColor(hex) {
|
|
230
|
+
let h = hex.slice(1);
|
|
231
|
+
if (h.length <= 4) h = [...h].map((c) => c + c).join("");
|
|
232
|
+
const n = parseInt(h.padEnd(8, "f"), 16);
|
|
233
|
+
return [n >>> 24 & 255, n >>> 16 & 255, n >>> 8 & 255, n & 255];
|
|
234
|
+
}
|
|
235
|
+
function formatColor([r, g, b, a]) {
|
|
236
|
+
const hex = (v) => Math.round(Math.max(0, Math.min(255, v))).toString(16).padStart(2, "0");
|
|
237
|
+
return a >= 255 ? `#${hex(r)}${hex(g)}${hex(b)}` : `#${hex(r)}${hex(g)}${hex(b)}${hex(a)}`;
|
|
238
|
+
}
|
|
239
|
+
function lerpValue(from, to, u) {
|
|
240
|
+
if (typeof from === "number" && typeof to === "number") {
|
|
241
|
+
return from + (to - from) * u;
|
|
242
|
+
}
|
|
243
|
+
if (isColor(from) && isColor(to)) {
|
|
244
|
+
const a = parseColor(from);
|
|
245
|
+
const b = parseColor(to);
|
|
246
|
+
return formatColor([
|
|
247
|
+
a[0] + (b[0] - a[0]) * u,
|
|
248
|
+
a[1] + (b[1] - a[1]) * u,
|
|
249
|
+
a[2] + (b[2] - a[2]) * u,
|
|
250
|
+
a[3] + (b[3] - a[3]) * u
|
|
251
|
+
]);
|
|
252
|
+
}
|
|
253
|
+
return to;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
// ../core/src/evaluate.ts
|
|
257
|
+
var IDENTITY = [1, 0, 0, 1, 0, 0];
|
|
258
|
+
function multiply(m, n) {
|
|
259
|
+
return [
|
|
260
|
+
m[0] * n[0] + m[2] * n[1],
|
|
261
|
+
m[1] * n[0] + m[3] * n[1],
|
|
262
|
+
m[0] * n[2] + m[2] * n[3],
|
|
263
|
+
m[1] * n[2] + m[3] * n[3],
|
|
264
|
+
m[0] * n[4] + m[2] * n[5] + m[4],
|
|
265
|
+
m[1] * n[4] + m[3] * n[5] + m[5]
|
|
266
|
+
];
|
|
267
|
+
}
|
|
268
|
+
function localMatrix(x, y, rotationDeg, scale) {
|
|
269
|
+
const r = rotationDeg * Math.PI / 180;
|
|
270
|
+
const cos = Math.cos(r) * scale;
|
|
271
|
+
const sin = Math.sin(r) * scale;
|
|
272
|
+
return [cos, sin, -sin, cos, x, y];
|
|
273
|
+
}
|
|
274
|
+
var ANCHOR_FACTORS = {
|
|
275
|
+
"top-left": [0, 0],
|
|
276
|
+
"top-center": [0.5, 0],
|
|
277
|
+
"top-right": [1, 0],
|
|
278
|
+
"center-left": [0, 0.5],
|
|
279
|
+
center: [0.5, 0.5],
|
|
280
|
+
"center-right": [1, 0.5],
|
|
281
|
+
"bottom-left": [0, 1],
|
|
282
|
+
"bottom-center": [0.5, 1],
|
|
283
|
+
"bottom-right": [1, 1]
|
|
284
|
+
};
|
|
285
|
+
var TEXT_ALIGN = { 0: "left", 0.5: "center", 1: "right" };
|
|
286
|
+
var TEXT_BASELINE = { 0: "top", 0.5: "middle", 1: "bottom" };
|
|
287
|
+
function behaviorEnvelope(b, t) {
|
|
288
|
+
const from = b.from ?? Number.NEGATIVE_INFINITY;
|
|
289
|
+
const until = b.until ?? Number.POSITIVE_INFINITY;
|
|
290
|
+
if (t < from || t > until) return 0;
|
|
291
|
+
const ramp = b.ramp ?? 0.2;
|
|
292
|
+
let envelope = 1;
|
|
293
|
+
if (Number.isFinite(from) && ramp > 0) envelope = Math.min(envelope, (t - from) / ramp);
|
|
294
|
+
if (Number.isFinite(until) && ramp > 0) envelope = Math.min(envelope, (until - t) / ramp);
|
|
295
|
+
return Math.max(0, Math.min(1, envelope));
|
|
296
|
+
}
|
|
297
|
+
function evaluate(compiled2, t) {
|
|
298
|
+
const ops = [];
|
|
299
|
+
const valueAt = (target, prop, fallback) => {
|
|
300
|
+
let value = compiled2.initialValues.get(`${target}.${prop}`) ?? fallback;
|
|
301
|
+
const segs = compiled2.segments.get(`${target}.${prop}`);
|
|
302
|
+
if (segs) {
|
|
303
|
+
let active;
|
|
304
|
+
for (const seg of segs) {
|
|
305
|
+
if (seg.t0 <= t) active = seg;
|
|
306
|
+
else break;
|
|
307
|
+
}
|
|
308
|
+
if (active) {
|
|
309
|
+
if (t >= active.t1) {
|
|
310
|
+
value = active.to;
|
|
311
|
+
} else {
|
|
312
|
+
const u = resolveEase(active.ease)((t - active.t0) / (active.t1 - active.t0));
|
|
313
|
+
value = lerpValue(active.from, active.to, u);
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
for (const b of compiled2.ir.behaviors ?? []) {
|
|
318
|
+
if (b.target === target && b.prop === prop && typeof value === "number") {
|
|
319
|
+
const envelope = behaviorEnvelope(b, t);
|
|
320
|
+
if (envelope > 0) value = value + envelope * sampleBehavior(b.behavior, t);
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
return value;
|
|
324
|
+
};
|
|
325
|
+
const num = (target, prop, fallback) => {
|
|
326
|
+
const v = valueAt(target, prop, fallback);
|
|
327
|
+
return typeof v === "number" ? v : fallback;
|
|
328
|
+
};
|
|
329
|
+
const str = (target, prop, fallback) => {
|
|
330
|
+
const v = valueAt(target, prop, fallback);
|
|
331
|
+
return typeof v === "string" ? v : String(v);
|
|
332
|
+
};
|
|
333
|
+
const opt = (target, prop, base) => {
|
|
334
|
+
const v = valueAt(target, prop, base ?? "");
|
|
335
|
+
return v === "" && base === void 0 ? void 0 : String(v);
|
|
336
|
+
};
|
|
337
|
+
const walk = (node, parent, parentOpacity) => {
|
|
338
|
+
const id = node.id;
|
|
339
|
+
if (node.type === "line") {
|
|
340
|
+
const opacity2 = parentOpacity * num(id, "opacity", node.props.opacity ?? 1);
|
|
341
|
+
if (opacity2 <= 0) return;
|
|
342
|
+
const progress = Math.max(0, Math.min(1, num(id, "progress", node.props.progress ?? 1)));
|
|
343
|
+
const x1 = num(id, "x1", node.props.x1);
|
|
344
|
+
const y1 = num(id, "y1", node.props.y1);
|
|
345
|
+
ops.push({
|
|
346
|
+
type: "line",
|
|
347
|
+
id,
|
|
348
|
+
transform: parent,
|
|
349
|
+
opacity: opacity2,
|
|
350
|
+
x1,
|
|
351
|
+
y1,
|
|
352
|
+
x2: x1 + (num(id, "x2", node.props.x2) - x1) * progress,
|
|
353
|
+
y2: y1 + (num(id, "y2", node.props.y2) - y1) * progress,
|
|
354
|
+
stroke: str(id, "stroke", node.props.stroke),
|
|
355
|
+
strokeWidth: num(id, "strokeWidth", node.props.strokeWidth ?? 1)
|
|
356
|
+
});
|
|
357
|
+
return;
|
|
358
|
+
}
|
|
359
|
+
const opacity = parentOpacity * num(id, "opacity", node.props.opacity ?? 1);
|
|
360
|
+
if (opacity <= 0) return;
|
|
361
|
+
const matrix = multiply(
|
|
362
|
+
parent,
|
|
363
|
+
localMatrix(
|
|
364
|
+
num(id, "x", node.props.x),
|
|
365
|
+
num(id, "y", node.props.y),
|
|
366
|
+
num(id, "rotation", node.props.rotation ?? 0),
|
|
367
|
+
num(id, "scale", node.props.scale ?? 1)
|
|
368
|
+
)
|
|
369
|
+
);
|
|
370
|
+
switch (node.type) {
|
|
371
|
+
case "group":
|
|
372
|
+
for (const child of node.children) walk(child, matrix, opacity);
|
|
373
|
+
return;
|
|
374
|
+
case "rect":
|
|
375
|
+
case "ellipse": {
|
|
376
|
+
const width = num(id, "width", node.props.width);
|
|
377
|
+
const height = num(id, "height", node.props.height);
|
|
378
|
+
const [ax, ay] = ANCHOR_FACTORS[node.props.anchor ?? "top-left"];
|
|
379
|
+
const strokeWidth = num(id, "strokeWidth", node.props.strokeWidth ?? 1);
|
|
380
|
+
const fill = opt(id, "fill", node.props.fill);
|
|
381
|
+
const stroke = opt(id, "stroke", node.props.stroke);
|
|
382
|
+
ops.push({
|
|
383
|
+
type: node.type,
|
|
384
|
+
id,
|
|
385
|
+
transform: matrix,
|
|
386
|
+
opacity,
|
|
387
|
+
width,
|
|
388
|
+
height,
|
|
389
|
+
offsetX: -width * ax,
|
|
390
|
+
offsetY: -height * ay,
|
|
391
|
+
...fill !== void 0 && { fill },
|
|
392
|
+
...stroke !== void 0 && { stroke, strokeWidth },
|
|
393
|
+
...node.type === "rect" && { radius: num(id, "radius", node.props.radius ?? 0) }
|
|
394
|
+
});
|
|
395
|
+
return;
|
|
396
|
+
}
|
|
397
|
+
case "text": {
|
|
398
|
+
const [ax, ay] = ANCHOR_FACTORS[node.props.anchor ?? "top-left"];
|
|
399
|
+
const raw = valueAt(id, "content", node.props.content);
|
|
400
|
+
const decimals = Math.max(
|
|
401
|
+
0,
|
|
402
|
+
Math.round(num(id, "contentDecimals", node.props.contentDecimals ?? 0))
|
|
403
|
+
);
|
|
404
|
+
ops.push({
|
|
405
|
+
type: "text",
|
|
406
|
+
id,
|
|
407
|
+
transform: matrix,
|
|
408
|
+
opacity,
|
|
409
|
+
content: typeof raw === "number" ? raw.toFixed(decimals) : raw,
|
|
410
|
+
fontFamily: str(id, "fontFamily", node.props.fontFamily),
|
|
411
|
+
fontSize: num(id, "fontSize", node.props.fontSize),
|
|
412
|
+
fontWeight: num(id, "fontWeight", node.props.fontWeight ?? 400),
|
|
413
|
+
fill: str(id, "fill", node.props.fill ?? "#ffffff"),
|
|
414
|
+
letterSpacing: num(id, "letterSpacing", node.props.letterSpacing ?? 0),
|
|
415
|
+
align: TEXT_ALIGN[ax] ?? "left",
|
|
416
|
+
baseline: TEXT_BASELINE[ay] ?? "top"
|
|
417
|
+
});
|
|
418
|
+
return;
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
};
|
|
422
|
+
for (const node of compiled2.ir.nodes) walk(node, IDENTITY, 1);
|
|
423
|
+
return ops;
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
// ../renderer-canvas/src/index.ts
|
|
427
|
+
function renderFrame(ctx2, compiled2, t) {
|
|
428
|
+
const { size, background } = compiled2.ir;
|
|
429
|
+
ctx2.setTransform(1, 0, 0, 1, 0, 0);
|
|
430
|
+
ctx2.clearRect(0, 0, size.width, size.height);
|
|
431
|
+
if (background) {
|
|
432
|
+
ctx2.fillStyle = background;
|
|
433
|
+
ctx2.fillRect(0, 0, size.width, size.height);
|
|
434
|
+
}
|
|
435
|
+
drawDisplayList(ctx2, evaluate(compiled2, t));
|
|
436
|
+
}
|
|
437
|
+
function drawDisplayList(ctx2, ops) {
|
|
438
|
+
for (const op of ops) {
|
|
439
|
+
ctx2.save();
|
|
440
|
+
ctx2.setTransform(...op.transform);
|
|
441
|
+
ctx2.globalAlpha = Math.max(0, Math.min(1, op.opacity));
|
|
442
|
+
switch (op.type) {
|
|
443
|
+
case "rect": {
|
|
444
|
+
ctx2.beginPath();
|
|
445
|
+
if (op.radius && op.radius > 0) {
|
|
446
|
+
ctx2.roundRect(op.offsetX, op.offsetY, op.width, op.height, op.radius);
|
|
447
|
+
} else {
|
|
448
|
+
ctx2.rect(op.offsetX, op.offsetY, op.width, op.height);
|
|
449
|
+
}
|
|
450
|
+
if (op.fill) {
|
|
451
|
+
ctx2.fillStyle = op.fill;
|
|
452
|
+
ctx2.fill();
|
|
453
|
+
}
|
|
454
|
+
if (op.stroke) {
|
|
455
|
+
ctx2.strokeStyle = op.stroke;
|
|
456
|
+
ctx2.lineWidth = op.strokeWidth ?? 1;
|
|
457
|
+
ctx2.stroke();
|
|
458
|
+
}
|
|
459
|
+
break;
|
|
460
|
+
}
|
|
461
|
+
case "ellipse": {
|
|
462
|
+
ctx2.beginPath();
|
|
463
|
+
ctx2.ellipse(
|
|
464
|
+
op.offsetX + op.width / 2,
|
|
465
|
+
op.offsetY + op.height / 2,
|
|
466
|
+
Math.abs(op.width / 2),
|
|
467
|
+
Math.abs(op.height / 2),
|
|
468
|
+
0,
|
|
469
|
+
0,
|
|
470
|
+
Math.PI * 2
|
|
471
|
+
);
|
|
472
|
+
if (op.fill) {
|
|
473
|
+
ctx2.fillStyle = op.fill;
|
|
474
|
+
ctx2.fill();
|
|
475
|
+
}
|
|
476
|
+
if (op.stroke) {
|
|
477
|
+
ctx2.strokeStyle = op.stroke;
|
|
478
|
+
ctx2.lineWidth = op.strokeWidth ?? 1;
|
|
479
|
+
ctx2.stroke();
|
|
480
|
+
}
|
|
481
|
+
break;
|
|
482
|
+
}
|
|
483
|
+
case "line": {
|
|
484
|
+
ctx2.beginPath();
|
|
485
|
+
ctx2.moveTo(op.x1, op.y1);
|
|
486
|
+
ctx2.lineTo(op.x2, op.y2);
|
|
487
|
+
ctx2.strokeStyle = op.stroke;
|
|
488
|
+
ctx2.lineWidth = op.strokeWidth;
|
|
489
|
+
ctx2.lineCap = "round";
|
|
490
|
+
ctx2.stroke();
|
|
491
|
+
break;
|
|
492
|
+
}
|
|
493
|
+
case "text": {
|
|
494
|
+
ctx2.font = `${op.fontWeight} ${op.fontSize}px ${quoteFamily(op.fontFamily)}`;
|
|
495
|
+
if (op.letterSpacing) ctx2.letterSpacing = `${op.letterSpacing}px`;
|
|
496
|
+
ctx2.textAlign = op.align;
|
|
497
|
+
ctx2.textBaseline = op.baseline;
|
|
498
|
+
ctx2.fillStyle = op.fill;
|
|
499
|
+
ctx2.fillText(op.content, 0, 0);
|
|
500
|
+
if (op.letterSpacing) ctx2.letterSpacing = "0px";
|
|
501
|
+
break;
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
ctx2.restore();
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
function quoteFamily(family) {
|
|
508
|
+
return family.includes(" ") && !family.includes('"') ? `"${family}"` : family;
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
// ../render-cli/src/browserEntry.ts
|
|
512
|
+
var compiled = null;
|
|
513
|
+
var ctx = null;
|
|
514
|
+
var canvas = null;
|
|
515
|
+
window.__reframe = {
|
|
516
|
+
init(ir) {
|
|
517
|
+
compiled = compileScene(ir);
|
|
518
|
+
canvas = document.createElement("canvas");
|
|
519
|
+
canvas.width = ir.size.width;
|
|
520
|
+
canvas.height = ir.size.height;
|
|
521
|
+
document.body.appendChild(canvas);
|
|
522
|
+
ctx = canvas.getContext("2d", { willReadFrequently: true });
|
|
523
|
+
if (!ctx) throw new Error("could not create 2d context");
|
|
524
|
+
return { duration: compiled.duration, fps: ir.fps ?? 30 };
|
|
525
|
+
},
|
|
526
|
+
renderFrame(t) {
|
|
527
|
+
if (!compiled || !ctx || !canvas) throw new Error("init() not called");
|
|
528
|
+
renderFrame(ctx, compiled, t);
|
|
529
|
+
return canvas.toDataURL("image/png");
|
|
530
|
+
}
|
|
531
|
+
};
|
|
532
|
+
})();
|