@pixerate/editor 0.58.0 → 0.59.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/dist/animation/index.cjs +9 -3
- package/dist/animation/index.cjs.map +1 -1
- package/dist/animation/index.d.cts +2 -2
- package/dist/animation/index.d.ts +2 -2
- package/dist/animation/index.js +9 -3
- package/dist/animation-editor/index.cjs +456 -83
- package/dist/animation-editor/index.cjs.map +1 -1
- package/dist/animation-editor/index.d.cts +159 -9
- package/dist/animation-editor/index.d.ts +159 -9
- package/dist/animation-editor/index.js +415 -42
- package/dist/animation-editor/index.js.map +1 -1
- package/dist/canvas/index.cjs +35 -29
- package/dist/canvas/index.cjs.map +1 -1
- package/dist/canvas/index.d.cts +4 -4
- package/dist/canvas/index.d.ts +4 -4
- package/dist/canvas/index.js +9 -3
- package/dist/canvas/index.js.map +1 -1
- package/dist/{chunk-O4ONBPZW.cjs → chunk-2FGRTYJN.cjs} +4 -4
- package/dist/{chunk-O4ONBPZW.cjs.map → chunk-2FGRTYJN.cjs.map} +1 -1
- package/dist/{chunk-XZ4L3R4K.cjs → chunk-2FHLHZAL.cjs} +39 -3
- package/dist/chunk-2FHLHZAL.cjs.map +1 -0
- package/dist/{chunk-PYMV7HLI.js → chunk-3TLL5OID.js} +2 -2
- package/dist/{chunk-UB2OY7FD.js → chunk-AGR4P4UZ.js} +38 -2
- package/dist/chunk-AGR4P4UZ.js.map +1 -0
- package/dist/{chunk-SHOVAHUB.js → chunk-JK6M6V7G.js} +2 -2
- package/dist/{chunk-FKONE6AO.cjs → chunk-YJLCBA7V.cjs} +3 -3
- package/dist/{chunk-FKONE6AO.cjs.map → chunk-YJLCBA7V.cjs.map} +1 -1
- package/dist/{easing-CNGk-W6d.d.ts → easing-lM0Xo9Ni.d.cts} +14 -2
- package/dist/{easing-CNGk-W6d.d.cts → easing-lM0Xo9Ni.d.ts} +14 -2
- package/dist/parameters/index.cjs +2 -2
- package/dist/parameters/index.d.cts +1 -1
- package/dist/parameters/index.d.ts +1 -1
- package/dist/parameters/index.js +1 -1
- package/dist/{player-W-5DX3Go.d.ts → player-DV4Jujdq.d.ts} +1 -1
- package/dist/{player-Dg0FT0UI.d.cts → player-DrcP2xqj.d.cts} +1 -1
- package/dist/vector/index.cjs +472 -472
- package/dist/vector/index.d.cts +1 -1
- package/dist/vector/index.d.ts +1 -1
- package/dist/vector/index.js +2 -2
- package/package.json +1 -1
- package/dist/chunk-UB2OY7FD.js.map +0 -1
- package/dist/chunk-XZ4L3R4K.cjs.map +0 -1
- /package/dist/{chunk-PYMV7HLI.js.map → chunk-3TLL5OID.js.map} +0 -0
- /package/dist/{chunk-SHOVAHUB.js.map → chunk-JK6M6V7G.js.map} +0 -0
|
@@ -17,7 +17,8 @@
|
|
|
17
17
|
|
|
18
18
|
|
|
19
19
|
|
|
20
|
-
|
|
20
|
+
|
|
21
|
+
var _chunkYJLCBA7Vcjs = require('../chunk-YJLCBA7V.cjs');
|
|
21
22
|
|
|
22
23
|
|
|
23
24
|
|
|
@@ -33,7 +34,7 @@ var _chunkQKLRAA4Tcjs = require('../chunk-QKLRAA4T.cjs');
|
|
|
33
34
|
var _chunkZCNLBRT6cjs = require('../chunk-ZCNLBRT6.cjs');
|
|
34
35
|
|
|
35
36
|
|
|
36
|
-
var
|
|
37
|
+
var _chunk2FGRTYJNcjs = require('../chunk-2FGRTYJN.cjs');
|
|
37
38
|
|
|
38
39
|
|
|
39
40
|
var _chunkMR3Z4OQIcjs = require('../chunk-MR3Z4OQI.cjs');
|
|
@@ -47,7 +48,9 @@ var _chunkA6PHCAGXcjs = require('../chunk-A6PHCAGX.cjs');
|
|
|
47
48
|
|
|
48
49
|
|
|
49
50
|
|
|
50
|
-
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
var _chunk2FHLHZALcjs = require('../chunk-2FHLHZAL.cjs');
|
|
51
54
|
|
|
52
55
|
// src/animation-editor/types.ts
|
|
53
56
|
var MOTION_COMMAND_TYPES = [
|
|
@@ -71,6 +74,7 @@ var MOTION_COMMAND_TYPES = [
|
|
|
71
74
|
"wait"
|
|
72
75
|
];
|
|
73
76
|
var MOTION_ENTER_STYLES = ["fade", "pop", "grow", "slide", "draw", "zoom", "rise", "wipe", "iris", "split"];
|
|
77
|
+
var MOTION_REPEAT_MODES = ["loop", "mirror", "offset"];
|
|
74
78
|
var MOTION_EXIT_STYLES = ["fade", "shrink", "slide", "undraw", "wipe"];
|
|
75
79
|
var MOTION_IRIS_SHAPES = ["circle", "box"];
|
|
76
80
|
var MOTION_SPLIT_ORIENTATIONS = ["horizontal", "vertical"];
|
|
@@ -139,6 +143,7 @@ var MOTION_ISSUE_CODES = {
|
|
|
139
143
|
"exit-hidden": { severity: "warning", wording: "A node exits or animates while it is hidden." },
|
|
140
144
|
"out-of-frame": { severity: "warning", wording: "A node a command animates ends up outside the camera." },
|
|
141
145
|
"too-fast": { severity: "warning", wording: "A change is too quick to follow." },
|
|
146
|
+
"spring-cut-short": { severity: "warning", wording: "A spring is given a duration shorter than it takes to settle, so it jumps to its end." },
|
|
142
147
|
"scene-too-long": { severity: "warning", wording: "The scene runs past its target length." },
|
|
143
148
|
"scene-too-short": { severity: "warning", wording: "The scene ends before its target length." },
|
|
144
149
|
"unknown-control-color": { severity: "warning", wording: "A control stands for a colour the scene never uses." },
|
|
@@ -196,17 +201,17 @@ ${problems.map((p) => `- ${p}`).join("\n")}`);
|
|
|
196
201
|
this.problems = problems;
|
|
197
202
|
}
|
|
198
203
|
};
|
|
199
|
-
var SCENE_KEYS = ["version", "name", "description", "artwork", "shots", "loop", "fps", "target", "controls", "presets"];
|
|
204
|
+
var SCENE_KEYS = ["version", "name", "description", "artwork", "shots", "loop", "fps", "steps", "target", "controls", "presets"];
|
|
200
205
|
var SHOT_KEYS = ["id", "label", "camera", "run", "hold"];
|
|
201
|
-
var CAMERA_KEYS = ["x", "y", "width", "height", "frame", "padding", "ease", "ms"];
|
|
202
|
-
var BASE_KEYS = ["type", "targets", "duration", "delay", "stagger", "with", "ease", "note"];
|
|
206
|
+
var CAMERA_KEYS = ["x", "y", "width", "height", "frame", "padding", "ease", "ms", "geometric"];
|
|
207
|
+
var BASE_KEYS = ["type", "targets", "duration", "delay", "stagger", "with", "ease", "note", "repeat", "repeatMode", "steps"];
|
|
203
208
|
var COMMAND_KEYS = {
|
|
204
209
|
enter: [...BASE_KEYS, "style", "direction", "distance", "scale", "shape", "orientation"],
|
|
205
210
|
exit: [...BASE_KEYS, "style", "direction", "distance"],
|
|
206
211
|
draw: [...BASE_KEYS, "from", "to"],
|
|
207
212
|
move: [...BASE_KEYS, "by", "to"],
|
|
208
213
|
rotate: [...BASE_KEYS, "by", "to"],
|
|
209
|
-
scale: [...BASE_KEYS, "to"],
|
|
214
|
+
scale: [...BASE_KEYS, "to", "geometric"],
|
|
210
215
|
fade: [...BASE_KEYS, "to"],
|
|
211
216
|
style: [...BASE_KEYS, "fill", "stroke", "strokeWidth"],
|
|
212
217
|
morph: [...BASE_KEYS, "into"],
|
|
@@ -254,7 +259,13 @@ function commandShapeProblems(command2, where) {
|
|
|
254
259
|
nonNegative("duration");
|
|
255
260
|
nonNegative("delay");
|
|
256
261
|
nonNegative("stagger");
|
|
257
|
-
if (command2.ease !== void 0 && !
|
|
262
|
+
if (command2.ease !== void 0 && !_chunk2FHLHZALcjs.isAnimationEasing.call(void 0, command2.ease)) p(`"ease" is not an easing; use a preset such as "ease-in-out" or "spring-bouncy", or { "type": "cubic-bezier", \u2026 } or { "type": "spring", \u2026 }.`);
|
|
263
|
+
if (command2.repeat !== void 0) {
|
|
264
|
+
if (type === "enter" || type === "exit") p(`"repeat" is not for ${type} (it brings things in or out once); repeat an emphasis or a move instead.`);
|
|
265
|
+
else if (!(Number.isInteger(command2.repeat) && command2.repeat >= 1 && command2.repeat <= 1e3)) p(`"repeat" must be a whole number from 1 to 1000.`);
|
|
266
|
+
}
|
|
267
|
+
if (command2.repeatMode !== void 0 && !MOTION_REPEAT_MODES.includes(command2.repeatMode)) p(`"repeatMode" must be one of ${MOTION_REPEAT_MODES.join(", ")}.`);
|
|
268
|
+
if (command2.steps !== void 0 && !(isNum(command2.steps) && command2.steps > 0 && command2.steps <= 120)) p(`"steps" must be frames per second from above 0 to 120 (12 holds each pose for two frames at 24 fps).`);
|
|
258
269
|
switch (type) {
|
|
259
270
|
case "enter":
|
|
260
271
|
case "exit": {
|
|
@@ -286,6 +297,7 @@ function commandShapeProblems(command2, where) {
|
|
|
286
297
|
break;
|
|
287
298
|
case "scale":
|
|
288
299
|
if (!(isNum(command2.to) || isPoint(command2.to))) p(`"to" must be a number or { "x": number, "y": number }.`);
|
|
300
|
+
if (command2.geometric !== void 0 && typeof command2.geometric !== "boolean") p(`"geometric" must be true or false.`);
|
|
289
301
|
break;
|
|
290
302
|
case "fade":
|
|
291
303
|
if (!(isNum(command2.to) && command2.to >= 0 && command2.to <= 1)) p(`"to" must be an opacity from 0 to 1.`);
|
|
@@ -349,8 +361,9 @@ function shotShapeProblems(shot, where) {
|
|
|
349
361
|
if (typeof c.frame === "string") {
|
|
350
362
|
if (c.padding !== void 0 && !isNum(c.padding)) problems.push(`${name} camera: "padding" must be a number.`);
|
|
351
363
|
} else if (!isRect) problems.push(`${name} camera: give { x, y, width, height } or { frame: "<node id>" }.`);
|
|
352
|
-
if (c.ease !== void 0 && !
|
|
364
|
+
if (c.ease !== void 0 && !_chunk2FHLHZALcjs.isAnimationEasing.call(void 0, c.ease)) problems.push(`${name} camera: "ease" is not an easing.`);
|
|
353
365
|
if (c.ms !== void 0 && !(isNum(c.ms) && c.ms >= 0)) problems.push(`${name} camera: "ms" must be a number \u2265 0.`);
|
|
366
|
+
if (c.geometric !== void 0 && typeof c.geometric !== "boolean") problems.push(`${name} camera: "geometric" must be true or false.`);
|
|
354
367
|
}
|
|
355
368
|
}
|
|
356
369
|
if (!Array.isArray(shot.run)) problems.push(`${name}: "run" must be a list of commands (it may be empty).`);
|
|
@@ -369,6 +382,7 @@ function motionSceneShapeProblems(input) {
|
|
|
369
382
|
problems.push('Scene: "artwork" must be a vector document { width, height, nodes }.');
|
|
370
383
|
if (input.loop !== void 0 && !["none", "loop", "ping-pong"].includes(input.loop)) problems.push('Scene: "loop" must be "none", "loop" or "ping-pong".');
|
|
371
384
|
if (input.fps !== void 0 && !(isNum(input.fps) && input.fps > 0 && input.fps <= 120)) problems.push('Scene: "fps" must be a number from 1 to 120.');
|
|
385
|
+
if (input.steps !== void 0 && !(isNum(input.steps) && input.steps > 0 && input.steps <= 120)) problems.push('Scene: "steps" must be frames per second from above 0 to 120.');
|
|
372
386
|
if (input.target !== void 0 && !(Array.isArray(input.target) && input.target.length === 2 && input.target.every(isNum) && input.target[0] <= input.target[1]))
|
|
373
387
|
problems.push('Scene: "target" must be [min, max] seconds.');
|
|
374
388
|
if (input.controls !== void 0) {
|
|
@@ -412,21 +426,21 @@ function indexMotionNodes(nodes) {
|
|
|
412
426
|
const out = /* @__PURE__ */ new Map();
|
|
413
427
|
const walk = (list, parentWorld, ancestors) => {
|
|
414
428
|
for (const node of list) {
|
|
415
|
-
const local =
|
|
416
|
-
const bounds =
|
|
429
|
+
const local = _chunkYJLCBA7Vcjs.vectorNodeBounds.call(void 0, node);
|
|
430
|
+
const bounds = _chunkYJLCBA7Vcjs.vectorNodeBounds.call(void 0, node, parentWorld);
|
|
417
431
|
const center = local ? { x: local.x + local.width / 2, y: local.y + local.height / 2 } : { x: 0, y: 0 };
|
|
418
432
|
if (!out.has(node.id)) out.set(node.id, { node, ancestors, parentWorld, center, bounds, box: local });
|
|
419
|
-
if (node.type === "group") walk(node.children,
|
|
433
|
+
if (node.type === "group") walk(node.children, _chunkYJLCBA7Vcjs.multiplyMatrices.call(void 0, _chunkYJLCBA7Vcjs.nodeMatrix.call(void 0, node), parentWorld), [...ancestors, node.id]);
|
|
420
434
|
}
|
|
421
435
|
};
|
|
422
|
-
walk(nodes,
|
|
436
|
+
walk(nodes, _chunkYJLCBA7Vcjs.IDENTITY_MATRIX, []);
|
|
423
437
|
indexCache.set(nodes, out);
|
|
424
438
|
return out;
|
|
425
439
|
}
|
|
426
440
|
var solid = (paint) => typeof paint === "string" && paint !== "none" ? paint : void 0;
|
|
427
441
|
function firstPaint(node, key) {
|
|
428
442
|
if (node.type === "path") return solid(node[key]);
|
|
429
|
-
for (const p of
|
|
443
|
+
for (const p of _chunkYJLCBA7Vcjs.collectPathNodes.call(void 0, node)) {
|
|
430
444
|
const s = solid(p[key]);
|
|
431
445
|
if (s) return s;
|
|
432
446
|
}
|
|
@@ -434,7 +448,7 @@ function firstPaint(node, key) {
|
|
|
434
448
|
}
|
|
435
449
|
function strokeReach(node) {
|
|
436
450
|
let reach = 0;
|
|
437
|
-
for (const p of node.type === "path" ? [node] :
|
|
451
|
+
for (const p of node.type === "path" ? [node] : _chunkYJLCBA7Vcjs.collectPathNodes.call(void 0, node)) {
|
|
438
452
|
if (p.stroke == null || p.stroke === "none") continue;
|
|
439
453
|
const width = _nullishCoalesce(p.strokeWidth, () => ( 1));
|
|
440
454
|
reach = Math.max(reach, p.strokeAlign === "outside" ? width : p.strokeAlign === "inside" ? 0 : width / 2);
|
|
@@ -504,7 +518,7 @@ function motionRestValue(entry, property, artwork) {
|
|
|
504
518
|
case "stroke":
|
|
505
519
|
return _nullishCoalesce((node && firstPaint(node, property)), () => ( "transparent"));
|
|
506
520
|
case "strokeWidth": {
|
|
507
|
-
const path = _optionalChain([node, 'optionalAccess', _6 => _6.type]) === "path" ? node : node ?
|
|
521
|
+
const path = _optionalChain([node, 'optionalAccess', _6 => _6.type]) === "path" ? node : node ? _chunkYJLCBA7Vcjs.collectPathNodes.call(void 0, node)[0] : void 0;
|
|
508
522
|
return _nullishCoalesce(_optionalChain([path, 'optionalAccess', _7 => _7.strokeWidth]), () => ( 1));
|
|
509
523
|
}
|
|
510
524
|
case "shape":
|
|
@@ -535,11 +549,33 @@ function interpolateMotionValue(from, to, u) {
|
|
|
535
549
|
}
|
|
536
550
|
function segmentProgress(segment, time) {
|
|
537
551
|
const span = segment.end - segment.start;
|
|
538
|
-
|
|
539
|
-
|
|
552
|
+
let elapsed = time - segment.start;
|
|
553
|
+
if (segment.steps && segment.steps > 0 && elapsed < span) elapsed = Math.floor(elapsed * segment.steps / 1e3 + 1e-9) / segment.steps * 1e3;
|
|
554
|
+
const u = span <= 0 ? 1 : Math.min(1, Math.max(0, elapsed / span));
|
|
555
|
+
return { u, eased: _chunk2FHLHZALcjs.evaluateEasing.call(void 0, segment.ease, segment.reverse ? 1 - u : u, span) };
|
|
556
|
+
}
|
|
557
|
+
function geometricValue(from, to, e) {
|
|
558
|
+
const grow = (a, b) => a > 0 && b > 0 ? a * (b / a) ** e : void 0;
|
|
559
|
+
if (typeof from === "number" && typeof to === "number") return grow(from, to);
|
|
560
|
+
if (Array.isArray(from) && Array.isArray(to) && from.length === 4 && to.length === 4) {
|
|
561
|
+
const [x0, y0, w0, h0] = from;
|
|
562
|
+
const [x1, y1, w1, h1] = to;
|
|
563
|
+
const w = grow(w0, w1);
|
|
564
|
+
const h = grow(h0, h1);
|
|
565
|
+
if (w === void 0 || h === void 0) return void 0;
|
|
566
|
+
const f = Math.abs(w1 - w0) > 1e-9 ? (w - w0) / (w1 - w0) : e;
|
|
567
|
+
const cx = x0 + w0 / 2 + (x1 + w1 / 2 - x0 - w0 / 2) * f;
|
|
568
|
+
const cy = y0 + h0 / 2 + (y1 + h1 / 2 - y0 - h0 / 2) * f;
|
|
569
|
+
return [cx - w / 2, cy - h / 2, w, h];
|
|
570
|
+
}
|
|
571
|
+
return void 0;
|
|
540
572
|
}
|
|
541
573
|
function segmentValue(segment, time, raw = false) {
|
|
542
574
|
const { eased } = segmentProgress(segment, time);
|
|
575
|
+
if (segment.geometric && !segment.behavior) {
|
|
576
|
+
const g = geometricValue(segment.from, segment.to, eased);
|
|
577
|
+
if (g !== void 0) return g;
|
|
578
|
+
}
|
|
543
579
|
if (!segment.behavior || raw) return interpolateMotionValue(segment.from, segment.to, eased);
|
|
544
580
|
const p = _chunkZCNLBRT6cjs.behaviorProgress.call(void 0, segment.behavior, eased, _nullishCoalesce(segment.index, () => ( 0)), segment.end - segment.start);
|
|
545
581
|
return typeof segment.from === "string" ? interpolateMotionValue(segment.from, segment.to, Math.min(1, Math.max(0, p))) : interpolateMotionValue(segment.from, segment.to, p);
|
|
@@ -579,20 +615,32 @@ function resolveMotionCamera(camera, scene) {
|
|
|
579
615
|
const r = camera;
|
|
580
616
|
return { x: r.x, y: r.y, width: r.width, height: r.height };
|
|
581
617
|
}
|
|
582
|
-
function motionPathLength(node, parentWorld =
|
|
618
|
+
function motionPathLength(node, parentWorld = _chunkYJLCBA7Vcjs.IDENTITY_MATRIX) {
|
|
583
619
|
let longest = 0;
|
|
584
|
-
const paths = node.type === "path" ? [node] :
|
|
620
|
+
const paths = node.type === "path" ? [node] : _chunkYJLCBA7Vcjs.collectPathNodes.call(void 0, node);
|
|
585
621
|
for (const p of paths) {
|
|
586
622
|
try {
|
|
587
|
-
longest = Math.max(longest, _chunkO2UH7LZEcjs.bezierPathLength.call(void 0,
|
|
623
|
+
longest = Math.max(longest, _chunkO2UH7LZEcjs.bezierPathLength.call(void 0, _chunkYJLCBA7Vcjs.vectorPathWorldBezier.call(void 0, p, parentWorld, { effects: false })));
|
|
588
624
|
} catch (e2) {
|
|
589
625
|
}
|
|
590
626
|
}
|
|
591
627
|
return longest;
|
|
592
628
|
}
|
|
629
|
+
function motionCommandRepeat(command2) {
|
|
630
|
+
if (command2.type === "wait" || command2.type === "enter" || command2.type === "exit") return 1;
|
|
631
|
+
const n = _nullishCoalesce(command2.repeat, () => ( 1));
|
|
632
|
+
return Number.isInteger(n) && n >= 1 ? n : 1;
|
|
633
|
+
}
|
|
593
634
|
function motionCommandDuration(command2, scene, target) {
|
|
635
|
+
return motionCycleDuration(command2, scene, target) * motionCommandRepeat(command2);
|
|
636
|
+
}
|
|
637
|
+
function motionCycleDuration(command2, scene, target) {
|
|
594
638
|
if (command2.type === "wait") return command2.duration;
|
|
595
639
|
if (command2.duration !== void 0) return command2.duration;
|
|
640
|
+
if (command2.ease !== void 0) {
|
|
641
|
+
const ease = _chunk2FHLHZALcjs.resolveEasing.call(void 0, command2.ease);
|
|
642
|
+
if (ease !== "hold" && ease.type === "spring") return _chunk2FHLHZALcjs.springSettleMs.call(void 0, ease);
|
|
643
|
+
}
|
|
596
644
|
if (command2.type === "draw" || command2.type === "enter" && command2.style === "draw" || command2.type === "exit" && command2.style === "undraw") {
|
|
597
645
|
const entry = target ? indexMotionNodes(scene.artwork.nodes).get(target) : void 0;
|
|
598
646
|
const length = entry ? motionPathLength(entry.node) : 0;
|
|
@@ -621,7 +669,7 @@ function compileMotionScene(scene) {
|
|
|
621
669
|
const rect = resolveMotionCamera(shot.camera, scene);
|
|
622
670
|
if (rect && rect.width > 0 && rect.height > 0) {
|
|
623
671
|
const ms = index === 0 ? 0 : _nullishCoalesce(shot.camera.ms, () => ( MOTION_CAMERA_MS));
|
|
624
|
-
cameraMoves.push({ shot: shot.id, start, end: start + ms, to: rect, ease: _nullishCoalesce(shot.camera.ease, () => ( MOTION_EASE_IN_OUT)) });
|
|
672
|
+
cameraMoves.push({ shot: shot.id, start, end: start + ms, to: rect, ease: _nullishCoalesce(shot.camera.ease, () => ( MOTION_EASE_IN_OUT)), geometric: shot.camera.geometric === true });
|
|
625
673
|
cursor = start + ms;
|
|
626
674
|
}
|
|
627
675
|
}
|
|
@@ -662,7 +710,7 @@ function compileMotionScene(scene) {
|
|
|
662
710
|
jobs.push({
|
|
663
711
|
start: move.start,
|
|
664
712
|
order: order++,
|
|
665
|
-
run: () => push({ node: "", property: "camera", start: move.start, end: move.end, from: valueAt("", "camera", move.start), to: [move.to.x, move.to.y, move.to.width, move.to.height], ease: move.ease, shot: move.shot, command: -1 })
|
|
713
|
+
run: () => push({ node: "", property: "camera", start: move.start, end: move.end, from: valueAt("", "camera", move.start), to: [move.to.x, move.to.y, move.to.width, move.to.height], ease: move.ease, shot: move.shot, command: -1, ...move.geometric ? { geometric: true } : {} })
|
|
666
714
|
});
|
|
667
715
|
}
|
|
668
716
|
for (const timing of commands) {
|
|
@@ -673,11 +721,37 @@ function compileMotionScene(scene) {
|
|
|
673
721
|
const entry = nodes.get(target);
|
|
674
722
|
if (!entry) return;
|
|
675
723
|
const start = timing.start + k * (_nullishCoalesce(command2.stagger, () => ( 0)));
|
|
676
|
-
const
|
|
677
|
-
jobs.push({ start, order: order++, run: () =>
|
|
724
|
+
const cycle = motionCycleDuration(command2, scene, target);
|
|
725
|
+
jobs.push({ start, order: order++, run: () => compileRepeated(command2, target, entry, start, cycle, timing) });
|
|
678
726
|
});
|
|
679
727
|
}
|
|
680
728
|
jobs.sort((a, b) => a.start - b.start || a.order - b.order).forEach((job) => job.run());
|
|
729
|
+
function compileRepeated(command2, node, entry, start, cycle, timing) {
|
|
730
|
+
const first = segments.length;
|
|
731
|
+
compileCommand(command2, node, entry, start, cycle, timing);
|
|
732
|
+
const made = segments.slice(first);
|
|
733
|
+
const steps = _nullishCoalesce(command2.steps, () => ( scene.steps));
|
|
734
|
+
if (steps && steps > 0) for (const s of made) s.steps = steps;
|
|
735
|
+
const n = motionCommandRepeat(command2);
|
|
736
|
+
const mode = _nullishCoalesce(command2.repeatMode, () => ( "loop"));
|
|
737
|
+
const shift = (v, by, k) => {
|
|
738
|
+
if (typeof v === "number" && typeof by === "number") return v + by * k;
|
|
739
|
+
if (Array.isArray(v) && Array.isArray(by) && v.length === by.length) return v.map((x, i) => x + by[i] * k);
|
|
740
|
+
return v;
|
|
741
|
+
};
|
|
742
|
+
for (let k = 1; k < n; k++) {
|
|
743
|
+
const at = start + k * cycle;
|
|
744
|
+
for (const s of made) {
|
|
745
|
+
if (mode === "mirror" && k % 2 === 1) {
|
|
746
|
+
push({ ...s, start: at + (start + cycle - s.end), end: at + (start + cycle - s.start), reverse: !s.reverse });
|
|
747
|
+
continue;
|
|
748
|
+
}
|
|
749
|
+
const delta = typeof s.from === "number" && typeof s.to === "number" ? s.to - s.from : Array.isArray(s.from) && Array.isArray(s.to) && s.from.length === s.to.length ? s.to.map((x, i) => x - s.from[i]) : 0;
|
|
750
|
+
const by = mode === "offset" ? delta : 0;
|
|
751
|
+
push({ ...s, start: at + (s.start - start), end: at + (s.end - start), from: shift(s.from, by, k), to: shift(s.to, by, k) });
|
|
752
|
+
}
|
|
753
|
+
}
|
|
754
|
+
}
|
|
681
755
|
function compileCommand(command2, node, entry, start, duration2, timing) {
|
|
682
756
|
const end = start + duration2;
|
|
683
757
|
const where = { shot: timing.shot, command: timing.command };
|
|
@@ -758,8 +832,8 @@ function compileMotionScene(scene) {
|
|
|
758
832
|
let dx = _nullishCoalesce(_optionalChain([command2, 'access', _16 => _16.by, 'optionalAccess', _17 => _17.x]), () => ( 0));
|
|
759
833
|
let dy = _nullishCoalesce(_optionalChain([command2, 'access', _18 => _18.by, 'optionalAccess', _19 => _19.y]), () => ( 0));
|
|
760
834
|
if (command2.to) {
|
|
761
|
-
const inv =
|
|
762
|
-
const target =
|
|
835
|
+
const inv = _chunkYJLCBA7Vcjs.invertMatrix.call(void 0, entry.parentWorld);
|
|
836
|
+
const target = _chunkYJLCBA7Vcjs.applyMatrix.call(void 0, inv, command2.to);
|
|
763
837
|
dx = target.x - (entry.center.x + num("x"));
|
|
764
838
|
dy = target.y - (entry.center.y + num("y"));
|
|
765
839
|
}
|
|
@@ -772,8 +846,9 @@ function compileMotionScene(scene) {
|
|
|
772
846
|
break;
|
|
773
847
|
case "scale": {
|
|
774
848
|
const to = typeof command2.to === "number" ? { x: command2.to, y: command2.to } : command2.to;
|
|
775
|
-
|
|
776
|
-
seg("
|
|
849
|
+
const geometric = command2.geometric ? { geometric: true } : {};
|
|
850
|
+
seg("scaleX", to.x, _nullishCoalesce(ease, () => ( MOTION_EASE_IN_OUT)), start, end, geometric);
|
|
851
|
+
seg("scaleY", to.y, _nullishCoalesce(ease, () => ( MOTION_EASE_IN_OUT)), start, end, geometric);
|
|
777
852
|
break;
|
|
778
853
|
}
|
|
779
854
|
case "fade":
|
|
@@ -877,13 +952,13 @@ function morphBetween(a, b) {
|
|
|
877
952
|
let inner = morphCache.get(a);
|
|
878
953
|
if (!inner) morphCache.set(a, inner = /* @__PURE__ */ new WeakMap());
|
|
879
954
|
let morph = inner.get(b);
|
|
880
|
-
if (!morph) inner.set(b, morph =
|
|
955
|
+
if (!morph) inner.set(b, morph = _chunkYJLCBA7Vcjs.createVectorMorph.call(void 0, a, b, { id: a.id }));
|
|
881
956
|
return morph;
|
|
882
957
|
}
|
|
883
958
|
function shapeIn(entry, shape) {
|
|
884
959
|
if (shape === entry) return entry.node;
|
|
885
|
-
const m =
|
|
886
|
-
return { ...shape.node, ...
|
|
960
|
+
const m = _chunkYJLCBA7Vcjs.multiplyMatrices.call(void 0, _chunkYJLCBA7Vcjs.multiplyMatrices.call(void 0, _chunkYJLCBA7Vcjs.nodeMatrix.call(void 0, shape.node), shape.parentWorld), _chunkYJLCBA7Vcjs.invertMatrix.call(void 0, entry.parentWorld));
|
|
961
|
+
return { ...shape.node, ..._chunkYJLCBA7Vcjs.isIdentityMatrix.call(void 0, m) ? { transform: void 0 } : { transform: m } };
|
|
887
962
|
}
|
|
888
963
|
function shapeAt(entry, timeline, nodes, time) {
|
|
889
964
|
const track = timeline.tracks.get(motionTrackKey(entry.node.id, "shape"));
|
|
@@ -903,7 +978,7 @@ function shapeAt(entry, timeline, nodes, time) {
|
|
|
903
978
|
function motionFollowRoute(entry, path) {
|
|
904
979
|
if (!path || path.node.type !== "path") return null;
|
|
905
980
|
try {
|
|
906
|
-
return _chunkO2UH7LZEcjs.transformBezierPath.call(void 0,
|
|
981
|
+
return _chunkO2UH7LZEcjs.transformBezierPath.call(void 0, _chunkYJLCBA7Vcjs.vectorPathWorldBezier.call(void 0, path.node, path.parentWorld, { effects: false }), _chunkYJLCBA7Vcjs.invertMatrix.call(void 0, entry.parentWorld));
|
|
907
982
|
} catch (e3) {
|
|
908
983
|
return null;
|
|
909
984
|
}
|
|
@@ -935,9 +1010,9 @@ function motionNodeOffset(entry, timeline, time, nodes = indexMotionNodes([entry
|
|
|
935
1010
|
const scaleX = read("scaleX", 1);
|
|
936
1011
|
const scaleY = read("scaleY", 1);
|
|
937
1012
|
const c = entry.center;
|
|
938
|
-
let matrix =
|
|
1013
|
+
let matrix = _chunkYJLCBA7Vcjs.multiplyMatrices.call(void 0, _chunkYJLCBA7Vcjs.multiplyMatrices.call(void 0, _chunkYJLCBA7Vcjs.scaleMatrix.call(void 0, scaleX, scaleY, c), _chunkYJLCBA7Vcjs.rotateMatrix.call(void 0, rotation, c)), _chunkYJLCBA7Vcjs.translateMatrix.call(void 0, dx, dy));
|
|
939
1014
|
const along = follow ? followAt(entry, timeline, nodes, time) : null;
|
|
940
|
-
if (along) matrix =
|
|
1015
|
+
if (along) matrix = _chunkYJLCBA7Vcjs.multiplyMatrices.call(void 0, matrix, _chunkYJLCBA7Vcjs.multiplyMatrices.call(void 0, _chunkYJLCBA7Vcjs.multiplyMatrices.call(void 0, _chunkYJLCBA7Vcjs.translateMatrix.call(void 0, -c.x, -c.y), _chunkYJLCBA7Vcjs.rotateMatrix.call(void 0, along.angle)), _chunkYJLCBA7Vcjs.translateMatrix.call(void 0, along.x, along.y)));
|
|
941
1016
|
return { matrix, dx, dy, rotation, scaleX, scaleY, follow: along };
|
|
942
1017
|
}
|
|
943
1018
|
function motionNodeClips(id, timeline, time) {
|
|
@@ -1004,7 +1079,7 @@ function motionFrameAt(scene, time, options = {}) {
|
|
|
1004
1079
|
const start = Math.min(1, Math.max(0, typeof drawStart === "number" ? drawStart : 0));
|
|
1005
1080
|
const end = Math.min(1, Math.max(0, typeof drawEnd === "number" ? drawEnd : 1));
|
|
1006
1081
|
if (start > 0 || end < 1) {
|
|
1007
|
-
const trim =
|
|
1082
|
+
const trim = _chunkYJLCBA7Vcjs.createVectorEffect.call(void 0, "trim", MOTION_DRAW_EFFECT_ID, { start: tidy(start * 100), end: tidy(end * 100) });
|
|
1008
1083
|
p = { ...p, effects: [trim, ...(_nullishCoalesce(p.effects, () => ( []))).filter((e) => e.id !== MOTION_DRAW_EFFECT_ID)] };
|
|
1009
1084
|
}
|
|
1010
1085
|
}
|
|
@@ -1018,8 +1093,8 @@ function motionFrameAt(scene, time, options = {}) {
|
|
|
1018
1093
|
if (o >= 1) delete out.opacity;
|
|
1019
1094
|
}
|
|
1020
1095
|
const offset = options.offsets === false ? null : motionNodeOffset(entry, timeline, time, nodes);
|
|
1021
|
-
if (offset && !
|
|
1022
|
-
const m =
|
|
1096
|
+
if (offset && !_chunkYJLCBA7Vcjs.isIdentityMatrix.call(void 0, offset.matrix)) {
|
|
1097
|
+
const m = _chunkYJLCBA7Vcjs.multiplyMatrices.call(void 0, _chunkYJLCBA7Vcjs.nodeMatrix.call(void 0, out), offset.matrix).map(tidy);
|
|
1023
1098
|
out = { ...out, transform: m };
|
|
1024
1099
|
}
|
|
1025
1100
|
return out;
|
|
@@ -1039,7 +1114,7 @@ function motionFrameToSvg(scene, time, options = {}) {
|
|
|
1039
1114
|
const wrap = { ...options.wrap };
|
|
1040
1115
|
for (const [id, clips] of Object.entries(frame.clips))
|
|
1041
1116
|
wrap[id] = [..._nullishCoalesce(wrap[id], () => ( [])), ...clips.map((clip, i) => ({ clip: { id: `${prefix}reveal-${id}-${i}`, shape: clip.shape, attrs: motionClipAttrs(clip) } }))];
|
|
1042
|
-
return withViewBox(
|
|
1117
|
+
return withViewBox(_chunkYJLCBA7Vcjs.vectorDocumentToSvg.call(void 0, frame.document, { ...options, wrap }), frame.camera);
|
|
1043
1118
|
}
|
|
1044
1119
|
function withViewBox(svg, box) {
|
|
1045
1120
|
const f = (n) => String(Math.round(n * 1e3) / 1e3);
|
|
@@ -1079,7 +1154,7 @@ function visibleAt(timeline, id, time, node) {
|
|
|
1079
1154
|
if (Array.isArray(rect) && (rect[2] <= 1e-4 || rect[3] <= 1e-4)) return false;
|
|
1080
1155
|
const circle = clip("clipCircle");
|
|
1081
1156
|
if (Array.isArray(circle) && circle[2] <= 1e-4) return false;
|
|
1082
|
-
const hasFill = node.type === "path" ? !!node.fill && node.fill !== "none" :
|
|
1157
|
+
const hasFill = node.type === "path" ? !!node.fill && node.fill !== "none" : _chunkYJLCBA7Vcjs.collectPathNodes.call(void 0, node).some((p) => p.fill && p.fill !== "none");
|
|
1083
1158
|
return num("drawEnd", 1) > 0.01 || hasFill && num("fillOpacity", 1) > 0.01;
|
|
1084
1159
|
}
|
|
1085
1160
|
function checkMotionScene(scene) {
|
|
@@ -1115,6 +1190,14 @@ function checkMotionScene(scene) {
|
|
|
1115
1190
|
const timing = timeline.commands.find((c) => c.shot === shot.id && c.command === i);
|
|
1116
1191
|
if (timing && timing.duration > 0 && timing.duration < MOTION_MIN_CHANGE_MS)
|
|
1117
1192
|
issues.push(motionIssue("too-fast", `This ${command2.type} lasts ${timing.duration} ms, too quick to follow; make it at least ${MOTION_MIN_CHANGE_MS} ms.`, { ...where, measured: timing.duration, needed: MOTION_MIN_CHANGE_MS, unit: "ms", ids: targets }));
|
|
1193
|
+
if (command2.duration !== void 0 && command2.ease !== void 0) {
|
|
1194
|
+
const ease = _chunk2FHLHZALcjs.resolveEasing.call(void 0, command2.ease);
|
|
1195
|
+
if (ease !== "hold" && ease.type === "spring") {
|
|
1196
|
+
const settle = _chunk2FHLHZALcjs.springSettleMs.call(void 0, ease);
|
|
1197
|
+
if (command2.duration < settle * 0.9)
|
|
1198
|
+
issues.push(motionIssue("spring-cut-short", `This ${command2.type}'s spring takes ${settle} ms to settle but lasts ${command2.duration} ms, so it jumps to its end; leave "duration" out (the spring sets it) or make it at least ${settle} ms.`, { ...where, measured: command2.duration, needed: settle, unit: "ms", ids: targets }));
|
|
1199
|
+
}
|
|
1200
|
+
}
|
|
1118
1201
|
if (command2.type === "follow" && known(command2.path, "unknown-path", "path", where) && nodes.get(command2.path).node.type !== "path")
|
|
1119
1202
|
issues.push(motionIssue("not-a-path", `"${command2.path}" is a group; follow a single path node.`, { ...where, ids: [command2.path] }));
|
|
1120
1203
|
if (command2.type === "morph") known(command2.into, "unknown-shape", "shape", where);
|
|
@@ -1134,7 +1217,7 @@ function checkMotionScene(scene) {
|
|
|
1134
1217
|
}
|
|
1135
1218
|
for (const id of targets) {
|
|
1136
1219
|
const node = nodes.get(id).node;
|
|
1137
|
-
const paths = node.type === "path" ? [node] :
|
|
1220
|
+
const paths = node.type === "path" ? [node] : _chunkYJLCBA7Vcjs.collectPathNodes.call(void 0, node);
|
|
1138
1221
|
if (paths.some((p) => p[key] !== void 0 && p[key] !== null && typeof p[key] !== "string"))
|
|
1139
1222
|
issues.push(motionIssue("paint-not-color", `"${id}" has a gradient ${key}; it switches to ${color} at the end instead of blending.`, { ...where, ids: [id] }));
|
|
1140
1223
|
}
|
|
@@ -1147,7 +1230,7 @@ function checkMotionScene(scene) {
|
|
|
1147
1230
|
else
|
|
1148
1231
|
for (const id of targets) {
|
|
1149
1232
|
const node = nodes.get(id).node;
|
|
1150
|
-
const paths = node.type === "path" ? [node] :
|
|
1233
|
+
const paths = node.type === "path" ? [node] : _chunkYJLCBA7Vcjs.collectPathNodes.call(void 0, node);
|
|
1151
1234
|
if (paths.some((p) => typeof p[key] !== "string"))
|
|
1152
1235
|
issues.push(motionIssue("paint-not-color", `"${id}" has a gradient or no ${key}, so a colour pulse cannot tint it; give it a solid ${key} or pulse the ${key === "fill" ? "stroke" : "fill"}.`, { ...where, ids: [id] }));
|
|
1153
1236
|
}
|
|
@@ -1272,6 +1355,11 @@ function applyOne(scene, edit) {
|
|
|
1272
1355
|
else if (edit.description !== void 0) next.description = edit.description;
|
|
1273
1356
|
if (edit.loop !== void 0) next.loop = edit.loop;
|
|
1274
1357
|
if (edit.fps !== void 0) next.fps = edit.fps;
|
|
1358
|
+
if (edit.steps === null) delete next.steps;
|
|
1359
|
+
else if (edit.steps !== void 0) {
|
|
1360
|
+
if (!(typeof edit.steps === "number" && edit.steps > 0 && edit.steps <= 120)) fail('"steps" must be frames per second from above 0 to 120 (12 is "on twos" at 24 fps), or null for smooth.');
|
|
1361
|
+
next.steps = edit.steps;
|
|
1362
|
+
}
|
|
1275
1363
|
if (edit.target === null) delete next.target;
|
|
1276
1364
|
else if (edit.target !== void 0) next.target = edit.target;
|
|
1277
1365
|
return next;
|
|
@@ -1407,12 +1495,19 @@ var FLAIR_EASES = {
|
|
|
1407
1495
|
"in-out": (t) => t < 0.5 ? 4 * t * t * t : 1 - (-2 * t + 2) ** 3 / 2,
|
|
1408
1496
|
out: (t) => 1 - (1 - t) ** 3
|
|
1409
1497
|
};
|
|
1498
|
+
var FLAIR_EXPORT_VERSION = "0.39";
|
|
1499
|
+
function flairAtLeast(version, min) {
|
|
1500
|
+
const a = version.split(".").map((n) => parseInt(n, 10) || 0);
|
|
1501
|
+
const b = min.split(".").map((n) => parseInt(n, 10) || 0);
|
|
1502
|
+
for (let i = 0; i < Math.max(a.length, b.length); i++) if ((_nullishCoalesce(a[i], () => ( 0))) !== (_nullishCoalesce(b[i], () => ( 0)))) return (_nullishCoalesce(a[i], () => ( 0))) > (_nullishCoalesce(b[i], () => ( 0)));
|
|
1503
|
+
return true;
|
|
1504
|
+
}
|
|
1410
1505
|
function flairEaseFor(ease) {
|
|
1411
1506
|
for (const name of ["linear", "out", "in-out"]) {
|
|
1412
1507
|
let worst = 0;
|
|
1413
1508
|
for (let i = 1; i < 40; i++) {
|
|
1414
1509
|
const t = i / 40;
|
|
1415
|
-
worst = Math.max(worst, Math.abs(
|
|
1510
|
+
worst = Math.max(worst, Math.abs(_chunk2FHLHZALcjs.evaluateEasing.call(void 0, ease, t, 1e3) - FLAIR_EASES[name](t)));
|
|
1416
1511
|
}
|
|
1417
1512
|
if (worst < 0.01) return name;
|
|
1418
1513
|
}
|
|
@@ -1421,11 +1516,11 @@ function flairEaseFor(ease) {
|
|
|
1421
1516
|
function flairCurveFor(ease, curves = true) {
|
|
1422
1517
|
const name = flairEaseFor(ease);
|
|
1423
1518
|
if (name || !curves) return name;
|
|
1424
|
-
const resolved =
|
|
1519
|
+
const resolved = _chunk2FHLHZALcjs.resolveEasing.call(void 0, ease);
|
|
1425
1520
|
if (resolved === "hold" || resolved.type !== "cubic-bezier") return void 0;
|
|
1426
1521
|
return [resolved.x1, resolved.y1, resolved.x2, resolved.y2];
|
|
1427
1522
|
}
|
|
1428
|
-
var overshoots = (ease) => typeof ease !== "string" && (ease[1] < 0 || ease[1] > 1 || ease[3] < 0 || ease[3] > 1);
|
|
1523
|
+
var overshoots = (ease) => typeof ease !== "string" && ("spring" in ease || ease[1] < 0 || ease[1] > 1 || ease[3] < 0 || ease[3] > 1);
|
|
1429
1524
|
var TRANSFORM_PROPS2 = ["x", "y", "rotation", "scaleX", "scaleY", "follow"];
|
|
1430
1525
|
var LINEAR_TRANSFORM = /* @__PURE__ */ new Set(["x", "y", "rotation", "scaleX", "scaleY"]);
|
|
1431
1526
|
function hex6(color) {
|
|
@@ -1435,9 +1530,24 @@ function hex6(color) {
|
|
|
1435
1530
|
function toFlairSvgMotion(scene, options = {}) {
|
|
1436
1531
|
const fps = _nullishCoalesce(_nullishCoalesce(options.fps, () => ( scene.fps)), () => ( 30));
|
|
1437
1532
|
const precision = _nullishCoalesce(options.precision, () => ( 3));
|
|
1438
|
-
const
|
|
1439
|
-
const
|
|
1440
|
-
const
|
|
1533
|
+
const flair = _nullishCoalesce(options.flair, () => ( FLAIR_EXPORT_VERSION));
|
|
1534
|
+
const curves = _nullishCoalesce(options.curves, () => ( flairAtLeast(flair, "0.36")));
|
|
1535
|
+
const behaviors = _nullishCoalesce(options.behaviors, () => ( flairAtLeast(flair, "0.38")));
|
|
1536
|
+
const springs = flairAtLeast(flair, "0.39");
|
|
1537
|
+
const strobes = flairAtLeast(flair, "0.39");
|
|
1538
|
+
const writeEase = (ease) => typeof ease === "string" || "spring" in ease ? ease : ease.map(round2);
|
|
1539
|
+
const easeOf = (segment) => {
|
|
1540
|
+
const resolved = _chunk2FHLHZALcjs.resolveEasing.call(void 0, segment.ease);
|
|
1541
|
+
if (resolved !== "hold" && resolved.type === "spring") {
|
|
1542
|
+
if (!springs || segment.reverse) return void 0;
|
|
1543
|
+
const { mass, stiffness, damping, velocity } = resolved;
|
|
1544
|
+
return { spring: { mass, stiffness, damping, velocity } };
|
|
1545
|
+
}
|
|
1546
|
+
if (!segment.reverse) return flairCurveFor(segment.ease, curves);
|
|
1547
|
+
const back = _chunk2FHLHZALcjs.reverseCubicBezier.call(void 0, segment.ease);
|
|
1548
|
+
return back && curves ? flairCurveFor(back, curves) : void 0;
|
|
1549
|
+
};
|
|
1550
|
+
const plays = (s) => (!s.behavior || behaviors) && (!s.steps || strobes) && !s.geometric;
|
|
1441
1551
|
const round2 = (n) => Math.round(n * 10 ** precision) / 10 ** precision;
|
|
1442
1552
|
const f = (n) => String(round2(n));
|
|
1443
1553
|
const timeline = compileMotionScene(scene);
|
|
@@ -1520,7 +1630,7 @@ function toFlairSvgMotion(scene, options = {}) {
|
|
|
1520
1630
|
raw: (t) => transformAt(t, true)
|
|
1521
1631
|
});
|
|
1522
1632
|
}
|
|
1523
|
-
const paths = node.type === "path" ? [node] :
|
|
1633
|
+
const paths = node.type === "path" ? [node] : _chunkYJLCBA7Vcjs.collectPathNodes.call(void 0, node);
|
|
1524
1634
|
for (const path of paths) {
|
|
1525
1635
|
if (pathsSeen.has(path.id)) continue;
|
|
1526
1636
|
pathsSeen.add(path.id);
|
|
@@ -1542,7 +1652,7 @@ function toFlairSvgMotion(scene, options = {}) {
|
|
|
1542
1652
|
if (shapeSegments.length) {
|
|
1543
1653
|
if (node.type !== "path") notes.push(`"${id}" is a group: its morph or effect animation is not exported (Flair tweens one path's "d").`);
|
|
1544
1654
|
else {
|
|
1545
|
-
const inverse =
|
|
1655
|
+
const inverse = _chunkYJLCBA7Vcjs.invertMatrix.call(void 0, _chunkYJLCBA7Vcjs.nodeMatrix.call(void 0, node));
|
|
1546
1656
|
channels.push({
|
|
1547
1657
|
id,
|
|
1548
1658
|
kind: "attr",
|
|
@@ -1551,9 +1661,9 @@ function toFlairSvgMotion(scene, options = {}) {
|
|
|
1551
1661
|
exact: () => false,
|
|
1552
1662
|
value: (t) => {
|
|
1553
1663
|
const frame = motionFrameAt(scene, t, { draw: false, offsets: false });
|
|
1554
|
-
const now =
|
|
1664
|
+
const now = _chunkYJLCBA7Vcjs.expandVectorAppearance.call(void 0, _optionalChain([indexMotionNodes, 'call', _37 => _37(frame.document.nodes), 'access', _38 => _38.get, 'call', _39 => _39(id), 'optionalAccess', _40 => _40.node]));
|
|
1555
1665
|
if (!now || now.type !== "path") return "";
|
|
1556
|
-
const bezier = _chunkO2UH7LZEcjs.transformBezierPath.call(void 0,
|
|
1666
|
+
const bezier = _chunkO2UH7LZEcjs.transformBezierPath.call(void 0, _chunkYJLCBA7Vcjs.vectorPathWorldBezier.call(void 0, now, _chunkYJLCBA7Vcjs.IDENTITY_MATRIX, { effects: false }), inverse);
|
|
1557
1667
|
return _chunkO2UH7LZEcjs.bezierPathToSvg.call(void 0, bezier, { precision: Math.min(precision, 2) });
|
|
1558
1668
|
}
|
|
1559
1669
|
});
|
|
@@ -1565,8 +1675,16 @@ function toFlairSvgMotion(scene, options = {}) {
|
|
|
1565
1675
|
const animate = [];
|
|
1566
1676
|
let sampled = 0;
|
|
1567
1677
|
const same = (a, b) => JSON.stringify(a) === JSON.stringify(b);
|
|
1568
|
-
const emit = (channel, delay, ms, from, to, ease,
|
|
1569
|
-
const
|
|
1678
|
+
const emit = (channel, delay, ms, from, to, ease, segment) => {
|
|
1679
|
+
const written = writeEase(ease);
|
|
1680
|
+
const base = {
|
|
1681
|
+
...channel.id ? { id: channel.id } : {},
|
|
1682
|
+
...delay > 0 ? { delay: round2(delay) } : {},
|
|
1683
|
+
ms: round2(ms),
|
|
1684
|
+
ease: written,
|
|
1685
|
+
..._optionalChain([segment, 'optionalAccess', _41 => _41.behavior]) ? { behavior: segment.behavior } : {},
|
|
1686
|
+
..._optionalChain([segment, 'optionalAccess', _42 => _42.steps]) ? { strobe: segment.steps } : {}
|
|
1687
|
+
};
|
|
1570
1688
|
if (channel.kind === "view") animate.push({ ...base, view: to });
|
|
1571
1689
|
else if (channel.kind === "draw") animate.push({ ...base, draw: [from, to] });
|
|
1572
1690
|
else if (channel.kind === "attrs") {
|
|
@@ -1588,15 +1706,14 @@ function toFlairSvgMotion(scene, options = {}) {
|
|
|
1588
1706
|
const active = channel.segments.filter((s) => s.start < b && s.end > a);
|
|
1589
1707
|
if (!active.length) continue;
|
|
1590
1708
|
const first = active[0];
|
|
1591
|
-
const
|
|
1592
|
-
|
|
1593
|
-
);
|
|
1594
|
-
const curve = together ? flairCurveFor(first.ease, curves) : void 0;
|
|
1709
|
+
const key = (x) => JSON.stringify([x.ease, x.behavior, _nullishCoalesce(x.reverse, () => ( false)), _nullishCoalesce(x.steps, () => ( 0))]);
|
|
1710
|
+
const together = active.every((x) => x.start === a && x.end === b && channel.exact(x) && key(x) === key(first));
|
|
1711
|
+
const curve = together ? easeOf(first) : void 0;
|
|
1595
1712
|
const ease = curve && (!overshoots(curve) || channel.overshoot) ? curve : void 0;
|
|
1596
1713
|
if (ease) {
|
|
1597
1714
|
const from = channel.value(a);
|
|
1598
1715
|
const to = first.behavior ? (_nullishCoalesce(channel.raw, () => ( channel.value)))(b) : channel.value(b);
|
|
1599
|
-
if (!same(from, to) || channel.kind === "view") emit(channel, a, b - a, from, to, ease, first
|
|
1716
|
+
if (!same(from, to) || channel.kind === "view") emit(channel, a, b - a, from, to, ease, first);
|
|
1600
1717
|
continue;
|
|
1601
1718
|
}
|
|
1602
1719
|
const n = Math.max(1, Math.ceil((b - a) * fps / 1e3));
|
|
@@ -1618,9 +1735,10 @@ function toFlairSvgMotion(scene, options = {}) {
|
|
|
1618
1735
|
for (const seg of follow.segments) {
|
|
1619
1736
|
const along = { id: follow.wrapper, along: follow.routes.get(seg.path), ...seg.rotate ? { orient: true } : {} };
|
|
1620
1737
|
const progress = (t) => round2(Math.min(1, Math.max(0, Number(segmentValue(seg, t)))));
|
|
1621
|
-
const curve = seg.end > seg.start ?
|
|
1738
|
+
const curve = seg.end > seg.start ? plays(seg) ? easeOf(seg) : void 0 : "linear";
|
|
1622
1739
|
if (curve && !(overshoots(curve) && seg.rotate)) {
|
|
1623
|
-
|
|
1740
|
+
const ends = seg.reverse ? [Number(seg.to), Number(seg.from)] : [Number(seg.from), Number(seg.to)];
|
|
1741
|
+
animate.push({ ...along, ...seg.start > 0 ? { delay: round2(seg.start) } : {}, ms: round2(seg.end - seg.start), ease: writeEase(curve), ...seg.steps ? { strobe: seg.steps } : {}, at: [round2(ends[0]), round2(ends[1])] });
|
|
1624
1742
|
continue;
|
|
1625
1743
|
}
|
|
1626
1744
|
const until = Math.min(seg.end, ...starts.filter((t) => t > seg.start));
|
|
@@ -1634,11 +1752,11 @@ function toFlairSvgMotion(scene, options = {}) {
|
|
|
1634
1752
|
}
|
|
1635
1753
|
}
|
|
1636
1754
|
animate.sort((x, y) => (_nullishCoalesce(x.delay, () => ( 0))) - (_nullishCoalesce(y.delay, () => ( 0))));
|
|
1637
|
-
let svg =
|
|
1755
|
+
let svg = _chunkYJLCBA7Vcjs.vectorDocumentToSvg.call(void 0, { ...scene.artwork, nodes: _chunkYJLCBA7Vcjs.expandVectorAppearances.call(void 0, scene.artwork.nodes) }, { pretty: false, precision, wrap });
|
|
1638
1756
|
if (routes.length) svg = svg.replace(/^(<svg[^>]*>)/, `$1<defs>${routes.join("")}</defs>`);
|
|
1639
1757
|
const lastChange = animate.reduce((m, a) => Math.max(m, (_nullishCoalesce(a.delay, () => ( 0))) + (_nullishCoalesce(a.ms, () => ( 0)))), 0);
|
|
1640
1758
|
let motion = { svg, animate };
|
|
1641
|
-
if (_optionalChain([scene, 'access',
|
|
1759
|
+
if (_optionalChain([scene, 'access', _43 => _43.controls, 'optionalAccess', _44 => _44.length])) {
|
|
1642
1760
|
const declarations = scene.controls.map((c) => `{{${c.name}: color default=${c.default.toUpperCase()}${c.label ? ` label='${c.label.replace(/'/g, "\u2019")}'` : ""}}}`).join(" ");
|
|
1643
1761
|
const swap = (text) => scene.controls.reduce((s, c) => s.replace(new RegExp(`${c.default}(?![0-9a-fA-F])`, "gi"), `{{${c.name}}}`), text);
|
|
1644
1762
|
svg = svg.replace(/^(<svg[^>]*>)/, `$1<!-- ${declarations} -->`);
|
|
@@ -1660,7 +1778,7 @@ function toFlairVectorEffect(scene, options = {}) {
|
|
|
1660
1778
|
tags: _nullishCoalesce(options.tags, () => ( [])),
|
|
1661
1779
|
motion: out.motion,
|
|
1662
1780
|
hold: out.hold,
|
|
1663
|
-
..._optionalChain([scene, 'access',
|
|
1781
|
+
..._optionalChain([scene, 'access', _45 => _45.presets, 'optionalAccess', _46 => _46.length]) ? { presets: scene.presets } : {},
|
|
1664
1782
|
...options.trigger ? { trigger: options.trigger } : {}
|
|
1665
1783
|
};
|
|
1666
1784
|
}
|
|
@@ -1678,6 +1796,196 @@ function toFlairEffectDocument(scene, options = {}) {
|
|
|
1678
1796
|
};
|
|
1679
1797
|
}
|
|
1680
1798
|
|
|
1799
|
+
// src/animation-editor/flair-import.ts
|
|
1800
|
+
var PLACEHOLDER = /\{\{\s*([A-Za-z_]\w*)\s*(?::\s*([A-Za-z0-9]*)([^}]*))?\}\}/g;
|
|
1801
|
+
function attributeOf(attrs, name) {
|
|
1802
|
+
const m = new RegExp(`(?:^|\\s)${name}=(?:'([^']*)'|(\\S+))`).exec(attrs);
|
|
1803
|
+
return m ? _nullishCoalesce(m[1], () => ( m[2])) : void 0;
|
|
1804
|
+
}
|
|
1805
|
+
function resolvePlaceholders(motion, notes) {
|
|
1806
|
+
const text = JSON.stringify(motion);
|
|
1807
|
+
const declared = /* @__PURE__ */ new Map();
|
|
1808
|
+
for (const m of text.matchAll(PLACEHOLDER)) {
|
|
1809
|
+
if (!m[2] || declared.has(m[1])) continue;
|
|
1810
|
+
const attrs = _nullishCoalesce(m[3], () => ( ""));
|
|
1811
|
+
const value = attributeOf(attrs, "default");
|
|
1812
|
+
const fallback = m[2] === "color" ? "#000000" : "0";
|
|
1813
|
+
declared.set(m[1], { type: m[2], value: m[2] === "color" ? (_nullishCoalesce(value, () => ( fallback))).toLowerCase() : _nullishCoalesce(value, () => ( fallback)), ...attributeOf(attrs, "label") ? { label: attributeOf(attrs, "label") } : {} });
|
|
1814
|
+
}
|
|
1815
|
+
const controls = [];
|
|
1816
|
+
for (const [name, d] of declared) {
|
|
1817
|
+
if (d.type === "color" && /^#[0-9a-fA-F]{6}$/.test(d.value)) controls.push({ name, type: "color", default: d.value.toLowerCase(), ...d.label ? { label: d.label } : {} });
|
|
1818
|
+
else notes.push(`Control "${name}" (${d.type}) is fixed at its default ${d.value}: scenes take colour controls only.`);
|
|
1819
|
+
}
|
|
1820
|
+
const unknown = /* @__PURE__ */ new Set();
|
|
1821
|
+
const filled = text.replace(PLACEHOLDER, (_, name) => {
|
|
1822
|
+
const d = declared.get(name);
|
|
1823
|
+
if (!d) unknown.add(name);
|
|
1824
|
+
return (_nullishCoalesce(_optionalChain([d, 'optionalAccess', _47 => _47.value]), () => ( ""))).replace(/"/g, '\\"');
|
|
1825
|
+
});
|
|
1826
|
+
for (const name of unknown) notes.push(`Placeholder "{{${name}}}" has no declaration, so it was left empty.`);
|
|
1827
|
+
return { motion: JSON.parse(filled), controls };
|
|
1828
|
+
}
|
|
1829
|
+
function easingOf(ease, fallback) {
|
|
1830
|
+
if (ease === void 0) return fallback;
|
|
1831
|
+
if (ease === "linear") return "linear";
|
|
1832
|
+
if (ease === "out") return MOTION_EASE_OUT;
|
|
1833
|
+
if (ease === "in-out") return MOTION_EASE_IN_OUT;
|
|
1834
|
+
if (Array.isArray(ease)) return { type: "cubic-bezier", x1: ease[0], y1: ease[1], x2: ease[2], y2: ease[3] };
|
|
1835
|
+
if (typeof ease === "object" && "spring" in ease) return { type: "spring", ...ease.spring };
|
|
1836
|
+
return fallback;
|
|
1837
|
+
}
|
|
1838
|
+
function poseOf(value, entry) {
|
|
1839
|
+
const t = _chunkYJLCBA7Vcjs.parseSvgTransform.call(void 0, String(value));
|
|
1840
|
+
const o = _chunkYJLCBA7Vcjs.multiplyMatrices.call(void 0, _nullishCoalesce(_chunkYJLCBA7Vcjs.invertMatrix.call(void 0, _chunkYJLCBA7Vcjs.nodeMatrix.call(void 0, entry.node)), () => ( [1, 0, 0, 1, 0, 0])), t);
|
|
1841
|
+
const sx = Math.hypot(o[0], o[1]);
|
|
1842
|
+
if (!(sx > 1e-9)) return null;
|
|
1843
|
+
const sy = (o[0] * o[3] - o[1] * o[2]) / sx;
|
|
1844
|
+
if (Math.abs(o[2] + o[1] / sx * sy) > 1e-6 || Math.abs(o[3] - o[0] / sx * sy) > 1e-6) return null;
|
|
1845
|
+
const c = entry.center;
|
|
1846
|
+
const moved = _chunkYJLCBA7Vcjs.applyMatrix.call(void 0, o, c);
|
|
1847
|
+
return { dx: moved.x - c.x, dy: moved.y - c.y, rotation: Math.atan2(o[1], o[0]) * 180 / Math.PI, sx, sy };
|
|
1848
|
+
}
|
|
1849
|
+
var near = (a, b, eps = 1e-6) => Math.abs(a - b) < eps;
|
|
1850
|
+
var r3 = (n) => Math.round(n * 1e3) / 1e3;
|
|
1851
|
+
function commandsFor(anim, nodes, entered, notes) {
|
|
1852
|
+
const id = anim.id;
|
|
1853
|
+
const entry = nodes.get(id);
|
|
1854
|
+
const where = `"${id}"`;
|
|
1855
|
+
if (!entry) {
|
|
1856
|
+
notes.push(`A change to ${where} was skipped: the artwork has no element with that id.`);
|
|
1857
|
+
return [];
|
|
1858
|
+
}
|
|
1859
|
+
const targets = [id];
|
|
1860
|
+
const duration = _nullishCoalesce(anim.ms, () => ( 800));
|
|
1861
|
+
const timing = {
|
|
1862
|
+
duration,
|
|
1863
|
+
...anim.strobe ? { steps: anim.strobe } : {},
|
|
1864
|
+
...anim.repeat && anim.repeat > 1 ? { repeat: anim.repeat, ...anim.alternate ? { repeatMode: "mirror" } : {} } : {}
|
|
1865
|
+
};
|
|
1866
|
+
const ease = (fallback) => ({ ease: easingOf(anim.ease, fallback) });
|
|
1867
|
+
const out = [];
|
|
1868
|
+
const behavior = anim.behavior;
|
|
1869
|
+
const unsupported = ["count", "write", "scramble", "solo"].filter((k) => anim[k] !== void 0);
|
|
1870
|
+
if (unsupported.length) notes.push(`${where}: ${unsupported.join(", ")} ${unsupported.length > 1 ? "are" : "is"} not part of scenes and was skipped.`);
|
|
1871
|
+
if (anim.draw !== void 0 && anim.draw !== false) {
|
|
1872
|
+
const [from, to] = anim.draw === true ? [0, 1] : anim.draw;
|
|
1873
|
+
out.push({ type: "draw", targets, from, to, ...timing, ...ease(MOTION_EASE_IN_OUT) });
|
|
1874
|
+
}
|
|
1875
|
+
if (anim.along) {
|
|
1876
|
+
const path = nodes.get(anim.along);
|
|
1877
|
+
if (!path || path.node.type !== "path") notes.push(`${where} follows "${anim.along}", which is not a path in the artwork; the follow was skipped.`);
|
|
1878
|
+
else out.push({ type: "follow", targets, path: anim.along, from: _nullishCoalesce(_optionalChain([anim, 'access', _48 => _48.at, 'optionalAccess', _49 => _49[0]]), () => ( 0)), to: _nullishCoalesce(_optionalChain([anim, 'access', _50 => _50.at, 'optionalAccess', _51 => _51[1]]), () => ( 1)), ...anim.orient ? { rotate: true } : {}, ...timing, ...ease(MOTION_EASE_IN_OUT) });
|
|
1879
|
+
}
|
|
1880
|
+
for (const [attr, [a, b]] of Object.entries(_nullishCoalesce(anim.attrs, () => ( {})))) {
|
|
1881
|
+
if (attr === "opacity") {
|
|
1882
|
+
const [from, to] = [Number(a), Number(b)];
|
|
1883
|
+
if (behavior) notes.push(`${where}: an opacity ${behavior.type} became a plain fade.`);
|
|
1884
|
+
if (from === 0 && to > 0 && !entered.has(id)) {
|
|
1885
|
+
entered.add(id);
|
|
1886
|
+
out.push({ type: "enter", targets, style: "fade", ...timing, ...ease(MOTION_EASE_OUT) });
|
|
1887
|
+
} else out.push({ type: "fade", targets, to: Math.min(1, Math.max(0, to)), ...timing, ...ease(MOTION_EASE_IN_OUT) });
|
|
1888
|
+
} else if (attr === "fill" || attr === "stroke") {
|
|
1889
|
+
if (_optionalChain([behavior, 'optionalAccess', _52 => _52.type]) === "pulse") out.push({ type: "color-pulse", targets, color: String(b), paint: attr, count: _nullishCoalesce(behavior.count, () => ( 1)), ...timing, ...ease("linear") });
|
|
1890
|
+
else {
|
|
1891
|
+
if (behavior) notes.push(`${where}: a ${attr} ${behavior.type} became a plain colour change.`);
|
|
1892
|
+
out.push({ type: "style", targets, [attr]: String(b), ...timing, ...ease(MOTION_EASE_IN_OUT) });
|
|
1893
|
+
}
|
|
1894
|
+
} else if (attr === "stroke-width") {
|
|
1895
|
+
out.push({ type: "style", targets, strokeWidth: Number(b), ...timing, ...ease(MOTION_EASE_IN_OUT) });
|
|
1896
|
+
} else if (attr === "transform") {
|
|
1897
|
+
const p0 = poseOf(a, entry);
|
|
1898
|
+
const p1 = poseOf(b, entry);
|
|
1899
|
+
if (!p0 || !p1) {
|
|
1900
|
+
notes.push(`${where}: a transform that skews was skipped (scenes move, turn and scale).`);
|
|
1901
|
+
continue;
|
|
1902
|
+
}
|
|
1903
|
+
const d = { x: r3(p1.dx - p0.dx), y: r3(p1.dy - p0.dy), r: r3(p1.rotation - p0.rotation), sx: p1.sx / p0.sx, sy: p1.sy / p0.sy };
|
|
1904
|
+
if (behavior) {
|
|
1905
|
+
const linear = ease("linear");
|
|
1906
|
+
const moves = d.x !== 0 || d.y !== 0;
|
|
1907
|
+
const turns = d.r !== 0;
|
|
1908
|
+
const scales = !near(d.sx, 1) || !near(d.sy, 1);
|
|
1909
|
+
if (behavior.type === "pulse" && scales && !moves && !turns) out.push({ type: "pulse", targets, amount: r3(d.sx - 1), count: _nullishCoalesce(behavior.count, () => ( 1)), ...timing, ...linear });
|
|
1910
|
+
else if (behavior.type === "oscillate" && (_nullishCoalesce(behavior.decay, () => ( 0))) > 0 && turns && !moves && !scales) out.push({ type: "teeter", targets, angle: d.r, count: _nullishCoalesce(behavior.count, () => ( 1)), ...timing, ...linear });
|
|
1911
|
+
else if (behavior.type === "oscillate" && (_nullishCoalesce(behavior.decay, () => ( 0))) > 0 && moves && !turns && !scales && (d.x === 0 || d.y === 0))
|
|
1912
|
+
out.push({ type: "shake", targets, amount: d.x || d.y, count: _nullishCoalesce(behavior.count, () => ( 1)), ...d.x === 0 ? { axis: "vertical" } : {}, ...timing, ...linear });
|
|
1913
|
+
else if (behavior.type === "oscillate" && !behavior.decay && d.x === 0 && d.y !== 0 && !turns && !scales) out.push({ type: "wave", targets, amount: -d.y, count: _nullishCoalesce(behavior.count, () => ( 1)), ...timing, ...linear });
|
|
1914
|
+
else if (behavior.type === "wriggle" && !scales)
|
|
1915
|
+
out.push({ type: "wiggle", targets, amount: Math.max(Math.abs(d.x), Math.abs(d.y)), ...turns ? { rotate: Math.abs(d.r) } : {}, frequency: _nullishCoalesce(behavior.frequency, () => ( 2)), octaves: _nullishCoalesce(behavior.octaves, () => ( 2)), seed: _nullishCoalesce(behavior.seed, () => ( 42)), ...timing, ...linear });
|
|
1916
|
+
else notes.push(`${where}: a transform ${behavior.type} that is not a pulse, shake, teeter, wave or wiggle was skipped.`);
|
|
1917
|
+
continue;
|
|
1918
|
+
}
|
|
1919
|
+
const e = ease(MOTION_EASE_IN_OUT);
|
|
1920
|
+
if (d.x !== 0 || d.y !== 0) out.push({ type: "move", targets, by: { x: d.x, y: d.y }, ...timing, ...e });
|
|
1921
|
+
if (d.r !== 0) out.push({ type: "rotate", targets, to: r3(p1.rotation), ...timing, ...e });
|
|
1922
|
+
if (!near(d.sx, 1) || !near(d.sy, 1)) out.push({ type: "scale", targets, to: near(p1.sx, p1.sy) ? r3(p1.sx) : { x: r3(p1.sx), y: r3(p1.sy) }, ...timing, ...e });
|
|
1923
|
+
} else notes.push(`${where}: changing "${attr}" is not part of scenes and was skipped.`);
|
|
1924
|
+
}
|
|
1925
|
+
return out;
|
|
1926
|
+
}
|
|
1927
|
+
function fromFlairSvgMotion(input, options = {}) {
|
|
1928
|
+
const notes = [];
|
|
1929
|
+
if (!input || typeof input.svg !== "string") throw new Error('Give a Flair SvgMotion: { svg: "<svg \u2026>", animate: [ \u2026 ] }.');
|
|
1930
|
+
const { motion, controls } = resolvePlaceholders(input, notes);
|
|
1931
|
+
const artwork = _chunkYJLCBA7Vcjs.parseVectorSvg.call(void 0, motion.svg);
|
|
1932
|
+
const nodes = indexMotionNodes(artwork.nodes);
|
|
1933
|
+
const byStep = /* @__PURE__ */ new Map();
|
|
1934
|
+
for (const anim of _nullishCoalesce(motion.animate, () => ( []))) {
|
|
1935
|
+
const step = Math.max(0, Math.floor(_nullishCoalesce(anim.step, () => ( 0))));
|
|
1936
|
+
if (!byStep.has(step)) byStep.set(step, []);
|
|
1937
|
+
byStep.get(step).push(anim);
|
|
1938
|
+
}
|
|
1939
|
+
const steps = [...byStep.keys()].sort((a, b) => a - b);
|
|
1940
|
+
if (steps.length > 1) notes.push("Each Flair step became a shot played after the one before (Flair steps wait for clicks or set times).");
|
|
1941
|
+
const entered = /* @__PURE__ */ new Set();
|
|
1942
|
+
const shots = steps.map((step, index) => {
|
|
1943
|
+
const anims = [...byStep.get(step)].sort((a, b) => (_nullishCoalesce(a.delay, () => ( 0))) - (_nullishCoalesce(b.delay, () => ( 0))));
|
|
1944
|
+
let camera;
|
|
1945
|
+
let cameraMs = 0;
|
|
1946
|
+
const run = [];
|
|
1947
|
+
let last = 0;
|
|
1948
|
+
for (const anim of anims) {
|
|
1949
|
+
if (anim.view !== void 0) {
|
|
1950
|
+
if (camera || (_nullishCoalesce(anim.delay, () => ( 0))) > 0) {
|
|
1951
|
+
notes.push(`Step ${step}: a camera move ${camera ? "after the first" : `${anim.delay} ms in`} was dropped; a shot frames the camera once, at its start.`);
|
|
1952
|
+
continue;
|
|
1953
|
+
}
|
|
1954
|
+
const ms = _nullishCoalesce(anim.ms, () => ( 800));
|
|
1955
|
+
const ease = easingOf(anim.ease, MOTION_EASE_IN_OUT);
|
|
1956
|
+
camera = typeof anim.view === "string" ? { frame: anim.view, padding: 0, ms, ease } : { x: anim.view[0], y: anim.view[1], width: anim.view[2], height: anim.view[3], ms, ease };
|
|
1957
|
+
cameraMs = index === 0 ? 0 : ms;
|
|
1958
|
+
continue;
|
|
1959
|
+
}
|
|
1960
|
+
if (!anim.id) continue;
|
|
1961
|
+
const at = Math.max(0, (_nullishCoalesce(anim.delay, () => ( 0))) - cameraMs);
|
|
1962
|
+
if (cameraMs && (_nullishCoalesce(anim.delay, () => ( 0))) < cameraMs) notes.push(`Step ${step}: a change to "${anim.id}" during the camera move now starts after it.`);
|
|
1963
|
+
for (const command2 of commandsFor(anim, nodes, entered, notes)) {
|
|
1964
|
+
const first = run.length === 0;
|
|
1965
|
+
run.push({ ...command2, ...first ? {} : { with: "previous" }, ...at - last > 0 || first ? { delay: first ? at : at - last } : {} });
|
|
1966
|
+
if (first && at === 0) delete run[run.length - 1].delay;
|
|
1967
|
+
last = at;
|
|
1968
|
+
}
|
|
1969
|
+
}
|
|
1970
|
+
return { id: `step-${step}`, ...camera ? { camera } : {}, run, ...index === steps.length - 1 ? { hold: _nullishCoalesce(options.hold, () => ( 600)) } : {} };
|
|
1971
|
+
});
|
|
1972
|
+
const scene = { version: 1, name: _nullishCoalesce(options.name, () => ( "from-flair")), artwork, shots, ...controls.length ? { controls } : {} };
|
|
1973
|
+
const problems = motionSceneShapeProblems(scene);
|
|
1974
|
+
if (problems.length) throw new Error(`The imported scene is not well formed:
|
|
1975
|
+
${problems.join("\n")}`);
|
|
1976
|
+
return { scene, notes: [...new Set(notes)], issues: checkMotionScene(scene) };
|
|
1977
|
+
}
|
|
1978
|
+
function fromFlairVectorEffect(effect, options = {}) {
|
|
1979
|
+
if (!effect || effect.kind !== "vector" || !effect.motion) throw new Error('Give a Flair vector effect: { kind: "vector", id, name, motion: { svg, animate } }.');
|
|
1980
|
+
const result = fromFlairSvgMotion(effect.motion, { name: _nullishCoalesce(_nullishCoalesce(options.name, () => ( effect.id)), () => ( effect.name)), hold: _nullishCoalesce(options.hold, () => ( effect.hold)) });
|
|
1981
|
+
const names = new Set((_nullishCoalesce(result.scene.controls, () => ( []))).map((c) => c.name));
|
|
1982
|
+
const presets = (_nullishCoalesce(effect.presets, () => ( []))).map((p) => ({ name: p.name, values: Object.fromEntries(Object.entries(p.values).filter(([k]) => names.has(k))) })).filter((p) => Object.keys(p.values).length);
|
|
1983
|
+
const scene = { ...result.scene, ...effect.description ? { description: effect.description } : {}, ...presets.length ? { presets } : {} };
|
|
1984
|
+
const notes = [...result.notes];
|
|
1985
|
+
if (effect.trigger) notes.push(`The effect plays on ${effect.trigger}; scenes play through, so the trigger is not kept (pass it back to toFlairVectorEffect).`);
|
|
1986
|
+
return { scene, notes, issues: checkMotionScene(scene) };
|
|
1987
|
+
}
|
|
1988
|
+
|
|
1681
1989
|
// src/animation-editor/controller.ts
|
|
1682
1990
|
var BLANK = { width: 800, height: 450, background: "#ffffff", nodes: [] };
|
|
1683
1991
|
var AnimationEditorController = (_class = class {
|
|
@@ -1700,7 +2008,7 @@ var AnimationEditorController = (_class = class {
|
|
|
1700
2008
|
this.issues = checkMotionScene(this.scene);
|
|
1701
2009
|
this.readOnly = _nullishCoalesce(options.readOnly, () => ( false));
|
|
1702
2010
|
this.onChange = options.onChange;
|
|
1703
|
-
this.player = new (0,
|
|
2011
|
+
this.player = new (0, _chunk2FGRTYJNcjs.AnimationPlayer)({ clock: options.clock, duration: () => compileMotionScene(this.scene).duration, loop: _nullishCoalesce(this.scene.loop, () => ( "none")) });
|
|
1704
2012
|
this.state = this.computeState();
|
|
1705
2013
|
this.connect();
|
|
1706
2014
|
}
|
|
@@ -1745,7 +2053,7 @@ var AnimationEditorController = (_class = class {
|
|
|
1745
2053
|
time: p.time,
|
|
1746
2054
|
playing: p.playing,
|
|
1747
2055
|
loop: p.loop,
|
|
1748
|
-
currentShot: _nullishCoalesce(_optionalChain([motionShotAt, 'call',
|
|
2056
|
+
currentShot: _nullishCoalesce(_optionalChain([motionShotAt, 'call', _53 => _53(timeline, p.time), 'optionalAccess', _54 => _54.id]), () => ( null)),
|
|
1749
2057
|
selection: this.selection,
|
|
1750
2058
|
readOnly: this.readOnly,
|
|
1751
2059
|
error: this.error,
|
|
@@ -1784,7 +2092,7 @@ var AnimationEditorController = (_class = class {
|
|
|
1784
2092
|
this.applying = false;
|
|
1785
2093
|
}
|
|
1786
2094
|
this.notify();
|
|
1787
|
-
_optionalChain([this, 'access',
|
|
2095
|
+
_optionalChain([this, 'access', _55 => _55.onChange, 'optionalCall', _56 => _56(scene)]);
|
|
1788
2096
|
}
|
|
1789
2097
|
commit(label, next) {
|
|
1790
2098
|
const before = this.scene;
|
|
@@ -1924,10 +2232,29 @@ var MS = (what) => ({ type: "number", minimum: 0, description: `${what} in milli
|
|
|
1924
2232
|
var FRACTION = (what) => ({ type: "number", minimum: 0, maximum: 1, description: what });
|
|
1925
2233
|
var POINT = (what) => object({ x: { type: "number", description: "Document units." }, y: { type: "number", description: "Document units." } }, ["x", "y"], what);
|
|
1926
2234
|
var EASE = {
|
|
1927
|
-
description: `Easing: a preset (${
|
|
2235
|
+
description: `Easing: a preset (${_chunk2FHLHZALcjs.ANIMATION_EASING_PRESETS.join(", ")}), { "type": "cubic-bezier", "x1", "y1", "x2", "y2" }, { "type": "spring", \u2026 } or { "type": "elastic", \u2026 }. The spring presets are Figma's: gentle, quick, bouncy, slow. A command with a spring and no "duration" lasts until the spring settles. Default: a cubic ease-out for entrances, cubic ease-in-out for moves.`,
|
|
1928
2236
|
anyOf: [
|
|
1929
|
-
{ type: "string", enum: [...
|
|
1930
|
-
object({ type: { const: "cubic-bezier", description: "Curve kind." }, x1: { type: "number", description: "Out handle x (0\u20131)." }, y1: { type: "number", description: "Out handle y." }, x2: { type: "number", description: "In handle x (0\u20131)." }, y2: { type: "number", description: "In handle y." } }, ["type", "x1", "y1", "x2", "y2"])
|
|
2237
|
+
{ type: "string", enum: [..._chunk2FHLHZALcjs.ANIMATION_EASING_PRESETS] },
|
|
2238
|
+
object({ type: { const: "cubic-bezier", description: "Curve kind." }, x1: { type: "number", description: "Out handle x (0\u20131)." }, y1: { type: "number", description: "Out handle y." }, x2: { type: "number", description: "In handle x (0\u20131)." }, y2: { type: "number", description: "In handle y." } }, ["type", "x1", "y1", "x2", "y2"]),
|
|
2239
|
+
object(
|
|
2240
|
+
{
|
|
2241
|
+
type: { const: "spring", description: "A damped spring (may overshoot)." },
|
|
2242
|
+
stiffness: { type: "number", exclusiveMinimum: 0, description: "Stiffness (default 170; quick 300, bouncy 600)." },
|
|
2243
|
+
damping: { type: "number", minimum: 0, description: "Damping (default 26; lower bounces more)." },
|
|
2244
|
+
mass: { type: "number", exclusiveMinimum: 0, description: "Mass (default 1)." },
|
|
2245
|
+
velocity: { type: "number", description: "Starting speed, in changes per second (default 0)." }
|
|
2246
|
+
},
|
|
2247
|
+
["type"]
|
|
2248
|
+
),
|
|
2249
|
+
object(
|
|
2250
|
+
{
|
|
2251
|
+
type: { const: "elastic", description: "A decaying wobble." },
|
|
2252
|
+
amplitude: { type: "number", minimum: 0, description: "Overshoot factor (default 1)." },
|
|
2253
|
+
period: { type: "number", exclusiveMinimum: 0, description: "Wobble period as a fraction of the length (default 0.3)." },
|
|
2254
|
+
mode: { type: "string", enum: ["out", "in", "in-out"], description: "Where it wobbles (default out)." }
|
|
2255
|
+
},
|
|
2256
|
+
["type"]
|
|
2257
|
+
)
|
|
1931
2258
|
]
|
|
1932
2259
|
};
|
|
1933
2260
|
var BASE = {
|
|
@@ -1937,7 +2264,10 @@ var BASE = {
|
|
|
1937
2264
|
stagger: MS("Start each further target this much later (default 0)"),
|
|
1938
2265
|
with: { type: "string", enum: ["after", "previous"], description: '"after" (default) starts when the commands before it finish; "previous" starts together with the command before.' },
|
|
1939
2266
|
ease: EASE,
|
|
1940
|
-
note: { type: "string", description: "Why this command is here, in a few words." }
|
|
2267
|
+
note: { type: "string", description: "Why this command is here, in a few words." },
|
|
2268
|
+
repeat: { type: "integer", minimum: 1, maximum: 1e3, description: 'Play it this many times (default 1); "duration" is one cycle. Not for enter or exit.' },
|
|
2269
|
+
repeatMode: { type: "string", enum: [...MOTION_REPEAT_MODES], description: "loop (default) restarts each cycle, mirror plays every other one backwards, offset carries on from where the last ended (rotate by 90 with repeat 4 makes a full turn)." },
|
|
2270
|
+
steps: { type: "number", exclusiveMinimum: 0, maximum: 120, description: `Stepped time: hold on a grid of this many frames per second (12 = "on twos"). Default: the scene's steps, else smooth.` }
|
|
1941
2271
|
};
|
|
1942
2272
|
var command = (type, description, extra = {}, required = []) => object({ type: { const: type, description }, ...BASE, ...extra }, ["type", "targets", ...required], description);
|
|
1943
2273
|
var MOTION_COMMAND_SCHEMA = {
|
|
@@ -1963,7 +2293,7 @@ var MOTION_COMMAND_SCHEMA = {
|
|
|
1963
2293
|
command("draw", "Draws strokes on (or off) to a fraction of their length.", { to: FRACTION("Fraction drawn at the end (default 1)."), from: FRACTION("Fraction drawn at the start (default: what is drawn now).") }),
|
|
1964
2294
|
command("move", "Moves targets by an offset or so their centre lands on a point.", { by: POINT("Offset in document units."), to: POINT("Point (document coordinates) for the centre.") }),
|
|
1965
2295
|
command("rotate", "Turns targets about their centre (degrees, clockwise).", { by: { type: "number", description: "Degrees to turn by." }, to: { type: "number", description: "Angle to turn to, in degrees from rest." } }),
|
|
1966
|
-
command("scale", "Scales targets about their centre (1 = rest size).", { to: { description: "A factor, or { x, y } factors.", anyOf: [{ type: "number" }, POINT("Factors per axis.")] } }, ["to"]),
|
|
2296
|
+
command("scale", "Scales targets about their centre (1 = rest size).", { to: { description: "A factor, or { x, y } factors.", anyOf: [{ type: "number" }, POINT("Factors per axis.")] }, geometric: { type: "boolean", description: "Grow by a steady factor, which reads as an even zoom (default false)." } }, ["to"]),
|
|
1967
2297
|
command("fade", "Changes opacity.", { to: FRACTION("Opacity at the end (0\u20131).") }, ["to"]),
|
|
1968
2298
|
command("style", "Blends solid fill / stroke colours (in OKLab) and stroke width. On a group it changes every path in it.", {
|
|
1969
2299
|
fill: { type: "string", description: "CSS colour (hex, rgb(), rgba())." },
|
|
@@ -2009,11 +2339,12 @@ var MOTION_COMMAND_SCHEMA = {
|
|
|
2009
2339
|
)
|
|
2010
2340
|
]
|
|
2011
2341
|
};
|
|
2342
|
+
var GEOMETRIC_CAMERA = { type: "boolean", description: "Zoom at a steady rate, so a big zoom does not rush at the start (default false)." };
|
|
2012
2343
|
var CAMERA = {
|
|
2013
2344
|
description: `Where the camera looks: a rectangle { x, y, width, height } in document units, or { frame: "<node id>", padding } to frame a node (fitted to the artwork's shape). It eases there over "ms" (default 1000); the first shot cuts.`,
|
|
2014
2345
|
anyOf: [
|
|
2015
|
-
object({ x: { type: "number", description: "Left." }, y: { type: "number", description: "Top." }, width: { type: "number", exclusiveMinimum: 0, description: "Width." }, height: { type: "number", exclusiveMinimum: 0, description: "Height." }, ease: EASE, ms: MS("Camera move length") }, ["x", "y", "width", "height"]),
|
|
2016
|
-
object({ frame: { type: "string", minLength: 1, description: "Node id to frame." }, padding: { type: "number", description: "Margin around it (default 24)." }, ease: EASE, ms: MS("Camera move length") }, ["frame"])
|
|
2346
|
+
object({ x: { type: "number", description: "Left." }, y: { type: "number", description: "Top." }, width: { type: "number", exclusiveMinimum: 0, description: "Width." }, height: { type: "number", exclusiveMinimum: 0, description: "Height." }, ease: EASE, ms: MS("Camera move length"), geometric: GEOMETRIC_CAMERA }, ["x", "y", "width", "height"]),
|
|
2347
|
+
object({ frame: { type: "string", minLength: 1, description: "Node id to frame." }, padding: { type: "number", description: "Margin around it (default 24)." }, ease: EASE, ms: MS("Camera move length"), geometric: GEOMETRIC_CAMERA }, ["frame"])
|
|
2017
2348
|
]
|
|
2018
2349
|
};
|
|
2019
2350
|
var SHOT_FIELDS = {
|
|
@@ -2039,6 +2370,7 @@ var SETTINGS = {
|
|
|
2039
2370
|
description: { type: ["string", "null"], description: "What it shows; null clears." },
|
|
2040
2371
|
loop: { type: "string", enum: ["none", "loop", "ping-pong"], description: "Playback past the end (default none)." },
|
|
2041
2372
|
fps: { type: "number", minimum: 1, maximum: 120, description: "Sampling rate for exports (default 30)." },
|
|
2373
|
+
steps: { anyOf: [{ type: "number", exclusiveMinimum: 0, maximum: 120 }, { type: "null" }], description: 'Stepped time for every command without its own: changes hold on a grid of this many frames per second (12 = "on twos" at 24 fps) for a hand-drawn look; null makes it smooth again (default).' },
|
|
2042
2374
|
target: { anyOf: [{ type: "array", items: { type: "number", minimum: 0 }, minItems: 2, maxItems: 2 }, { type: "null" }], description: "Wanted length [min, max] in seconds; null clears." }
|
|
2043
2375
|
};
|
|
2044
2376
|
var EDIT_OP = {
|
|
@@ -2217,6 +2549,7 @@ function createAnimationEditorToolProvider(controller, options = {}) {
|
|
|
2217
2549
|
}
|
|
2218
2550
|
};
|
|
2219
2551
|
const CURVES_DESCRIPTION = "Write cubic-bezier easings (ease, ease-in, ease-out, ease-in-out, back eases, custom curves) as one change with ease: [x1, y1, x2, y2] (default true). Needs Flair 0.36 or later; false samples them into linear steps for older Flair.";
|
|
2552
|
+
const FLAIR_DESCRIPTION = "The Flair version to export for (default 0.39): springs and stepped time need 0.39, behaviours 0.38, cubic-bezier curves 0.36; older versions get those sampled into linear steps.";
|
|
2220
2553
|
const BEHAVIORS_DESCRIPTION = "Write pulses, shakes, teeters, waves, wiggles and colour pulses as one change with a Flair behavior, so they play exactly (default true; needs Flair 0.38 or later). False samples them into linear steps for older Flair.";
|
|
2221
2554
|
const exportFlairMotion = {
|
|
2222
2555
|
name: "export_flair_motion",
|
|
@@ -2225,10 +2558,11 @@ function createAnimationEditorToolProvider(controller, options = {}) {
|
|
|
2225
2558
|
inputSchema: object({
|
|
2226
2559
|
fps: { type: "number", minimum: 1, maximum: 120, description: "Sampling rate for changes Flair cannot express exactly (default: the scene's fps, else 30)." },
|
|
2227
2560
|
curves: { type: "boolean", description: CURVES_DESCRIPTION },
|
|
2228
|
-
behaviors: { type: "boolean", description: BEHAVIORS_DESCRIPTION }
|
|
2561
|
+
behaviors: { type: "boolean", description: BEHAVIORS_DESCRIPTION },
|
|
2562
|
+
flair: { type: "string", pattern: "^\\d+\\.\\d+(\\.\\d+)?$", description: FLAIR_DESCRIPTION }
|
|
2229
2563
|
}),
|
|
2230
2564
|
annotations: { title: "Export as Flair vector motion", ...READ },
|
|
2231
|
-
handler: ({ fps, curves, behaviors }) => controller.toFlairMotion({ ...fps ? { fps } : {}, ...curves === void 0 ? {} : { curves }, ...behaviors === void 0 ? {} : { behaviors } })
|
|
2565
|
+
handler: ({ fps, curves, behaviors, flair }) => controller.toFlairMotion({ ...fps ? { fps } : {}, ...curves === void 0 ? {} : { curves }, ...behaviors === void 0 ? {} : { behaviors }, ...flair ? { flair } : {} })
|
|
2232
2566
|
};
|
|
2233
2567
|
const exportFlairEffect = {
|
|
2234
2568
|
name: "export_flair_effect",
|
|
@@ -2241,7 +2575,8 @@ function createAnimationEditorToolProvider(controller, options = {}) {
|
|
|
2241
2575
|
category: { type: "string", minLength: 1, description: "Flair catalog category, e.g. icon, loader, ui, data, text, morph (default icon)." },
|
|
2242
2576
|
tags: { type: "array", items: { type: "string" }, description: "Search tags (default none)." },
|
|
2243
2577
|
curves: { type: "boolean", description: CURVES_DESCRIPTION },
|
|
2244
|
-
behaviors: { type: "boolean", description: BEHAVIORS_DESCRIPTION }
|
|
2578
|
+
behaviors: { type: "boolean", description: BEHAVIORS_DESCRIPTION },
|
|
2579
|
+
flair: { type: "string", pattern: "^\\d+\\.\\d+(\\.\\d+)?$", description: FLAIR_DESCRIPTION }
|
|
2245
2580
|
}),
|
|
2246
2581
|
annotations: { title: "Export as a Flair effect", ...READ },
|
|
2247
2582
|
handler: (args) => controller.toFlairEffect(args)
|
|
@@ -2255,13 +2590,41 @@ function createAnimationEditorToolProvider(controller, options = {}) {
|
|
|
2255
2590
|
handler: ({ svg }) => {
|
|
2256
2591
|
let artwork;
|
|
2257
2592
|
try {
|
|
2258
|
-
artwork =
|
|
2593
|
+
artwork = _chunkYJLCBA7Vcjs.parseVectorSvg.call(void 0, svg);
|
|
2259
2594
|
} catch (error) {
|
|
2260
2595
|
throw new Error(`The SVG could not be read: ${error.message}. Nothing was changed.`);
|
|
2261
2596
|
}
|
|
2262
2597
|
return apply({ op: "set_artwork", artwork }, "Replace artwork", (s) => ({ elements: describeMotionElements(s) }));
|
|
2263
2598
|
}
|
|
2264
2599
|
};
|
|
2600
|
+
const importFlairEffect = {
|
|
2601
|
+
name: "import_flair_effect",
|
|
2602
|
+
title: "Import a Flair effect",
|
|
2603
|
+
description: "Replaces the whole animation with a Flair vector effect or SvgMotion (one undo step): its SVG becomes the artwork, {{name: color}} placeholders become colour controls, and each Flair step becomes a shot of commands (fade, enter, style, move, rotate, scale, draw, follow, pulse, shake, teeter, wave, wiggle, color-pulse). Returns the shots, the elements, notes on anything not carried over and the issues. With dry_run it only reports what the import would give.",
|
|
2604
|
+
inputSchema: object(
|
|
2605
|
+
{
|
|
2606
|
+
effect: { type: "object", description: 'A Flair catalog vector effect { kind: "vector", id, name, motion: { svg, animate }, hold?, presets? }. Give this or "motion".' },
|
|
2607
|
+
motion: { type: "object", description: 'A Flair SvgMotion { svg, animate }. Give this or "effect".' },
|
|
2608
|
+
name: { type: "string", minLength: 1, description: `Name for the scene (default: the effect's id, else "from-flair").` },
|
|
2609
|
+
dry_run: { type: "boolean", description: "Report without replacing anything (default false)." }
|
|
2610
|
+
},
|
|
2611
|
+
[]
|
|
2612
|
+
),
|
|
2613
|
+
annotations: { title: "Import a Flair effect", ...DESTRUCTIVE },
|
|
2614
|
+
handler: ({ effect, motion, name, dry_run = false }) => {
|
|
2615
|
+
if (effect === void 0 === (motion === void 0)) throw new Error('Give exactly one of "effect" (a Flair vector effect) or "motion" (a Flair SvgMotion). Nothing was changed.');
|
|
2616
|
+
let result;
|
|
2617
|
+
try {
|
|
2618
|
+
result = effect !== void 0 ? fromFlairVectorEffect(effect, name ? { name } : {}) : fromFlairSvgMotion(motion, name ? { name } : {});
|
|
2619
|
+
} catch (error) {
|
|
2620
|
+
throw new Error(`The Flair effect could not be imported: ${error.message} Nothing was changed.`);
|
|
2621
|
+
}
|
|
2622
|
+
const report = { notes: result.notes, issues: issueSummary(result.issues), shots: describeMotionShots(result.scene).map(({ id, start, end, commands }) => ({ id, start, end, commands: commands.length })) };
|
|
2623
|
+
if (dry_run) return { dry_run: true, ...report, hint: "Nothing was replaced; call again without dry_run to import." };
|
|
2624
|
+
const applied = controller.apply({ op: "replace_scene", scene: result.scene }, "Import Flair effect");
|
|
2625
|
+
return outcome(applied, applied.ok ? { ...report, elements: describeMotionElements(result.scene) } : {});
|
|
2626
|
+
}
|
|
2627
|
+
};
|
|
2265
2628
|
const insertShots = {
|
|
2266
2629
|
name: "insert_shots",
|
|
2267
2630
|
title: "Add shots",
|
|
@@ -2308,7 +2671,7 @@ function createAnimationEditorToolProvider(controller, options = {}) {
|
|
|
2308
2671
|
inputSchema: object({ shot: SHOT_ID, commands: { type: "array", items: MOTION_COMMAND_SCHEMA, minItems: 1, description: "Commands to add." }, at: { type: "integer", minimum: 0, description: "Index in the run (default: the end)." } }, ["shot", "commands"]),
|
|
2309
2672
|
annotations: { title: "Add commands to a shot", ...WRITE },
|
|
2310
2673
|
handler: ({ shot, commands, at }) => {
|
|
2311
|
-
const before = _nullishCoalesce(_optionalChain([scene, 'call',
|
|
2674
|
+
const before = _nullishCoalesce(_optionalChain([scene, 'call', _57 => _57(), 'access', _58 => _58.shots, 'access', _59 => _59.find, 'call', _60 => _60((s) => s.id === shot), 'optionalAccess', _61 => _61.run, 'access', _62 => _62.length]), () => ( 0));
|
|
2312
2675
|
const first = _nullishCoalesce(at, () => ( before));
|
|
2313
2676
|
return apply({ op: "add_commands", shot, commands, at }, "Add commands", (s) => {
|
|
2314
2677
|
const shots = describeMotionShots(s);
|
|
@@ -2344,7 +2707,7 @@ function createAnimationEditorToolProvider(controller, options = {}) {
|
|
|
2344
2707
|
const setSettings = {
|
|
2345
2708
|
name: "set_animation_settings",
|
|
2346
2709
|
title: "Change animation settings",
|
|
2347
|
-
description: "Sets the name, description, loop mode, export frame rate or target length. Returns the timing.",
|
|
2710
|
+
description: "Sets the name, description, loop mode, export frame rate, stepped time or target length. Returns the timing.",
|
|
2348
2711
|
inputSchema: object(SETTINGS),
|
|
2349
2712
|
annotations: { title: "Change animation settings", ...WRITE_IDEMPOTENT },
|
|
2350
2713
|
handler: (args) => apply({ op: "set_settings", ...args }, "Change settings")
|
|
@@ -2423,7 +2786,7 @@ function createAnimationEditorToolProvider(controller, options = {}) {
|
|
|
2423
2786
|
}
|
|
2424
2787
|
};
|
|
2425
2788
|
const reads = [getAnimation, getShot, findElements, checkAnimation, renderFrame, exportFlairMotion, exportFlairEffect, seekAnimation];
|
|
2426
|
-
const writes = [setArtworkSvg, insertShots, updateShot, deleteShots, moveShot, addCommands, updateCommand, deleteCommands, setSettings, setControls, editAnimation, writeAnimation, undo, redo];
|
|
2789
|
+
const writes = [setArtworkSvg, importFlairEffect, insertShots, updateShot, deleteShots, moveShot, addCommands, updateCommand, deleteCommands, setSettings, setControls, editAnimation, writeAnimation, undo, redo];
|
|
2427
2790
|
return {
|
|
2428
2791
|
id: _nullishCoalesce(options.id, () => ( "animation")),
|
|
2429
2792
|
namespace: options.namespace,
|
|
@@ -2458,11 +2821,14 @@ Commands
|
|
|
2458
2821
|
- enter also has zoom (settles from larger) and rise (rises a short way, overshooting a touch); pop and zoom take a starting "scale".
|
|
2459
2822
|
- morph turns a shape into another node's shape (keep the target hidden); follow moves along a path node (with "rotate", moves made meanwhile turn with the path).
|
|
2460
2823
|
- Emphasis draws the eye and returns to rest: pulse (swell), shake (side to side, or "axis": "vertical"), teeter (rock), wave (bob, no settling), wiggle (seeded wander; the same seed wanders the same way) and color-pulse (tint and back).
|
|
2824
|
+
- Any command but enter and exit can "repeat" (loop, mirror back and forth, or offset to carry on: rotate by 90, repeat 4 is a full turn); "duration" is one cycle.
|
|
2825
|
+
- Springs ("spring-gentle", "spring-quick", "spring-bouncy", "spring-slow" or { "type": "spring", \u2026 }) feel physical; leave "duration" out and the spring sets it.
|
|
2826
|
+
- "steps": 12 (on a command, or for the whole scene with set_animation_settings) holds poses "on twos" for a hand-drawn look. "geometric": true on scale or a camera zooms at an even rate.
|
|
2461
2827
|
- Commands share their names and defaults with Flair's motion primitives (get_animation shows each one's "flairPrimitive"), and emphasis, reveals and follows export to Flair exactly.
|
|
2462
2828
|
- effect tweens a numeric parameter of a live path effect already on the node (twist angle, wiggle time, zig zag size).
|
|
2463
2829
|
|
|
2464
2830
|
Working method
|
|
2465
|
-
1. get_animation to learn the element ids (set_animation_artwork_svg first if there is no artwork: give every element you will animate an id).
|
|
2831
|
+
1. get_animation to learn the element ids (set_animation_artwork_svg first if there is no artwork: give every element you will animate an id; or import_flair_effect to start from a Flair effect and edit it).
|
|
2466
2832
|
2. Plan a few shots, each one idea. Add them with insert_shots, or edit_animation to make several edits as one step (dry_run first when unsure).
|
|
2467
2833
|
3. check_animation and fix every error and warning; render_animation_frame (shot + "end") to look at the result.
|
|
2468
2834
|
4. To make a reusable Flair effect, set_animation_controls (colour controls stand for every use of their default colour) and export_flair_effect.
|
|
@@ -2540,5 +2906,12 @@ Keep motion purposeful: one thing moving at a time reads best, ease-out for arri
|
|
|
2540
2906
|
|
|
2541
2907
|
|
|
2542
2908
|
|
|
2543
|
-
|
|
2909
|
+
|
|
2910
|
+
|
|
2911
|
+
|
|
2912
|
+
|
|
2913
|
+
|
|
2914
|
+
|
|
2915
|
+
|
|
2916
|
+
exports.ANIMATION_EDITOR_AGENT_INSTRUCTIONS = ANIMATION_EDITOR_AGENT_INSTRUCTIONS; exports.AnimationEditorController = AnimationEditorController; exports.FLAIR_EASES = FLAIR_EASES; exports.FLAIR_EXPORT_VERSION = FLAIR_EXPORT_VERSION; exports.MOTION_CAMERA_MS = MOTION_CAMERA_MS; exports.MOTION_COMMAND_SCHEMA = MOTION_COMMAND_SCHEMA; exports.MOTION_COMMAND_TYPES = MOTION_COMMAND_TYPES; exports.MOTION_DEFAULT_DURATIONS = MOTION_DEFAULT_DURATIONS; exports.MOTION_DEFAULT_HOLD = MOTION_DEFAULT_HOLD; exports.MOTION_DIRECTIONS = MOTION_DIRECTIONS; exports.MOTION_DRAW_EFFECT_ID = MOTION_DRAW_EFFECT_ID; exports.MOTION_DRAW_MS_PER_UNIT = MOTION_DRAW_MS_PER_UNIT; exports.MOTION_DRAW_RANGE = MOTION_DRAW_RANGE; exports.MOTION_EASE_IN_OUT = MOTION_EASE_IN_OUT; exports.MOTION_EASE_OUT = MOTION_EASE_OUT; exports.MOTION_EDIT_OPS = MOTION_EDIT_OPS; exports.MOTION_ENTER_STYLES = MOTION_ENTER_STYLES; exports.MOTION_EXIT_STYLES = MOTION_EXIT_STYLES; exports.MOTION_IRIS_SHAPES = MOTION_IRIS_SHAPES; exports.MOTION_ISSUE_CODES = MOTION_ISSUE_CODES; exports.MOTION_MIN_CHANGE_MS = MOTION_MIN_CHANGE_MS; exports.MOTION_REPEAT_MODES = MOTION_REPEAT_MODES; exports.MOTION_SPLIT_ORIENTATIONS = MOTION_SPLIT_ORIENTATIONS; exports.MotionSceneFormatError = MotionSceneFormatError; exports.activeSegment = activeSegment; exports.applyMotionControlValues = applyMotionControlValues; exports.applyMotionEdits = applyMotionEdits; exports.checkMotionScene = checkMotionScene; exports.commandShapeProblems = commandShapeProblems; exports.compileMotionScene = compileMotionScene; exports.createAnimationEditorToolProvider = createAnimationEditorToolProvider; exports.createMotionScene = createMotionScene; exports.describeMotionElements = describeMotionElements; exports.describeMotionShots = describeMotionShots; exports.fitMotionRect = fitMotionRect; exports.flairAtLeast = flairAtLeast; exports.flairCurveFor = flairCurveFor; exports.flairEaseFor = flairEaseFor; exports.freshShotId = freshShotId; exports.fromFlairSvgMotion = fromFlairSvgMotion; exports.fromFlairVectorEffect = fromFlairVectorEffect; exports.indexMotionNodes = indexMotionNodes; exports.interpolateMotionValue = interpolateMotionValue; exports.isMotionCommand = isMotionCommand; exports.isMotionShot = isMotionShot; exports.motionClipAttrs = motionClipAttrs; exports.motionCommandDuration = motionCommandDuration; exports.motionCommandRepeat = motionCommandRepeat; exports.motionCycleDuration = motionCycleDuration; exports.motionFlairPrimitive = motionFlairPrimitive; exports.motionFollowRoute = motionFollowRoute; exports.motionFrameAt = motionFrameAt; exports.motionFrameToSvg = motionFrameToSvg; exports.motionIssue = motionIssue; exports.motionIssueKey = motionIssueKey; exports.motionIssueLine = motionIssueLine; exports.motionNodeClips = motionNodeClips; exports.motionNodeOffset = motionNodeOffset; exports.motionPathLength = motionPathLength; exports.motionRestValue = motionRestValue; exports.motionRevealClip = motionRevealClip; exports.motionRevealPadding = motionRevealPadding; exports.motionSceneShapeProblems = motionSceneShapeProblems; exports.motionSceneToJson = motionSceneToJson; exports.motionShotAt = motionShotAt; exports.motionTrackKey = motionTrackKey; exports.namedWithSuggestion = namedWithSuggestion; exports.parseMotionScene = parseMotionScene; exports.resolveMotionCamera = resolveMotionCamera; exports.segmentProgress = segmentProgress; exports.segmentValue = segmentValue; exports.shotShapeProblems = shotShapeProblems; exports.suggestName = suggestName; exports.toFlairEffectDocument = toFlairEffectDocument; exports.toFlairSvgMotion = toFlairSvgMotion; exports.toFlairVectorEffect = toFlairVectorEffect; exports.trackValue = trackValue; exports.withViewBox = withViewBox;
|
|
2544
2917
|
//# sourceMappingURL=index.cjs.map
|