@pixerate/editor 0.68.0 → 0.70.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-editor/index.cjs +323 -61
- package/dist/animation-editor/index.cjs.map +1 -1
- package/dist/animation-editor/index.d.cts +118 -6
- package/dist/animation-editor/index.d.ts +118 -6
- package/dist/animation-editor/index.js +277 -15
- package/dist/animation-editor/index.js.map +1 -1
- package/package.json +1 -1
|
@@ -56,6 +56,7 @@ var _chunk2FHLHZALcjs = require('../chunk-2FHLHZAL.cjs');
|
|
|
56
56
|
|
|
57
57
|
// src/animation-editor/types.ts
|
|
58
58
|
var MOTION_TRIGGERS = ["click", "hover", "press", "scroll"];
|
|
59
|
+
var MOTION_CLIP_TARGET = "$";
|
|
59
60
|
var MOTION_COMMAND_TYPES = [
|
|
60
61
|
"enter",
|
|
61
62
|
"exit",
|
|
@@ -67,6 +68,7 @@ var MOTION_COMMAND_TYPES = [
|
|
|
67
68
|
"style",
|
|
68
69
|
"morph",
|
|
69
70
|
"match",
|
|
71
|
+
"play",
|
|
70
72
|
"follow",
|
|
71
73
|
"pulse",
|
|
72
74
|
"shake",
|
|
@@ -94,6 +96,8 @@ var MOTION_DEFAULT_DURATIONS = {
|
|
|
94
96
|
style: 500,
|
|
95
97
|
morph: 900,
|
|
96
98
|
match: 900,
|
|
99
|
+
/** A clip's length comes from its commands (this is only a fallback). */
|
|
100
|
+
play: 600,
|
|
97
101
|
follow: 1400,
|
|
98
102
|
pulse: 600,
|
|
99
103
|
shake: 500,
|
|
@@ -137,6 +141,7 @@ var MOTION_ISSUE_CODES = {
|
|
|
137
141
|
"unknown-path": { severity: "error", wording: "A follow command names a path the artwork does not have." },
|
|
138
142
|
"not-a-path": { severity: "error", wording: "A follow path must be a path node, not a group." },
|
|
139
143
|
"reveal-no-box": { severity: "warning", wording: "A wipe, iris or split cannot clip a node with no width or height (a lone straight line), so it fades instead." },
|
|
144
|
+
"unknown-clip": { severity: "error", wording: "A play command names a clip the scene does not have." },
|
|
140
145
|
"unknown-shape": { severity: "error", wording: "A morph names a shape the artwork does not have." },
|
|
141
146
|
"match-self": { severity: "error", wording: "A match turns a group into itself or into a group it holds (or that holds it)." },
|
|
142
147
|
"match-unpaired": { severity: "note", wording: `Parts of a match's "into" group have no partner in the target, so they do not appear.` },
|
|
@@ -214,14 +219,15 @@ ${problems.map((p) => `- ${p}`).join("\n")}`);
|
|
|
214
219
|
this.problems = problems;
|
|
215
220
|
}
|
|
216
221
|
};
|
|
217
|
-
var SCENE_KEYS = ["version", "name", "description", "artwork", "shots", "loop", "trigger", "fps", "steps", "beat", "narration", "target", "controls", "presets"];
|
|
222
|
+
var SCENE_KEYS = ["version", "name", "description", "artwork", "shots", "clips", "loop", "trigger", "fps", "steps", "beat", "narration", "target", "controls", "presets"];
|
|
218
223
|
var SHOT_KEYS = ["id", "label", "camera", "at", "run", "hold", "speed"];
|
|
219
224
|
var CAMERA_KEYS = ["x", "y", "width", "height", "frame", "padding", "ease", "ms", "geometric"];
|
|
220
225
|
var BASE_KEYS = ["type", "targets", "duration", "delay", "stagger", "with", "ease", "note", "repeat", "repeatMode", "steps"];
|
|
226
|
+
var DRAW_ORDER_KEYS = ["order", "pace", "align", "sortBy", "origin"];
|
|
221
227
|
var COMMAND_KEYS = {
|
|
222
|
-
enter: [...BASE_KEYS, "style", "direction", "distance", "scale", "shape", "orientation"],
|
|
223
|
-
exit: [...BASE_KEYS, "style", "direction", "distance"],
|
|
224
|
-
draw: [...BASE_KEYS, "from", "to"],
|
|
228
|
+
enter: [...BASE_KEYS, ...DRAW_ORDER_KEYS, "style", "direction", "distance", "scale", "shape", "orientation"],
|
|
229
|
+
exit: [...BASE_KEYS, ...DRAW_ORDER_KEYS, "style", "direction", "distance"],
|
|
230
|
+
draw: [...BASE_KEYS, ...DRAW_ORDER_KEYS, "from", "to", "comet"],
|
|
225
231
|
move: [...BASE_KEYS, "by", "to", "via", "pacing", "blend"],
|
|
226
232
|
rotate: [...BASE_KEYS, "by", "to", "blend"],
|
|
227
233
|
scale: [...BASE_KEYS, "to", "geometric", "blend"],
|
|
@@ -229,6 +235,7 @@ var COMMAND_KEYS = {
|
|
|
229
235
|
style: [...BASE_KEYS, "fill", "stroke", "strokeWidth"],
|
|
230
236
|
morph: [...BASE_KEYS, "into"],
|
|
231
237
|
match: [...BASE_KEYS, "into"],
|
|
238
|
+
play: ["type", "targets", "delay", "stagger", "with", "note", "clip", "speed"],
|
|
232
239
|
follow: [...BASE_KEYS, "path", "rotate", "from", "to"],
|
|
233
240
|
pulse: [...BASE_KEYS, "amount", "count", "blend"],
|
|
234
241
|
shake: [...BASE_KEYS, "amount", "count", "axis", "blend"],
|
|
@@ -264,6 +271,14 @@ function commandShapeProblems(command2, where) {
|
|
|
264
271
|
if (command2[key] !== void 0 && !isNum(command2[key])) p(`"${key}" must be a number.`);
|
|
265
272
|
};
|
|
266
273
|
if (command2.with !== void 0 && command2.with !== "after" && command2.with !== "previous") p(`"with" must be "after" or "previous".`);
|
|
274
|
+
if (DRAW_ORDER_KEYS.some((k) => command2[k] !== void 0)) {
|
|
275
|
+
if (type === "enter" && command2.style !== "draw" || type === "exit" && command2.style !== "undraw") p(`"order", "pace", "align", "sortBy" and "origin" go with drawing: style "${type === "enter" ? "draw" : "undraw"}".`);
|
|
276
|
+
if (command2.order !== void 0 && command2.order !== "together" && command2.order !== "sequence") p(`"order" must be "together" or "sequence" (one stroke after another).`);
|
|
277
|
+
if (command2.pace !== void 0 && command2.pace !== "time" && command2.pace !== "speed") p(`"pace" must be "time" (each stroke takes the whole time) or "speed" (all at one speed).`);
|
|
278
|
+
if (command2.align !== void 0 && command2.align !== "start" && command2.align !== "end") p(`"align" must be "start" or "end".`);
|
|
279
|
+
if (command2.sortBy !== void 0 && command2.sortBy !== "document" && command2.sortBy !== "distance") p(`"sortBy" must be "document" or "distance".`);
|
|
280
|
+
if (command2.origin !== void 0 && !isPoint(command2.origin)) p(`"origin" must be { "x": number, "y": number } (document units).`);
|
|
281
|
+
}
|
|
267
282
|
if (command2.note !== void 0 && typeof command2.note !== "string") p(`"note" must be a string.`);
|
|
268
283
|
if (type === "wait") {
|
|
269
284
|
if (!isNum(command2.duration) || command2.duration < 0) p(`"duration" must be a number of milliseconds \u2265 0.`);
|
|
@@ -297,6 +312,8 @@ function commandShapeProblems(command2, where) {
|
|
|
297
312
|
case "draw":
|
|
298
313
|
fraction("from");
|
|
299
314
|
fraction("to");
|
|
315
|
+
fraction("comet");
|
|
316
|
+
if (command2.comet !== void 0 && (command2.from !== void 0 || command2.to !== void 0)) p(`"comet" runs the whole path; leave out "from" and "to".`);
|
|
300
317
|
break;
|
|
301
318
|
case "move":
|
|
302
319
|
if (command2.by === void 0 && command2.to === void 0) p(`give "by" (an offset) or "to" (a point).`);
|
|
@@ -326,6 +343,10 @@ function commandShapeProblems(command2, where) {
|
|
|
326
343
|
if (command2.stroke !== void 0 && typeof command2.stroke !== "string") p(`"stroke" must be a CSS colour.`);
|
|
327
344
|
if (command2.strokeWidth !== void 0 && !(isNum(command2.strokeWidth) && command2.strokeWidth >= 0)) p(`"strokeWidth" must be a number \u2265 0.`);
|
|
328
345
|
break;
|
|
346
|
+
case "play":
|
|
347
|
+
if (typeof command2.clip !== "string" || !command2.clip) p(`"clip" must be the name of one of the scene's clips.`);
|
|
348
|
+
if (command2.speed !== void 0 && !(isNum(command2.speed) && command2.speed >= 0.05 && command2.speed <= 20)) p(`"speed" must be a number from 0.05 to 20 (2 plays the clip twice as fast).`);
|
|
349
|
+
break;
|
|
329
350
|
case "morph":
|
|
330
351
|
case "match":
|
|
331
352
|
if (typeof command2.into !== "string" || !command2.into) p(`"into" must be the id of a ${type === "match" ? "group to become" : "node whose shape to take"}.`);
|
|
@@ -363,6 +384,25 @@ function commandShapeProblems(command2, where) {
|
|
|
363
384
|
}
|
|
364
385
|
return problems;
|
|
365
386
|
}
|
|
387
|
+
function clipsShapeProblems(clips) {
|
|
388
|
+
if (!isObject(clips)) return ['Scene: "clips" must be an object of named clips { "pop": { "run": [ \u2026 ] } }.'];
|
|
389
|
+
const problems = [];
|
|
390
|
+
for (const [name, clip] of Object.entries(clips)) {
|
|
391
|
+
const where = `Clip "${name}"`;
|
|
392
|
+
if (!/^[A-Za-z_][\w-]*$/.test(name)) problems.push(`${where}: a clip name is letters, digits, "_" or "-" (e.g. "pop-in").`);
|
|
393
|
+
if (!isObject(clip) || !Array.isArray(clip.run)) {
|
|
394
|
+
problems.push(`${where} must be { "description"?, "run": [commands] }.`);
|
|
395
|
+
continue;
|
|
396
|
+
}
|
|
397
|
+
unknownKeys(clip, ["description", "run"], where, problems);
|
|
398
|
+
if (clip.description !== void 0 && typeof clip.description !== "string") problems.push(`${where}: "description" must be a string.`);
|
|
399
|
+
clip.run.forEach((command2, i) => {
|
|
400
|
+
if (isObject(command2) && command2.type === "play") problems.push(`${where}, command ${i + 1}: a clip cannot play another clip; copy its commands in instead.`);
|
|
401
|
+
else problems.push(...commandShapeProblems(command2, `${where}, command ${i + 1}`));
|
|
402
|
+
});
|
|
403
|
+
}
|
|
404
|
+
return problems;
|
|
405
|
+
}
|
|
366
406
|
function shotShapeProblems(shot, where) {
|
|
367
407
|
if (!isObject(shot)) return [`${where} must be an object.`];
|
|
368
408
|
const problems = [];
|
|
@@ -410,6 +450,7 @@ function motionSceneShapeProblems(input) {
|
|
|
410
450
|
const art = input.artwork;
|
|
411
451
|
if (!isObject(art) || !isNum(art.width) || !isNum(art.height) || !(art.width > 0) || !(art.height > 0) || !Array.isArray(art.nodes))
|
|
412
452
|
problems.push('Scene: "artwork" must be a vector document { width, height, nodes }.');
|
|
453
|
+
if (input.clips !== void 0) problems.push(...clipsShapeProblems(input.clips));
|
|
413
454
|
if (input.trigger !== void 0 && !MOTION_TRIGGERS.includes(input.trigger)) problems.push(`Scene: "trigger" must be one of ${MOTION_TRIGGERS.join(", ")}.`);
|
|
414
455
|
if (input.loop !== void 0 && !["none", "loop", "ping-pong"].includes(input.loop)) problems.push('Scene: "loop" must be "none", "loop" or "ping-pong".');
|
|
415
456
|
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.');
|
|
@@ -793,8 +834,38 @@ function motionCommandRepeat(command2) {
|
|
|
793
834
|
function motionCommandDuration(command2, scene, target) {
|
|
794
835
|
return motionCycleDuration(command2, scene, target) * motionCommandRepeat(command2);
|
|
795
836
|
}
|
|
837
|
+
function motionClipLayout(scene, clip, target, speed = 1) {
|
|
838
|
+
const run = _optionalChain([scene, 'access', _22 => _22.clips, 'optionalAccess', _23 => _23[clip], 'optionalAccess', _24 => _24.run]);
|
|
839
|
+
if (!run) return { items: [], length: 0 };
|
|
840
|
+
const f = speed > 0 && Number.isFinite(speed) ? 1 / speed : 1;
|
|
841
|
+
const items = [];
|
|
842
|
+
let runEnd = 0;
|
|
843
|
+
let prevStart = 0;
|
|
844
|
+
run.forEach((raw, i) => {
|
|
845
|
+
const base = raw.with === "previous" && i > 0 ? prevStart : runEnd;
|
|
846
|
+
if (raw.type === "wait") {
|
|
847
|
+
prevStart = base;
|
|
848
|
+
runEnd = Math.max(runEnd, base + raw.duration * f);
|
|
849
|
+
return;
|
|
850
|
+
}
|
|
851
|
+
if (raw.type === "play") return;
|
|
852
|
+
const command2 = { ...raw, targets: raw.targets.map((t) => t === MOTION_CLIP_TARGET ? target : t) };
|
|
853
|
+
const begin = base + (_nullishCoalesce(command2.delay, () => ( 0))) * f;
|
|
854
|
+
let end = begin;
|
|
855
|
+
command2.targets.forEach((t, k) => {
|
|
856
|
+
const cycle = motionCycleDuration(command2, scene, t) * f;
|
|
857
|
+
const s = begin + k * (_nullishCoalesce(command2.stagger, () => ( 0))) * f;
|
|
858
|
+
items.push({ command: command2, target: t, start: s, cycle });
|
|
859
|
+
end = Math.max(end, s + cycle * motionCommandRepeat(command2));
|
|
860
|
+
});
|
|
861
|
+
prevStart = begin;
|
|
862
|
+
runEnd = Math.max(runEnd, end);
|
|
863
|
+
});
|
|
864
|
+
return { items, length: runEnd };
|
|
865
|
+
}
|
|
796
866
|
function motionCycleDuration(command2, scene, target) {
|
|
797
867
|
if (command2.type === "wait") return command2.duration;
|
|
868
|
+
if (command2.type === "play") return motionClipLayout(scene, command2.clip, _nullishCoalesce(target, () => ( MOTION_CLIP_TARGET)), command2.speed).length;
|
|
798
869
|
if (command2.duration !== void 0) return command2.duration;
|
|
799
870
|
if (command2.ease !== void 0) {
|
|
800
871
|
const ease = _chunk2FHLHZALcjs.resolveEasing.call(void 0, command2.ease);
|
|
@@ -806,7 +877,7 @@ function motionCycleDuration(command2, scene, target) {
|
|
|
806
877
|
const ms = length * MOTION_DRAW_MS_PER_UNIT;
|
|
807
878
|
return Math.round(Math.min(MOTION_DRAW_RANGE[1], Math.max(MOTION_DRAW_RANGE[0], ms)));
|
|
808
879
|
}
|
|
809
|
-
if (command2.type === "move" && _optionalChain([command2, 'access',
|
|
880
|
+
if (command2.type === "move" && _optionalChain([command2, 'access', _25 => _25.via, 'optionalAccess', _26 => _26.length])) return MOTION_DEFAULT_DURATIONS.move + 400 * command2.via.length;
|
|
810
881
|
return MOTION_DEFAULT_DURATIONS[command2.type];
|
|
811
882
|
}
|
|
812
883
|
var MOTION_EASE_OUT = { type: "cubic-bezier", x1: 1 / 3, y1: 1, x2: 2 / 3, y2: 1 };
|
|
@@ -884,6 +955,19 @@ function compileMotionScene(scene) {
|
|
|
884
955
|
const shot = scene.shots[timing.shotIndex];
|
|
885
956
|
const command2 = shot.run[timing.command];
|
|
886
957
|
if (command2.type === "wait") continue;
|
|
958
|
+
if (command2.type === "play") {
|
|
959
|
+
const f = motionShotTimeScale(shot);
|
|
960
|
+
command2.targets.forEach((target, k) => {
|
|
961
|
+
if (!nodes.has(target)) return;
|
|
962
|
+
const at = timing.start + k * (_nullishCoalesce(command2.stagger, () => ( 0))) * f;
|
|
963
|
+
for (const item of motionClipLayout(scene, command2.clip, target, (_nullishCoalesce(command2.speed, () => ( 1))) / f).items) {
|
|
964
|
+
const entry = nodes.get(item.target);
|
|
965
|
+
if (!entry) continue;
|
|
966
|
+
jobs.push({ start: at + item.start, order: order++, run: () => compileRepeated(item.command, item.target, entry, at + item.start, item.cycle, timing) });
|
|
967
|
+
}
|
|
968
|
+
});
|
|
969
|
+
continue;
|
|
970
|
+
}
|
|
887
971
|
command2.targets.forEach((target, k) => {
|
|
888
972
|
const entry = nodes.get(target);
|
|
889
973
|
if (!entry) return;
|
|
@@ -930,19 +1014,58 @@ function compileMotionScene(scene) {
|
|
|
930
1014
|
return typeof v === "number" ? v : 0;
|
|
931
1015
|
};
|
|
932
1016
|
const ease = command2.ease;
|
|
1017
|
+
const drawWindows = (s, e, o) => {
|
|
1018
|
+
const order2 = _nullishCoalesce(o.order, () => ( "together"));
|
|
1019
|
+
const pace = _nullishCoalesce(o.pace, () => ( "time"));
|
|
1020
|
+
if (order2 === "together" && pace === "time") return null;
|
|
1021
|
+
const paths = entry.node.type === "path" ? [entry.node] : _chunkYJLCBA7Vcjs.collectPathNodes.call(void 0, entry.node);
|
|
1022
|
+
let list = paths.map((p) => {
|
|
1023
|
+
const at = nodes.get(p.id);
|
|
1024
|
+
let first = { x: 0, y: 0 };
|
|
1025
|
+
try {
|
|
1026
|
+
const m = _chunkYJLCBA7Vcjs.vectorPathWorldBezier.call(void 0, p, _nullishCoalesce(_optionalChain([at, 'optionalAccess', _27 => _27.parentWorld]), () => ( _chunkYJLCBA7Vcjs.IDENTITY_MATRIX)), { effects: false })[0];
|
|
1027
|
+
if (m && m.type === "M") first = { x: m.x, y: m.y };
|
|
1028
|
+
} catch (e3) {
|
|
1029
|
+
}
|
|
1030
|
+
return { id: p.id, len: Math.max(1e-6, motionPathLength(p, _optionalChain([at, 'optionalAccess', _28 => _28.parentWorld]))), first };
|
|
1031
|
+
});
|
|
1032
|
+
if (!list.length) return null;
|
|
1033
|
+
const span = Math.max(0, e - s);
|
|
1034
|
+
if (order2 === "sequence") {
|
|
1035
|
+
if (o.sortBy === "distance") {
|
|
1036
|
+
const b = entry.bounds;
|
|
1037
|
+
const origin = _nullishCoalesce(o.origin, () => ( (b ? { x: b.x + b.width / 2, y: b.y + b.height / 2 } : { x: 0, y: 0 })));
|
|
1038
|
+
list = [...list].sort((a, c) => Math.hypot(a.first.x - origin.x, a.first.y - origin.y) - Math.hypot(c.first.x - origin.x, c.first.y - origin.y));
|
|
1039
|
+
}
|
|
1040
|
+
const total = list.reduce((sum, p) => sum + p.len, 0);
|
|
1041
|
+
let at = s;
|
|
1042
|
+
return list.map((p) => {
|
|
1043
|
+
const w = { id: p.id, start: at, end: at + span * p.len / total };
|
|
1044
|
+
at = w.end;
|
|
1045
|
+
return w;
|
|
1046
|
+
});
|
|
1047
|
+
}
|
|
1048
|
+
const longest = Math.max(...list.map((p) => p.len));
|
|
1049
|
+
return list.map((p) => {
|
|
1050
|
+
const d = span * p.len / longest;
|
|
1051
|
+
return o.align === "end" ? { id: p.id, start: e - d, end: e } : { id: p.id, start: s, end: s + d };
|
|
1052
|
+
});
|
|
1053
|
+
};
|
|
933
1054
|
switch (command2.type) {
|
|
934
1055
|
case "enter": {
|
|
935
1056
|
const style = _nullishCoalesce(command2.style, () => ( "fade"));
|
|
936
1057
|
const opacity = rest("opacity");
|
|
937
1058
|
const reveal = style === "wipe" ? motionRevealClip(entry, { style, direction: command2.direction }) : style === "iris" ? motionRevealClip(entry, { style, shape: command2.shape }) : style === "split" ? motionRevealClip(entry, { style, orientation: command2.orientation }) : null;
|
|
938
|
-
for (const p of ["clipRect", "clipCircle"]) if (p !== _optionalChain([reveal, 'optionalAccess',
|
|
1059
|
+
for (const p of ["clipRect", "clipCircle"]) if (p !== _optionalChain([reveal, 'optionalAccess', _29 => _29.property]) && tracks.has(motionTrackKey(node, p))) seg(p, rest(p), "linear", start, start);
|
|
939
1060
|
if (reveal) {
|
|
940
1061
|
seg("opacity", opacity, "linear", start, start);
|
|
941
1062
|
seg(reveal.property, reveal.to, _nullishCoalesce(ease, () => ( MOTION_EASE_OUT)), start, end, { from: reveal.from });
|
|
942
1063
|
break;
|
|
943
1064
|
}
|
|
944
1065
|
if (style === "draw") {
|
|
945
|
-
|
|
1066
|
+
const windows = drawWindows(start, start + duration2 * 0.75, command2);
|
|
1067
|
+
if (windows) for (const w of windows) push({ node: w.id, property: "drawEnd", start: w.start, end: w.end, from: 0, to: 1, ease: _nullishCoalesce(ease, () => ( MOTION_EASE_IN_OUT)), ...where });
|
|
1068
|
+
else seg("drawEnd", 1, _nullishCoalesce(ease, () => ( MOTION_EASE_IN_OUT)), start, start + duration2 * 0.75, { from: 0 });
|
|
946
1069
|
seg("fillOpacity", 1, MOTION_EASE_OUT, start + duration2 * 0.6, end, { from: 0 });
|
|
947
1070
|
break;
|
|
948
1071
|
}
|
|
@@ -976,7 +1099,9 @@ function compileMotionScene(scene) {
|
|
|
976
1099
|
if (wipe) seg(wipe.property, wipe.to, _nullishCoalesce(ease, () => ( "ease-in")), start, end, { from: wipe.from });
|
|
977
1100
|
else if (style === "undraw") {
|
|
978
1101
|
seg("fillOpacity", 0, "ease-in", start, start + duration2 * 0.4);
|
|
979
|
-
|
|
1102
|
+
const windows = drawWindows(start, end, command2);
|
|
1103
|
+
if (windows) for (const w of windows) push({ node: w.id, property: "drawEnd", start: w.start, end: w.end, from: valueAt(w.id, "drawEnd", w.start), to: 0, ease: _nullishCoalesce(ease, () => ( MOTION_EASE_IN_OUT)), ...where });
|
|
1104
|
+
else seg("drawEnd", 0, _nullishCoalesce(ease, () => ( MOTION_EASE_IN_OUT)));
|
|
980
1105
|
} else if (style === "shrink") {
|
|
981
1106
|
seg("scaleX", 0, _nullishCoalesce(ease, () => ( "ease-in")));
|
|
982
1107
|
seg("scaleY", 0, _nullishCoalesce(ease, () => ( "ease-in")));
|
|
@@ -991,21 +1116,31 @@ function compileMotionScene(scene) {
|
|
|
991
1116
|
break;
|
|
992
1117
|
}
|
|
993
1118
|
case "draw": {
|
|
994
|
-
const
|
|
995
|
-
|
|
1119
|
+
const e = _nullishCoalesce(ease, () => ( MOTION_EASE_IN_OUT));
|
|
1120
|
+
const comet = command2.comet !== void 0 ? Math.min(1, Math.max(0, command2.comet)) : void 0;
|
|
1121
|
+
const windows = _nullishCoalesce(drawWindows(start, end, command2), () => ( [{ id: node, start, end }]));
|
|
1122
|
+
for (const w of windows) {
|
|
1123
|
+
if (comet !== void 0) {
|
|
1124
|
+
push({ node: w.id, property: "drawEnd", start: w.start, end: w.end, from: 0, to: 1 + comet, ease: e, ...where });
|
|
1125
|
+
push({ node: w.id, property: "drawStart", start: w.start, end: w.end, from: -comet, to: 1, ease: e, ...where });
|
|
1126
|
+
continue;
|
|
1127
|
+
}
|
|
1128
|
+
const drawn = tracks.has(motionTrackKey(w.id, "drawEnd"));
|
|
1129
|
+
push({ node: w.id, property: "drawEnd", start: w.start, end: w.end, from: _nullishCoalesce(command2.from, () => ( (drawn ? valueAt(w.id, "drawEnd", w.start) : 0))), to: _nullishCoalesce(command2.to, () => ( 1)), ease: e, ...where });
|
|
1130
|
+
}
|
|
996
1131
|
break;
|
|
997
1132
|
}
|
|
998
1133
|
case "move": {
|
|
999
1134
|
const e = _nullishCoalesce(ease, () => ( MOTION_EASE_IN_OUT));
|
|
1000
|
-
let dx = _nullishCoalesce(_optionalChain([command2, 'access',
|
|
1001
|
-
let dy = _nullishCoalesce(_optionalChain([command2, 'access',
|
|
1135
|
+
let dx = _nullishCoalesce(_optionalChain([command2, 'access', _30 => _30.by, 'optionalAccess', _31 => _31.x]), () => ( 0));
|
|
1136
|
+
let dy = _nullishCoalesce(_optionalChain([command2, 'access', _32 => _32.by, 'optionalAccess', _33 => _33.y]), () => ( 0));
|
|
1002
1137
|
if (command2.to) {
|
|
1003
1138
|
const inv = _chunkYJLCBA7Vcjs.invertMatrix.call(void 0, entry.parentWorld);
|
|
1004
1139
|
const target = _chunkYJLCBA7Vcjs.applyMatrix.call(void 0, inv, command2.to);
|
|
1005
1140
|
dx = target.x - (entry.center.x + num("x"));
|
|
1006
1141
|
dy = target.y - (entry.center.y + num("y"));
|
|
1007
1142
|
}
|
|
1008
|
-
if (_optionalChain([command2, 'access',
|
|
1143
|
+
if (_optionalChain([command2, 'access', _34 => _34.via, 'optionalAccess', _35 => _35.length])) {
|
|
1009
1144
|
const inv = _chunkYJLCBA7Vcjs.invertMatrix.call(void 0, entry.parentWorld);
|
|
1010
1145
|
const x0 = num("x");
|
|
1011
1146
|
const y0 = num("y");
|
|
@@ -1116,7 +1251,7 @@ var MOTION_DRAW_EFFECT_ID = "~draw";
|
|
|
1116
1251
|
var TRANSFORM_PROPS = ["x", "y", "rotation", "scaleX", "scaleY", "follow"];
|
|
1117
1252
|
var tidy = (v) => Math.round(v * 1e6) / 1e6 || 0;
|
|
1118
1253
|
function applyMotionControlValues(scene, values) {
|
|
1119
|
-
if (!values || !_optionalChain([scene, 'access',
|
|
1254
|
+
if (!values || !_optionalChain([scene, 'access', _36 => _36.controls, 'optionalAccess', _37 => _37.length])) return scene;
|
|
1120
1255
|
const map = /* @__PURE__ */ new Map();
|
|
1121
1256
|
for (const control of scene.controls) {
|
|
1122
1257
|
const value = values[control.name];
|
|
@@ -1162,7 +1297,7 @@ function shapeIn(entry, shape) {
|
|
|
1162
1297
|
}
|
|
1163
1298
|
function shapeAt(entry, timeline, nodes, time) {
|
|
1164
1299
|
const track = timeline.tracks.get(motionTrackKey(entry.node.id, "shape"));
|
|
1165
|
-
if (!_optionalChain([track, 'optionalAccess',
|
|
1300
|
+
if (!_optionalChain([track, 'optionalAccess', _38 => _38.length])) return null;
|
|
1166
1301
|
const s = activeSegment(track, time);
|
|
1167
1302
|
const fromId = String(s ? s.from : track[0].from);
|
|
1168
1303
|
if (!s) return fromId === entry.node.id ? null : nodes.get(fromId) ? shapeIn(entry, nodes.get(fromId)) : null;
|
|
@@ -1179,14 +1314,14 @@ function motionFollowRoute(entry, path) {
|
|
|
1179
1314
|
if (!path || path.node.type !== "path") return null;
|
|
1180
1315
|
try {
|
|
1181
1316
|
return _chunkO2UH7LZEcjs.transformBezierPath.call(void 0, _chunkYJLCBA7Vcjs.vectorPathWorldBezier.call(void 0, path.node, path.parentWorld, { effects: false }), _chunkYJLCBA7Vcjs.invertMatrix.call(void 0, entry.parentWorld));
|
|
1182
|
-
} catch (
|
|
1317
|
+
} catch (e4) {
|
|
1183
1318
|
return null;
|
|
1184
1319
|
}
|
|
1185
1320
|
}
|
|
1186
1321
|
function followAt(entry, timeline, nodes, time) {
|
|
1187
1322
|
const track = timeline.tracks.get(motionTrackKey(entry.node.id, "follow"));
|
|
1188
|
-
const s = _nullishCoalesce(activeSegment(track, time), () => ( _optionalChain([track, 'optionalAccess',
|
|
1189
|
-
if (!_optionalChain([s, 'optionalAccess',
|
|
1323
|
+
const s = _nullishCoalesce(activeSegment(track, time), () => ( _optionalChain([track, 'optionalAccess', _39 => _39[0]])));
|
|
1324
|
+
if (!_optionalChain([s, 'optionalAccess', _40 => _40.path])) return null;
|
|
1190
1325
|
const route = motionFollowRoute(entry, nodes.get(s.path));
|
|
1191
1326
|
const progress = Number(trackValue(track, time));
|
|
1192
1327
|
if (!route || !(progress >= 0)) return null;
|
|
@@ -1194,7 +1329,7 @@ function followAt(entry, timeline, nodes, time) {
|
|
|
1194
1329
|
const at = _chunkO2UH7LZEcjs.bezierPathPointAtLength.call(void 0, route, Math.min(1, Math.max(0, progress)) * _chunkO2UH7LZEcjs.bezierPathLength.call(void 0, route));
|
|
1195
1330
|
if (!at) return null;
|
|
1196
1331
|
return { x: at.point[0], y: at.point[1], angle: s.rotate ? at.angle * 180 / Math.PI : 0 };
|
|
1197
|
-
} catch (
|
|
1332
|
+
} catch (e5) {
|
|
1198
1333
|
return null;
|
|
1199
1334
|
}
|
|
1200
1335
|
}
|
|
@@ -1246,7 +1381,7 @@ function motionFrameAt(scene, time, options = {}) {
|
|
|
1246
1381
|
if (out.visible === void 0) delete out.visible;
|
|
1247
1382
|
}
|
|
1248
1383
|
const effectTracks = [...timeline.tracks.keys()].filter((k) => k.startsWith(`${id}\0effect:`));
|
|
1249
|
-
if (effectTracks.length && _optionalChain([out, 'access',
|
|
1384
|
+
if (effectTracks.length && _optionalChain([out, 'access', _41 => _41.effects, 'optionalAccess', _42 => _42.length])) {
|
|
1250
1385
|
out = {
|
|
1251
1386
|
...out,
|
|
1252
1387
|
effects: out.effects.map((effect) => {
|
|
@@ -1359,7 +1494,7 @@ function motionPath(scene, id, options = {}) {
|
|
|
1359
1494
|
const r = (n) => Math.round(n * 100) / 100;
|
|
1360
1495
|
const centreAt = (t) => {
|
|
1361
1496
|
const node = indexMotionNodes(motionFrameAtCache(scene, t)).get(id);
|
|
1362
|
-
const b = _optionalChain([node, 'optionalAccess',
|
|
1497
|
+
const b = _optionalChain([node, 'optionalAccess', _43 => _43.bounds]);
|
|
1363
1498
|
return b ? { t: r(t), x: r(b.x + b.width / 2), y: r(b.y + b.height / 2) } : null;
|
|
1364
1499
|
};
|
|
1365
1500
|
const points = [];
|
|
@@ -1399,14 +1534,14 @@ function motionStageSvg(scene, time, options = {}) {
|
|
|
1399
1534
|
const open = current.indexOf(">") + 1;
|
|
1400
1535
|
const close = current.lastIndexOf("</svg>");
|
|
1401
1536
|
const body = current.slice(open, close);
|
|
1402
|
-
const bg = svgOptions.background !== false && scene.artwork.background ? _nullishCoalesce(_optionalChain([/^\s*<rect[^>]*\/>/, 'access',
|
|
1537
|
+
const bg = svgOptions.background !== false && scene.artwork.background ? _nullishCoalesce(_optionalChain([/^\s*<rect[^>]*\/>/, 'access', _44 => _44.exec, 'call', _45 => _45(body), 'optionalAccess', _46 => _46[0]]), () => ( "")) : "";
|
|
1403
1538
|
return `${current.slice(0, open)}${bg}${ghosts.join("")}${body.slice(bg.length)}${paths}</svg>`;
|
|
1404
1539
|
}
|
|
1405
1540
|
|
|
1406
1541
|
// src/animation-editor/check.ts
|
|
1407
1542
|
var MOTION_MIN_CHANGE_MS = 120;
|
|
1408
1543
|
var NUMERIC_PARAMS = (node, effectId) => {
|
|
1409
|
-
const effect = _optionalChain([node, 'access',
|
|
1544
|
+
const effect = _optionalChain([node, 'access', _47 => _47.effects, 'optionalAccess', _48 => _48.find, 'call', _49 => _49((e) => e.id === effectId)]);
|
|
1410
1545
|
if (!effect) return null;
|
|
1411
1546
|
return Object.entries(effect.params).filter(([, v]) => typeof v === "number").map(([k]) => k);
|
|
1412
1547
|
};
|
|
@@ -1437,7 +1572,11 @@ function visibleAt(timeline, id, time, node) {
|
|
|
1437
1572
|
const circle = clip("clipCircle");
|
|
1438
1573
|
if (Array.isArray(circle) && circle[2] <= 1e-4) return false;
|
|
1439
1574
|
const hasFill = node.type === "path" ? !!node.fill && node.fill !== "none" : _chunkYJLCBA7Vcjs.collectPathNodes.call(void 0, node).some((p) => p.fill && p.fill !== "none");
|
|
1440
|
-
|
|
1575
|
+
const drawn = num("drawEnd", 1) > 0.01 && (node.type === "path" || _chunkYJLCBA7Vcjs.collectPathNodes.call(void 0, node).length === 0 || _chunkYJLCBA7Vcjs.collectPathNodes.call(void 0, node).some((p) => {
|
|
1576
|
+
const v = trackValue(timeline.tracks.get(motionTrackKey(p.id, "drawEnd")), time);
|
|
1577
|
+
return typeof v !== "number" || v > 0.01;
|
|
1578
|
+
}));
|
|
1579
|
+
return drawn || hasFill && num("fillOpacity", 1) > 0.01;
|
|
1441
1580
|
}
|
|
1442
1581
|
function checkMotionScene(scene) {
|
|
1443
1582
|
const issues = [];
|
|
@@ -1483,6 +1622,8 @@ function checkMotionScene(scene) {
|
|
|
1483
1622
|
if (command2.type === "follow" && known(command2.path, "unknown-path", "path", where) && nodes.get(command2.path).node.type !== "path")
|
|
1484
1623
|
issues.push(motionIssue("not-a-path", `"${command2.path}" is a group; follow a single path node.`, { ...where, ids: [command2.path] }));
|
|
1485
1624
|
if (command2.type === "morph") known(command2.into, "unknown-shape", "shape", where);
|
|
1625
|
+
if (command2.type === "play" && !_optionalChain([scene, 'access', _50 => _50.clips, 'optionalAccess', _51 => _51[command2.clip]]))
|
|
1626
|
+
issues.push(motionIssue("unknown-clip", `There is no clip ${namedWithSuggestion(command2.clip, Object.keys(_nullishCoalesce(scene.clips, () => ( {}))))}; define it with set_animation_clip.`, where));
|
|
1486
1627
|
if (command2.type === "match" && known(command2.into, "unknown-shape", "group", where)) {
|
|
1487
1628
|
const into = nodes.get(command2.into);
|
|
1488
1629
|
for (const id of targets) {
|
|
@@ -1555,6 +1696,12 @@ function checkMotionScene(scene) {
|
|
|
1555
1696
|
}
|
|
1556
1697
|
});
|
|
1557
1698
|
});
|
|
1699
|
+
for (const [name, clip] of Object.entries(_nullishCoalesce(scene.clips, () => ( {}))))
|
|
1700
|
+
for (const command2 of clip.run)
|
|
1701
|
+
if (command2.type !== "wait") {
|
|
1702
|
+
for (const id of command2.targets)
|
|
1703
|
+
if (id !== MOTION_CLIP_TARGET && !nodes.has(id)) issues.push(motionIssue("unknown-target", `Clip "${name}" names ${namedWithSuggestion(id, [...nodes.keys()])}, which the artwork does not have; use "$" for the node the clip is played on.`, { ids: [id] }));
|
|
1704
|
+
}
|
|
1558
1705
|
if (scene.trigger) {
|
|
1559
1706
|
if ((_nullishCoalesce(scene.loop, () => ( "none"))) !== "none") issues.push(motionIssue("trigger-loop", `The scene plays on ${scene.trigger}, so its "${scene.loop}" loop does not apply; set loop to "none" or remove the trigger.`, {}));
|
|
1560
1707
|
if (scene.trigger === "click") {
|
|
@@ -1568,7 +1715,7 @@ function checkMotionScene(scene) {
|
|
|
1568
1715
|
if (!shot.at) return;
|
|
1569
1716
|
const timing = timeline.shots[i];
|
|
1570
1717
|
if (timing.cue === void 0) {
|
|
1571
|
-
const what = shot.at.beat !== void 0 ? `beat ${shot.at.beat}, but the scene has no beat grid; set one with set_animation_settings ({ "beat": { "bpm": \u2026 } })` : `word ${shot.at.word}, but the narration has ${_nullishCoalesce(_optionalChain([scene, 'access',
|
|
1718
|
+
const what = shot.at.beat !== void 0 ? `beat ${shot.at.beat}, but the scene has no beat grid; set one with set_animation_settings ({ "beat": { "bpm": \u2026 } })` : `word ${shot.at.word}, but the narration has ${_nullishCoalesce(_optionalChain([scene, 'access', _52 => _52.narration, 'optionalAccess', _53 => _53.words, 'access', _54 => _54.length]), () => ( 0))} words (indexes from 0)`;
|
|
1572
1719
|
issues.push(motionIssue("unknown-cue", `Shot "${shot.id}" is cued to ${what}.`, { shot: shot.id }));
|
|
1573
1720
|
} else if (timing.missedBy !== void 0) {
|
|
1574
1721
|
const late = Math.round(timing.missedBy);
|
|
@@ -1601,7 +1748,7 @@ function checkMotionScene(scene) {
|
|
|
1601
1748
|
const cam = frame.camera;
|
|
1602
1749
|
const docIndex = indexMotionNodes(frame.document.nodes);
|
|
1603
1750
|
for (const id of command2.targets) {
|
|
1604
|
-
const b = _optionalChain([docIndex, 'access',
|
|
1751
|
+
const b = _optionalChain([docIndex, 'access', _55 => _55.get, 'call', _56 => _56(id), 'optionalAccess', _57 => _57.bounds]);
|
|
1605
1752
|
if (!b || reported.has(id) || !visibleAt(timeline, id, timing.end, nodes.get(id).node)) continue;
|
|
1606
1753
|
const outside = b.x + b.width < cam.x || b.y + b.height < cam.y || b.x > cam.x + cam.width || b.y > cam.y + cam.height;
|
|
1607
1754
|
if (outside) {
|
|
@@ -1638,6 +1785,7 @@ var MOTION_EDIT_OPS = [
|
|
|
1638
1785
|
"move_command",
|
|
1639
1786
|
"set_artwork",
|
|
1640
1787
|
"set_controls",
|
|
1788
|
+
"set_clip",
|
|
1641
1789
|
"replace_scene"
|
|
1642
1790
|
];
|
|
1643
1791
|
var EditError = class extends Error {
|
|
@@ -1694,6 +1842,23 @@ function applyOne(scene, edit) {
|
|
|
1694
1842
|
else if (edit.target !== void 0) next.target = edit.target;
|
|
1695
1843
|
return next;
|
|
1696
1844
|
}
|
|
1845
|
+
case "set_clip": {
|
|
1846
|
+
if (typeof edit.name !== "string" || !edit.name) fail('Give the clip a "name".');
|
|
1847
|
+
const clips = { ..._nullishCoalesce(scene.clips, () => ( {})) };
|
|
1848
|
+
if (edit.clip === null) {
|
|
1849
|
+
if (!clips[edit.name]) fail(`There is no clip ${namedWithSuggestion(edit.name, Object.keys(clips))} to delete.`);
|
|
1850
|
+
const playing = scene.shots.filter((s) => s.run.some((c) => c.type === "play" && c.clip === edit.name)).map((s) => `"${s.id}"`);
|
|
1851
|
+
if (playing.length) fail(`Clip "${edit.name}" is played in shot${playing.length > 1 ? "s" : ""} ${playing.join(", ")}; remove those play commands first.`);
|
|
1852
|
+
delete clips[edit.name];
|
|
1853
|
+
} else {
|
|
1854
|
+
const problems = clipsShapeProblems({ [edit.name]: edit.clip });
|
|
1855
|
+
if (problems.length) fail(problems.join(" "));
|
|
1856
|
+
clips[edit.name] = edit.clip;
|
|
1857
|
+
}
|
|
1858
|
+
const next = { ...scene, clips };
|
|
1859
|
+
if (!Object.keys(clips).length) delete next.clips;
|
|
1860
|
+
return next;
|
|
1861
|
+
}
|
|
1697
1862
|
case "insert_shots": {
|
|
1698
1863
|
if (!edit.shots.length) fail("Give at least one shot.");
|
|
1699
1864
|
edit.shots.forEach((shot, i) => {
|
|
@@ -1807,9 +1972,9 @@ function applyMotionEdits(scene, edits) {
|
|
|
1807
1972
|
let next = scene;
|
|
1808
1973
|
for (let i = 0; i < list.length; i++) {
|
|
1809
1974
|
const edit = list[i];
|
|
1810
|
-
const prefix = list.length > 1 ? `op ${i + 1} (${_optionalChain([edit, 'optionalAccess',
|
|
1975
|
+
const prefix = list.length > 1 ? `op ${i + 1} (${_optionalChain([edit, 'optionalAccess', _58 => _58.op])}): ` : "";
|
|
1811
1976
|
if (!edit || !MOTION_EDIT_OPS.includes(edit.op))
|
|
1812
|
-
return { ok: false, error: `${prefix}Unknown op ${namedWithSuggestion(String(_optionalChain([edit, 'optionalAccess',
|
|
1977
|
+
return { ok: false, error: `${prefix}Unknown op ${namedWithSuggestion(String(_optionalChain([edit, 'optionalAccess', _59 => _59.op])), MOTION_EDIT_OPS)}. Nothing was changed.` };
|
|
1813
1978
|
try {
|
|
1814
1979
|
next = applyOne(next, edit);
|
|
1815
1980
|
} catch (error) {
|
|
@@ -1969,7 +2134,7 @@ function toFlairSvgMotion(scene, options = {}) {
|
|
|
1969
2134
|
const wrapper = freshId(`${id}--follow`);
|
|
1970
2135
|
const centre = freshId(`${id}--follow-centre`);
|
|
1971
2136
|
const c = entry.center;
|
|
1972
|
-
const at = _optionalChain([motionNodeOffset, 'call',
|
|
2137
|
+
const at = _optionalChain([motionNodeOffset, 'call', _60 => _60(entry, timeline, 0, nodes), 'optionalAccess', _61 => _61.follow]);
|
|
1973
2138
|
const placed = at ? `translate(${f(at.x)} ${f(at.y)})${at.angle ? ` rotate(${f(at.angle)})` : ""}` : void 0;
|
|
1974
2139
|
(wrap[id] ??= []).push({ id: wrapper, ...placed ? { transform: placed } : {} }, { id: centre, transform: `translate(${f(-c.x)} ${f(-c.y)})` });
|
|
1975
2140
|
follows.push({ id, wrapper, routes: ids, segments: followSegments.filter((seg) => seg.path && ids.has(seg.path)) });
|
|
@@ -2029,7 +2194,7 @@ function toFlairSvgMotion(scene, options = {}) {
|
|
|
2029
2194
|
exact: () => false,
|
|
2030
2195
|
value: (t) => {
|
|
2031
2196
|
const frame = motionFrameAt(scene, t, { draw: false, offsets: false });
|
|
2032
|
-
const now = _chunkYJLCBA7Vcjs.expandVectorAppearance.call(void 0, _optionalChain([indexMotionNodes, 'call',
|
|
2197
|
+
const now = _chunkYJLCBA7Vcjs.expandVectorAppearance.call(void 0, _optionalChain([indexMotionNodes, 'call', _62 => _62(frame.document.nodes), 'access', _63 => _63.get, 'call', _64 => _64(id), 'optionalAccess', _65 => _65.node]));
|
|
2033
2198
|
if (!now || now.type !== "path") return "";
|
|
2034
2199
|
const bezier = _chunkO2UH7LZEcjs.transformBezierPath.call(void 0, _chunkYJLCBA7Vcjs.vectorPathWorldBezier.call(void 0, now, _chunkYJLCBA7Vcjs.IDENTITY_MATRIX, { effects: false }), inverse);
|
|
2035
2200
|
return _chunkO2UH7LZEcjs.bezierPathToSvg.call(void 0, bezier, { precision: Math.min(precision, 2) });
|
|
@@ -2050,8 +2215,8 @@ function toFlairSvgMotion(scene, options = {}) {
|
|
|
2050
2215
|
...delay > 0 ? { delay: round2(delay) } : {},
|
|
2051
2216
|
ms: round2(ms),
|
|
2052
2217
|
ease: written,
|
|
2053
|
-
..._optionalChain([segment, 'optionalAccess',
|
|
2054
|
-
..._optionalChain([segment, 'optionalAccess',
|
|
2218
|
+
..._optionalChain([segment, 'optionalAccess', _66 => _66.behavior]) ? { behavior: segment.behavior } : {},
|
|
2219
|
+
..._optionalChain([segment, 'optionalAccess', _67 => _67.steps]) ? { strobe: segment.steps } : {}
|
|
2055
2220
|
};
|
|
2056
2221
|
if (channel.kind === "view") animate.push({ ...base, view: to });
|
|
2057
2222
|
else if (channel.kind === "draw") animate.push({ ...base, draw: [from, to] });
|
|
@@ -2140,7 +2305,7 @@ function toFlairSvgMotion(scene, options = {}) {
|
|
|
2140
2305
|
let svg = _chunkYJLCBA7Vcjs.vectorDocumentToSvg.call(void 0, { ...scene.artwork, nodes: _chunkYJLCBA7Vcjs.expandVectorAppearances.call(void 0, scene.artwork.nodes) }, { pretty: false, precision, wrap });
|
|
2141
2306
|
if (routes.length) svg = svg.replace(/^(<svg[^>]*>)/, `$1<defs>${routes.join("")}</defs>`);
|
|
2142
2307
|
let motion = { svg, animate };
|
|
2143
|
-
if (_optionalChain([scene, 'access',
|
|
2308
|
+
if (_optionalChain([scene, 'access', _68 => _68.controls, 'optionalAccess', _69 => _69.length])) {
|
|
2144
2309
|
const declarations = scene.controls.map((c) => `{{${c.name}: color default=${c.default.toUpperCase()}${c.label ? ` label='${c.label.replace(/'/g, "\u2019")}'` : ""}}}`).join(" ");
|
|
2145
2310
|
const swap = (text) => scene.controls.reduce((s, c) => s.replace(new RegExp(`${c.default}(?![0-9a-fA-F])`, "gi"), `{{${c.name}}}`), text);
|
|
2146
2311
|
svg = svg.replace(/^(<svg[^>]*>)/, `$1<!-- ${declarations} -->`);
|
|
@@ -2162,7 +2327,7 @@ function toFlairVectorEffect(scene, options = {}) {
|
|
|
2162
2327
|
tags: _nullishCoalesce(options.tags, () => ( [])),
|
|
2163
2328
|
motion: out.motion,
|
|
2164
2329
|
hold: out.hold,
|
|
2165
|
-
..._optionalChain([scene, 'access',
|
|
2330
|
+
..._optionalChain([scene, 'access', _70 => _70.presets, 'optionalAccess', _71 => _71.length]) ? { presets: scene.presets } : {},
|
|
2166
2331
|
...out.trigger ? { trigger: out.trigger } : {}
|
|
2167
2332
|
};
|
|
2168
2333
|
}
|
|
@@ -2206,7 +2371,7 @@ function resolvePlaceholders(motion, notes) {
|
|
|
2206
2371
|
const filled = text.replace(PLACEHOLDER, (_, name) => {
|
|
2207
2372
|
const d = declared.get(name);
|
|
2208
2373
|
if (!d) unknown.add(name);
|
|
2209
|
-
return (_nullishCoalesce(_optionalChain([d, 'optionalAccess',
|
|
2374
|
+
return (_nullishCoalesce(_optionalChain([d, 'optionalAccess', _72 => _72.value]), () => ( ""))).replace(/"/g, '\\"');
|
|
2210
2375
|
});
|
|
2211
2376
|
for (const name of unknown) notes.push(`Placeholder "{{${name}}}" has no declaration, so it was left empty.`);
|
|
2212
2377
|
return { motion: JSON.parse(filled), controls };
|
|
@@ -2260,7 +2425,7 @@ function commandsFor(anim, nodes, entered, notes) {
|
|
|
2260
2425
|
if (anim.along) {
|
|
2261
2426
|
const path = nodes.get(anim.along);
|
|
2262
2427
|
if (!path || path.node.type !== "path") notes.push(`${where} follows "${anim.along}", which is not a path in the artwork; the follow was skipped.`);
|
|
2263
|
-
else out.push({ type: "follow", targets, path: anim.along, from: _nullishCoalesce(_optionalChain([anim, 'access',
|
|
2428
|
+
else out.push({ type: "follow", targets, path: anim.along, from: _nullishCoalesce(_optionalChain([anim, 'access', _73 => _73.at, 'optionalAccess', _74 => _74[0]]), () => ( 0)), to: _nullishCoalesce(_optionalChain([anim, 'access', _75 => _75.at, 'optionalAccess', _76 => _76[1]]), () => ( 1)), ...anim.orient ? { rotate: true } : {}, ...timing, ...ease(MOTION_EASE_IN_OUT) });
|
|
2264
2429
|
}
|
|
2265
2430
|
for (const [attr, [a, b]] of Object.entries(_nullishCoalesce(anim.attrs, () => ( {})))) {
|
|
2266
2431
|
if (attr === "opacity") {
|
|
@@ -2271,7 +2436,7 @@ function commandsFor(anim, nodes, entered, notes) {
|
|
|
2271
2436
|
out.push({ type: "enter", targets, style: "fade", ...timing, ...ease(MOTION_EASE_OUT) });
|
|
2272
2437
|
} else out.push({ type: "fade", targets, to: Math.min(1, Math.max(0, to)), ...timing, ...ease(MOTION_EASE_IN_OUT) });
|
|
2273
2438
|
} else if (attr === "fill" || attr === "stroke") {
|
|
2274
|
-
if (_optionalChain([behavior, 'optionalAccess',
|
|
2439
|
+
if (_optionalChain([behavior, 'optionalAccess', _77 => _77.type]) === "pulse") out.push({ type: "color-pulse", targets, color: String(b), paint: attr, count: _nullishCoalesce(behavior.count, () => ( 1)), ...timing, ...ease("linear") });
|
|
2275
2440
|
else {
|
|
2276
2441
|
if (behavior) notes.push(`${where}: a ${attr} ${behavior.type} became a plain colour change.`);
|
|
2277
2442
|
out.push({ type: "style", targets, [attr]: String(b), ...timing, ...ease(MOTION_EASE_IN_OUT) });
|
|
@@ -2554,7 +2719,7 @@ function toLottie(scene, options = {}) {
|
|
|
2554
2719
|
const shapeSegments = [...timeline.tracks.entries()].filter(([k]) => k.startsWith(`${path.id}\0`) && (k.endsWith("\0shape") || k.includes("\0effect:"))).flatMap(([, v]) => v);
|
|
2555
2720
|
const geometryAt = (t) => {
|
|
2556
2721
|
if (!shapeSegments.length) return _chunkYJLCBA7Vcjs.vectorPathWorldBezier.call(void 0, path);
|
|
2557
|
-
const now = _optionalChain([indexMotionNodes, 'call',
|
|
2722
|
+
const now = _optionalChain([indexMotionNodes, 'call', _78 => _78(motionFrameAt(scene, t, { draw: false, offsets: false }).document.nodes), 'access', _79 => _79.get, 'call', _80 => _80(path.id), 'optionalAccess', _81 => _81.node]);
|
|
2558
2723
|
return now && now.type === "path" ? _chunkYJLCBA7Vcjs.vectorPathWorldBezier.call(void 0, _chunkYJLCBA7Vcjs.expandVectorAppearances.call(void 0, [now])[0]) : _chunkYJLCBA7Vcjs.vectorPathWorldBezier.call(void 0, path);
|
|
2559
2724
|
};
|
|
2560
2725
|
const rest = lottieContours(_chunkO2UH7LZEcjs.toBezierPath.call(void 0, geometryAt(0)), round2);
|
|
@@ -2582,11 +2747,13 @@ function toLottie(scene, options = {}) {
|
|
|
2582
2747
|
const widthFrom = owner(path, "strokeWidth");
|
|
2583
2748
|
const fadeFrom = owner(path, "fillOpacity");
|
|
2584
2749
|
const drawFrom = owner(path, "drawEnd");
|
|
2585
|
-
|
|
2586
|
-
|
|
2587
|
-
|
|
2750
|
+
const tailFrom = owner(path, "drawStart");
|
|
2751
|
+
if (drawFrom || tailFrom) {
|
|
2752
|
+
const pct = (track2, fallback) => (t) => [Math.min(1, Math.max(0, Number(_nullishCoalesce(trackValue(track2, t), () => ( fallback))))) * 100];
|
|
2753
|
+
const head = drawFrom ? track(drawFrom, "drawEnd") : [];
|
|
2754
|
+
const tail = tailFrom ? track(tailFrom, "drawStart") : [];
|
|
2755
|
+
items.push({ ty: "tm", nm: "Draw on", s: tail.length ? animate(tail, pct(tail, 0)) : { a: 0, k: 0 }, e: head.length ? animate(head, pct(head, 1)) : { a: 0, k: 100 }, o: { a: 0, k: 0 }, m: 1 });
|
|
2588
2756
|
}
|
|
2589
|
-
if (owner(path, "drawStart")) notes.push(`"${path.id}": drawing from a start other than 0 is not exported.`);
|
|
2590
2757
|
const stroke = paintOf(path.stroke, "stroke", path.id);
|
|
2591
2758
|
if (stroke || strokeFrom) {
|
|
2592
2759
|
const colour = strokeFrom ? track(strokeFrom, "stroke") : [];
|
|
@@ -2600,7 +2767,7 @@ function toLottie(scene, options = {}) {
|
|
|
2600
2767
|
lj: path.strokeLinejoin === "round" ? 2 : path.strokeLinejoin === "bevel" ? 3 : 1,
|
|
2601
2768
|
ml: 4
|
|
2602
2769
|
});
|
|
2603
|
-
if (_optionalChain([path, 'access',
|
|
2770
|
+
if (_optionalChain([path, 'access', _82 => _82.strokeDasharray, 'optionalAccess', _83 => _83.length])) notes.push(`"${path.id}": its dashes are not exported; the stroke is solid.`);
|
|
2604
2771
|
if (path.strokeAlign && path.strokeAlign !== "center") notes.push(`"${path.id}": its stroke is drawn centred (Lottie has no inside or outside strokes).`);
|
|
2605
2772
|
}
|
|
2606
2773
|
const fill = paintOf(path.fill, "fill", path.id);
|
|
@@ -2751,7 +2918,7 @@ var AnimationEditorController = (_class = class {
|
|
|
2751
2918
|
time: p.time,
|
|
2752
2919
|
playing: p.playing,
|
|
2753
2920
|
loop: p.loop,
|
|
2754
|
-
currentShot: _nullishCoalesce(_optionalChain([motionShotAt, 'call',
|
|
2921
|
+
currentShot: _nullishCoalesce(_optionalChain([motionShotAt, 'call', _84 => _84(timeline, p.time), 'optionalAccess', _85 => _85.id]), () => ( null)),
|
|
2755
2922
|
selection: this.selection,
|
|
2756
2923
|
readOnly: this.readOnly,
|
|
2757
2924
|
error: this.error,
|
|
@@ -2790,7 +2957,7 @@ var AnimationEditorController = (_class = class {
|
|
|
2790
2957
|
this.applying = false;
|
|
2791
2958
|
}
|
|
2792
2959
|
this.notify();
|
|
2793
|
-
_optionalChain([this, 'access',
|
|
2960
|
+
_optionalChain([this, 'access', _86 => _86.onChange, 'optionalCall', _87 => _87(scene)]);
|
|
2794
2961
|
}
|
|
2795
2962
|
commit(label, next) {
|
|
2796
2963
|
const before = this.scene;
|
|
@@ -2972,11 +3139,19 @@ var BLEND = (fallback) => ({
|
|
|
2972
3139
|
enum: [...MOTION_BLENDS],
|
|
2973
3140
|
description: `"add" makes it a layer over the pose that stacks with other changes (a shake on a slide); "replace" makes it a change the next one takes over from (default ${fallback}).`
|
|
2974
3141
|
});
|
|
3142
|
+
var DRAW_ORDER = {
|
|
3143
|
+
order: { type: "string", enum: ["together", "sequence"], description: "For drawing a group or several paths: together (default) or sequence (one stroke after another, each for a time in proportion to its length)." },
|
|
3144
|
+
pace: { type: "string", enum: ["time", "speed"], description: "With together: time (default, every stroke takes the whole time) or speed (all at one speed, so short strokes take less)." },
|
|
3145
|
+
align: { type: "string", enum: ["start", "end"], description: "With pace speed: start them together (default) or finish them together." },
|
|
3146
|
+
sortBy: { type: "string", enum: ["document", "distance"], description: "With sequence: stacking order (default) or distance from origin (a radial reveal)." },
|
|
3147
|
+
origin: object({ x: { type: "number", description: "Document units." }, y: { type: "number", description: "Document units." } }, ["x", "y"], "With sortBy distance: where strokes are drawn out from (default the target's centre).")
|
|
3148
|
+
};
|
|
2975
3149
|
var command = (type, description, extra = {}, required = []) => object({ type: { const: type, description }, ...BASE, ...extra }, ["type", "targets", ...required], description);
|
|
2976
3150
|
var MOTION_COMMAND_SCHEMA = {
|
|
2977
3151
|
description: `A command (one of ${MOTION_COMMAND_TYPES.join(", ")}). Commands in a shot play one after another unless "with": "previous".`,
|
|
2978
3152
|
oneOf: [
|
|
2979
3153
|
command("enter", "Brings hidden targets in; they are hidden before this shot.", {
|
|
3154
|
+
...DRAW_ORDER,
|
|
2980
3155
|
style: {
|
|
2981
3156
|
type: "string",
|
|
2982
3157
|
enum: [...MOTION_ENTER_STYLES],
|
|
@@ -2989,11 +3164,12 @@ var MOTION_COMMAND_SCHEMA = {
|
|
|
2989
3164
|
orientation: { type: "string", enum: [...MOTION_SPLIT_ORIENTATIONS], description: "For split: horizontal (default) opens up and down from a horizontal line through the middle; vertical opens left and right." }
|
|
2990
3165
|
}),
|
|
2991
3166
|
command("exit", "Takes targets out.", {
|
|
3167
|
+
...DRAW_ORDER,
|
|
2992
3168
|
style: { type: "string", enum: [...MOTION_EXIT_STYLES], description: "fade (default), shrink, slide, undraw, or wipe (hides behind a travelling edge through a clip over the target's box: a wipe in, reversed)." },
|
|
2993
3169
|
direction: { type: "string", enum: [...MOTION_DIRECTIONS], description: "For slide: direction of travel (default down). For wipe: the way the edge travels (default right, hiding from the left)." },
|
|
2994
3170
|
distance: { type: "number", description: "For slide: document units (default 40)." }
|
|
2995
3171
|
}),
|
|
2996
|
-
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).") }),
|
|
3172
|
+
command("draw", "Draws strokes on (or off) to a fraction of their length.", { ...DRAW_ORDER, comet: FRACTION("A travelling stroke this fraction of the length long: it runs along the path and off the end (leave out from and to)."), to: FRACTION("Fraction drawn at the end (default 1)."), from: FRACTION("Fraction drawn at the start (default: what is drawn now).") }),
|
|
2997
3173
|
command("move", "Moves targets by an offset or so their centre lands on a point.", { via: { type: "array", items: POINT("A point the centre passes through."), minItems: 1, maxItems: 64, description: 'Waypoints (document units) the centre passes through on the way to "to" or "by", smoothly (velocity carries through each) and without overshooting any. Default length 700 ms + 400 per waypoint.' }, pacing: { type: "string", enum: ["even", "equal"], description: "With via: even (default) keeps the speed steady along the path; equal gives each stretch the same time." }, blend: BLEND("replace"), by: POINT("Offset in document units."), to: POINT("Point (document coordinates) for the centre.") }),
|
|
2998
3174
|
command("rotate", "Turns targets about their centre (degrees, clockwise).", { blend: BLEND("replace"), by: { type: "number", description: "Degrees to turn by." }, to: { type: "number", description: "Angle to turn to, in degrees from rest." } }),
|
|
2999
3175
|
command("scale", "Scales targets about their centre (1 = rest size).", { blend: BLEND("replace"), 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"]),
|
|
@@ -3003,6 +3179,20 @@ var MOTION_COMMAND_SCHEMA = {
|
|
|
3003
3179
|
stroke: { type: "string", description: "CSS colour (hex, rgb(), rgba())." },
|
|
3004
3180
|
strokeWidth: { type: "number", minimum: 0, description: "Document units." }
|
|
3005
3181
|
}),
|
|
3182
|
+
object(
|
|
3183
|
+
{
|
|
3184
|
+
type: { const: "play", description: 'Plays a clip (set_animation_clip) on the targets: its commands, with "$" standing for each target, timed as in a shot.' },
|
|
3185
|
+
targets: NODE_IDS,
|
|
3186
|
+
clip: { type: "string", minLength: 1, description: "The clip's name (get_animation lists them)." },
|
|
3187
|
+
speed: { type: "number", minimum: 0.05, maximum: 20, description: "Play it this many times as fast (default 1)." },
|
|
3188
|
+
delay: BASE.delay,
|
|
3189
|
+
stagger: BASE.stagger,
|
|
3190
|
+
with: BASE.with,
|
|
3191
|
+
note: BASE.note
|
|
3192
|
+
},
|
|
3193
|
+
["type", "targets", "clip"],
|
|
3194
|
+
"Plays a clip on the targets."
|
|
3195
|
+
),
|
|
3006
3196
|
command("match", `Smart Animate: tweens a group into another group (usually hidden), part by part. Parts pair by name, then by id with the group ids taken out (card-a-title \u2194 card-b-title), then by kind in order; each takes its partner's shape, paint and place. Unpaired parts fade out; parts of "into" with no partner do not appear (check_animation lists them).`, { into: { type: "string", minLength: 1, description: "Id of the group to become." } }, ["into"]),
|
|
3007
3197
|
command("morph", "Morphs each target into the shape (and paint) of another node, which usually stays hidden.", { into: { type: "string", minLength: 1, description: "Id of the node whose shape to take." } }, ["into"]),
|
|
3008
3198
|
command("follow", "Moves targets along a path node, centre on the path.", {
|
|
@@ -3211,6 +3401,7 @@ function createAnimationEditorToolProvider(controller, options = {}) {
|
|
|
3211
3401
|
...s.target ? { target: s.target } : {},
|
|
3212
3402
|
...includeElements ? { elements: describeMotionElements(s) } : {},
|
|
3213
3403
|
shots: describeMotionShots(s),
|
|
3404
|
+
...s.clips ? { clips: Object.fromEntries(Object.entries(s.clips).map(([name, c]) => [name, { ...c.description ? { description: c.description } : {}, commands: c.run.length, length: Math.round(motionClipLayout(s, name, "$").length) }])) } : {},
|
|
3214
3405
|
...s.controls ? { controls: s.controls } : {},
|
|
3215
3406
|
...s.presets ? { presets: s.presets } : {},
|
|
3216
3407
|
issues: issueSummary(controller.getState().issues),
|
|
@@ -3312,9 +3503,9 @@ function createAnimationEditorToolProvider(controller, options = {}) {
|
|
|
3312
3503
|
handler: ({ from = 0, to, limit = 200 }) => {
|
|
3313
3504
|
const s = scene();
|
|
3314
3505
|
const timeline = compileMotionScene(s);
|
|
3315
|
-
const words = _nullishCoalesce(_optionalChain([s, 'access',
|
|
3506
|
+
const words = _nullishCoalesce(_optionalChain([s, 'access', _88 => _88.narration, 'optionalAccess', _89 => _89.words]), () => ( []));
|
|
3316
3507
|
const end = _nullishCoalesce(to, () => ( Math.max(timeline.duration, words.length ? _nullishCoalesce(words[words.length - 1].end, () => ( words[words.length - 1].start)) : 0)));
|
|
3317
|
-
const shotAt = (ms) => _optionalChain([timeline, 'access',
|
|
3508
|
+
const shotAt = (ms) => _optionalChain([timeline, 'access', _90 => _90.shots, 'access', _91 => _91.find, 'call', _92 => _92((x) => ms >= x.start && ms < x.end), 'optionalAccess', _93 => _93.id]);
|
|
3318
3509
|
const beats = [];
|
|
3319
3510
|
if (s.beat && s.beat.bpm > 0) {
|
|
3320
3511
|
const first = Math.max(0, Math.ceil(motionBeatAt(s.beat, from).beat - 1e-9));
|
|
@@ -3456,6 +3647,68 @@ function createAnimationEditorToolProvider(controller, options = {}) {
|
|
|
3456
3647
|
return apply(edits, "Fit to length", () => report);
|
|
3457
3648
|
}
|
|
3458
3649
|
};
|
|
3650
|
+
const setClip = {
|
|
3651
|
+
name: "set_animation_clip",
|
|
3652
|
+
title: "Define a reusable clip",
|
|
3653
|
+
description: `Defines, replaces or (with clip null) deletes a named clip: commands written against the target "$" that a play command plays on any node, e.g. a "pop-settle" or "attention" used many times. One undo step. Returns the clip's length and the timing.`,
|
|
3654
|
+
inputSchema: object(
|
|
3655
|
+
{
|
|
3656
|
+
name: { type: "string", pattern: "^[A-Za-z_][\\w-]*$", description: 'Clip name, e.g. "pop-settle".' },
|
|
3657
|
+
clip: {
|
|
3658
|
+
anyOf: [object({ description: { type: "string", description: "What it does, in a few words." }, run: { type: "array", items: MOTION_COMMAND_SCHEMA, minItems: 1, description: 'Commands, timed as in a shot; target "$" for the node it is played on. Clips cannot play clips.' } }, ["run"]), { type: "null" }],
|
|
3659
|
+
description: "The clip, or null to delete it (refused while a play command uses it)."
|
|
3660
|
+
}
|
|
3661
|
+
},
|
|
3662
|
+
["name", "clip"]
|
|
3663
|
+
),
|
|
3664
|
+
annotations: { title: "Define a reusable clip", ...WRITE_IDEMPOTENT },
|
|
3665
|
+
handler: ({ name, clip }) => apply({ op: "set_clip", name, clip }, clip === null ? "Delete clip" : "Set clip", (s) => clip === null ? { deleted: name } : { clip: name, length: Math.round(motionClipLayout(s, name, "$").length) })
|
|
3666
|
+
};
|
|
3667
|
+
const copyAnimation = {
|
|
3668
|
+
name: "copy_animation",
|
|
3669
|
+
title: "Copy one node's animation to others",
|
|
3670
|
+
description: 'Copies every command that animates "from" (in all shots, or the ones given) so it also animates "to": each copy goes right after its original and starts with it. One undo step. Returns how many commands were copied per shot. For motion you will reuse often, a clip (set_animation_clip + play) is tidier.',
|
|
3671
|
+
inputSchema: object(
|
|
3672
|
+
{
|
|
3673
|
+
from: { type: "string", minLength: 1, description: "Element id whose commands to copy." },
|
|
3674
|
+
to: { type: "array", items: { type: "string", minLength: 1 }, minItems: 1, description: "Element ids to give the same motion." },
|
|
3675
|
+
shots: { type: "array", items: SHOT_ID, description: "Only these shots (default all)." },
|
|
3676
|
+
stagger: MS("Start each copy this much after the one before (default 0)")
|
|
3677
|
+
},
|
|
3678
|
+
["from", "to"]
|
|
3679
|
+
),
|
|
3680
|
+
annotations: { title: "Copy one node's animation to others", ...WRITE },
|
|
3681
|
+
handler: ({ from, to, shots, stagger }) => {
|
|
3682
|
+
const s = scene();
|
|
3683
|
+
const ids = describeMotionElements(s).map((e) => e.id);
|
|
3684
|
+
for (const id of [from, ...to]) if (!ids.includes(id)) throw new Error(`There is no element ${namedWithSuggestion(id, ids)}. Nothing was changed.`);
|
|
3685
|
+
if (to.includes(from)) throw new Error(`"to" includes "${from}" itself; give other elements. Nothing was changed.`);
|
|
3686
|
+
const only = shots ? new Set(shots) : null;
|
|
3687
|
+
if (only) {
|
|
3688
|
+
for (const id of only) if (!s.shots.some((x) => x.id === id)) throw new Error(`There is no shot ${namedWithSuggestion(id, s.shots.map((x) => x.id))}. Nothing was changed.`);
|
|
3689
|
+
}
|
|
3690
|
+
const copied = {};
|
|
3691
|
+
const edits = [];
|
|
3692
|
+
for (const shot of s.shots) {
|
|
3693
|
+
if (only && !only.has(shot.id)) continue;
|
|
3694
|
+
const run = [];
|
|
3695
|
+
let n = 0;
|
|
3696
|
+
for (const c of shot.run) {
|
|
3697
|
+
run.push(c);
|
|
3698
|
+
if (c.type === "wait" || !c.targets.includes(from)) continue;
|
|
3699
|
+
const { delay: _delay, ...rest } = c;
|
|
3700
|
+
run.push({ ...rest, targets: to, with: "previous", ...stagger ? { stagger } : {} });
|
|
3701
|
+
n++;
|
|
3702
|
+
}
|
|
3703
|
+
if (n) {
|
|
3704
|
+
copied[shot.id] = n;
|
|
3705
|
+
edits.push({ op: "update_shot", shot: shot.id, set: { run } });
|
|
3706
|
+
}
|
|
3707
|
+
}
|
|
3708
|
+
if (!edits.length) throw new Error(`No command animates "${from}"${only ? " in those shots" : ""}; nothing to copy.`);
|
|
3709
|
+
return apply(edits, "Copy animation", () => ({ copied }));
|
|
3710
|
+
}
|
|
3711
|
+
};
|
|
3459
3712
|
const insertShots = {
|
|
3460
3713
|
name: "insert_shots",
|
|
3461
3714
|
title: "Add shots",
|
|
@@ -3502,7 +3755,7 @@ function createAnimationEditorToolProvider(controller, options = {}) {
|
|
|
3502
3755
|
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"]),
|
|
3503
3756
|
annotations: { title: "Add commands to a shot", ...WRITE },
|
|
3504
3757
|
handler: ({ shot, commands, at }) => {
|
|
3505
|
-
const before = _nullishCoalesce(_optionalChain([scene, 'call',
|
|
3758
|
+
const before = _nullishCoalesce(_optionalChain([scene, 'call', _94 => _94(), 'access', _95 => _95.shots, 'access', _96 => _96.find, 'call', _97 => _97((s) => s.id === shot), 'optionalAccess', _98 => _98.run, 'access', _99 => _99.length]), () => ( 0));
|
|
3506
3759
|
const first = _nullishCoalesce(at, () => ( before));
|
|
3507
3760
|
return apply({ op: "add_commands", shot, commands, at }, "Add commands", (s) => {
|
|
3508
3761
|
const shots = describeMotionShots(s);
|
|
@@ -3632,7 +3885,7 @@ function createAnimationEditorToolProvider(controller, options = {}) {
|
|
|
3632
3885
|
}
|
|
3633
3886
|
};
|
|
3634
3887
|
const reads = [getAnimation, getShot, findElements, getMotionPath, getTimingGrid, checkAnimation, renderFrame, exportFlairMotion, exportFlairEffect, exportLottie, seekAnimation];
|
|
3635
|
-
const writes = [setArtworkSvg, fitAnimation, importFlairEffect, insertShots, updateShot, deleteShots, moveShot, addCommands, updateCommand, deleteCommands, moveCommand, setSettings, setControls, editAnimation, writeAnimation, undo, redo];
|
|
3888
|
+
const writes = [setArtworkSvg, setClip, copyAnimation, fitAnimation, importFlairEffect, insertShots, updateShot, deleteShots, moveShot, addCommands, updateCommand, deleteCommands, moveCommand, setSettings, setControls, editAnimation, writeAnimation, undo, redo];
|
|
3636
3889
|
return {
|
|
3637
3890
|
id: _nullishCoalesce(options.id, () => ( "animation")),
|
|
3638
3891
|
namespace: options.namespace,
|
|
@@ -3664,6 +3917,8 @@ Commands
|
|
|
3664
3917
|
- enter (fade, pop, grow, slide, draw) and exit (fade, shrink, slide, undraw) bring things in and out.
|
|
3665
3918
|
- enter also reveals through a clip over the target's box: wipe (behind an edge travelling "direction", default right), iris (from the centre; "shape": circle or box) and split (from a line through the middle; "orientation": horizontal or vertical). exit wipe hides the same way. Reveals need a width and a height: use draw / undraw for a straight line.
|
|
3666
3919
|
- draw draws strokes on to a fraction; move, rotate, scale and fade change the pose (about the centre); style blends solid colours and stroke width.
|
|
3920
|
+
- Drawing a group of strokes: "order": "sequence" draws them one after another (a signature, a diagram), "sortBy": "distance" from "origin" reveals outward, "pace": "speed" keeps one speed; draw with "comet": 0.2 sends a short stroke along a path and off the end.
|
|
3921
|
+
- Reuse motion: set_animation_clip defines a clip (commands targeting "$"), and { "type": "play", "clip": name, "targets": [...] } plays it on any nodes (with stagger and speed). copy_animation gives other nodes one node's motion.
|
|
3667
3922
|
- move takes "via" waypoints for a curved, smooth path that passes exactly through each point (steady speed by default); use it instead of chaining moves, which stop at each point.
|
|
3668
3923
|
- enter also has zoom (settles from larger) and rise (rises a short way, overshooting a touch); pop and zoom take a starting "scale".
|
|
3669
3924
|
- morph turns a shape into another node's shape (keep the target hidden); match does the same for a whole group, part by part (Smart Animate: draw the end state as a second, hidden group whose parts share names with the first); follow moves along a path node (with "rotate", moves made meanwhile turn with the path).
|
|
@@ -3690,7 +3945,7 @@ var TIMING = /* @__PURE__ */ new Set(["duration", "delay", "stagger", "with", "e
|
|
|
3690
3945
|
var COLORS = /* @__PURE__ */ new Set(["fill", "stroke", "color"]);
|
|
3691
3946
|
var NODES = /* @__PURE__ */ new Set(["into", "path"]);
|
|
3692
3947
|
function branch(type) {
|
|
3693
|
-
return (_nullishCoalesce(MOTION_COMMAND_SCHEMA.oneOf, () => ( []))).find((b) => _optionalChain([b, 'access',
|
|
3948
|
+
return (_nullishCoalesce(MOTION_COMMAND_SCHEMA.oneOf, () => ( []))).find((b) => _optionalChain([b, 'access', _100 => _100.properties, 'optionalAccess', _101 => _101.type, 'optionalAccess', _102 => _102.const]) === type);
|
|
3694
3949
|
}
|
|
3695
3950
|
function unitOf(type, name) {
|
|
3696
3951
|
if (name === "duration" || name === "delay" || name === "stagger") return "ms";
|
|
@@ -3725,7 +3980,7 @@ function kindOf(name, schema) {
|
|
|
3725
3980
|
}
|
|
3726
3981
|
function motionCommandFields(type) {
|
|
3727
3982
|
const schema = branch(type);
|
|
3728
|
-
if (!_optionalChain([schema, 'optionalAccess',
|
|
3983
|
+
if (!_optionalChain([schema, 'optionalAccess', _103 => _103.properties])) return [];
|
|
3729
3984
|
const required = new Set(_nullishCoalesce(schema.required, () => ( [])));
|
|
3730
3985
|
const fields = Object.entries(schema.properties).filter(([name]) => name !== "type").map(([name, s]) => {
|
|
3731
3986
|
const kind = kindOf(name, s);
|
|
@@ -3782,7 +4037,7 @@ function defaultMotionCommand(type, targets, scene) {
|
|
|
3782
4037
|
if (type === "wait") return { type: "wait", duration: 500 };
|
|
3783
4038
|
if (!targets.length) return { error: "Pick a target first: click an element on the stage." };
|
|
3784
4039
|
const nodes = [...indexMotionNodes(scene.artwork.nodes).values()];
|
|
3785
|
-
const other = (pred) => _optionalChain([nodes, 'access',
|
|
4040
|
+
const other = (pred) => _optionalChain([nodes, 'access', _104 => _104.find, 'call', _105 => _105((e) => !targets.includes(e.node.id) && pred(e)), 'optionalAccess', _106 => _106.node, 'access', _107 => _107.id]);
|
|
3786
4041
|
const base = { targets: [...targets] };
|
|
3787
4042
|
switch (type) {
|
|
3788
4043
|
case "scale":
|
|
@@ -3803,14 +4058,18 @@ function defaultMotionCommand(type, targets, scene) {
|
|
|
3803
4058
|
const into = _nullishCoalesce(other((e) => e.node.type === "group" && e.node.visible === false), () => ( other((e) => e.node.type === "group")));
|
|
3804
4059
|
return into ? { type, ...base, into } : { error: "Match needs another group to become; group the end state (it can stay hidden)." };
|
|
3805
4060
|
}
|
|
4061
|
+
case "play": {
|
|
4062
|
+
const clip = Object.keys(_nullishCoalesce(scene.clips, () => ( {})))[0];
|
|
4063
|
+
return clip ? { type, ...base, clip } : { error: 'Play needs a clip; define one first (set_animation_clip), with "$" as its target.' };
|
|
4064
|
+
}
|
|
3806
4065
|
case "follow": {
|
|
3807
4066
|
const path = _nullishCoalesce(other((e) => e.node.type === "path" && e.node.visible === false), () => ( other((e) => e.node.type === "path")));
|
|
3808
4067
|
return path ? { type, ...base, path } : { error: "Follow needs a path to travel along; draw one (it can stay hidden)." };
|
|
3809
4068
|
}
|
|
3810
4069
|
case "effect": {
|
|
3811
4070
|
for (const id of targets) {
|
|
3812
|
-
const node = _optionalChain([nodes, 'access',
|
|
3813
|
-
for (const effect of _nullishCoalesce(_optionalChain([node, 'optionalAccess',
|
|
4071
|
+
const node = _optionalChain([nodes, 'access', _108 => _108.find, 'call', _109 => _109((e) => e.node.id === id), 'optionalAccess', _110 => _110.node]);
|
|
4072
|
+
for (const effect of _nullishCoalesce(_optionalChain([node, 'optionalAccess', _111 => _111.effects]), () => ( []))) {
|
|
3814
4073
|
const param = Object.entries(effect.params).find(([, v]) => typeof v === "number");
|
|
3815
4074
|
if (param) return { type, ...base, effect: effect.id, param: param[0], to: param[1] + 1 };
|
|
3816
4075
|
}
|
|
@@ -3835,11 +4094,11 @@ function motionCommandRows(scene, shotId) {
|
|
|
3835
4094
|
return scene.shots[index].run.map((command2, i) => {
|
|
3836
4095
|
const t = timeline.commands.find((c) => c.shotIndex === index && c.command === i);
|
|
3837
4096
|
const primitive = motionFlairPrimitive(command2);
|
|
3838
|
-
return { index: i, command: command2, summary: motionCommandSummary(command2), start: _nullishCoalesce(_optionalChain([t, 'optionalAccess',
|
|
4097
|
+
return { index: i, command: command2, summary: motionCommandSummary(command2), start: _nullishCoalesce(_optionalChain([t, 'optionalAccess', _112 => _112.start]), () => ( 0)), end: _nullishCoalesce(_optionalChain([t, 'optionalAccess', _113 => _113.end]), () => ( 0)), ...primitive ? { flairPrimitive: primitive } : {} };
|
|
3839
4098
|
});
|
|
3840
4099
|
}
|
|
3841
4100
|
function duplicateMotionCommandEdit(scene, shot, command2) {
|
|
3842
|
-
const run = _nullishCoalesce(_optionalChain([scene, 'access',
|
|
4101
|
+
const run = _nullishCoalesce(_optionalChain([scene, 'access', _114 => _114.shots, 'access', _115 => _115.find, 'call', _116 => _116((s) => s.id === shot), 'optionalAccess', _117 => _117.run]), () => ( []));
|
|
3843
4102
|
return { op: "add_commands", shot, commands: run[command2] ? [structuredClone(run[command2])] : [], at: command2 + 1 };
|
|
3844
4103
|
}
|
|
3845
4104
|
var MOTION_EASE_CHOICES = [
|
|
@@ -3924,7 +4183,7 @@ function motionCueFromKey(key) {
|
|
|
3924
4183
|
function motionCueChoices(scene, limit = 256) {
|
|
3925
4184
|
const choices = [];
|
|
3926
4185
|
const s = (ms) => `${(ms / 1e3).toFixed(2)} s`;
|
|
3927
|
-
const end = Math.max(compileMotionScene(scene).duration, ..._nullishCoalesce(_optionalChain([scene, 'access',
|
|
4186
|
+
const end = Math.max(compileMotionScene(scene).duration, ..._nullishCoalesce(_optionalChain([scene, 'access', _118 => _118.narration, 'optionalAccess', _119 => _119.words, 'access', _120 => _120.map, 'call', _121 => _121((w) => _nullishCoalesce(w.end, () => ( w.start)))]), () => ( [0]))) + 2e3;
|
|
3928
4187
|
if (scene.beat && scene.beat.bpm > 0) {
|
|
3929
4188
|
for (let beat = 0; choices.length < limit; beat++) {
|
|
3930
4189
|
const time = motionBeatTime(scene.beat, beat);
|
|
@@ -3934,7 +4193,7 @@ function motionCueChoices(scene, limit = 256) {
|
|
|
3934
4193
|
choices.push({ value: `beat:${beat}`, label: `Beat ${beat} (bar ${at.bar}.${at.beatInBar}) \xB7 ${s(time)}`, time });
|
|
3935
4194
|
}
|
|
3936
4195
|
}
|
|
3937
|
-
_optionalChain([scene, 'access',
|
|
4196
|
+
_optionalChain([scene, 'access', _122 => _122.narration, 'optionalAccess', _123 => _123.words, 'access', _124 => _124.forEach, 'call', _125 => _125((w, i) => {
|
|
3938
4197
|
if (choices.length < limit * 2) choices.push({ value: `word:${i}`, label: `Word ${i} \u201C${w.text}\u201D \xB7 ${s(w.start)}`, time: w.start });
|
|
3939
4198
|
})]);
|
|
3940
4199
|
return choices;
|
|
@@ -3943,7 +4202,7 @@ function motionShotCueTime(scene, cue) {
|
|
|
3943
4202
|
return cue ? motionCueTime(scene, cue) : void 0;
|
|
3944
4203
|
}
|
|
3945
4204
|
function formatMotionNarration(narration) {
|
|
3946
|
-
return (_nullishCoalesce(_optionalChain([narration, 'optionalAccess',
|
|
4205
|
+
return (_nullishCoalesce(_optionalChain([narration, 'optionalAccess', _126 => _126.words]), () => ( []))).map((w) => `${w.start} ${w.text}`).join("\n");
|
|
3947
4206
|
}
|
|
3948
4207
|
function parseMotionNarration(text) {
|
|
3949
4208
|
const lines = text.split("\n").map((l) => l.trim()).filter(Boolean);
|
|
@@ -4080,5 +4339,8 @@ function parseMotionNarration(text) {
|
|
|
4080
4339
|
|
|
4081
4340
|
|
|
4082
4341
|
|
|
4083
|
-
|
|
4342
|
+
|
|
4343
|
+
|
|
4344
|
+
|
|
4345
|
+
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_ADDITIVE_PROPERTIES = MOTION_ADDITIVE_PROPERTIES; exports.MOTION_BLENDS = MOTION_BLENDS; exports.MOTION_CAMERA_MS = MOTION_CAMERA_MS; exports.MOTION_CLIP_TARGET = MOTION_CLIP_TARGET; 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_CHOICES = MOTION_EASE_CHOICES; 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_SPEED_RANGE = MOTION_SPEED_RANGE; exports.MOTION_SPLIT_ORIENTATIONS = MOTION_SPLIT_ORIENTATIONS; exports.MOTION_TRIGGERS = MOTION_TRIGGERS; exports.MotionSceneFormatError = MotionSceneFormatError; exports.activeSegment = activeSegment; exports.applyMotionControlValues = applyMotionControlValues; exports.applyMotionEdits = applyMotionEdits; exports.checkMotionScene = checkMotionScene; exports.clipsShapeProblems = clipsShapeProblems; exports.commandShapeProblems = commandShapeProblems; exports.compileMotionScene = compileMotionScene; exports.createAnimationEditorToolProvider = createAnimationEditorToolProvider; exports.createMotionScene = createMotionScene; exports.defaultMotionCommand = defaultMotionCommand; exports.describeMotionElements = describeMotionElements; exports.describeMotionShots = describeMotionShots; exports.duplicateMotionCommandEdit = duplicateMotionCommandEdit; exports.fitMotionRect = fitMotionRect; exports.fitMotionScene = fitMotionScene; exports.fitMotionSceneEdits = fitMotionSceneEdits; exports.flairAtLeast = flairAtLeast; exports.flairCurveFor = flairCurveFor; exports.flairEaseFor = flairEaseFor; exports.formatMotionNarration = formatMotionNarration; exports.freshShotId = freshShotId; exports.fromFlairSvgMotion = fromFlairSvgMotion; exports.fromFlairVectorEffect = fromFlairVectorEffect; exports.highlightMotionSvg = highlightMotionSvg; exports.indexMotionNodes = indexMotionNodes; exports.interpolateMotionValue = interpolateMotionValue; exports.isMotionCommand = isMotionCommand; exports.isMotionShot = isMotionShot; exports.matchMotionNodes = matchMotionNodes; exports.motionAddTrackKey = motionAddTrackKey; exports.motionBeatAt = motionBeatAt; exports.motionBeatTime = motionBeatTime; exports.motionClipAttrs = motionClipAttrs; exports.motionClipLayout = motionClipLayout; exports.motionCommandDuration = motionCommandDuration; exports.motionCommandFields = motionCommandFields; exports.motionCommandRepeat = motionCommandRepeat; exports.motionCommandRows = motionCommandRows; exports.motionCommandSummary = motionCommandSummary; exports.motionCueChoices = motionCueChoices; exports.motionCueFromKey = motionCueFromKey; exports.motionCueKey = motionCueKey; exports.motionCueTime = motionCueTime; exports.motionCycleDuration = motionCycleDuration; exports.motionEaseKey = motionEaseKey; exports.motionEasingCurve = motionEasingCurve; exports.motionEasingGuides = motionEasingGuides; exports.motionEasingPath = motionEasingPath; exports.motionFieldEdit = motionFieldEdit; exports.motionFieldLabel = motionFieldLabel; exports.motionFlairPrimitive = motionFlairPrimitive; exports.motionFollowRoute = motionFollowRoute; exports.motionFrameAt = motionFrameAt; exports.motionFrameToSvg = motionFrameToSvg; exports.motionGhosts = motionGhosts; exports.motionIssue = motionIssue; exports.motionIssueKey = motionIssueKey; exports.motionIssueLine = motionIssueLine; exports.motionMovingIds = motionMovingIds; exports.motionNodeAtElement = motionNodeAtElement; exports.motionNodeClips = motionNodeClips; exports.motionNodeOffset = motionNodeOffset; exports.motionPath = motionPath; exports.motionPathLength = motionPathLength; exports.motionPathsSvg = motionPathsSvg; exports.motionRestValue = motionRestValue; exports.motionRevealClip = motionRevealClip; exports.motionRevealPadding = motionRevealPadding; exports.motionRulerTicks = motionRulerTicks; exports.motionSceneShapeProblems = motionSceneShapeProblems; exports.motionSceneToJson = motionSceneToJson; exports.motionShotAt = motionShotAt; exports.motionShotCueTime = motionShotCueTime; exports.motionShotTimeScale = motionShotTimeScale; exports.motionStageSvg = motionStageSvg; exports.motionTrackKey = motionTrackKey; exports.namedWithSuggestion = namedWithSuggestion; exports.parseMotionFieldInput = parseMotionFieldInput; exports.parseMotionNarration = parseMotionNarration; exports.parseMotionScene = parseMotionScene; exports.resolveMotionCamera = resolveMotionCamera; exports.segmentProgress = segmentProgress; exports.segmentValue = segmentValue; exports.shotShapeProblems = shotShapeProblems; exports.smoothMotionLegs = smoothMotionLegs; exports.smoothMotionTimes = smoothMotionTimes; exports.suggestName = suggestName; exports.toFlairEffectDocument = toFlairEffectDocument; exports.toFlairSvgMotion = toFlairSvgMotion; exports.toFlairVectorEffect = toFlairVectorEffect; exports.toLottie = toLottie; exports.trackValue = trackValue; exports.withMotionLayers = withMotionLayers; exports.withViewBox = withViewBox;
|
|
4084
4346
|
//# sourceMappingURL=index.cjs.map
|