reframe-video 0.1.2 → 0.2.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/assets/sfx/LICENSE.md +1 -1
- package/assets/sfx/bong_001.ogg +0 -0
- package/assets/sfx/click_001.ogg +0 -0
- package/assets/sfx/confirmation_002.ogg +0 -0
- package/assets/sfx/confirmation_003.ogg +0 -0
- package/assets/sfx/confirmation_004.ogg +0 -0
- package/assets/sfx/glass_001.ogg +0 -0
- package/assets/sfx/maximize_001.ogg +0 -0
- package/assets/sfx/maximize_002.ogg +0 -0
- package/assets/sfx/maximize_005.ogg +0 -0
- package/assets/sfx/maximize_009.ogg +0 -0
- package/assets/sfx/open_001.ogg +0 -0
- package/assets/sfx/pluck_001.ogg +0 -0
- package/assets/sfx/pluck_002.ogg +0 -0
- package/assets/sfx/select_001.ogg +0 -0
- package/assets/sfx/select_002.ogg +0 -0
- package/assets/sfx/select_003.ogg +0 -0
- package/dist/bin.js +724 -131
- package/dist/browserEntry.js +130 -68
- package/dist/cli.js +445 -85
- package/dist/index.js +674 -86
- package/dist/labels.js +606 -0
- package/dist/renderer-canvas.js +15 -0
- package/dist/trace-cli.js +9 -9
- package/dist/types/audio.d.ts +9 -0
- package/dist/types/compile.d.ts +1 -0
- package/dist/types/compose.d.ts +18 -2
- package/dist/types/composeComposition.d.ts +27 -0
- package/dist/types/devicePreset.d.ts +65 -0
- package/dist/types/dsl.d.ts +12 -1
- package/dist/types/evaluate.d.ts +32 -0
- package/dist/types/index.d.ts +6 -3
- package/dist/types/ir.d.ts +68 -0
- package/dist/types/motionOps.d.ts +36 -0
- package/dist/types/path.d.ts +7 -3
- package/dist/types/validate.d.ts +4 -1
- package/guides/edsl-guide.md +2 -1
- package/package.json +1 -1
- package/preview/index.html +56 -3
- package/preview/src/main.ts +1132 -46
- package/preview/src/panel.ts +478 -8
- package/preview/src/store.ts +323 -6
package/dist/index.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
// ../core/src/ir.ts
|
|
2
|
+
var DEFAULT_CROSSFADE = 0.5;
|
|
2
3
|
var DEFAULT_TO_DURATION = 0.5;
|
|
3
4
|
var DEFAULT_TWEEN_DURATION = 0.5;
|
|
4
5
|
var DEFAULT_MOTIONPATH_DURATION = 1;
|
|
@@ -26,7 +27,7 @@ function segCountOf(points, closed) {
|
|
|
26
27
|
if (n < 2) return 0;
|
|
27
28
|
return closed ? n : n - 1;
|
|
28
29
|
}
|
|
29
|
-
function pathPoint(points, closed, u) {
|
|
30
|
+
function pathPoint(points, closed, u, curviness = 1) {
|
|
30
31
|
const n = points.length;
|
|
31
32
|
if (n === 0) return [0, 0];
|
|
32
33
|
if (n === 1) return [points[0][0], points[0][1]];
|
|
@@ -35,19 +36,41 @@ function pathPoint(points, closed, u) {
|
|
|
35
36
|
const [p0, p1, p2, p3] = controls(points, closed, i);
|
|
36
37
|
const t2 = t * t;
|
|
37
38
|
const t3 = t2 * t;
|
|
38
|
-
|
|
39
|
-
|
|
39
|
+
if (curviness === 1) {
|
|
40
|
+
const f = (a, b, c, d) => 0.5 * (2 * b + (-a + c) * t + (2 * a - 5 * b + 4 * c - d) * t2 + (-a + 3 * b - 3 * c + d) * t3);
|
|
41
|
+
return [f(p0[0], p1[0], p2[0], p3[0]), f(p0[1], p1[1], p2[1], p3[1])];
|
|
42
|
+
}
|
|
43
|
+
const h00 = 2 * t3 - 3 * t2 + 1;
|
|
44
|
+
const h10 = t3 - 2 * t2 + t;
|
|
45
|
+
const h01 = -2 * t3 + 3 * t2;
|
|
46
|
+
const h11 = t3 - t2;
|
|
47
|
+
const k = curviness * 0.5;
|
|
48
|
+
const H = (a, b, c, d) => h00 * b + h10 * k * (c - a) + h01 * c + h11 * k * (d - b);
|
|
49
|
+
return [H(p0[0], p1[0], p2[0], p3[0]), H(p0[1], p1[1], p2[1], p3[1])];
|
|
40
50
|
}
|
|
41
|
-
function pathTangentAngle(points, closed, u) {
|
|
51
|
+
function pathTangentAngle(points, closed, u, curviness = 1) {
|
|
42
52
|
const n = points.length;
|
|
43
53
|
if (n < 2) return 0;
|
|
44
54
|
const segs = segCountOf(points, closed);
|
|
45
55
|
const { i, t } = locate(segs, u);
|
|
46
56
|
const [p0, p1, p2, p3] = controls(points, closed, i);
|
|
47
57
|
const t2 = t * t;
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
58
|
+
let dx;
|
|
59
|
+
let dy;
|
|
60
|
+
if (curviness === 1) {
|
|
61
|
+
const d = (a, b, c, e) => 0.5 * (-a + c + 2 * (2 * a - 5 * b + 4 * c - e) * t + 3 * (-a + 3 * b - 3 * c + e) * t2);
|
|
62
|
+
dx = d(p0[0], p1[0], p2[0], p3[0]);
|
|
63
|
+
dy = d(p0[1], p1[1], p2[1], p3[1]);
|
|
64
|
+
} else {
|
|
65
|
+
const g00 = 6 * t2 - 6 * t;
|
|
66
|
+
const g10 = 3 * t2 - 4 * t + 1;
|
|
67
|
+
const g01 = -6 * t2 + 6 * t;
|
|
68
|
+
const g11 = 3 * t2 - 2 * t;
|
|
69
|
+
const k = curviness * 0.5;
|
|
70
|
+
const D = (a, b, c, e) => g00 * b + g10 * k * (c - a) + g01 * c + g11 * k * (e - b);
|
|
71
|
+
dx = D(p0[0], p1[0], p2[0], p3[0]);
|
|
72
|
+
dy = D(p0[1], p1[1], p2[1], p3[1]);
|
|
73
|
+
}
|
|
51
74
|
if (dx === 0 && dy === 0) return 0;
|
|
52
75
|
return Math.atan2(dy, dx) * 180 / Math.PI;
|
|
53
76
|
}
|
|
@@ -124,8 +147,8 @@ function compileScene(ir) {
|
|
|
124
147
|
const currentValue = (target, prop) => {
|
|
125
148
|
const v = current.get(key(target, prop));
|
|
126
149
|
if (v !== void 0) return v;
|
|
127
|
-
if (prop === "opacity" || prop === "scale" || prop === "progress") return 1;
|
|
128
|
-
if (prop === "rotation") return 0;
|
|
150
|
+
if (prop === "opacity" || prop === "scale" || prop === "progress" || prop === "scaleX" || prop === "scaleY") return 1;
|
|
151
|
+
if (prop === "rotation" || prop === "skewX" || prop === "skewY") return 0;
|
|
129
152
|
throw new Error(`cannot animate "${prop}" of "${target}": no base value to start from`);
|
|
130
153
|
};
|
|
131
154
|
const labelTimes = /* @__PURE__ */ new Map();
|
|
@@ -228,16 +251,17 @@ function compileScene(ir) {
|
|
|
228
251
|
const duration = tl.duration ?? DEFAULT_MOTIONPATH_DURATION;
|
|
229
252
|
const points = tl.points;
|
|
230
253
|
const closed = tl.closed ?? false;
|
|
254
|
+
const curviness = tl.curviness ?? 1;
|
|
231
255
|
const autoRotate = tl.autoRotate ?? false;
|
|
232
256
|
const rotateOffset = tl.rotateOffset ?? 0;
|
|
233
257
|
let list = motionPaths.get(tl.target);
|
|
234
258
|
if (!list) motionPaths.set(tl.target, list = []);
|
|
235
|
-
list.push({ t0: start, t1: start + duration, points, closed, autoRotate, rotateOffset, ...tl.ease !== void 0 && { ease: tl.ease } });
|
|
259
|
+
list.push({ t0: start, t1: start + duration, points, closed, curviness, autoRotate, rotateOffset, ...tl.ease !== void 0 && { ease: tl.ease } });
|
|
236
260
|
if (points.length > 0) {
|
|
237
|
-
const [ex, ey] = pathPoint(points, closed, 1);
|
|
261
|
+
const [ex, ey] = pathPoint(points, closed, 1, curviness);
|
|
238
262
|
current.set(key(tl.target, "x"), ex);
|
|
239
263
|
current.set(key(tl.target, "y"), ey);
|
|
240
|
-
if (autoRotate) current.set(key(tl.target, "rotation"), pathTangentAngle(points, closed, 1) + rotateOffset);
|
|
264
|
+
if (autoRotate) current.set(key(tl.target, "rotation"), pathTangentAngle(points, closed, 1, curviness) + rotateOffset);
|
|
241
265
|
}
|
|
242
266
|
return start + duration;
|
|
243
267
|
}
|
|
@@ -284,7 +308,7 @@ function compileScene(ir) {
|
|
|
284
308
|
}
|
|
285
309
|
|
|
286
310
|
// ../core/src/validate.ts
|
|
287
|
-
var COMMON_PROPS = ["x", "y", "opacity", "rotation", "scale", "anchor"];
|
|
311
|
+
var COMMON_PROPS = ["x", "y", "opacity", "rotation", "scale", "scaleX", "scaleY", "skewX", "skewY", "anchor"];
|
|
288
312
|
var PROPS_BY_TYPE = {
|
|
289
313
|
rect: [...COMMON_PROPS, "width", "height", "fill", "stroke", "strokeWidth", "radius"],
|
|
290
314
|
ellipse: [...COMMON_PROPS, "width", "height", "fill", "stroke", "strokeWidth"],
|
|
@@ -312,7 +336,18 @@ function validateScene(ir) {
|
|
|
312
336
|
problems.push(`duplicate node id "${node.id}" \u2014 every node id must be unique`);
|
|
313
337
|
}
|
|
314
338
|
nodeById.set(node.id, node);
|
|
315
|
-
if (node.type === "group")
|
|
339
|
+
if (node.type === "group") {
|
|
340
|
+
const clip = node.props.clip;
|
|
341
|
+
if (clip) {
|
|
342
|
+
if (clip.kind !== "rect" && clip.kind !== "ellipse") {
|
|
343
|
+
problems.push(`group "${node.id}" clip: unknown kind "${clip.kind}" \u2014 use "rect" or "ellipse"`);
|
|
344
|
+
}
|
|
345
|
+
if (!(clip.width > 0) || !(clip.height > 0)) {
|
|
346
|
+
problems.push(`group "${node.id}" clip: width and height must be > 0`);
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
collect(node.children);
|
|
350
|
+
}
|
|
316
351
|
}
|
|
317
352
|
};
|
|
318
353
|
collect(ir.nodes);
|
|
@@ -395,6 +430,9 @@ function validateScene(ir) {
|
|
|
395
430
|
if (tl.duration !== void 0 && tl.duration <= 0) {
|
|
396
431
|
problems.push(`${path2}: motionPath "${tl.target}" duration must be > 0`);
|
|
397
432
|
}
|
|
433
|
+
if (tl.curviness !== void 0 && tl.curviness < 0) {
|
|
434
|
+
problems.push(`${path2}: motionPath "${tl.target}" curviness must be >= 0`);
|
|
435
|
+
}
|
|
398
436
|
break;
|
|
399
437
|
}
|
|
400
438
|
case "wait":
|
|
@@ -413,6 +451,13 @@ function validateScene(ir) {
|
|
|
413
451
|
if (tl.scale !== void 0 && tl.scale <= 0) {
|
|
414
452
|
problems.push(`${path2}: beat "${tl.name}" scale must be > 0`);
|
|
415
453
|
}
|
|
454
|
+
for (const id of tl.nodes ?? []) {
|
|
455
|
+
if (!nodeById.has(id)) {
|
|
456
|
+
problems.push(
|
|
457
|
+
`${path2}: beat "${tl.name}" owns unknown node "${id}" \u2014 known ids: ${[...nodeById.keys()].join(", ")}`
|
|
458
|
+
);
|
|
459
|
+
}
|
|
460
|
+
}
|
|
416
461
|
tl.children.forEach((c, i) => checkTimeline(c, `${path2}.beat(${tl.name})[${i}]`));
|
|
417
462
|
break;
|
|
418
463
|
}
|
|
@@ -453,6 +498,36 @@ function validateScene(ir) {
|
|
|
453
498
|
}
|
|
454
499
|
if (problems.length > 0) throw new SceneValidationError(problems);
|
|
455
500
|
}
|
|
501
|
+
var TRANSITIONS = ["cut", "crossfade"];
|
|
502
|
+
function validateComposition(comp) {
|
|
503
|
+
const problems = [];
|
|
504
|
+
if (comp.scenes.length === 0) problems.push("composition has no scenes");
|
|
505
|
+
const seen = /* @__PURE__ */ new Set();
|
|
506
|
+
for (const [i, entry] of comp.scenes.entries()) {
|
|
507
|
+
const where = `scenes[${i}]`;
|
|
508
|
+
try {
|
|
509
|
+
validateScene(entry.scene);
|
|
510
|
+
} catch (err) {
|
|
511
|
+
if (err instanceof SceneValidationError) {
|
|
512
|
+
for (const p of err.problems) problems.push(`${where} (scene "${entry.scene.id}"): ${p}`);
|
|
513
|
+
} else throw err;
|
|
514
|
+
}
|
|
515
|
+
if (seen.has(entry.scene.id)) {
|
|
516
|
+
problems.push(`${where}: duplicate scene id "${entry.scene.id}" \u2014 scene ids must be unique in a composition`);
|
|
517
|
+
}
|
|
518
|
+
seen.add(entry.scene.id);
|
|
519
|
+
if (entry.transition !== void 0 && !TRANSITIONS.includes(entry.transition)) {
|
|
520
|
+
problems.push(`${where}: unknown transition "${entry.transition}" \u2014 valid: ${TRANSITIONS.join(", ")}`);
|
|
521
|
+
}
|
|
522
|
+
if (typeof entry.at === "string" && Number.isNaN(Number(entry.at))) {
|
|
523
|
+
problems.push(`${where}: "at" string "${entry.at}" is not a number (use "-0.5"/"+0.5" or a number)`);
|
|
524
|
+
}
|
|
525
|
+
if (typeof entry.at === "number" && entry.at < 0) {
|
|
526
|
+
problems.push(`${where}: absolute "at" must be >= 0`);
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
if (problems.length > 0) throw new SceneValidationError(problems);
|
|
530
|
+
}
|
|
456
531
|
|
|
457
532
|
// ../core/src/dsl.ts
|
|
458
533
|
function scene(input) {
|
|
@@ -463,6 +538,11 @@ function scene(input) {
|
|
|
463
538
|
}
|
|
464
539
|
return ir;
|
|
465
540
|
}
|
|
541
|
+
function composition(input) {
|
|
542
|
+
const ir = { version: 1, ...input };
|
|
543
|
+
validateComposition(ir);
|
|
544
|
+
return ir;
|
|
545
|
+
}
|
|
466
546
|
function rect(props) {
|
|
467
547
|
const { id, ...rest } = props;
|
|
468
548
|
return { type: "rect", id, props: rest };
|
|
@@ -522,11 +602,47 @@ function wiggle(target, prop, params, window = {}) {
|
|
|
522
602
|
return { target, prop, ...window, behavior: { kind: "named", name: "wiggle", params } };
|
|
523
603
|
}
|
|
524
604
|
|
|
605
|
+
// ../core/src/composeComposition.ts
|
|
606
|
+
function compileComposition(comp) {
|
|
607
|
+
const scenes = [];
|
|
608
|
+
let prevEnd = 0;
|
|
609
|
+
comp.scenes.forEach((entry, i) => {
|
|
610
|
+
const compiled = compileScene(entry.scene);
|
|
611
|
+
const duration2 = compiled.duration;
|
|
612
|
+
const transition = entry.transition ?? "cut";
|
|
613
|
+
const append = i === 0 ? 0 : prevEnd;
|
|
614
|
+
let start;
|
|
615
|
+
if (typeof entry.at === "number") {
|
|
616
|
+
start = entry.at;
|
|
617
|
+
} else if (typeof entry.at === "string") {
|
|
618
|
+
start = append + Number(entry.at);
|
|
619
|
+
} else if (transition === "crossfade" && i > 0) {
|
|
620
|
+
start = append - DEFAULT_CROSSFADE;
|
|
621
|
+
} else {
|
|
622
|
+
start = append;
|
|
623
|
+
}
|
|
624
|
+
start = Math.max(0, start);
|
|
625
|
+
const overlap = i > 0 ? Math.max(0, prevEnd - start) : 0;
|
|
626
|
+
scenes.push({ id: entry.scene.id, scene: entry.scene, compiled, start, duration: duration2, transition, overlap });
|
|
627
|
+
prevEnd = start + duration2;
|
|
628
|
+
});
|
|
629
|
+
const duration = scenes.reduce((max, s) => Math.max(max, s.start + s.duration), 0);
|
|
630
|
+
return { ir: comp, scenes, duration };
|
|
631
|
+
}
|
|
632
|
+
|
|
525
633
|
// ../core/src/compose.ts
|
|
526
634
|
var SCENE_PATCHABLE = ["background", "duration", "fps"];
|
|
527
635
|
function composeScene(base, ...overlays) {
|
|
528
636
|
const ir = structuredClone(base);
|
|
529
637
|
const report = { applied: [], orphans: [], warnings: [] };
|
|
638
|
+
const baseNodeIds = /* @__PURE__ */ new Set();
|
|
639
|
+
const collectBase = (nodes) => {
|
|
640
|
+
for (const node of nodes) {
|
|
641
|
+
baseNodeIds.add(node.id);
|
|
642
|
+
if (node.type === "group") collectBase(node.children);
|
|
643
|
+
}
|
|
644
|
+
};
|
|
645
|
+
collectBase(base.nodes);
|
|
530
646
|
overlays.forEach((overlay, index) => {
|
|
531
647
|
const layer = overlay.name ?? `overlay-${index}`;
|
|
532
648
|
if (overlay.target !== void 0 && overlay.target !== ir.id) {
|
|
@@ -534,12 +650,12 @@ function composeScene(base, ...overlays) {
|
|
|
534
650
|
`${layer}: authored against scene "${overlay.target}" but composing onto "${ir.id}"`
|
|
535
651
|
);
|
|
536
652
|
}
|
|
537
|
-
applyOverlay(ir, overlay, layer, report);
|
|
653
|
+
applyOverlay(ir, overlay, layer, report, baseNodeIds);
|
|
538
654
|
});
|
|
539
655
|
validateScene(ir);
|
|
540
656
|
return { ir, report };
|
|
541
657
|
}
|
|
542
|
-
function applyOverlay(ir, overlay, layer, report) {
|
|
658
|
+
function applyOverlay(ir, overlay, layer, report, baseNodeIds) {
|
|
543
659
|
const nodeById = /* @__PURE__ */ new Map();
|
|
544
660
|
const collect = (nodes) => {
|
|
545
661
|
for (const node of nodes) {
|
|
@@ -654,7 +770,7 @@ function applyOverlay(ir, overlay, layer, report) {
|
|
|
654
770
|
to: ["duration", "ease", "stagger"],
|
|
655
771
|
tween: ["duration", "ease"],
|
|
656
772
|
wait: ["duration"],
|
|
657
|
-
motionPath: ["points", "duration", "ease"],
|
|
773
|
+
motionPath: ["points", "duration", "ease", "curviness", "autoRotate"],
|
|
658
774
|
beat: ["at", "gap", "scale", "duration", "order"]
|
|
659
775
|
};
|
|
660
776
|
let timingPatched = false;
|
|
@@ -692,6 +808,49 @@ function applyOverlay(ir, overlay, layer, report) {
|
|
|
692
808
|
nodeById.set(node.id, node);
|
|
693
809
|
applied(`addNodes.${node.id}`, "add-node");
|
|
694
810
|
}
|
|
811
|
+
for (const id of overlay.removeNodes ?? []) {
|
|
812
|
+
if (baseNodeIds.has(id)) {
|
|
813
|
+
orphan(
|
|
814
|
+
`removeNodes.${id}`,
|
|
815
|
+
`"${id}" is a base scene node \u2014 the scene owns it; hide it with opacity: 0 instead of removing`
|
|
816
|
+
);
|
|
817
|
+
continue;
|
|
818
|
+
}
|
|
819
|
+
const index = ir.nodes.findIndex((n) => n.id === id);
|
|
820
|
+
if (index < 0) {
|
|
821
|
+
orphan(
|
|
822
|
+
`removeNodes.${id}`,
|
|
823
|
+
`unknown overlay-added node "${id}" \u2014 nothing to remove`
|
|
824
|
+
);
|
|
825
|
+
continue;
|
|
826
|
+
}
|
|
827
|
+
ir.nodes.splice(index, 1);
|
|
828
|
+
nodeById.delete(id);
|
|
829
|
+
applied(`removeNodes.${id}`, "remove-node");
|
|
830
|
+
}
|
|
831
|
+
if (overlay.addTimeline && overlay.addTimeline.length > 0) {
|
|
832
|
+
const collectTargets = (tl, out) => {
|
|
833
|
+
if (tl.kind === "tween" || tl.kind === "motionPath") out.add(tl.target);
|
|
834
|
+
if ("children" in tl) tl.children.forEach((c) => collectTargets(c, out));
|
|
835
|
+
};
|
|
836
|
+
const valid = [];
|
|
837
|
+
overlay.addTimeline.forEach((frag, i) => {
|
|
838
|
+
const targets = /* @__PURE__ */ new Set();
|
|
839
|
+
collectTargets(frag, targets);
|
|
840
|
+
const missing = [...targets].filter((id) => !nodeById.has(id));
|
|
841
|
+
if (missing.length > 0) {
|
|
842
|
+
orphan(`addTimeline[${i}]`, `targets unknown node(s) ${missing.join(", ")} \u2014 known ids: ${knownIds()}`);
|
|
843
|
+
return;
|
|
844
|
+
}
|
|
845
|
+
valid.push(structuredClone(frag));
|
|
846
|
+
applied(`addTimeline[${i}]`, "add-timeline");
|
|
847
|
+
});
|
|
848
|
+
if (valid.length > 0) {
|
|
849
|
+
ir.timeline = ir.timeline ? { kind: "par", children: [ir.timeline, ...valid] } : valid.length === 1 ? valid[0] : { kind: "par", children: valid };
|
|
850
|
+
delete ir.duration;
|
|
851
|
+
ir.duration = compileScene(ir).duration;
|
|
852
|
+
}
|
|
853
|
+
}
|
|
695
854
|
}
|
|
696
855
|
function formatComposeReport(report) {
|
|
697
856
|
const lines = [];
|
|
@@ -884,6 +1043,310 @@ function motionPreset(name, opts) {
|
|
|
884
1043
|
}
|
|
885
1044
|
}
|
|
886
1045
|
|
|
1046
|
+
// ../core/src/devicePreset.ts
|
|
1047
|
+
var DEVICE_PRESET_NAMES = ["phone", "tablet", "laptop", "browser", "watch", "monitor", "tv", "foldable", "terminal", "car"];
|
|
1048
|
+
var DARK = { body: "#15161C", bodyStroke: "#2A2D38", screen: "#0E0F15", detail: "#3A3D48", chrome: "#1B1D24", chromeText: "#9AA0AD" };
|
|
1049
|
+
var LIGHT = { body: "#E7E9EE", bodyStroke: "#C3C7D1", screen: "#FFFFFF", detail: "#AEB3C0", chrome: "#F2F3F6", chromeText: "#5B606C" };
|
|
1050
|
+
var SCREENS = {
|
|
1051
|
+
phone: { width: 352, height: 736, radius: 38 },
|
|
1052
|
+
tablet: { width: 544, height: 764, radius: 18 },
|
|
1053
|
+
laptop: { width: 840, height: 520, radius: 8, cy: -150 },
|
|
1054
|
+
browser: { width: 984, height: 568, radius: 6, cy: 24 },
|
|
1055
|
+
watch: { width: 184, height: 224, radius: 44 },
|
|
1056
|
+
monitor: { width: 1056, height: 600, radius: 6 },
|
|
1057
|
+
tv: { width: 1280, height: 720, radius: 8, cy: -24 },
|
|
1058
|
+
foldable: { width: 760, height: 560, radius: 20 },
|
|
1059
|
+
terminal: { width: 900, height: 560, radius: 6, cy: 18 },
|
|
1060
|
+
car: { width: 1e3, height: 520, radius: 24 }
|
|
1061
|
+
};
|
|
1062
|
+
var BOUNDS = {
|
|
1063
|
+
phone: { width: 392, height: 812 },
|
|
1064
|
+
tablet: { width: 600, height: 820 },
|
|
1065
|
+
laptop: { width: 1100, height: 650 },
|
|
1066
|
+
browser: { width: 1e3, height: 660 },
|
|
1067
|
+
watch: { width: 220, height: 300 },
|
|
1068
|
+
monitor: { width: 1120, height: 860 },
|
|
1069
|
+
tv: { width: 1340, height: 920 },
|
|
1070
|
+
foldable: { width: 800, height: 600 },
|
|
1071
|
+
terminal: { width: 916, height: 636 },
|
|
1072
|
+
car: { width: 1060, height: 600 }
|
|
1073
|
+
};
|
|
1074
|
+
var isLandscape = (name, o) => (name === "phone" || name === "tablet") && o.orientation === "landscape";
|
|
1075
|
+
function screenDims(name, o) {
|
|
1076
|
+
const d = SCREENS[name];
|
|
1077
|
+
const base = { cx: d.cx ?? 0, cy: d.cy ?? 0 };
|
|
1078
|
+
return isLandscape(name, o) ? { width: d.height, height: d.width, radius: d.radius, ...base } : { width: d.width, height: d.height, radius: d.radius, ...base };
|
|
1079
|
+
}
|
|
1080
|
+
function deviceScreen(name, opts = {}) {
|
|
1081
|
+
const d = screenDims(name, opts);
|
|
1082
|
+
return { x: 0, y: 0, width: d.width, height: d.height, radius: d.radius };
|
|
1083
|
+
}
|
|
1084
|
+
function deviceScreenCenter(name, opts = {}) {
|
|
1085
|
+
const d = screenDims(name, opts);
|
|
1086
|
+
return { x: d.cx, y: d.cy };
|
|
1087
|
+
}
|
|
1088
|
+
function deviceBounds(name, opts = {}) {
|
|
1089
|
+
const b = BOUNDS[name];
|
|
1090
|
+
return isLandscape(name, opts) ? { width: b.height, height: b.width } : { ...b };
|
|
1091
|
+
}
|
|
1092
|
+
function screenGroup(id, p, o, cx, cy, dims, content) {
|
|
1093
|
+
return group({ id: `${id}-screen`, x: cx, y: cy, clip: { kind: "rect", x: -dims.width / 2, y: -dims.height / 2, width: dims.width, height: dims.height, radius: dims.radius } }, [
|
|
1094
|
+
rect({ id: `${id}-screenbg`, x: 0, y: 0, anchor: "center", width: dims.width, height: dims.height, fill: o.screen ?? p.screen }),
|
|
1095
|
+
group({ id: `${id}-content`, x: 0, y: 0 }, content)
|
|
1096
|
+
]);
|
|
1097
|
+
}
|
|
1098
|
+
function buildDevice(name, id, p, o, content) {
|
|
1099
|
+
const dims = screenDims(name, o);
|
|
1100
|
+
const sw = dims.width;
|
|
1101
|
+
const sh = dims.height;
|
|
1102
|
+
const screen = () => screenGroup(id, p, o, dims.cx, dims.cy, dims, content);
|
|
1103
|
+
switch (name) {
|
|
1104
|
+
case "phone":
|
|
1105
|
+
case "tablet": {
|
|
1106
|
+
const bezel = name === "phone" ? 20 : 28;
|
|
1107
|
+
const bodyW = sw + bezel * 2;
|
|
1108
|
+
const bodyH = sh + bezel * 2;
|
|
1109
|
+
const bodyR = name === "phone" ? 54 : 34;
|
|
1110
|
+
const land = isLandscape(name, o);
|
|
1111
|
+
const nodes = [
|
|
1112
|
+
rect({ id: `${id}-body`, x: 0, y: 0, anchor: "center", width: bodyW, height: bodyH, fill: p.body, stroke: p.bodyStroke, strokeWidth: 2, radius: bodyR }),
|
|
1113
|
+
screen()
|
|
1114
|
+
];
|
|
1115
|
+
if (name === "phone") {
|
|
1116
|
+
nodes.push(
|
|
1117
|
+
land ? rect({ id: `${id}-notch`, x: -sw / 2 + 16, y: 0, anchor: "center", width: 30, height: 96, fill: "#000000", radius: 15 }) : rect({ id: `${id}-notch`, x: 0, y: -sh / 2 + 16, anchor: "center", width: 96, height: 30, fill: "#000000", radius: 15 }),
|
|
1118
|
+
land ? rect({ id: `${id}-home`, x: sw / 2 - 4, y: 0, anchor: "center", width: 5, height: 120, fill: p.detail, radius: 3 }) : rect({ id: `${id}-home`, x: 0, y: sh / 2 - 18, anchor: "center", width: 120, height: 5, fill: p.detail, radius: 3 })
|
|
1119
|
+
);
|
|
1120
|
+
if (!land) {
|
|
1121
|
+
nodes.push(
|
|
1122
|
+
rect({ id: `${id}-pwr`, x: bodyW / 2, y: -bodyH * 0.1, anchor: "center", width: 4, height: 78, fill: p.detail, radius: 2 }),
|
|
1123
|
+
rect({ id: `${id}-volup`, x: -bodyW / 2, y: -bodyH * 0.16, anchor: "center", width: 4, height: 48, fill: p.detail, radius: 2 }),
|
|
1124
|
+
rect({ id: `${id}-voldn`, x: -bodyW / 2, y: -bodyH * 0.16 + 60, anchor: "center", width: 4, height: 48, fill: p.detail, radius: 2 })
|
|
1125
|
+
);
|
|
1126
|
+
}
|
|
1127
|
+
} else {
|
|
1128
|
+
nodes.push(
|
|
1129
|
+
rect({ id: `${id}-camera`, x: land ? -sw / 2 - 14 : 0, y: land ? 0 : -sh / 2 - 14, anchor: "center", width: 8, height: 8, fill: p.detail, radius: 4 }),
|
|
1130
|
+
rect({ id: `${id}-pwr`, x: land ? -bodyW * 0.18 : bodyW * 0.18, y: land ? -bodyH / 2 : -bodyH / 2, anchor: "center", width: 60, height: 4, fill: p.detail, radius: 2 })
|
|
1131
|
+
);
|
|
1132
|
+
}
|
|
1133
|
+
return nodes;
|
|
1134
|
+
}
|
|
1135
|
+
case "laptop": {
|
|
1136
|
+
const lidTop = dims.cy - (sh + 40) / 2;
|
|
1137
|
+
const keyRows = [0, 1, 2, 3].map(
|
|
1138
|
+
(r) => rect({ id: `${id}-keys${r}`, x: 0, y: 150 + r * 11, anchor: "center", width: 640 + r * 50, height: 6, fill: p.chrome, radius: 3 })
|
|
1139
|
+
);
|
|
1140
|
+
return [
|
|
1141
|
+
path({ id: `${id}-base`, x: 0, y: 0, d: "M -450 140 L 450 140 L 520 196 L -520 196 Z", fill: p.body, stroke: p.bodyStroke, strokeWidth: 2 }),
|
|
1142
|
+
rect({ id: `${id}-foot-l`, x: -360, y: 198, anchor: "center", width: 70, height: 5, fill: p.detail, radius: 3 }),
|
|
1143
|
+
rect({ id: `${id}-foot-r`, x: 360, y: 198, anchor: "center", width: 70, height: 5, fill: p.detail, radius: 3 }),
|
|
1144
|
+
...keyRows,
|
|
1145
|
+
rect({ id: `${id}-trackpad`, x: 0, y: 184, anchor: "center", width: 150, height: 8, fill: p.detail, radius: 4 }),
|
|
1146
|
+
rect({ id: `${id}-hinge`, x: 0, y: 134, anchor: "center", width: 900, height: 10, fill: p.detail, radius: 5 }),
|
|
1147
|
+
screen(),
|
|
1148
|
+
ellipse({ id: `${id}-webcam`, x: 0, y: lidTop + 14, anchor: "center", width: 6, height: 6, fill: p.detail }),
|
|
1149
|
+
rect({ id: `${id}-lid`, x: 0, y: dims.cy, anchor: "center", width: sw + 40, height: sh + 40, stroke: p.bodyStroke, strokeWidth: 2, radius: 18 })
|
|
1150
|
+
];
|
|
1151
|
+
}
|
|
1152
|
+
case "browser": {
|
|
1153
|
+
const winW = sw + 16;
|
|
1154
|
+
const winH = sh + 92;
|
|
1155
|
+
const barY = -winH / 2 + 24;
|
|
1156
|
+
return [
|
|
1157
|
+
rect({ id: `${id}-win`, x: 0, y: 0, anchor: "center", width: winW, height: winH, fill: p.chrome, stroke: p.bodyStroke, strokeWidth: 1.5, radius: 14 }),
|
|
1158
|
+
ellipse({ id: `${id}-dot1`, x: -winW / 2 + 30, y: barY, anchor: "center", width: 13, height: 13, fill: "#FF5F57" }),
|
|
1159
|
+
ellipse({ id: `${id}-dot2`, x: -winW / 2 + 54, y: barY, anchor: "center", width: 13, height: 13, fill: "#FEBC2E" }),
|
|
1160
|
+
ellipse({ id: `${id}-dot3`, x: -winW / 2 + 78, y: barY, anchor: "center", width: 13, height: 13, fill: "#28C840" }),
|
|
1161
|
+
// an active tab tucked under the lights
|
|
1162
|
+
rect({ id: `${id}-tab`, x: -winW / 2 + 230, y: barY, anchor: "center", width: 190, height: 30, fill: o.screen ?? p.screen, radius: 8 }),
|
|
1163
|
+
text({ id: `${id}-tabtext`, x: -winW / 2 + 156, y: barY, anchor: "center-left", content: "Overview", fontFamily: "Inter", fontSize: 13, fill: p.chromeText }),
|
|
1164
|
+
rect({ id: `${id}-urlpill`, x: 96, y: barY, anchor: "center", width: 700, height: 26, fill: o.screen ?? p.screen, stroke: p.bodyStroke, strokeWidth: 1, radius: 13 }),
|
|
1165
|
+
rect({ id: `${id}-lock`, x: 96 - 330, y: barY, anchor: "center", width: 8, height: 10, fill: p.chromeText, radius: 2 }),
|
|
1166
|
+
text({ id: `${id}-urltext`, x: 96 - 312, y: barY, anchor: "center-left", content: urlText(o.url), fontFamily: "Inter", fontSize: 14, fill: p.chromeText }),
|
|
1167
|
+
screen()
|
|
1168
|
+
];
|
|
1169
|
+
}
|
|
1170
|
+
case "watch": {
|
|
1171
|
+
const bw = sw + 36;
|
|
1172
|
+
const bh = sh + 36;
|
|
1173
|
+
return [
|
|
1174
|
+
// straps (drawn behind the body) flaring out top & bottom
|
|
1175
|
+
path({ id: `${id}-bandtop`, x: 0, y: -bh / 2 + 4, d: "M -78 0 L 78 0 L 64 -86 L -64 -86 Z", fill: p.body, stroke: p.bodyStroke, strokeWidth: 2 }),
|
|
1176
|
+
path({ id: `${id}-bandbot`, x: 0, y: bh / 2 - 4, d: "M -78 0 L 78 0 L 64 86 L -64 86 Z", fill: p.body, stroke: p.bodyStroke, strokeWidth: 2 }),
|
|
1177
|
+
rect({ id: `${id}-body`, x: 0, y: 0, anchor: "center", width: bw, height: bh, fill: p.body, stroke: p.bodyStroke, strokeWidth: 3, radius: 60 }),
|
|
1178
|
+
screen(),
|
|
1179
|
+
rect({ id: `${id}-crown`, x: bw / 2, y: -20, anchor: "center", width: 14, height: 40, fill: p.detail, radius: 6 }),
|
|
1180
|
+
rect({ id: `${id}-button`, x: bw / 2 - 2, y: 40, anchor: "center", width: 8, height: 34, fill: p.detail, radius: 4 })
|
|
1181
|
+
];
|
|
1182
|
+
}
|
|
1183
|
+
case "monitor": {
|
|
1184
|
+
const panelW = sw + 44;
|
|
1185
|
+
const panelH = sh + 60;
|
|
1186
|
+
return [
|
|
1187
|
+
rect({ id: `${id}-panel`, x: 0, y: 0, anchor: "center", width: panelW, height: panelH, fill: p.body, stroke: p.bodyStroke, strokeWidth: 2, radius: 16 }),
|
|
1188
|
+
screen(),
|
|
1189
|
+
ellipse({ id: `${id}-led`, x: panelW / 2 - 26, y: panelH / 2 - 16, anchor: "center", width: 6, height: 6, fill: "#28C840" }),
|
|
1190
|
+
rect({ id: `${id}-neck`, x: 0, y: panelH / 2 + 60, anchor: "center", width: 60, height: 120, fill: p.body }),
|
|
1191
|
+
path({ id: `${id}-stand`, x: 0, y: panelH / 2 + 60, d: "M -160 50 L 160 50 L 220 80 L -220 80 Z", fill: p.body, stroke: p.bodyStroke, strokeWidth: 2 })
|
|
1192
|
+
];
|
|
1193
|
+
}
|
|
1194
|
+
case "tv": {
|
|
1195
|
+
const panelW = sw + 44;
|
|
1196
|
+
const panelH = sh + 48;
|
|
1197
|
+
const panelBottom = dims.cy + panelH / 2;
|
|
1198
|
+
return [
|
|
1199
|
+
rect({ id: `${id}-panel`, x: 0, y: dims.cy, anchor: "center", width: panelW, height: panelH, fill: p.body, stroke: p.bodyStroke, strokeWidth: 2, radius: 12 }),
|
|
1200
|
+
screen(),
|
|
1201
|
+
ellipse({ id: `${id}-brand`, x: 0, y: panelBottom - 12, anchor: "center", width: 6, height: 6, fill: p.detail }),
|
|
1202
|
+
rect({ id: `${id}-neck`, x: 0, y: panelBottom + 48, anchor: "center", width: 64, height: 96, fill: p.body }),
|
|
1203
|
+
path({ id: `${id}-stand`, x: 0, y: panelBottom + 96, d: "M -210 0 L 210 0 L 270 34 L -270 34 Z", fill: p.body, stroke: p.bodyStroke, strokeWidth: 2 })
|
|
1204
|
+
];
|
|
1205
|
+
}
|
|
1206
|
+
case "foldable": {
|
|
1207
|
+
const bodyW = sw + 40;
|
|
1208
|
+
const bodyH = sh + 40;
|
|
1209
|
+
return [
|
|
1210
|
+
rect({ id: `${id}-hinge-l`, x: -bodyW / 2, y: 0, anchor: "center", width: 8, height: bodyH * 0.5, fill: p.detail, radius: 4 }),
|
|
1211
|
+
rect({ id: `${id}-hinge-r`, x: bodyW / 2, y: 0, anchor: "center", width: 8, height: bodyH * 0.5, fill: p.detail, radius: 4 }),
|
|
1212
|
+
rect({ id: `${id}-body`, x: 0, y: 0, anchor: "center", width: bodyW, height: bodyH, fill: p.body, stroke: p.bodyStroke, strokeWidth: 2, radius: 28 }),
|
|
1213
|
+
screen(),
|
|
1214
|
+
rect({ id: `${id}-crease`, x: 0, y: 0, anchor: "center", width: 4, height: sh, fill: p.bodyStroke, radius: 2, opacity: 0.5 }),
|
|
1215
|
+
ellipse({ id: `${id}-cam1`, x: -10, y: -sh / 2 + 18, anchor: "center", width: 8, height: 8, fill: p.detail }),
|
|
1216
|
+
ellipse({ id: `${id}-cam2`, x: 10, y: -sh / 2 + 18, anchor: "center", width: 8, height: 8, fill: p.detail })
|
|
1217
|
+
];
|
|
1218
|
+
}
|
|
1219
|
+
case "terminal": {
|
|
1220
|
+
const winW = sw + 16;
|
|
1221
|
+
const winH = sh + 76;
|
|
1222
|
+
return [
|
|
1223
|
+
rect({ id: `${id}-win`, x: 0, y: 0, anchor: "center", width: winW, height: winH, fill: p.chrome, stroke: p.bodyStroke, strokeWidth: 1.5, radius: 12 }),
|
|
1224
|
+
ellipse({ id: `${id}-dot1`, x: -winW / 2 + 28, y: -winH / 2 + 22, anchor: "center", width: 12, height: 12, fill: "#FF5F57" }),
|
|
1225
|
+
ellipse({ id: `${id}-dot2`, x: -winW / 2 + 50, y: -winH / 2 + 22, anchor: "center", width: 12, height: 12, fill: "#FEBC2E" }),
|
|
1226
|
+
ellipse({ id: `${id}-dot3`, x: -winW / 2 + 72, y: -winH / 2 + 22, anchor: "center", width: 12, height: 12, fill: "#28C840" }),
|
|
1227
|
+
rect({ id: `${id}-tab`, x: -winW / 2 + 170, y: -winH / 2 + 22, anchor: "center", width: 130, height: 24, fill: o.screen ?? p.screen, radius: 6 }),
|
|
1228
|
+
text({ id: `${id}-title`, x: -winW / 2 + 170, y: -winH / 2 + 22, anchor: "center", content: urlText(o.url ?? "zsh"), fontFamily: "Inter", fontSize: 13, fill: p.chromeText }),
|
|
1229
|
+
screen()
|
|
1230
|
+
];
|
|
1231
|
+
}
|
|
1232
|
+
case "car": {
|
|
1233
|
+
const bodyW = sw + 60;
|
|
1234
|
+
const bodyH = sh + 60;
|
|
1235
|
+
return [
|
|
1236
|
+
rect({ id: `${id}-body`, x: 0, y: 0, anchor: "center", width: bodyW, height: bodyH, fill: p.body, stroke: p.bodyStroke, strokeWidth: 2, radius: 40 }),
|
|
1237
|
+
ellipse({ id: `${id}-knob`, x: -bodyW / 2 + 18, y: 0, anchor: "center", width: 22, height: 22, fill: p.body, stroke: p.detail, strokeWidth: 3 }),
|
|
1238
|
+
screen(),
|
|
1239
|
+
ellipse({ id: `${id}-btn1`, x: -44, y: sh / 2 + 16, anchor: "center", width: 12, height: 12, fill: p.detail }),
|
|
1240
|
+
ellipse({ id: `${id}-btn2`, x: 0, y: sh / 2 + 16, anchor: "center", width: 12, height: 12, fill: p.detail }),
|
|
1241
|
+
ellipse({ id: `${id}-btn3`, x: 44, y: sh / 2 + 16, anchor: "center", width: 12, height: 12, fill: p.detail })
|
|
1242
|
+
];
|
|
1243
|
+
}
|
|
1244
|
+
}
|
|
1245
|
+
}
|
|
1246
|
+
var urlText = (url) => {
|
|
1247
|
+
const u = url ?? "reframe.video";
|
|
1248
|
+
return u.length > 70 ? `${u.slice(0, 67)}\u2026` : u;
|
|
1249
|
+
};
|
|
1250
|
+
function devicePreset(name, opts = {}) {
|
|
1251
|
+
const id = opts.id ?? "device";
|
|
1252
|
+
const p = opts.color === "light" ? LIGHT : DARK;
|
|
1253
|
+
const children = buildDevice(name, id, p, opts, opts.content ?? []);
|
|
1254
|
+
return group(
|
|
1255
|
+
{
|
|
1256
|
+
id,
|
|
1257
|
+
x: opts.x ?? 0,
|
|
1258
|
+
y: opts.y ?? 0,
|
|
1259
|
+
...opts.scale !== void 0 && opts.scale !== 1 && { scale: opts.scale },
|
|
1260
|
+
...opts.opacity !== void 0 && opts.opacity !== 1 && { opacity: opts.opacity }
|
|
1261
|
+
},
|
|
1262
|
+
children
|
|
1263
|
+
);
|
|
1264
|
+
}
|
|
1265
|
+
|
|
1266
|
+
// ../core/src/motionOps.ts
|
|
1267
|
+
var MOTION_OPS = ["rotate", "zoom", "ken-burns", "slide-in", "fade", "draw-on", "pulse"];
|
|
1268
|
+
var clamp012 = (n) => Math.max(0, Math.min(1, n));
|
|
1269
|
+
function settleEase2(e) {
|
|
1270
|
+
return e < 0.34 ? "easeOutCubic" : e < 0.67 ? "easeOutBack" : "easeOutElastic";
|
|
1271
|
+
}
|
|
1272
|
+
function fromVec2(from, dist) {
|
|
1273
|
+
switch (from) {
|
|
1274
|
+
case "right":
|
|
1275
|
+
return [dist, 0];
|
|
1276
|
+
case "top":
|
|
1277
|
+
return [0, -dist];
|
|
1278
|
+
case "bottom":
|
|
1279
|
+
return [0, dist];
|
|
1280
|
+
default:
|
|
1281
|
+
return [-dist, 0];
|
|
1282
|
+
}
|
|
1283
|
+
}
|
|
1284
|
+
var motionOpLabel = (name, target) => `op-${name}-${target}`;
|
|
1285
|
+
function motionOp(name, target, opts = {}) {
|
|
1286
|
+
const e = clamp012(opts.energy ?? 0.5);
|
|
1287
|
+
const sp = Math.max(0.25, opts.speed ?? 1);
|
|
1288
|
+
const amt = opts.amount ?? 1;
|
|
1289
|
+
const b = { scale: 1, x: 0, y: 0, rotation: 0, ...opts.base };
|
|
1290
|
+
const d = (base) => base / sp;
|
|
1291
|
+
const label = motionOpLabel(name, target);
|
|
1292
|
+
switch (name) {
|
|
1293
|
+
case "rotate":
|
|
1294
|
+
return { timeline: beat(label, {}, [tween(target, { rotation: b.rotation + 360 * amt }, { duration: d(1), ease: settleEase2(e) })]) };
|
|
1295
|
+
case "zoom": {
|
|
1296
|
+
const peak = b.scale * (1 + 0.22 * amt);
|
|
1297
|
+
return {
|
|
1298
|
+
timeline: beat(label, {}, [
|
|
1299
|
+
seq(
|
|
1300
|
+
tween(target, { scale: peak }, { duration: d(0.4), ease: "easeOutBack" }),
|
|
1301
|
+
tween(target, { scale: b.scale }, { duration: d(0.45), ease: "easeInOutQuad" })
|
|
1302
|
+
)
|
|
1303
|
+
])
|
|
1304
|
+
};
|
|
1305
|
+
}
|
|
1306
|
+
case "ken-burns":
|
|
1307
|
+
return {
|
|
1308
|
+
timeline: beat(label, {}, [
|
|
1309
|
+
par(
|
|
1310
|
+
tween(target, { scale: b.scale * (1 + 0.1 * amt) }, { duration: d(3), ease: "easeInOutQuad" }),
|
|
1311
|
+
tween(target, { x: b.x + 26 * amt, y: b.y - 16 * amt }, { duration: d(3), ease: "easeInOutQuad" })
|
|
1312
|
+
)
|
|
1313
|
+
])
|
|
1314
|
+
};
|
|
1315
|
+
case "slide-in": {
|
|
1316
|
+
const [dx, dy] = fromVec2(opts.from ?? "left", 320 * amt);
|
|
1317
|
+
return {
|
|
1318
|
+
setup: { [target]: { x: b.x + dx, y: b.y + dy, opacity: 0 } },
|
|
1319
|
+
timeline: beat(label, {}, [
|
|
1320
|
+
par(
|
|
1321
|
+
tween(target, { x: b.x, y: b.y }, { duration: d(0.7), ease: settleEase2(e) }),
|
|
1322
|
+
tween(target, { opacity: 1 }, { duration: d(0.4), ease: "easeOutQuad" })
|
|
1323
|
+
)
|
|
1324
|
+
])
|
|
1325
|
+
};
|
|
1326
|
+
}
|
|
1327
|
+
case "fade":
|
|
1328
|
+
return {
|
|
1329
|
+
setup: { [target]: { opacity: 0 } },
|
|
1330
|
+
timeline: beat(label, {}, [tween(target, { opacity: 1 }, { duration: d(0.6), ease: "easeOutQuad" })])
|
|
1331
|
+
};
|
|
1332
|
+
case "draw-on":
|
|
1333
|
+
return {
|
|
1334
|
+
setup: { [target]: { progress: 0 } },
|
|
1335
|
+
timeline: beat(label, {}, [tween(target, { progress: 1 }, { duration: d(1.3), ease: "easeInOutQuad" })])
|
|
1336
|
+
};
|
|
1337
|
+
case "pulse": {
|
|
1338
|
+
const hi = b.scale * (1 + 0.12 * amt);
|
|
1339
|
+
const pulses = 2 + Math.round(amt);
|
|
1340
|
+
const steps = [];
|
|
1341
|
+
for (let i = 0; i < pulses; i++) {
|
|
1342
|
+
steps.push(tween(target, { scale: hi }, { duration: d(0.22), ease: "easeOutQuad" }));
|
|
1343
|
+
steps.push(tween(target, { scale: b.scale }, { duration: d(0.22), ease: "easeInQuad" }));
|
|
1344
|
+
}
|
|
1345
|
+
return { timeline: beat(label, {}, [seq(...steps)]) };
|
|
1346
|
+
}
|
|
1347
|
+
}
|
|
1348
|
+
}
|
|
1349
|
+
|
|
887
1350
|
// ../core/src/audio.ts
|
|
888
1351
|
var SFX_DURATION = {
|
|
889
1352
|
whoosh: 0.35,
|
|
@@ -929,6 +1392,15 @@ function resolveAudioPlan(compiled) {
|
|
|
929
1392
|
});
|
|
930
1393
|
}
|
|
931
1394
|
cues.sort((a, b) => a.t - b.t);
|
|
1395
|
+
return {
|
|
1396
|
+
duration,
|
|
1397
|
+
bgm: resolveBgm(audio.bgm),
|
|
1398
|
+
cues,
|
|
1399
|
+
duckWindows: mergeDuckWindows(cues, duration),
|
|
1400
|
+
warnings
|
|
1401
|
+
};
|
|
1402
|
+
}
|
|
1403
|
+
function mergeDuckWindows(cues, duration) {
|
|
932
1404
|
const duckWindows = [];
|
|
933
1405
|
for (const cue of cues) {
|
|
934
1406
|
const window = { t0: cue.t, t1: Math.min(duration, cue.t + cue.duration) };
|
|
@@ -936,23 +1408,68 @@ function resolveAudioPlan(compiled) {
|
|
|
936
1408
|
if (last && window.t0 <= last.t1 + 0.1) last.t1 = Math.max(last.t1, window.t1);
|
|
937
1409
|
else duckWindows.push(window);
|
|
938
1410
|
}
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
1411
|
+
return duckWindows;
|
|
1412
|
+
}
|
|
1413
|
+
function resolveBgm(b) {
|
|
1414
|
+
if (!b) return null;
|
|
1415
|
+
const duck = b.duck === false ? null : {
|
|
1416
|
+
depth: b.duck?.depth ?? 0.5,
|
|
1417
|
+
attack: b.duck?.attack ?? 0.05,
|
|
1418
|
+
release: b.duck?.release ?? 0.25
|
|
1419
|
+
};
|
|
1420
|
+
return {
|
|
1421
|
+
source: b.file ? { kind: "file", path: b.file } : { kind: "synth", name: b.synth ?? "ambient-pad" },
|
|
1422
|
+
gain: b.gain ?? 0.5,
|
|
1423
|
+
fadeIn: b.fadeIn ?? 0,
|
|
1424
|
+
fadeOut: b.fadeOut ?? 0,
|
|
1425
|
+
duck
|
|
1426
|
+
};
|
|
1427
|
+
}
|
|
1428
|
+
function resolveCompositionAudioPlan(comp) {
|
|
1429
|
+
const audio = comp.ir.audio;
|
|
1430
|
+
const duration = comp.duration;
|
|
1431
|
+
const warnings = [];
|
|
1432
|
+
const cues = [];
|
|
1433
|
+
for (const placement of comp.scenes) {
|
|
1434
|
+
const plan = resolveAudioPlan(placement.compiled);
|
|
1435
|
+
if (!plan) continue;
|
|
1436
|
+
if (plan.bgm) {
|
|
1437
|
+
warnings.push(`scene "${placement.id}": per-scene bgm ignored \u2014 set bgm at the composition level`);
|
|
1438
|
+
}
|
|
1439
|
+
for (const w of plan.warnings) warnings.push(`scene "${placement.id}": ${w}`);
|
|
1440
|
+
for (const cue of plan.cues) {
|
|
1441
|
+
const t = cue.t + placement.start;
|
|
1442
|
+
if (t >= duration) continue;
|
|
1443
|
+
cues.push({ ...cue, t });
|
|
1444
|
+
}
|
|
1445
|
+
}
|
|
1446
|
+
for (const [index, cue] of (audio?.cues ?? []).entries()) {
|
|
1447
|
+
if (typeof cue.at !== "number") {
|
|
1448
|
+
warnings.push(`composition cue[${index}]: "at" must be an absolute number (no composition labels) \u2014 dropped`);
|
|
1449
|
+
continue;
|
|
1450
|
+
}
|
|
1451
|
+
const t = Math.max(0, cue.at + (cue.offset ?? 0));
|
|
1452
|
+
const cueDuration = cue.sfx ? SFX_DURATION[cue.sfx] : FILE_CUE_DURATION;
|
|
1453
|
+
if (t >= duration) {
|
|
1454
|
+
warnings.push(`composition cue[${index}] at ${t.toFixed(2)}s past the composition end \u2014 dropped`);
|
|
1455
|
+
continue;
|
|
1456
|
+
}
|
|
1457
|
+
cues.push({
|
|
1458
|
+
t,
|
|
1459
|
+
gain: cue.gain ?? 1,
|
|
1460
|
+
duration: cueDuration,
|
|
1461
|
+
source: cue.sfx ? { kind: "sfx", name: cue.sfx, params: cue.params ?? {} } : { kind: "file", path: cue.file }
|
|
1462
|
+
});
|
|
954
1463
|
}
|
|
955
|
-
|
|
1464
|
+
if (!audio?.bgm && cues.length === 0) return null;
|
|
1465
|
+
cues.sort((a, b) => a.t - b.t);
|
|
1466
|
+
return {
|
|
1467
|
+
duration,
|
|
1468
|
+
bgm: resolveBgm(audio?.bgm),
|
|
1469
|
+
cues,
|
|
1470
|
+
duckWindows: mergeDuckWindows(cues, duration),
|
|
1471
|
+
warnings
|
|
1472
|
+
};
|
|
956
1473
|
}
|
|
957
1474
|
|
|
958
1475
|
// ../core/src/behaviors.ts
|
|
@@ -1106,11 +1623,22 @@ function multiply(m, n) {
|
|
|
1106
1623
|
m[1] * n[4] + m[3] * n[5] + m[5]
|
|
1107
1624
|
];
|
|
1108
1625
|
}
|
|
1109
|
-
function localMatrix(x, y, rotationDeg, scale) {
|
|
1626
|
+
function localMatrix(x, y, rotationDeg, scale, scaleX = 1, scaleY = 1, skewXDeg = 0, skewYDeg = 0) {
|
|
1110
1627
|
const r = rotationDeg * Math.PI / 180;
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1628
|
+
if (scaleX === 1 && scaleY === 1 && skewXDeg === 0 && skewYDeg === 0) {
|
|
1629
|
+
const cos = Math.cos(r) * scale;
|
|
1630
|
+
const sin = Math.sin(r) * scale;
|
|
1631
|
+
return [cos, sin, -sin, cos, x, y];
|
|
1632
|
+
}
|
|
1633
|
+
const c = Math.cos(r);
|
|
1634
|
+
const s = Math.sin(r);
|
|
1635
|
+
const tx = Math.tan(skewXDeg * Math.PI / 180);
|
|
1636
|
+
const ty = Math.tan(skewYDeg * Math.PI / 180);
|
|
1637
|
+
const R = [c, s, -s, c, 0, 0];
|
|
1638
|
+
const K = [1, ty, tx, 1, 0, 0];
|
|
1639
|
+
const S = [scale * scaleX, 0, 0, scale * scaleY, 0, 0];
|
|
1640
|
+
const m = multiply(R, multiply(K, S));
|
|
1641
|
+
return [m[0], m[1], m[2], m[3], x, y];
|
|
1114
1642
|
}
|
|
1115
1643
|
var ANCHOR_FACTORS = {
|
|
1116
1644
|
"top-left": [0, 0],
|
|
@@ -1135,53 +1663,86 @@ function behaviorEnvelope(b, t) {
|
|
|
1135
1663
|
if (Number.isFinite(until) && ramp > 0) envelope = Math.min(envelope, (until - t) / ramp);
|
|
1136
1664
|
return Math.max(0, Math.min(1, envelope));
|
|
1137
1665
|
}
|
|
1138
|
-
function
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1666
|
+
function sampleProp(compiled, t, target, prop, fallback) {
|
|
1667
|
+
let value = compiled.initialValues.get(`${target}.${prop}`) ?? fallback;
|
|
1668
|
+
let segStart = Number.NEGATIVE_INFINITY;
|
|
1669
|
+
const segs = compiled.segments.get(`${target}.${prop}`);
|
|
1670
|
+
if (segs) {
|
|
1671
|
+
let active;
|
|
1672
|
+
for (const seg of segs) {
|
|
1673
|
+
if (seg.t0 <= t) active = seg;
|
|
1674
|
+
else break;
|
|
1675
|
+
}
|
|
1676
|
+
if (active) {
|
|
1677
|
+
segStart = active.t0;
|
|
1678
|
+
if (t >= active.t1) {
|
|
1679
|
+
value = active.to;
|
|
1680
|
+
} else {
|
|
1681
|
+
const u = resolveEase(active.ease)((t - active.t0) / (active.t1 - active.t0));
|
|
1682
|
+
value = lerpValue(active.from, active.to, u);
|
|
1683
|
+
}
|
|
1684
|
+
}
|
|
1685
|
+
}
|
|
1686
|
+
if (prop === "x" || prop === "y" || prop === "rotation") {
|
|
1687
|
+
const drivers = compiled.motionPaths.get(target);
|
|
1688
|
+
if (drivers) {
|
|
1145
1689
|
let active;
|
|
1146
|
-
for (const
|
|
1147
|
-
if (
|
|
1690
|
+
for (const d of drivers) {
|
|
1691
|
+
if (d.t0 <= t) active = d;
|
|
1148
1692
|
else break;
|
|
1149
1693
|
}
|
|
1150
|
-
if (active) {
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
value = lerpValue(active.from, active.to, u);
|
|
1157
|
-
}
|
|
1694
|
+
if (active && active.t0 >= segStart && (prop !== "rotation" || active.autoRotate) && active.points.length > 0) {
|
|
1695
|
+
const span = active.t1 - active.t0;
|
|
1696
|
+
const u = span <= 0 ? 1 : resolveEase(active.ease)(Math.max(0, Math.min(1, (t - active.t0) / span)));
|
|
1697
|
+
if (prop === "x") value = pathPoint(active.points, active.closed, u, active.curviness)[0];
|
|
1698
|
+
else if (prop === "y") value = pathPoint(active.points, active.closed, u, active.curviness)[1];
|
|
1699
|
+
else value = pathTangentAngle(active.points, active.closed, u, active.curviness) + active.rotateOffset;
|
|
1158
1700
|
}
|
|
1159
1701
|
}
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
if (d.t0 <= t) active = d;
|
|
1166
|
-
else break;
|
|
1167
|
-
}
|
|
1168
|
-
if (active && active.t0 >= segStart && (prop !== "rotation" || active.autoRotate) && active.points.length > 0) {
|
|
1169
|
-
const span = active.t1 - active.t0;
|
|
1170
|
-
const u = span <= 0 ? 1 : resolveEase(active.ease)(Math.max(0, Math.min(1, (t - active.t0) / span)));
|
|
1171
|
-
if (prop === "x") value = pathPoint(active.points, active.closed, u)[0];
|
|
1172
|
-
else if (prop === "y") value = pathPoint(active.points, active.closed, u)[1];
|
|
1173
|
-
else value = pathTangentAngle(active.points, active.closed, u) + active.rotateOffset;
|
|
1174
|
-
}
|
|
1175
|
-
}
|
|
1702
|
+
}
|
|
1703
|
+
for (const b of compiled.ir.behaviors ?? []) {
|
|
1704
|
+
if (b.target === target && b.prop === prop && typeof value === "number") {
|
|
1705
|
+
const envelope = behaviorEnvelope(b, t);
|
|
1706
|
+
if (envelope > 0) value = value + envelope * sampleBehavior(b.behavior, t);
|
|
1176
1707
|
}
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1708
|
+
}
|
|
1709
|
+
return value;
|
|
1710
|
+
}
|
|
1711
|
+
function nodeParentMatrix(compiled, id, t) {
|
|
1712
|
+
const num = (target, prop, fallback) => {
|
|
1713
|
+
const v = sampleProp(compiled, t, target, prop, fallback);
|
|
1714
|
+
return typeof v === "number" ? v : fallback;
|
|
1715
|
+
};
|
|
1716
|
+
let result = null;
|
|
1717
|
+
const walk = (node, parent) => {
|
|
1718
|
+
if (node.id === id) {
|
|
1719
|
+
result = parent;
|
|
1720
|
+
return true;
|
|
1721
|
+
}
|
|
1722
|
+
if (node.type === "group") {
|
|
1723
|
+
const m = multiply(
|
|
1724
|
+
parent,
|
|
1725
|
+
localMatrix(
|
|
1726
|
+
num(node.id, "x", node.props.x),
|
|
1727
|
+
num(node.id, "y", node.props.y),
|
|
1728
|
+
num(node.id, "rotation", node.props.rotation ?? 0),
|
|
1729
|
+
num(node.id, "scale", node.props.scale ?? 1),
|
|
1730
|
+
num(node.id, "scaleX", node.props.scaleX ?? 1),
|
|
1731
|
+
num(node.id, "scaleY", node.props.scaleY ?? 1),
|
|
1732
|
+
num(node.id, "skewX", node.props.skewX ?? 0),
|
|
1733
|
+
num(node.id, "skewY", node.props.skewY ?? 0)
|
|
1734
|
+
)
|
|
1735
|
+
);
|
|
1736
|
+
for (const child of node.children) if (walk(child, m)) return true;
|
|
1182
1737
|
}
|
|
1183
|
-
return
|
|
1738
|
+
return false;
|
|
1184
1739
|
};
|
|
1740
|
+
for (const node of compiled.ir.nodes) if (walk(node, IDENTITY)) break;
|
|
1741
|
+
return result;
|
|
1742
|
+
}
|
|
1743
|
+
function evaluate(compiled, t) {
|
|
1744
|
+
const ops = [];
|
|
1745
|
+
const valueAt = (target, prop, fallback) => sampleProp(compiled, t, target, prop, fallback);
|
|
1185
1746
|
const num = (target, prop, fallback) => {
|
|
1186
1747
|
const v = valueAt(target, prop, fallback);
|
|
1187
1748
|
return typeof v === "number" ? v : fallback;
|
|
@@ -1194,8 +1755,9 @@ function evaluate(compiled, t) {
|
|
|
1194
1755
|
const v = valueAt(target, prop, base ?? "");
|
|
1195
1756
|
return v === "" && base === void 0 ? void 0 : String(v);
|
|
1196
1757
|
};
|
|
1197
|
-
const walk = (node, parent, parentOpacity) => {
|
|
1758
|
+
const walk = (node, parent, parentOpacity, clips) => {
|
|
1198
1759
|
const id = node.id;
|
|
1760
|
+
const clipSpread = clips.length > 0 ? { clips } : void 0;
|
|
1199
1761
|
if (node.type === "line") {
|
|
1200
1762
|
const opacity2 = parentOpacity * num(id, "opacity", node.props.opacity ?? 1);
|
|
1201
1763
|
if (opacity2 <= 0) return;
|
|
@@ -1212,7 +1774,8 @@ function evaluate(compiled, t) {
|
|
|
1212
1774
|
x2: x1 + (num(id, "x2", node.props.x2) - x1) * progress,
|
|
1213
1775
|
y2: y1 + (num(id, "y2", node.props.y2) - y1) * progress,
|
|
1214
1776
|
stroke: str(id, "stroke", node.props.stroke),
|
|
1215
|
-
strokeWidth: num(id, "strokeWidth", node.props.strokeWidth ?? 1)
|
|
1777
|
+
strokeWidth: num(id, "strokeWidth", node.props.strokeWidth ?? 1),
|
|
1778
|
+
...clipSpread
|
|
1216
1779
|
});
|
|
1217
1780
|
return;
|
|
1218
1781
|
}
|
|
@@ -1224,13 +1787,19 @@ function evaluate(compiled, t) {
|
|
|
1224
1787
|
num(id, "x", node.props.x),
|
|
1225
1788
|
num(id, "y", node.props.y),
|
|
1226
1789
|
num(id, "rotation", node.props.rotation ?? 0),
|
|
1227
|
-
num(id, "scale", node.props.scale ?? 1)
|
|
1790
|
+
num(id, "scale", node.props.scale ?? 1),
|
|
1791
|
+
num(id, "scaleX", node.props.scaleX ?? 1),
|
|
1792
|
+
num(id, "scaleY", node.props.scaleY ?? 1),
|
|
1793
|
+
num(id, "skewX", node.props.skewX ?? 0),
|
|
1794
|
+
num(id, "skewY", node.props.skewY ?? 0)
|
|
1228
1795
|
)
|
|
1229
1796
|
);
|
|
1230
1797
|
switch (node.type) {
|
|
1231
|
-
case "group":
|
|
1232
|
-
|
|
1798
|
+
case "group": {
|
|
1799
|
+
const childClips = node.props.clip ? [...clips, { transform: matrix, shape: node.props.clip }] : clips;
|
|
1800
|
+
for (const child of node.children) walk(child, matrix, opacity, childClips);
|
|
1233
1801
|
return;
|
|
1802
|
+
}
|
|
1234
1803
|
case "rect":
|
|
1235
1804
|
case "ellipse": {
|
|
1236
1805
|
const width = num(id, "width", node.props.width);
|
|
@@ -1250,7 +1819,8 @@ function evaluate(compiled, t) {
|
|
|
1250
1819
|
offsetY: -height * ay,
|
|
1251
1820
|
...fill !== void 0 && { fill },
|
|
1252
1821
|
...stroke !== void 0 && { stroke, strokeWidth },
|
|
1253
|
-
...node.type === "rect" && { radius: num(id, "radius", node.props.radius ?? 0) }
|
|
1822
|
+
...node.type === "rect" && { radius: num(id, "radius", node.props.radius ?? 0) },
|
|
1823
|
+
...clipSpread
|
|
1254
1824
|
});
|
|
1255
1825
|
return;
|
|
1256
1826
|
}
|
|
@@ -1267,7 +1837,8 @@ function evaluate(compiled, t) {
|
|
|
1267
1837
|
width,
|
|
1268
1838
|
height,
|
|
1269
1839
|
offsetX: -width * ax,
|
|
1270
|
-
offsetY: -height * ay
|
|
1840
|
+
offsetY: -height * ay,
|
|
1841
|
+
...clipSpread
|
|
1271
1842
|
});
|
|
1272
1843
|
return;
|
|
1273
1844
|
}
|
|
@@ -1284,7 +1855,8 @@ function evaluate(compiled, t) {
|
|
|
1284
1855
|
d: str(id, "d", node.props.d),
|
|
1285
1856
|
progress: Math.max(0, Math.min(1, num(id, "progress", node.props.progress ?? 1))),
|
|
1286
1857
|
...fill !== void 0 && { fill },
|
|
1287
|
-
...stroke !== void 0 && { stroke, strokeWidth: num(id, "strokeWidth", node.props.strokeWidth ?? 1) }
|
|
1858
|
+
...stroke !== void 0 && { stroke, strokeWidth: num(id, "strokeWidth", node.props.strokeWidth ?? 1) },
|
|
1859
|
+
...clipSpread
|
|
1288
1860
|
});
|
|
1289
1861
|
return;
|
|
1290
1862
|
}
|
|
@@ -1307,13 +1879,14 @@ function evaluate(compiled, t) {
|
|
|
1307
1879
|
fill: str(id, "fill", node.props.fill ?? "#ffffff"),
|
|
1308
1880
|
letterSpacing: num(id, "letterSpacing", node.props.letterSpacing ?? 0),
|
|
1309
1881
|
align: TEXT_ALIGN[ax] ?? "left",
|
|
1310
|
-
baseline: TEXT_BASELINE[ay] ?? "top"
|
|
1882
|
+
baseline: TEXT_BASELINE[ay] ?? "top",
|
|
1883
|
+
...clipSpread
|
|
1311
1884
|
});
|
|
1312
1885
|
return;
|
|
1313
1886
|
}
|
|
1314
1887
|
}
|
|
1315
1888
|
};
|
|
1316
|
-
for (const node of compiled.ir.nodes) walk(node, IDENTITY, 1);
|
|
1889
|
+
for (const node of compiled.ir.nodes) walk(node, IDENTITY, 1, []);
|
|
1317
1890
|
return ops;
|
|
1318
1891
|
}
|
|
1319
1892
|
|
|
@@ -1394,19 +1967,28 @@ function sketchToTimeline(sketch, nodeIds) {
|
|
|
1394
1967
|
return par(...steps);
|
|
1395
1968
|
}
|
|
1396
1969
|
export {
|
|
1970
|
+
DEFAULT_CROSSFADE,
|
|
1397
1971
|
DEFAULT_FPS,
|
|
1398
1972
|
DEFAULT_MOTIONPATH_DURATION,
|
|
1399
1973
|
DEFAULT_TO_DURATION,
|
|
1400
1974
|
DEFAULT_TWEEN_DURATION,
|
|
1975
|
+
DEVICE_PRESET_NAMES,
|
|
1401
1976
|
EASE_NAMES,
|
|
1977
|
+
MOTION_OPS,
|
|
1402
1978
|
PRESET_NAMES,
|
|
1403
1979
|
PROPS_BY_TYPE,
|
|
1404
1980
|
SFX_DURATION,
|
|
1405
1981
|
SceneValidationError,
|
|
1406
1982
|
beat,
|
|
1407
1983
|
collectImageSrcs,
|
|
1984
|
+
compileComposition,
|
|
1408
1985
|
compileScene,
|
|
1409
1986
|
composeScene,
|
|
1987
|
+
composition,
|
|
1988
|
+
deviceBounds,
|
|
1989
|
+
devicePreset,
|
|
1990
|
+
deviceScreen,
|
|
1991
|
+
deviceScreenCenter,
|
|
1410
1992
|
ellipse,
|
|
1411
1993
|
evaluate,
|
|
1412
1994
|
formatComposeReport,
|
|
@@ -1415,8 +1997,11 @@ export {
|
|
|
1415
1997
|
isColor,
|
|
1416
1998
|
lerpValue,
|
|
1417
1999
|
line,
|
|
2000
|
+
motionOp,
|
|
2001
|
+
motionOpLabel,
|
|
1418
2002
|
motionPath,
|
|
1419
2003
|
motionPreset,
|
|
2004
|
+
nodeParentMatrix,
|
|
1420
2005
|
oscillate,
|
|
1421
2006
|
par,
|
|
1422
2007
|
path,
|
|
@@ -1424,8 +2009,10 @@ export {
|
|
|
1424
2009
|
pathTangentAngle,
|
|
1425
2010
|
rect,
|
|
1426
2011
|
resolveAudioPlan,
|
|
2012
|
+
resolveCompositionAudioPlan,
|
|
1427
2013
|
resolveEase,
|
|
1428
2014
|
sampleBehavior,
|
|
2015
|
+
sampleProp,
|
|
1429
2016
|
scene,
|
|
1430
2017
|
seq,
|
|
1431
2018
|
sketchToTimeline,
|
|
@@ -1433,6 +2020,7 @@ export {
|
|
|
1433
2020
|
text,
|
|
1434
2021
|
to,
|
|
1435
2022
|
tween,
|
|
2023
|
+
validateComposition,
|
|
1436
2024
|
validateScene,
|
|
1437
2025
|
wait,
|
|
1438
2026
|
wiggle
|