@pixerate/editor 0.57.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 +23 -3
- package/dist/animation/index.cjs.map +1 -1
- package/dist/animation/index.d.cts +3 -2
- package/dist/animation/index.d.ts +3 -2
- package/dist/animation/index.js +23 -3
- package/dist/animation-editor/index.cjs +836 -158
- package/dist/animation-editor/index.cjs.map +1 -1
- package/dist/animation-editor/index.d.cts +454 -40
- package/dist/animation-editor/index.d.ts +454 -40
- package/dist/animation-editor/index.js +790 -112
- package/dist/animation-editor/index.js.map +1 -1
- package/dist/behavior-Ymbld6jL.d.cts +60 -0
- package/dist/behavior-Ymbld6jL.d.ts +60 -0
- 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-3NYXDBBW.js +122 -0
- package/dist/chunk-3NYXDBBW.js.map +1 -0
- package/dist/{chunk-NONDTTH7.js → chunk-3TLL5OID.js} +17 -2
- package/dist/{chunk-NONDTTH7.js.map → chunk-3TLL5OID.js.map} +1 -1
- 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-XCXQDPID.cjs → chunk-YJLCBA7V.cjs} +25 -10
- package/dist/chunk-YJLCBA7V.cjs.map +1 -0
- package/dist/chunk-ZCNLBRT6.cjs +122 -0
- package/dist/chunk-ZCNLBRT6.cjs.map +1 -0
- 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/{svg-CPK82TtU.d.ts → svg-Dh17jCyA.d.ts} +22 -1
- package/dist/{svg-CLg2zqZA.d.cts → svg-ymShkNtu.d.cts} +22 -1
- package/dist/vector/index.cjs +472 -472
- package/dist/vector/index.d.cts +3 -3
- package/dist/vector/index.d.ts +3 -3
- package/dist/vector/index.js +2 -2
- package/package.json +1 -1
- package/dist/chunk-UB2OY7FD.js.map +0 -1
- package/dist/chunk-XCXQDPID.cjs.map +0 -1
- package/dist/chunk-XZ4L3R4K.cjs.map +0 -1
- /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
|
|
|
@@ -29,7 +30,11 @@ var _chunkO2UH7LZEcjs = require('../chunk-O2UH7LZE.cjs');
|
|
|
29
30
|
var _chunkQKLRAA4Tcjs = require('../chunk-QKLRAA4T.cjs');
|
|
30
31
|
|
|
31
32
|
|
|
32
|
-
|
|
33
|
+
|
|
34
|
+
var _chunkZCNLBRT6cjs = require('../chunk-ZCNLBRT6.cjs');
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
var _chunk2FGRTYJNcjs = require('../chunk-2FGRTYJN.cjs');
|
|
33
38
|
|
|
34
39
|
|
|
35
40
|
var _chunkMR3Z4OQIcjs = require('../chunk-MR3Z4OQI.cjs');
|
|
@@ -43,7 +48,9 @@ var _chunkA6PHCAGXcjs = require('../chunk-A6PHCAGX.cjs');
|
|
|
43
48
|
|
|
44
49
|
|
|
45
50
|
|
|
46
|
-
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
var _chunk2FHLHZALcjs = require('../chunk-2FHLHZAL.cjs');
|
|
47
54
|
|
|
48
55
|
// src/animation-editor/types.ts
|
|
49
56
|
var MOTION_COMMAND_TYPES = [
|
|
@@ -59,11 +66,18 @@ var MOTION_COMMAND_TYPES = [
|
|
|
59
66
|
"follow",
|
|
60
67
|
"pulse",
|
|
61
68
|
"shake",
|
|
69
|
+
"teeter",
|
|
70
|
+
"wave",
|
|
71
|
+
"wiggle",
|
|
72
|
+
"color-pulse",
|
|
62
73
|
"effect",
|
|
63
74
|
"wait"
|
|
64
75
|
];
|
|
65
|
-
var MOTION_ENTER_STYLES = ["fade", "pop", "grow", "slide", "draw"];
|
|
66
|
-
var
|
|
76
|
+
var MOTION_ENTER_STYLES = ["fade", "pop", "grow", "slide", "draw", "zoom", "rise", "wipe", "iris", "split"];
|
|
77
|
+
var MOTION_REPEAT_MODES = ["loop", "mirror", "offset"];
|
|
78
|
+
var MOTION_EXIT_STYLES = ["fade", "shrink", "slide", "undraw", "wipe"];
|
|
79
|
+
var MOTION_IRIS_SHAPES = ["circle", "box"];
|
|
80
|
+
var MOTION_SPLIT_ORIENTATIONS = ["horizontal", "vertical"];
|
|
67
81
|
var MOTION_DIRECTIONS = ["up", "down", "left", "right"];
|
|
68
82
|
var MOTION_DEFAULT_DURATIONS = {
|
|
69
83
|
enter: 600,
|
|
@@ -77,12 +91,37 @@ var MOTION_DEFAULT_DURATIONS = {
|
|
|
77
91
|
follow: 1400,
|
|
78
92
|
pulse: 600,
|
|
79
93
|
shake: 500,
|
|
94
|
+
teeter: 600,
|
|
95
|
+
wave: 1e3,
|
|
96
|
+
wiggle: 1500,
|
|
97
|
+
"color-pulse": 600,
|
|
80
98
|
effect: 900
|
|
81
99
|
};
|
|
82
100
|
var MOTION_DEFAULT_HOLD = 600;
|
|
83
101
|
var MOTION_CAMERA_MS = 1e3;
|
|
84
102
|
var MOTION_DRAW_MS_PER_UNIT = 1.5;
|
|
85
103
|
var MOTION_DRAW_RANGE = [500, 2500];
|
|
104
|
+
function motionFlairPrimitive(command2) {
|
|
105
|
+
switch (command2.type) {
|
|
106
|
+
case "enter":
|
|
107
|
+
return `entrance.${_nullishCoalesce(command2.style, () => ( "fade"))}`;
|
|
108
|
+
case "exit":
|
|
109
|
+
return `exit.${_nullishCoalesce(command2.style, () => ( "fade"))}`;
|
|
110
|
+
case "pulse":
|
|
111
|
+
case "shake":
|
|
112
|
+
case "teeter":
|
|
113
|
+
case "wave":
|
|
114
|
+
case "wiggle":
|
|
115
|
+
case "color-pulse":
|
|
116
|
+
return `emphasis.${command2.type}`;
|
|
117
|
+
case "rotate":
|
|
118
|
+
return command2.by !== void 0 ? "emphasis.spin" : void 0;
|
|
119
|
+
case "follow":
|
|
120
|
+
return "path.follow";
|
|
121
|
+
default:
|
|
122
|
+
return void 0;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
86
125
|
|
|
87
126
|
// src/animation-editor/issues.ts
|
|
88
127
|
var MOTION_ISSUE_CODES = {
|
|
@@ -91,18 +130,20 @@ var MOTION_ISSUE_CODES = {
|
|
|
91
130
|
"unknown-target": { severity: "error", wording: "A command names a node the artwork does not have." },
|
|
92
131
|
"unknown-path": { severity: "error", wording: "A follow command names a path the artwork does not have." },
|
|
93
132
|
"not-a-path": { severity: "error", wording: "A follow path must be a path node, not a group." },
|
|
133
|
+
"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." },
|
|
94
134
|
"unknown-shape": { severity: "error", wording: "A morph names a shape the artwork does not have." },
|
|
95
135
|
"unknown-effect": { severity: "error", wording: "An effect command names an effect the node does not have." },
|
|
96
136
|
"unknown-effect-param": { severity: "error", wording: "An effect command names a parameter that is not a number of that effect." },
|
|
97
137
|
"unknown-frame": { severity: "error", wording: "A camera frames a node the artwork does not have." },
|
|
98
138
|
"invalid-camera": { severity: "error", wording: "A camera rectangle has no area." },
|
|
99
139
|
"invalid-color": { severity: "error", wording: "A colour cannot be read." },
|
|
100
|
-
"paint-not-color": { severity: "warning", wording: "A style command recolours a gradient or empty paint, which only switches at the end." },
|
|
140
|
+
"paint-not-color": { severity: "warning", wording: "A style command recolours a gradient or empty paint, which only switches at the end, or a colour pulse has no solid paint to tint." },
|
|
101
141
|
"overlap": { severity: "warning", wording: "Two commands change the same property of a node at the same time; the later one wins." },
|
|
102
142
|
"enter-visible": { severity: "warning", wording: "A node enters while it is already showing, so it blinks out first." },
|
|
103
143
|
"exit-hidden": { severity: "warning", wording: "A node exits or animates while it is hidden." },
|
|
104
144
|
"out-of-frame": { severity: "warning", wording: "A node a command animates ends up outside the camera." },
|
|
105
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." },
|
|
106
147
|
"scene-too-long": { severity: "warning", wording: "The scene runs past its target length." },
|
|
107
148
|
"scene-too-short": { severity: "warning", wording: "The scene ends before its target length." },
|
|
108
149
|
"unknown-control-color": { severity: "warning", wording: "A control stands for a colour the scene never uses." },
|
|
@@ -160,23 +201,27 @@ ${problems.map((p) => `- ${p}`).join("\n")}`);
|
|
|
160
201
|
this.problems = problems;
|
|
161
202
|
}
|
|
162
203
|
};
|
|
163
|
-
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"];
|
|
164
205
|
var SHOT_KEYS = ["id", "label", "camera", "run", "hold"];
|
|
165
|
-
var CAMERA_KEYS = ["x", "y", "width", "height", "frame", "padding", "ease", "ms"];
|
|
166
|
-
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"];
|
|
167
208
|
var COMMAND_KEYS = {
|
|
168
|
-
enter: [...BASE_KEYS, "style", "direction", "distance"],
|
|
209
|
+
enter: [...BASE_KEYS, "style", "direction", "distance", "scale", "shape", "orientation"],
|
|
169
210
|
exit: [...BASE_KEYS, "style", "direction", "distance"],
|
|
170
211
|
draw: [...BASE_KEYS, "from", "to"],
|
|
171
212
|
move: [...BASE_KEYS, "by", "to"],
|
|
172
213
|
rotate: [...BASE_KEYS, "by", "to"],
|
|
173
|
-
scale: [...BASE_KEYS, "to"],
|
|
214
|
+
scale: [...BASE_KEYS, "to", "geometric"],
|
|
174
215
|
fade: [...BASE_KEYS, "to"],
|
|
175
216
|
style: [...BASE_KEYS, "fill", "stroke", "strokeWidth"],
|
|
176
217
|
morph: [...BASE_KEYS, "into"],
|
|
177
218
|
follow: [...BASE_KEYS, "path", "rotate", "from", "to"],
|
|
178
219
|
pulse: [...BASE_KEYS, "amount", "count"],
|
|
179
|
-
shake: [...BASE_KEYS, "amount", "count"],
|
|
220
|
+
shake: [...BASE_KEYS, "amount", "count", "axis"],
|
|
221
|
+
teeter: [...BASE_KEYS, "angle", "count"],
|
|
222
|
+
wave: [...BASE_KEYS, "amount", "count"],
|
|
223
|
+
wiggle: [...BASE_KEYS, "amount", "frequency", "octaves", "rotate", "seed"],
|
|
224
|
+
"color-pulse": [...BASE_KEYS, "color", "paint", "count"],
|
|
180
225
|
effect: [...BASE_KEYS, "effect", "param", "to"],
|
|
181
226
|
wait: ["type", "duration", "with", "note"]
|
|
182
227
|
};
|
|
@@ -214,7 +259,13 @@ function commandShapeProblems(command2, where) {
|
|
|
214
259
|
nonNegative("duration");
|
|
215
260
|
nonNegative("delay");
|
|
216
261
|
nonNegative("stagger");
|
|
217
|
-
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).`);
|
|
218
269
|
switch (type) {
|
|
219
270
|
case "enter":
|
|
220
271
|
case "exit": {
|
|
@@ -223,6 +274,9 @@ function commandShapeProblems(command2, where) {
|
|
|
223
274
|
p(`"style" must be one of ${styles.join(", ")}${typeof command2.style === "string" ? ` (got ${namedWithSuggestion(command2.style, styles)})` : ""}.`);
|
|
224
275
|
if (command2.direction !== void 0 && !MOTION_DIRECTIONS.includes(command2.direction)) p(`"direction" must be one of ${MOTION_DIRECTIONS.join(", ")}.`);
|
|
225
276
|
optionalNumber("distance");
|
|
277
|
+
if (command2.scale !== void 0 && !(isNum(command2.scale) && command2.scale >= 0)) p(`"scale" must be a number \u2265 0 (the starting scale for pop and zoom).`);
|
|
278
|
+
if (command2.shape !== void 0 && !MOTION_IRIS_SHAPES.includes(command2.shape)) p(`"shape" must be one of ${MOTION_IRIS_SHAPES.join(", ")} (the opening of an iris).`);
|
|
279
|
+
if (command2.orientation !== void 0 && !MOTION_SPLIT_ORIENTATIONS.includes(command2.orientation)) p(`"orientation" must be one of ${MOTION_SPLIT_ORIENTATIONS.join(", ")} (the line a split opens from).`);
|
|
226
280
|
break;
|
|
227
281
|
}
|
|
228
282
|
case "draw":
|
|
@@ -243,6 +297,7 @@ function commandShapeProblems(command2, where) {
|
|
|
243
297
|
break;
|
|
244
298
|
case "scale":
|
|
245
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.`);
|
|
246
301
|
break;
|
|
247
302
|
case "fade":
|
|
248
303
|
if (!(isNum(command2.to) && command2.to >= 0 && command2.to <= 1)) p(`"to" must be an opacity from 0 to 1.`);
|
|
@@ -264,8 +319,22 @@ function commandShapeProblems(command2, where) {
|
|
|
264
319
|
break;
|
|
265
320
|
case "pulse":
|
|
266
321
|
case "shake":
|
|
322
|
+
case "teeter":
|
|
323
|
+
case "wave":
|
|
324
|
+
case "color-pulse":
|
|
267
325
|
optionalNumber("amount");
|
|
326
|
+
optionalNumber("angle");
|
|
268
327
|
if (command2.count !== void 0 && !(Number.isInteger(command2.count) && command2.count >= 1)) p(`"count" must be a whole number \u2265 1.`);
|
|
328
|
+
if (command2.axis !== void 0 && command2.axis !== "horizontal" && command2.axis !== "vertical") p(`"axis" must be "horizontal" or "vertical".`);
|
|
329
|
+
if (command2.color !== void 0 && typeof command2.color !== "string") p(`"color" must be a CSS colour.`);
|
|
330
|
+
if (command2.paint !== void 0 && command2.paint !== "fill" && command2.paint !== "stroke") p(`"paint" must be "fill" or "stroke".`);
|
|
331
|
+
break;
|
|
332
|
+
case "wiggle":
|
|
333
|
+
optionalNumber("amount");
|
|
334
|
+
optionalNumber("rotate");
|
|
335
|
+
if (command2.frequency !== void 0 && !(isNum(command2.frequency) && command2.frequency > 0)) p(`"frequency" must be a number of Hz > 0.`);
|
|
336
|
+
if (command2.octaves !== void 0 && !(Number.isInteger(command2.octaves) && command2.octaves >= 1 && command2.octaves <= 8)) p(`"octaves" must be a whole number from 1 to 8.`);
|
|
337
|
+
if (command2.seed !== void 0 && !(Number.isInteger(command2.seed) && command2.seed >= 0)) p(`"seed" must be a whole number \u2265 0.`);
|
|
269
338
|
break;
|
|
270
339
|
case "effect":
|
|
271
340
|
if (typeof command2.effect !== "string" || !command2.effect) p(`"effect" must be the id of one of the node's effects.`);
|
|
@@ -292,8 +361,9 @@ function shotShapeProblems(shot, where) {
|
|
|
292
361
|
if (typeof c.frame === "string") {
|
|
293
362
|
if (c.padding !== void 0 && !isNum(c.padding)) problems.push(`${name} camera: "padding" must be a number.`);
|
|
294
363
|
} else if (!isRect) problems.push(`${name} camera: give { x, y, width, height } or { frame: "<node id>" }.`);
|
|
295
|
-
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.`);
|
|
296
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.`);
|
|
297
367
|
}
|
|
298
368
|
}
|
|
299
369
|
if (!Array.isArray(shot.run)) problems.push(`${name}: "run" must be a list of commands (it may be empty).`);
|
|
@@ -312,6 +382,7 @@ function motionSceneShapeProblems(input) {
|
|
|
312
382
|
problems.push('Scene: "artwork" must be a vector document { width, height, nodes }.');
|
|
313
383
|
if (input.loop !== void 0 && !["none", "loop", "ping-pong"].includes(input.loop)) problems.push('Scene: "loop" must be "none", "loop" or "ping-pong".');
|
|
314
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.');
|
|
315
386
|
if (input.target !== void 0 && !(Array.isArray(input.target) && input.target.length === 2 && input.target.every(isNum) && input.target[0] <= input.target[1]))
|
|
316
387
|
problems.push('Scene: "target" must be [min, max] seconds.');
|
|
317
388
|
if (input.controls !== void 0) {
|
|
@@ -355,26 +426,76 @@ function indexMotionNodes(nodes) {
|
|
|
355
426
|
const out = /* @__PURE__ */ new Map();
|
|
356
427
|
const walk = (list, parentWorld, ancestors) => {
|
|
357
428
|
for (const node of list) {
|
|
358
|
-
const local =
|
|
359
|
-
const bounds =
|
|
429
|
+
const local = _chunkYJLCBA7Vcjs.vectorNodeBounds.call(void 0, node);
|
|
430
|
+
const bounds = _chunkYJLCBA7Vcjs.vectorNodeBounds.call(void 0, node, parentWorld);
|
|
360
431
|
const center = local ? { x: local.x + local.width / 2, y: local.y + local.height / 2 } : { x: 0, y: 0 };
|
|
361
|
-
if (!out.has(node.id)) out.set(node.id, { node, ancestors, parentWorld, center, bounds });
|
|
362
|
-
if (node.type === "group") walk(node.children,
|
|
432
|
+
if (!out.has(node.id)) out.set(node.id, { node, ancestors, parentWorld, center, bounds, box: local });
|
|
433
|
+
if (node.type === "group") walk(node.children, _chunkYJLCBA7Vcjs.multiplyMatrices.call(void 0, _chunkYJLCBA7Vcjs.nodeMatrix.call(void 0, node), parentWorld), [...ancestors, node.id]);
|
|
363
434
|
}
|
|
364
435
|
};
|
|
365
|
-
walk(nodes,
|
|
436
|
+
walk(nodes, _chunkYJLCBA7Vcjs.IDENTITY_MATRIX, []);
|
|
366
437
|
indexCache.set(nodes, out);
|
|
367
438
|
return out;
|
|
368
439
|
}
|
|
369
440
|
var solid = (paint) => typeof paint === "string" && paint !== "none" ? paint : void 0;
|
|
370
441
|
function firstPaint(node, key) {
|
|
371
442
|
if (node.type === "path") return solid(node[key]);
|
|
372
|
-
for (const p of
|
|
443
|
+
for (const p of _chunkYJLCBA7Vcjs.collectPathNodes.call(void 0, node)) {
|
|
373
444
|
const s = solid(p[key]);
|
|
374
445
|
if (s) return s;
|
|
375
446
|
}
|
|
376
447
|
return void 0;
|
|
377
448
|
}
|
|
449
|
+
function strokeReach(node) {
|
|
450
|
+
let reach = 0;
|
|
451
|
+
for (const p of node.type === "path" ? [node] : _chunkYJLCBA7Vcjs.collectPathNodes.call(void 0, node)) {
|
|
452
|
+
if (p.stroke == null || p.stroke === "none") continue;
|
|
453
|
+
const width = _nullishCoalesce(p.strokeWidth, () => ( 1));
|
|
454
|
+
reach = Math.max(reach, p.strokeAlign === "outside" ? width : p.strokeAlign === "inside" ? 0 : width / 2);
|
|
455
|
+
}
|
|
456
|
+
return reach;
|
|
457
|
+
}
|
|
458
|
+
function motionRevealPadding(entry) {
|
|
459
|
+
const box = entry.box;
|
|
460
|
+
if (!box || !(box.width > 0) || !(box.height > 0)) return null;
|
|
461
|
+
const s = strokeReach(entry.node);
|
|
462
|
+
return [Math.min(1, s / box.width + 0.02), Math.min(1, s / box.height + 0.02)];
|
|
463
|
+
}
|
|
464
|
+
var clipRound = (n) => Math.round(n * 1e4) / 1e4 || 0;
|
|
465
|
+
function revealFull(property, [px, py]) {
|
|
466
|
+
return (property === "clipRect" ? [-px, -py, 1 + 2 * px, 1 + 2 * py] : [0.5, 0.5, Math.hypot(0.5 + px, 0.5 + py)]).map(clipRound);
|
|
467
|
+
}
|
|
468
|
+
var OPPOSITE = { up: "down", down: "up", left: "right", right: "left" };
|
|
469
|
+
function motionRevealClip(entry, reveal) {
|
|
470
|
+
const pad = motionRevealPadding(entry);
|
|
471
|
+
if (!pad) return null;
|
|
472
|
+
const [px, py] = pad;
|
|
473
|
+
const [w, h] = [1 + 2 * px, 1 + 2 * py];
|
|
474
|
+
const rect = (from, to) => ({ property: "clipRect", from: from.map(clipRound), to: to.map(clipRound) });
|
|
475
|
+
if (reveal.style === "iris") {
|
|
476
|
+
if (reveal.shape === "box") return rect([0.5, 0.5, 0, 0], [-px, -py, w, h]);
|
|
477
|
+
return { property: "clipCircle", from: [0.5, 0.5, 0], to: revealFull("clipCircle", pad) };
|
|
478
|
+
}
|
|
479
|
+
if (reveal.style === "split") {
|
|
480
|
+
if (reveal.orientation === "vertical") return rect([0.5, -py, 0, h], [-px, -py, w, h]);
|
|
481
|
+
return rect([-px, 0.5, w, 0], [-px, -py, w, h]);
|
|
482
|
+
}
|
|
483
|
+
const direction = _nullishCoalesce(reveal.direction, () => ( "right"));
|
|
484
|
+
if (reveal.hide) {
|
|
485
|
+
const shown = motionRevealClip(entry, { style: "wipe", direction: OPPOSITE[direction] });
|
|
486
|
+
return { ...shown, from: shown.to, to: shown.from };
|
|
487
|
+
}
|
|
488
|
+
switch (direction) {
|
|
489
|
+
case "right":
|
|
490
|
+
return rect([-px, -py, 0, h], [-px, -py, w, h]);
|
|
491
|
+
case "left":
|
|
492
|
+
return rect([1 + px, -py, 0, h], [-px, -py, w, h]);
|
|
493
|
+
case "down":
|
|
494
|
+
return rect([-px, -py, w, 0], [-px, -py, w, h]);
|
|
495
|
+
case "up":
|
|
496
|
+
return rect([-px, 1 + py, w, 0], [-px, -py, w, h]);
|
|
497
|
+
}
|
|
498
|
+
}
|
|
378
499
|
function motionRestValue(entry, property, artwork) {
|
|
379
500
|
const node = _optionalChain([entry, 'optionalAccess', _4 => _4.node]);
|
|
380
501
|
switch (property) {
|
|
@@ -397,11 +518,16 @@ function motionRestValue(entry, property, artwork) {
|
|
|
397
518
|
case "stroke":
|
|
398
519
|
return _nullishCoalesce((node && firstPaint(node, property)), () => ( "transparent"));
|
|
399
520
|
case "strokeWidth": {
|
|
400
|
-
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;
|
|
401
522
|
return _nullishCoalesce(_optionalChain([path, 'optionalAccess', _7 => _7.strokeWidth]), () => ( 1));
|
|
402
523
|
}
|
|
403
524
|
case "shape":
|
|
404
525
|
return _nullishCoalesce(_optionalChain([node, 'optionalAccess', _8 => _8.id]), () => ( ""));
|
|
526
|
+
case "clipRect":
|
|
527
|
+
case "clipCircle": {
|
|
528
|
+
const pad = entry && motionRevealPadding(entry);
|
|
529
|
+
return pad ? revealFull(property, pad) : [];
|
|
530
|
+
}
|
|
405
531
|
case "camera":
|
|
406
532
|
return [0, 0, artwork.width, artwork.height];
|
|
407
533
|
}
|
|
@@ -423,16 +549,36 @@ function interpolateMotionValue(from, to, u) {
|
|
|
423
549
|
}
|
|
424
550
|
function segmentProgress(segment, time) {
|
|
425
551
|
const span = segment.end - segment.start;
|
|
426
|
-
|
|
427
|
-
|
|
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) };
|
|
428
556
|
}
|
|
429
|
-
function
|
|
430
|
-
const
|
|
431
|
-
|
|
432
|
-
if (
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
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;
|
|
572
|
+
}
|
|
573
|
+
function segmentValue(segment, time, raw = false) {
|
|
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
|
+
}
|
|
579
|
+
if (!segment.behavior || raw) return interpolateMotionValue(segment.from, segment.to, eased);
|
|
580
|
+
const p = _chunkZCNLBRT6cjs.behaviorProgress.call(void 0, segment.behavior, eased, _nullishCoalesce(segment.index, () => ( 0)), segment.end - segment.start);
|
|
581
|
+
return typeof segment.from === "string" ? interpolateMotionValue(segment.from, segment.to, Math.min(1, Math.max(0, p))) : interpolateMotionValue(segment.from, segment.to, p);
|
|
436
582
|
}
|
|
437
583
|
function activeSegment(track, time) {
|
|
438
584
|
if (!_optionalChain([track, 'optionalAccess', _12 => _12.length])) return void 0;
|
|
@@ -440,10 +586,10 @@ function activeSegment(track, time) {
|
|
|
440
586
|
for (const s of track) if (s.start <= time && (!found || s.start >= found.start)) found = s;
|
|
441
587
|
return found;
|
|
442
588
|
}
|
|
443
|
-
function trackValue(track, time) {
|
|
589
|
+
function trackValue(track, time, raw = false) {
|
|
444
590
|
if (!_optionalChain([track, 'optionalAccess', _13 => _13.length])) return void 0;
|
|
445
591
|
const s = activeSegment(track, time);
|
|
446
|
-
if (s) return segmentValue(s, time);
|
|
592
|
+
if (s) return segmentValue(s, time, raw);
|
|
447
593
|
let first = track[0];
|
|
448
594
|
for (const t of track) if (t.start < first.start) first = t;
|
|
449
595
|
return first.from;
|
|
@@ -469,20 +615,32 @@ function resolveMotionCamera(camera, scene) {
|
|
|
469
615
|
const r = camera;
|
|
470
616
|
return { x: r.x, y: r.y, width: r.width, height: r.height };
|
|
471
617
|
}
|
|
472
|
-
function motionPathLength(node, parentWorld =
|
|
618
|
+
function motionPathLength(node, parentWorld = _chunkYJLCBA7Vcjs.IDENTITY_MATRIX) {
|
|
473
619
|
let longest = 0;
|
|
474
|
-
const paths = node.type === "path" ? [node] :
|
|
620
|
+
const paths = node.type === "path" ? [node] : _chunkYJLCBA7Vcjs.collectPathNodes.call(void 0, node);
|
|
475
621
|
for (const p of paths) {
|
|
476
622
|
try {
|
|
477
|
-
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 })));
|
|
478
624
|
} catch (e2) {
|
|
479
625
|
}
|
|
480
626
|
}
|
|
481
627
|
return longest;
|
|
482
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
|
+
}
|
|
483
634
|
function motionCommandDuration(command2, scene, target) {
|
|
635
|
+
return motionCycleDuration(command2, scene, target) * motionCommandRepeat(command2);
|
|
636
|
+
}
|
|
637
|
+
function motionCycleDuration(command2, scene, target) {
|
|
484
638
|
if (command2.type === "wait") return command2.duration;
|
|
485
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
|
+
}
|
|
486
644
|
if (command2.type === "draw" || command2.type === "enter" && command2.style === "draw" || command2.type === "exit" && command2.style === "undraw") {
|
|
487
645
|
const entry = target ? indexMotionNodes(scene.artwork.nodes).get(target) : void 0;
|
|
488
646
|
const length = entry ? motionPathLength(entry.node) : 0;
|
|
@@ -511,7 +669,7 @@ function compileMotionScene(scene) {
|
|
|
511
669
|
const rect = resolveMotionCamera(shot.camera, scene);
|
|
512
670
|
if (rect && rect.width > 0 && rect.height > 0) {
|
|
513
671
|
const ms = index === 0 ? 0 : _nullishCoalesce(shot.camera.ms, () => ( MOTION_CAMERA_MS));
|
|
514
|
-
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 });
|
|
515
673
|
cursor = start + ms;
|
|
516
674
|
}
|
|
517
675
|
}
|
|
@@ -552,7 +710,7 @@ function compileMotionScene(scene) {
|
|
|
552
710
|
jobs.push({
|
|
553
711
|
start: move.start,
|
|
554
712
|
order: order++,
|
|
555
|
-
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 } : {} })
|
|
556
714
|
});
|
|
557
715
|
}
|
|
558
716
|
for (const timing of commands) {
|
|
@@ -563,11 +721,37 @@ function compileMotionScene(scene) {
|
|
|
563
721
|
const entry = nodes.get(target);
|
|
564
722
|
if (!entry) return;
|
|
565
723
|
const start = timing.start + k * (_nullishCoalesce(command2.stagger, () => ( 0)));
|
|
566
|
-
const
|
|
567
|
-
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) });
|
|
568
726
|
});
|
|
569
727
|
}
|
|
570
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
|
+
}
|
|
571
755
|
function compileCommand(command2, node, entry, start, duration2, timing) {
|
|
572
756
|
const end = start + duration2;
|
|
573
757
|
const where = { shot: timing.shot, command: timing.command };
|
|
@@ -582,15 +766,27 @@ function compileMotionScene(scene) {
|
|
|
582
766
|
case "enter": {
|
|
583
767
|
const style = _nullishCoalesce(command2.style, () => ( "fade"));
|
|
584
768
|
const opacity = rest("opacity");
|
|
769
|
+
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;
|
|
770
|
+
for (const p of ["clipRect", "clipCircle"]) if (p !== _optionalChain([reveal, 'optionalAccess', _15 => _15.property]) && tracks.has(motionTrackKey(node, p))) seg(p, rest(p), "linear", start, start);
|
|
771
|
+
if (reveal) {
|
|
772
|
+
seg("opacity", opacity, "linear", start, start);
|
|
773
|
+
seg(reveal.property, reveal.to, _nullishCoalesce(ease, () => ( MOTION_EASE_OUT)), start, end, { from: reveal.from });
|
|
774
|
+
break;
|
|
775
|
+
}
|
|
585
776
|
if (style === "draw") {
|
|
586
777
|
seg("drawEnd", 1, _nullishCoalesce(ease, () => ( MOTION_EASE_IN_OUT)), start, start + duration2 * 0.75, { from: 0 });
|
|
587
778
|
seg("fillOpacity", 1, MOTION_EASE_OUT, start + duration2 * 0.6, end, { from: 0 });
|
|
588
779
|
break;
|
|
589
780
|
}
|
|
590
|
-
if (style === "pop") {
|
|
591
|
-
|
|
592
|
-
seg("
|
|
593
|
-
seg("
|
|
781
|
+
if (style === "pop" || style === "zoom") {
|
|
782
|
+
const from = _nullishCoalesce(command2.scale, () => ( (style === "pop" ? 0.5 : 1.6)));
|
|
783
|
+
seg("opacity", opacity, MOTION_EASE_OUT, start, start + duration2 * (style === "pop" ? 0.4 : 0.5), { from: 0 });
|
|
784
|
+
seg("scaleX", 1, _nullishCoalesce(ease, () => ( (style === "pop" ? "ease-out-back" : MOTION_EASE_OUT))), start, end, { from });
|
|
785
|
+
seg("scaleY", 1, _nullishCoalesce(ease, () => ( (style === "pop" ? "ease-out-back" : MOTION_EASE_OUT))), start, end, { from });
|
|
786
|
+
} else if (style === "rise") {
|
|
787
|
+
const y = num("y");
|
|
788
|
+
seg("opacity", opacity, MOTION_EASE_OUT, start, start + duration2 * 0.5, { from: 0 });
|
|
789
|
+
seg("y", y, _nullishCoalesce(ease, () => ( "ease-out-back")), start, end, { from: y + (_nullishCoalesce(command2.distance, () => ( 20))) });
|
|
594
790
|
} else if (style === "grow") {
|
|
595
791
|
seg("opacity", opacity, MOTION_EASE_OUT, start, start + duration2 * 0.3, { from: 0 });
|
|
596
792
|
seg("scaleX", 1, _nullishCoalesce(ease, () => ( MOTION_EASE_OUT)), start, end, { from: 0 });
|
|
@@ -608,7 +804,9 @@ function compileMotionScene(scene) {
|
|
|
608
804
|
}
|
|
609
805
|
case "exit": {
|
|
610
806
|
const style = _nullishCoalesce(command2.style, () => ( "fade"));
|
|
611
|
-
|
|
807
|
+
const wipe = style === "wipe" ? motionRevealClip(entry, { style, direction: command2.direction, hide: true }) : null;
|
|
808
|
+
if (wipe) seg(wipe.property, wipe.to, _nullishCoalesce(ease, () => ( "ease-in")), start, end, { from: wipe.from });
|
|
809
|
+
else if (style === "undraw") {
|
|
612
810
|
seg("fillOpacity", 0, "ease-in", start, start + duration2 * 0.4);
|
|
613
811
|
seg("drawEnd", 0, _nullishCoalesce(ease, () => ( MOTION_EASE_IN_OUT)));
|
|
614
812
|
} else if (style === "shrink") {
|
|
@@ -624,20 +822,18 @@ function compileMotionScene(scene) {
|
|
|
624
822
|
} else seg("opacity", 0, _nullishCoalesce(ease, () => ( "ease-in")));
|
|
625
823
|
break;
|
|
626
824
|
}
|
|
627
|
-
case "draw":
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
seg("drawEnd", _nullishCoalesce(command2.to, () => ( 1)), _nullishCoalesce(ease, () => ( MOTION_EASE_IN_OUT)), start, end, command2.from !== void 0 ? { from: command2.from } : drawn ? {} : { from: 0 });
|
|
631
|
-
break;
|
|
632
|
-
}
|
|
825
|
+
case "draw": {
|
|
826
|
+
const drawn = tracks.has(motionTrackKey(node, "drawEnd"));
|
|
827
|
+
seg("drawEnd", _nullishCoalesce(command2.to, () => ( 1)), _nullishCoalesce(ease, () => ( MOTION_EASE_IN_OUT)), start, end, command2.from !== void 0 ? { from: command2.from } : drawn ? {} : { from: 0 });
|
|
633
828
|
break;
|
|
829
|
+
}
|
|
634
830
|
case "move": {
|
|
635
831
|
const e = _nullishCoalesce(ease, () => ( MOTION_EASE_IN_OUT));
|
|
636
|
-
let dx = _nullishCoalesce(_optionalChain([command2, 'access',
|
|
637
|
-
let dy = _nullishCoalesce(_optionalChain([command2, 'access',
|
|
832
|
+
let dx = _nullishCoalesce(_optionalChain([command2, 'access', _16 => _16.by, 'optionalAccess', _17 => _17.x]), () => ( 0));
|
|
833
|
+
let dy = _nullishCoalesce(_optionalChain([command2, 'access', _18 => _18.by, 'optionalAccess', _19 => _19.y]), () => ( 0));
|
|
638
834
|
if (command2.to) {
|
|
639
|
-
const inv =
|
|
640
|
-
const target =
|
|
835
|
+
const inv = _chunkYJLCBA7Vcjs.invertMatrix.call(void 0, entry.parentWorld);
|
|
836
|
+
const target = _chunkYJLCBA7Vcjs.applyMatrix.call(void 0, inv, command2.to);
|
|
641
837
|
dx = target.x - (entry.center.x + num("x"));
|
|
642
838
|
dy = target.y - (entry.center.y + num("y"));
|
|
643
839
|
}
|
|
@@ -650,8 +846,9 @@ function compileMotionScene(scene) {
|
|
|
650
846
|
break;
|
|
651
847
|
case "scale": {
|
|
652
848
|
const to = typeof command2.to === "number" ? { x: command2.to, y: command2.to } : command2.to;
|
|
653
|
-
|
|
654
|
-
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);
|
|
655
852
|
break;
|
|
656
853
|
}
|
|
657
854
|
case "fade":
|
|
@@ -668,14 +865,35 @@ function compileMotionScene(scene) {
|
|
|
668
865
|
case "follow":
|
|
669
866
|
seg("follow", _nullishCoalesce(command2.to, () => ( 1)), _nullishCoalesce(ease, () => ( MOTION_EASE_IN_OUT)), start, end, { from: _nullishCoalesce(command2.from, () => ( 0)), path: command2.path, rotate: _nullishCoalesce(command2.rotate, () => ( false)) });
|
|
670
867
|
break;
|
|
868
|
+
// Emphasis: a change toward a peak that a behaviour swings round (as Flair's primitives do).
|
|
671
869
|
case "pulse": {
|
|
672
|
-
const
|
|
673
|
-
|
|
674
|
-
seg("
|
|
870
|
+
const behavior = { type: "pulse", count: _nullishCoalesce(command2.count, () => ( 1)) };
|
|
871
|
+
const k = 1 + (_nullishCoalesce(command2.amount, () => ( 0.15)));
|
|
872
|
+
seg("scaleX", num("scaleX") * k, _nullishCoalesce(ease, () => ( "linear")), start, end, { behavior });
|
|
873
|
+
seg("scaleY", num("scaleY") * k, _nullishCoalesce(ease, () => ( "linear")), start, end, { behavior });
|
|
874
|
+
break;
|
|
875
|
+
}
|
|
876
|
+
case "shake": {
|
|
877
|
+
const p = command2.axis === "vertical" ? "y" : "x";
|
|
878
|
+
seg(p, num(p) + (_nullishCoalesce(command2.amount, () => ( 8))), _nullishCoalesce(ease, () => ( "linear")), start, end, { behavior: { type: "oscillate", count: _nullishCoalesce(command2.count, () => ( 3)), decay: 1 } });
|
|
879
|
+
break;
|
|
880
|
+
}
|
|
881
|
+
case "teeter":
|
|
882
|
+
seg("rotation", num("rotation") + (_nullishCoalesce(command2.angle, () => ( 8))), _nullishCoalesce(ease, () => ( "linear")), start, end, { behavior: { type: "oscillate", count: _nullishCoalesce(command2.count, () => ( 3)), decay: 1 } });
|
|
883
|
+
break;
|
|
884
|
+
case "wave":
|
|
885
|
+
seg("y", num("y") - (_nullishCoalesce(command2.amount, () => ( 6))), _nullishCoalesce(ease, () => ( "linear")), start, end, { behavior: { type: "oscillate", count: _nullishCoalesce(command2.count, () => ( 2)) } });
|
|
886
|
+
break;
|
|
887
|
+
case "wiggle": {
|
|
888
|
+
const behavior = { type: "wriggle", frequency: _nullishCoalesce(command2.frequency, () => ( 3)), octaves: _nullishCoalesce(command2.octaves, () => ( 2)), seed: command2.seed || _chunkZCNLBRT6cjs.motionSeedFromId.call(void 0, node) };
|
|
889
|
+
const amount = _nullishCoalesce(command2.amount, () => ( 6));
|
|
890
|
+
seg("x", num("x") + amount, _nullishCoalesce(ease, () => ( "linear")), start, end, { behavior, index: 0 });
|
|
891
|
+
seg("y", num("y") + amount, _nullishCoalesce(ease, () => ( "linear")), start, end, { behavior, index: 1 });
|
|
892
|
+
if (command2.rotate) seg("rotation", num("rotation") + command2.rotate, _nullishCoalesce(ease, () => ( "linear")), start, end, { behavior, index: 2 });
|
|
675
893
|
break;
|
|
676
894
|
}
|
|
677
|
-
case "
|
|
678
|
-
seg("
|
|
895
|
+
case "color-pulse":
|
|
896
|
+
seg(_nullishCoalesce(command2.paint, () => ( "fill")), _nullishCoalesce(command2.color, () => ( "#f59e0b")), _nullishCoalesce(ease, () => ( "linear")), start, end, { behavior: { type: "pulse", count: _nullishCoalesce(command2.count, () => ( 1)) } });
|
|
679
897
|
break;
|
|
680
898
|
case "effect":
|
|
681
899
|
seg(`effect:${command2.effect}:${command2.param}`, command2.to, _nullishCoalesce(ease, () => ( MOTION_EASE_IN_OUT)));
|
|
@@ -698,7 +916,7 @@ var MOTION_DRAW_EFFECT_ID = "~draw";
|
|
|
698
916
|
var TRANSFORM_PROPS = ["x", "y", "rotation", "scaleX", "scaleY", "follow"];
|
|
699
917
|
var tidy = (v) => Math.round(v * 1e6) / 1e6 || 0;
|
|
700
918
|
function applyMotionControlValues(scene, values) {
|
|
701
|
-
if (!values || !_optionalChain([scene, 'access',
|
|
919
|
+
if (!values || !_optionalChain([scene, 'access', _20 => _20.controls, 'optionalAccess', _21 => _21.length])) return scene;
|
|
702
920
|
const map = /* @__PURE__ */ new Map();
|
|
703
921
|
for (const control of scene.controls) {
|
|
704
922
|
const value = values[control.name];
|
|
@@ -734,17 +952,17 @@ function morphBetween(a, b) {
|
|
|
734
952
|
let inner = morphCache.get(a);
|
|
735
953
|
if (!inner) morphCache.set(a, inner = /* @__PURE__ */ new WeakMap());
|
|
736
954
|
let morph = inner.get(b);
|
|
737
|
-
if (!morph) inner.set(b, morph =
|
|
955
|
+
if (!morph) inner.set(b, morph = _chunkYJLCBA7Vcjs.createVectorMorph.call(void 0, a, b, { id: a.id }));
|
|
738
956
|
return morph;
|
|
739
957
|
}
|
|
740
958
|
function shapeIn(entry, shape) {
|
|
741
959
|
if (shape === entry) return entry.node;
|
|
742
|
-
const m =
|
|
743
|
-
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 } };
|
|
744
962
|
}
|
|
745
963
|
function shapeAt(entry, timeline, nodes, time) {
|
|
746
964
|
const track = timeline.tracks.get(motionTrackKey(entry.node.id, "shape"));
|
|
747
|
-
if (!_optionalChain([track, 'optionalAccess',
|
|
965
|
+
if (!_optionalChain([track, 'optionalAccess', _22 => _22.length])) return null;
|
|
748
966
|
const s = activeSegment(track, time);
|
|
749
967
|
const fromId = String(s ? s.from : track[0].from);
|
|
750
968
|
if (!s) return fromId === entry.node.id ? null : nodes.get(fromId) ? shapeIn(entry, nodes.get(fromId)) : null;
|
|
@@ -757,44 +975,53 @@ function shapeAt(entry, timeline, nodes, time) {
|
|
|
757
975
|
if (u >= 1) return b === entry ? null : shapeIn(entry, b);
|
|
758
976
|
return morphBetween(shapeIn(entry, a), shapeIn(entry, b)).at(u);
|
|
759
977
|
}
|
|
978
|
+
function motionFollowRoute(entry, path) {
|
|
979
|
+
if (!path || path.node.type !== "path") return null;
|
|
980
|
+
try {
|
|
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));
|
|
982
|
+
} catch (e3) {
|
|
983
|
+
return null;
|
|
984
|
+
}
|
|
985
|
+
}
|
|
760
986
|
function followAt(entry, timeline, nodes, time) {
|
|
761
987
|
const track = timeline.tracks.get(motionTrackKey(entry.node.id, "follow"));
|
|
762
|
-
const s = _nullishCoalesce(activeSegment(track, time), () => ( _optionalChain([track, 'optionalAccess',
|
|
763
|
-
if (!_optionalChain([s, 'optionalAccess',
|
|
764
|
-
const
|
|
765
|
-
if (!path || path.node.type !== "path") return null;
|
|
988
|
+
const s = _nullishCoalesce(activeSegment(track, time), () => ( _optionalChain([track, 'optionalAccess', _23 => _23[0]])));
|
|
989
|
+
if (!_optionalChain([s, 'optionalAccess', _24 => _24.path])) return null;
|
|
990
|
+
const route = motionFollowRoute(entry, nodes.get(s.path));
|
|
766
991
|
const progress = Number(trackValue(track, time));
|
|
767
|
-
if (!(progress >= 0)) return null;
|
|
992
|
+
if (!route || !(progress >= 0)) return null;
|
|
768
993
|
try {
|
|
769
|
-
const
|
|
770
|
-
const at = _chunkO2UH7LZEcjs.bezierPathPointAtLength.call(void 0, bezier, Math.min(1, Math.max(0, progress)) * _chunkO2UH7LZEcjs.bezierPathLength.call(void 0, bezier));
|
|
994
|
+
const at = _chunkO2UH7LZEcjs.bezierPathPointAtLength.call(void 0, route, Math.min(1, Math.max(0, progress)) * _chunkO2UH7LZEcjs.bezierPathLength.call(void 0, route));
|
|
771
995
|
if (!at) return null;
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
} catch (e3) {
|
|
996
|
+
return { x: at.point[0], y: at.point[1], angle: s.rotate ? at.angle * 180 / Math.PI : 0 };
|
|
997
|
+
} catch (e4) {
|
|
775
998
|
return null;
|
|
776
999
|
}
|
|
777
1000
|
}
|
|
778
|
-
function motionNodeOffset(entry, timeline, time, nodes = indexMotionNodes([entry.node])) {
|
|
1001
|
+
function motionNodeOffset(entry, timeline, time, nodes = indexMotionNodes([entry.node]), raw = false, follow = true) {
|
|
779
1002
|
const read = (p, rest) => {
|
|
780
|
-
const v = trackValue(timeline.tracks.get(motionTrackKey(entry.node.id, p)), time);
|
|
1003
|
+
const v = trackValue(timeline.tracks.get(motionTrackKey(entry.node.id, p)), time, raw);
|
|
781
1004
|
return typeof v === "number" ? v : rest;
|
|
782
1005
|
};
|
|
783
|
-
if (!TRANSFORM_PROPS.some((p) => timeline.tracks.has(motionTrackKey(entry.node.id, p)))) return null;
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
1006
|
+
if (!TRANSFORM_PROPS.some((p) => (follow || p !== "follow") && timeline.tracks.has(motionTrackKey(entry.node.id, p)))) return null;
|
|
1007
|
+
const dx = read("x", 0);
|
|
1008
|
+
const dy = read("y", 0);
|
|
1009
|
+
const rotation = read("rotation", 0);
|
|
787
1010
|
const scaleX = read("scaleX", 1);
|
|
788
1011
|
const scaleY = read("scaleY", 1);
|
|
789
|
-
const follow = followAt(entry, timeline, nodes, time);
|
|
790
|
-
if (follow) {
|
|
791
|
-
dx += follow.x - entry.center.x;
|
|
792
|
-
dy += follow.y - entry.center.y;
|
|
793
|
-
rotation += follow.angle;
|
|
794
|
-
}
|
|
795
1012
|
const c = entry.center;
|
|
796
|
-
|
|
797
|
-
|
|
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));
|
|
1014
|
+
const along = follow ? followAt(entry, timeline, nodes, time) : null;
|
|
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)));
|
|
1016
|
+
return { matrix, dx, dy, rotation, scaleX, scaleY, follow: along };
|
|
1017
|
+
}
|
|
1018
|
+
function motionNodeClips(id, timeline, time) {
|
|
1019
|
+
const out = [];
|
|
1020
|
+
const rect = trackValue(timeline.tracks.get(motionTrackKey(id, "clipRect")), time);
|
|
1021
|
+
if (Array.isArray(rect) && rect.length === 4) out.push({ shape: "rect", x: tidy(rect[0]), y: tidy(rect[1]), width: Math.max(0, tidy(rect[2])), height: Math.max(0, tidy(rect[3])) });
|
|
1022
|
+
const circle = trackValue(timeline.tracks.get(motionTrackKey(id, "clipCircle")), time);
|
|
1023
|
+
if (Array.isArray(circle) && circle.length === 3) out.push({ shape: "circle", cx: tidy(circle[0]), cy: tidy(circle[1]), r: Math.max(0, tidy(circle[2])) });
|
|
1024
|
+
return out;
|
|
798
1025
|
}
|
|
799
1026
|
function motionFrameAt(scene, time, options = {}) {
|
|
800
1027
|
const s = applyMotionControlValues(scene, options.values);
|
|
@@ -803,12 +1030,15 @@ function motionFrameAt(scene, time, options = {}) {
|
|
|
803
1030
|
const drawOn = options.draw !== false;
|
|
804
1031
|
const value = (id, p) => trackValue(timeline.tracks.get(motionTrackKey(id, p)), time);
|
|
805
1032
|
const animated = new Set(timeline.segments.map((seg) => seg.node));
|
|
1033
|
+
const clips = {};
|
|
806
1034
|
const visit = (node) => {
|
|
807
1035
|
let out = node.type === "group" ? { ...node, children: node.children.map(visit) } : node;
|
|
808
1036
|
if (!animated.has(node.id)) return out;
|
|
809
1037
|
const entry = nodes.get(node.id);
|
|
810
1038
|
if (!entry) return out;
|
|
811
1039
|
const id = node.id;
|
|
1040
|
+
const clip = motionNodeClips(id, timeline, time);
|
|
1041
|
+
if (clip.length) clips[id] = clip;
|
|
812
1042
|
const shape = shapeAt(entry, timeline, nodes, time);
|
|
813
1043
|
if (shape) {
|
|
814
1044
|
const { name, visible, opacity: opacity2, locked } = node;
|
|
@@ -816,7 +1046,7 @@ function motionFrameAt(scene, time, options = {}) {
|
|
|
816
1046
|
if (out.visible === void 0) delete out.visible;
|
|
817
1047
|
}
|
|
818
1048
|
const effectTracks = [...timeline.tracks.keys()].filter((k) => k.startsWith(`${id}\0effect:`));
|
|
819
|
-
if (effectTracks.length && _optionalChain([out, 'access',
|
|
1049
|
+
if (effectTracks.length && _optionalChain([out, 'access', _25 => _25.effects, 'optionalAccess', _26 => _26.length])) {
|
|
820
1050
|
out = {
|
|
821
1051
|
...out,
|
|
822
1052
|
effects: out.effects.map((effect) => {
|
|
@@ -849,7 +1079,7 @@ function motionFrameAt(scene, time, options = {}) {
|
|
|
849
1079
|
const start = Math.min(1, Math.max(0, typeof drawStart === "number" ? drawStart : 0));
|
|
850
1080
|
const end = Math.min(1, Math.max(0, typeof drawEnd === "number" ? drawEnd : 1));
|
|
851
1081
|
if (start > 0 || end < 1) {
|
|
852
|
-
const trim =
|
|
1082
|
+
const trim = _chunkYJLCBA7Vcjs.createVectorEffect.call(void 0, "trim", MOTION_DRAW_EFFECT_ID, { start: tidy(start * 100), end: tidy(end * 100) });
|
|
853
1083
|
p = { ...p, effects: [trim, ...(_nullishCoalesce(p.effects, () => ( []))).filter((e) => e.id !== MOTION_DRAW_EFFECT_ID)] };
|
|
854
1084
|
}
|
|
855
1085
|
}
|
|
@@ -863,8 +1093,8 @@ function motionFrameAt(scene, time, options = {}) {
|
|
|
863
1093
|
if (o >= 1) delete out.opacity;
|
|
864
1094
|
}
|
|
865
1095
|
const offset = options.offsets === false ? null : motionNodeOffset(entry, timeline, time, nodes);
|
|
866
|
-
if (offset && !
|
|
867
|
-
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);
|
|
868
1098
|
out = { ...out, transform: m };
|
|
869
1099
|
}
|
|
870
1100
|
return out;
|
|
@@ -872,11 +1102,19 @@ function motionFrameAt(scene, time, options = {}) {
|
|
|
872
1102
|
const document = { ...s.artwork, nodes: s.artwork.nodes.map(visit) };
|
|
873
1103
|
const cam = trackValue(timeline.tracks.get(motionTrackKey("", "camera")), time);
|
|
874
1104
|
const box = Array.isArray(cam) ? cam : [0, 0, s.artwork.width, s.artwork.height];
|
|
875
|
-
return { time, document, camera: { x: tidy(box[0]), y: tidy(box[1]), width: tidy(box[2]), height: tidy(box[3]) } };
|
|
1105
|
+
return { time, document, camera: { x: tidy(box[0]), y: tidy(box[1]), width: tidy(box[2]), height: tidy(box[3]) }, clips };
|
|
1106
|
+
}
|
|
1107
|
+
function motionClipAttrs(clip) {
|
|
1108
|
+
const { shape: _shape, ...attrs } = clip;
|
|
1109
|
+
return attrs;
|
|
876
1110
|
}
|
|
877
1111
|
function motionFrameToSvg(scene, time, options = {}) {
|
|
878
1112
|
const frame = motionFrameAt(scene, time, options);
|
|
879
|
-
|
|
1113
|
+
const prefix = _nullishCoalesce(options.idPrefix, () => ( "vg-"));
|
|
1114
|
+
const wrap = { ...options.wrap };
|
|
1115
|
+
for (const [id, clips] of Object.entries(frame.clips))
|
|
1116
|
+
wrap[id] = [..._nullishCoalesce(wrap[id], () => ( [])), ...clips.map((clip, i) => ({ clip: { id: `${prefix}reveal-${id}-${i}`, shape: clip.shape, attrs: motionClipAttrs(clip) } }))];
|
|
1117
|
+
return withViewBox(_chunkYJLCBA7Vcjs.vectorDocumentToSvg.call(void 0, frame.document, { ...options, wrap }), frame.camera);
|
|
880
1118
|
}
|
|
881
1119
|
function withViewBox(svg, box) {
|
|
882
1120
|
const f = (n) => String(Math.round(n * 1e3) / 1e3);
|
|
@@ -886,7 +1124,7 @@ function withViewBox(svg, box) {
|
|
|
886
1124
|
// src/animation-editor/check.ts
|
|
887
1125
|
var MOTION_MIN_CHANGE_MS = 120;
|
|
888
1126
|
var NUMERIC_PARAMS = (node, effectId) => {
|
|
889
|
-
const effect = _optionalChain([node, 'access',
|
|
1127
|
+
const effect = _optionalChain([node, 'access', _27 => _27.effects, 'optionalAccess', _28 => _28.find, 'call', _29 => _29((e) => e.id === effectId)]);
|
|
890
1128
|
if (!effect) return null;
|
|
891
1129
|
return Object.entries(effect.params).filter(([, v]) => typeof v === "number").map(([k]) => k);
|
|
892
1130
|
};
|
|
@@ -911,7 +1149,12 @@ function visibleAt(timeline, id, time, node) {
|
|
|
911
1149
|
if (node.visible === false) return false;
|
|
912
1150
|
if (num("opacity", _nullishCoalesce(node.opacity, () => ( 1))) <= 0.01) return false;
|
|
913
1151
|
if (Math.abs(num("scaleX", 1) * num("scaleY", 1)) <= 1e-4) return false;
|
|
914
|
-
const
|
|
1152
|
+
const clip = (p) => trackValue(timeline.tracks.get(motionTrackKey(id, p)), time);
|
|
1153
|
+
const rect = clip("clipRect");
|
|
1154
|
+
if (Array.isArray(rect) && (rect[2] <= 1e-4 || rect[3] <= 1e-4)) return false;
|
|
1155
|
+
const circle = clip("clipCircle");
|
|
1156
|
+
if (Array.isArray(circle) && circle[2] <= 1e-4) return false;
|
|
1157
|
+
const hasFill = node.type === "path" ? !!node.fill && node.fill !== "none" : _chunkYJLCBA7Vcjs.collectPathNodes.call(void 0, node).some((p) => p.fill && p.fill !== "none");
|
|
915
1158
|
return num("drawEnd", 1) > 0.01 || hasFill && num("fillOpacity", 1) > 0.01;
|
|
916
1159
|
}
|
|
917
1160
|
function checkMotionScene(scene) {
|
|
@@ -947,9 +1190,23 @@ function checkMotionScene(scene) {
|
|
|
947
1190
|
const timing = timeline.commands.find((c) => c.shot === shot.id && c.command === i);
|
|
948
1191
|
if (timing && timing.duration > 0 && timing.duration < MOTION_MIN_CHANGE_MS)
|
|
949
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
|
+
}
|
|
950
1201
|
if (command2.type === "follow" && known(command2.path, "unknown-path", "path", where) && nodes.get(command2.path).node.type !== "path")
|
|
951
1202
|
issues.push(motionIssue("not-a-path", `"${command2.path}" is a group; follow a single path node.`, { ...where, ids: [command2.path] }));
|
|
952
1203
|
if (command2.type === "morph") known(command2.into, "unknown-shape", "shape", where);
|
|
1204
|
+
const style = command2.type === "enter" || command2.type === "exit" ? command2.style : void 0;
|
|
1205
|
+
if (style === "wipe" || style === "iris" || style === "split") {
|
|
1206
|
+
for (const id of targets)
|
|
1207
|
+
if (!motionRevealPadding(nodes.get(id)))
|
|
1208
|
+
issues.push(motionIssue("reveal-no-box", `"${id}" has no width or height (a straight line), so a ${style} cannot clip it and it fades instead; use style "${command2.type === "enter" ? "draw" : "undraw"}" for a line.`, { ...where, ids: [id] }));
|
|
1209
|
+
}
|
|
953
1210
|
if (command2.type === "style") {
|
|
954
1211
|
for (const key of ["fill", "stroke"]) {
|
|
955
1212
|
const color = command2[key];
|
|
@@ -960,12 +1217,24 @@ function checkMotionScene(scene) {
|
|
|
960
1217
|
}
|
|
961
1218
|
for (const id of targets) {
|
|
962
1219
|
const node = nodes.get(id).node;
|
|
963
|
-
const paths = node.type === "path" ? [node] :
|
|
1220
|
+
const paths = node.type === "path" ? [node] : _chunkYJLCBA7Vcjs.collectPathNodes.call(void 0, node);
|
|
964
1221
|
if (paths.some((p) => p[key] !== void 0 && p[key] !== null && typeof p[key] !== "string"))
|
|
965
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] }));
|
|
966
1223
|
}
|
|
967
1224
|
}
|
|
968
1225
|
}
|
|
1226
|
+
if (command2.type === "color-pulse") {
|
|
1227
|
+
const key = _nullishCoalesce(command2.paint, () => ( "fill"));
|
|
1228
|
+
const color = _nullishCoalesce(command2.color, () => ( "#f59e0b"));
|
|
1229
|
+
if (_chunkA6PHCAGXcjs.normalizeTokenColor.call(void 0, color) === null) issues.push(motionIssue("invalid-color", `color "${color}" is not a colour this editor reads; use hex ("#0ea5e9"), rgb() or rgba().`, where));
|
|
1230
|
+
else
|
|
1231
|
+
for (const id of targets) {
|
|
1232
|
+
const node = nodes.get(id).node;
|
|
1233
|
+
const paths = node.type === "path" ? [node] : _chunkYJLCBA7Vcjs.collectPathNodes.call(void 0, node);
|
|
1234
|
+
if (paths.some((p) => typeof p[key] !== "string"))
|
|
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] }));
|
|
1236
|
+
}
|
|
1237
|
+
}
|
|
969
1238
|
if (command2.type === "effect") {
|
|
970
1239
|
for (const id of targets) {
|
|
971
1240
|
const node = nodes.get(id).node;
|
|
@@ -1016,7 +1285,7 @@ function checkMotionScene(scene) {
|
|
|
1016
1285
|
const cam = frame.camera;
|
|
1017
1286
|
const docIndex = indexMotionNodes(frame.document.nodes);
|
|
1018
1287
|
for (const id of command2.targets) {
|
|
1019
|
-
const b = _optionalChain([docIndex, 'access',
|
|
1288
|
+
const b = _optionalChain([docIndex, 'access', _30 => _30.get, 'call', _31 => _31(id), 'optionalAccess', _32 => _32.bounds]);
|
|
1020
1289
|
if (!b || reported.has(id) || !visibleAt(timeline, id, timing.end, nodes.get(id).node)) continue;
|
|
1021
1290
|
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;
|
|
1022
1291
|
if (outside) {
|
|
@@ -1086,6 +1355,11 @@ function applyOne(scene, edit) {
|
|
|
1086
1355
|
else if (edit.description !== void 0) next.description = edit.description;
|
|
1087
1356
|
if (edit.loop !== void 0) next.loop = edit.loop;
|
|
1088
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
|
+
}
|
|
1089
1363
|
if (edit.target === null) delete next.target;
|
|
1090
1364
|
else if (edit.target !== void 0) next.target = edit.target;
|
|
1091
1365
|
return next;
|
|
@@ -1193,9 +1467,9 @@ function applyMotionEdits(scene, edits) {
|
|
|
1193
1467
|
let next = scene;
|
|
1194
1468
|
for (let i = 0; i < list.length; i++) {
|
|
1195
1469
|
const edit = list[i];
|
|
1196
|
-
const prefix = list.length > 1 ? `op ${i + 1} (${_optionalChain([edit, 'optionalAccess',
|
|
1470
|
+
const prefix = list.length > 1 ? `op ${i + 1} (${_optionalChain([edit, 'optionalAccess', _33 => _33.op])}): ` : "";
|
|
1197
1471
|
if (!edit || !MOTION_EDIT_OPS.includes(edit.op))
|
|
1198
|
-
return { ok: false, error: `${prefix}Unknown op ${namedWithSuggestion(String(_optionalChain([edit, 'optionalAccess',
|
|
1472
|
+
return { ok: false, error: `${prefix}Unknown op ${namedWithSuggestion(String(_optionalChain([edit, 'optionalAccess', _34 => _34.op])), MOTION_EDIT_OPS)}. Nothing was changed.` };
|
|
1199
1473
|
try {
|
|
1200
1474
|
next = applyOne(next, edit);
|
|
1201
1475
|
} catch (error) {
|
|
@@ -1221,12 +1495,19 @@ var FLAIR_EASES = {
|
|
|
1221
1495
|
"in-out": (t) => t < 0.5 ? 4 * t * t * t : 1 - (-2 * t + 2) ** 3 / 2,
|
|
1222
1496
|
out: (t) => 1 - (1 - t) ** 3
|
|
1223
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
|
+
}
|
|
1224
1505
|
function flairEaseFor(ease) {
|
|
1225
1506
|
for (const name of ["linear", "out", "in-out"]) {
|
|
1226
1507
|
let worst = 0;
|
|
1227
1508
|
for (let i = 1; i < 40; i++) {
|
|
1228
1509
|
const t = i / 40;
|
|
1229
|
-
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)));
|
|
1230
1511
|
}
|
|
1231
1512
|
if (worst < 0.01) return name;
|
|
1232
1513
|
}
|
|
@@ -1235,11 +1516,11 @@ function flairEaseFor(ease) {
|
|
|
1235
1516
|
function flairCurveFor(ease, curves = true) {
|
|
1236
1517
|
const name = flairEaseFor(ease);
|
|
1237
1518
|
if (name || !curves) return name;
|
|
1238
|
-
const resolved =
|
|
1519
|
+
const resolved = _chunk2FHLHZALcjs.resolveEasing.call(void 0, ease);
|
|
1239
1520
|
if (resolved === "hold" || resolved.type !== "cubic-bezier") return void 0;
|
|
1240
1521
|
return [resolved.x1, resolved.y1, resolved.x2, resolved.y2];
|
|
1241
1522
|
}
|
|
1242
|
-
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);
|
|
1243
1524
|
var TRANSFORM_PROPS2 = ["x", "y", "rotation", "scaleX", "scaleY", "follow"];
|
|
1244
1525
|
var LINEAR_TRANSFORM = /* @__PURE__ */ new Set(["x", "y", "rotation", "scaleX", "scaleY"]);
|
|
1245
1526
|
function hex6(color) {
|
|
@@ -1249,7 +1530,24 @@ function hex6(color) {
|
|
|
1249
1530
|
function toFlairSvgMotion(scene, options = {}) {
|
|
1250
1531
|
const fps = _nullishCoalesce(_nullishCoalesce(options.fps, () => ( scene.fps)), () => ( 30));
|
|
1251
1532
|
const precision = _nullishCoalesce(options.precision, () => ( 3));
|
|
1252
|
-
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;
|
|
1253
1551
|
const round2 = (n) => Math.round(n * 10 ** precision) / 10 ** precision;
|
|
1254
1552
|
const f = (n) => String(round2(n));
|
|
1255
1553
|
const timeline = compileMotionScene(scene);
|
|
@@ -1264,35 +1562,81 @@ function toFlairSvgMotion(scene, options = {}) {
|
|
|
1264
1562
|
};
|
|
1265
1563
|
const animatedNodes = new Set(timeline.segments.map((s) => s.node).filter(Boolean));
|
|
1266
1564
|
const pathsSeen = /* @__PURE__ */ new Set();
|
|
1565
|
+
const wrap = {};
|
|
1566
|
+
const routes = [];
|
|
1567
|
+
const follows = [];
|
|
1568
|
+
const usedIds = new Set(nodes.keys());
|
|
1569
|
+
const freshId = (base) => {
|
|
1570
|
+
let id = base;
|
|
1571
|
+
for (let n = 2; usedIds.has(id); n++) id = `${base}-${n}`;
|
|
1572
|
+
usedIds.add(id);
|
|
1573
|
+
return id;
|
|
1574
|
+
};
|
|
1267
1575
|
for (const id of animatedNodes) {
|
|
1268
1576
|
const entry = nodes.get(id);
|
|
1269
1577
|
const node = entry.node;
|
|
1578
|
+
const drawn = [...entry.ancestors.map((a) => nodes.get(a).node), node].every((n) => n.visible !== false);
|
|
1579
|
+
for (const [p, name] of [["clipRect", "reveal"], ["clipCircle", "iris"]]) {
|
|
1580
|
+
if (!drawn || !timeline.tracks.has(motionTrackKey(id, p))) continue;
|
|
1581
|
+
const wrapper = freshId(`${id}--${name}`);
|
|
1582
|
+
const clipId = freshId(`${wrapper}-clip`);
|
|
1583
|
+
const shapeId = freshId(`${wrapper}-clip-shape`);
|
|
1584
|
+
const clipAt = (t) => motionNodeClips(id, timeline, t).find((c) => c.shape === (p === "clipRect" ? "rect" : "circle"));
|
|
1585
|
+
const rest = clipAt(0);
|
|
1586
|
+
(wrap[id] ??= []).push({ id: wrapper, clip: { id: clipId, shapeId, shape: rest.shape, attrs: motionClipAttrs(rest) } });
|
|
1587
|
+
channels.push({ id: shapeId, kind: "attrs", attrs: Object.keys(motionClipAttrs(rest)), segments: [...track(id, p)], exact: () => true, value: (t) => Object.values(motionClipAttrs(clipAt(t))).map((v) => Math.round(v * 1e4) / 1e4) });
|
|
1588
|
+
}
|
|
1589
|
+
const followSegments = track(id, "follow");
|
|
1590
|
+
if (drawn && followSegments.length) {
|
|
1591
|
+
const ids = /* @__PURE__ */ new Map();
|
|
1592
|
+
for (const seg of followSegments) {
|
|
1593
|
+
if (!seg.path || ids.has(seg.path)) continue;
|
|
1594
|
+
const route = motionFollowRoute(entry, nodes.get(seg.path));
|
|
1595
|
+
if (!route) continue;
|
|
1596
|
+
const routeId = freshId(`${id}--follow-route`);
|
|
1597
|
+
ids.set(seg.path, routeId);
|
|
1598
|
+
routes.push(`<path id="${routeId}" d="${_chunkO2UH7LZEcjs.bezierPathToSvg.call(void 0, route, { precision })}" fill="none"/>`);
|
|
1599
|
+
}
|
|
1600
|
+
if (ids.size) {
|
|
1601
|
+
const wrapper = freshId(`${id}--follow`);
|
|
1602
|
+
const centre = freshId(`${id}--follow-centre`);
|
|
1603
|
+
const c = entry.center;
|
|
1604
|
+
const at = _optionalChain([motionNodeOffset, 'call', _35 => _35(entry, timeline, 0, nodes), 'optionalAccess', _36 => _36.follow]);
|
|
1605
|
+
const placed = at ? `translate(${f(at.x)} ${f(at.y)})${at.angle ? ` rotate(${f(at.angle)})` : ""}` : void 0;
|
|
1606
|
+
(wrap[id] ??= []).push({ id: wrapper, ...placed ? { transform: placed } : {} }, { id: centre, transform: `translate(${f(-c.x)} ${f(-c.y)})` });
|
|
1607
|
+
follows.push({ id, wrapper, routes: ids, segments: followSegments.filter((seg) => seg.path && ids.has(seg.path)) });
|
|
1608
|
+
}
|
|
1609
|
+
}
|
|
1270
1610
|
if (timeline.tracks.has(motionTrackKey(id, "opacity")))
|
|
1271
|
-
channels.push({ id, kind: "attr", attr: "opacity", segments: [...track(id, "opacity")], exact:
|
|
1272
|
-
const
|
|
1611
|
+
channels.push({ id, kind: "attr", attr: "opacity", segments: [...track(id, "opacity")], exact: plays, value: (t) => round2(Math.min(1, Math.max(0, Number(trackValue(track(id, "opacity"), t))))) });
|
|
1612
|
+
const followed = follows.some((x) => x.id === id);
|
|
1613
|
+
const transformSegments = TRANSFORM_PROPS2.flatMap((p) => followed && p === "follow" ? [] : track(id, p));
|
|
1273
1614
|
if (transformSegments.length) {
|
|
1274
1615
|
const base = node.transform ? ` matrix(${node.transform.map(f).join(" ")})` : "";
|
|
1275
1616
|
const c = entry.center;
|
|
1617
|
+
const transformAt = (t, raw) => {
|
|
1618
|
+
const o = motionNodeOffset(entry, timeline, t, nodes, raw, !followed);
|
|
1619
|
+
return `translate(${f(o.dx)} ${f(o.dy)}) rotate(${f(o.rotation)} ${f(c.x)} ${f(c.y)}) translate(${f(c.x)} ${f(c.y)}) scale(${f(o.scaleX)} ${f(o.scaleY)}) translate(${f(-c.x)} ${f(-c.y)})${base}`;
|
|
1620
|
+
};
|
|
1276
1621
|
channels.push({
|
|
1277
1622
|
id,
|
|
1278
1623
|
kind: "attr",
|
|
1279
1624
|
attr: "transform",
|
|
1280
1625
|
segments: transformSegments,
|
|
1281
|
-
exact: (s) => LINEAR_TRANSFORM.has(s.property) &&
|
|
1626
|
+
exact: (s) => LINEAR_TRANSFORM.has(s.property) && plays(s),
|
|
1282
1627
|
overshoot: true,
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
}
|
|
1628
|
+
// x, y and the angle are the transform's numbers 0, 1 and 2 (`MotionSegment.index`).
|
|
1629
|
+
value: (t) => transformAt(t, false),
|
|
1630
|
+
raw: (t) => transformAt(t, true)
|
|
1287
1631
|
});
|
|
1288
1632
|
}
|
|
1289
|
-
const paths = node.type === "path" ? [node] :
|
|
1633
|
+
const paths = node.type === "path" ? [node] : _chunkYJLCBA7Vcjs.collectPathNodes.call(void 0, node);
|
|
1290
1634
|
for (const path of paths) {
|
|
1291
1635
|
if (pathsSeen.has(path.id)) continue;
|
|
1292
1636
|
pathsSeen.add(path.id);
|
|
1293
1637
|
const paint = (p, attr) => {
|
|
1294
1638
|
const from = owner(path, p);
|
|
1295
|
-
if (from) channels.push({ id: path.id, kind: "attr", attr, segments: [...track(from, p)], exact: () =>
|
|
1639
|
+
if (from) channels.push({ id: path.id, kind: "attr", attr, segments: [...track(from, p)], exact: plays, value: (t) => hex6(trackValue(track(from, p), t)), raw: (t) => hex6(trackValue(track(from, p), t, true)) });
|
|
1296
1640
|
};
|
|
1297
1641
|
paint("fill", "fill");
|
|
1298
1642
|
paint("stroke", "stroke");
|
|
@@ -1308,7 +1652,7 @@ function toFlairSvgMotion(scene, options = {}) {
|
|
|
1308
1652
|
if (shapeSegments.length) {
|
|
1309
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").`);
|
|
1310
1654
|
else {
|
|
1311
|
-
const inverse =
|
|
1655
|
+
const inverse = _chunkYJLCBA7Vcjs.invertMatrix.call(void 0, _chunkYJLCBA7Vcjs.nodeMatrix.call(void 0, node));
|
|
1312
1656
|
channels.push({
|
|
1313
1657
|
id,
|
|
1314
1658
|
kind: "attr",
|
|
@@ -1317,9 +1661,9 @@ function toFlairSvgMotion(scene, options = {}) {
|
|
|
1317
1661
|
exact: () => false,
|
|
1318
1662
|
value: (t) => {
|
|
1319
1663
|
const frame = motionFrameAt(scene, t, { draw: false, offsets: false });
|
|
1320
|
-
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]));
|
|
1321
1665
|
if (!now || now.type !== "path") return "";
|
|
1322
|
-
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);
|
|
1323
1667
|
return _chunkO2UH7LZEcjs.bezierPathToSvg.call(void 0, bezier, { precision: Math.min(precision, 2) });
|
|
1324
1668
|
}
|
|
1325
1669
|
});
|
|
@@ -1331,15 +1675,30 @@ function toFlairSvgMotion(scene, options = {}) {
|
|
|
1331
1675
|
const animate = [];
|
|
1332
1676
|
let sampled = 0;
|
|
1333
1677
|
const same = (a, b) => JSON.stringify(a) === JSON.stringify(b);
|
|
1334
|
-
const emit = (channel, delay, ms, from, to, ease) => {
|
|
1335
|
-
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
|
+
};
|
|
1336
1688
|
if (channel.kind === "view") animate.push({ ...base, view: to });
|
|
1337
1689
|
else if (channel.kind === "draw") animate.push({ ...base, draw: [from, to] });
|
|
1338
|
-
else
|
|
1690
|
+
else if (channel.kind === "attrs") {
|
|
1691
|
+
const [a, b] = [from, to];
|
|
1692
|
+
const attrs = Object.fromEntries(channel.attrs.flatMap((name, k) => a[k] !== b[k] ? [[name, [a[k], b[k]]]] : []));
|
|
1693
|
+
animate.push({ ...base, attrs });
|
|
1694
|
+
} else animate.push({ ...base, attrs: { [channel.attr]: [from, to] } });
|
|
1339
1695
|
};
|
|
1340
1696
|
for (const channel of channels) {
|
|
1341
1697
|
const cuts = channel.segments.filter((s) => s.end <= s.start);
|
|
1342
|
-
for (const cut of cuts)
|
|
1698
|
+
for (const cut of cuts) {
|
|
1699
|
+
const [from, to] = [channel.value(Math.max(0, cut.start - 1e-3)), channel.value(cut.start)];
|
|
1700
|
+
if (cut.start <= 0 || !same(from, to) || channel.kind === "view") emit(channel, cut.start, 0, from, to, "linear");
|
|
1701
|
+
}
|
|
1343
1702
|
const times = [...new Set(channel.segments.flatMap((s) => [s.start, s.end]))].sort((a, b) => a - b);
|
|
1344
1703
|
for (let i = 0; i + 1 < times.length; i++) {
|
|
1345
1704
|
const a = times[i];
|
|
@@ -1347,13 +1706,14 @@ function toFlairSvgMotion(scene, options = {}) {
|
|
|
1347
1706
|
const active = channel.segments.filter((s) => s.start < b && s.end > a);
|
|
1348
1707
|
if (!active.length) continue;
|
|
1349
1708
|
const first = active[0];
|
|
1350
|
-
const
|
|
1351
|
-
const
|
|
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;
|
|
1352
1712
|
const ease = curve && (!overshoots(curve) || channel.overshoot) ? curve : void 0;
|
|
1353
1713
|
if (ease) {
|
|
1354
1714
|
const from = channel.value(a);
|
|
1355
|
-
const to = channel.value(b);
|
|
1356
|
-
if (!same(from, to) || channel.kind === "view") emit(channel, a, b - a, from, to, ease);
|
|
1715
|
+
const to = first.behavior ? (_nullishCoalesce(channel.raw, () => ( channel.value)))(b) : channel.value(b);
|
|
1716
|
+
if (!same(from, to) || channel.kind === "view") emit(channel, a, b - a, from, to, ease, first);
|
|
1357
1717
|
continue;
|
|
1358
1718
|
}
|
|
1359
1719
|
const n = Math.max(1, Math.ceil((b - a) * fps / 1e3));
|
|
@@ -1370,11 +1730,33 @@ function toFlairSvgMotion(scene, options = {}) {
|
|
|
1370
1730
|
}
|
|
1371
1731
|
}
|
|
1372
1732
|
}
|
|
1733
|
+
for (const follow of follows) {
|
|
1734
|
+
const starts = follow.segments.map((seg) => seg.start);
|
|
1735
|
+
for (const seg of follow.segments) {
|
|
1736
|
+
const along = { id: follow.wrapper, along: follow.routes.get(seg.path), ...seg.rotate ? { orient: true } : {} };
|
|
1737
|
+
const progress = (t) => round2(Math.min(1, Math.max(0, Number(segmentValue(seg, t)))));
|
|
1738
|
+
const curve = seg.end > seg.start ? plays(seg) ? easeOf(seg) : void 0 : "linear";
|
|
1739
|
+
if (curve && !(overshoots(curve) && seg.rotate)) {
|
|
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])] });
|
|
1742
|
+
continue;
|
|
1743
|
+
}
|
|
1744
|
+
const until = Math.min(seg.end, ...starts.filter((t) => t > seg.start));
|
|
1745
|
+
const n = Math.max(1, Math.ceil((until - seg.start) * fps / 1e3));
|
|
1746
|
+
for (let k = 1; k <= n; k++) {
|
|
1747
|
+
const t0 = seg.start + (until - seg.start) * (k - 1) / n;
|
|
1748
|
+
const t1 = seg.start + (until - seg.start) * k / n;
|
|
1749
|
+
animate.push({ ...along, ...t0 > 0 ? { delay: round2(t0) } : {}, ms: round2(t1 - t0), ease: "linear", at: [progress(t0), progress(t1)] });
|
|
1750
|
+
sampled++;
|
|
1751
|
+
}
|
|
1752
|
+
}
|
|
1753
|
+
}
|
|
1373
1754
|
animate.sort((x, y) => (_nullishCoalesce(x.delay, () => ( 0))) - (_nullishCoalesce(y.delay, () => ( 0))));
|
|
1374
|
-
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 });
|
|
1756
|
+
if (routes.length) svg = svg.replace(/^(<svg[^>]*>)/, `$1<defs>${routes.join("")}</defs>`);
|
|
1375
1757
|
const lastChange = animate.reduce((m, a) => Math.max(m, (_nullishCoalesce(a.delay, () => ( 0))) + (_nullishCoalesce(a.ms, () => ( 0)))), 0);
|
|
1376
1758
|
let motion = { svg, animate };
|
|
1377
|
-
if (_optionalChain([scene, 'access',
|
|
1759
|
+
if (_optionalChain([scene, 'access', _43 => _43.controls, 'optionalAccess', _44 => _44.length])) {
|
|
1378
1760
|
const declarations = scene.controls.map((c) => `{{${c.name}: color default=${c.default.toUpperCase()}${c.label ? ` label='${c.label.replace(/'/g, "\u2019")}'` : ""}}}`).join(" ");
|
|
1379
1761
|
const swap = (text) => scene.controls.reduce((s, c) => s.replace(new RegExp(`${c.default}(?![0-9a-fA-F])`, "gi"), `{{${c.name}}}`), text);
|
|
1380
1762
|
svg = svg.replace(/^(<svg[^>]*>)/, `$1<!-- ${declarations} -->`);
|
|
@@ -1396,7 +1778,7 @@ function toFlairVectorEffect(scene, options = {}) {
|
|
|
1396
1778
|
tags: _nullishCoalesce(options.tags, () => ( [])),
|
|
1397
1779
|
motion: out.motion,
|
|
1398
1780
|
hold: out.hold,
|
|
1399
|
-
..._optionalChain([scene, 'access',
|
|
1781
|
+
..._optionalChain([scene, 'access', _45 => _45.presets, 'optionalAccess', _46 => _46.length]) ? { presets: scene.presets } : {},
|
|
1400
1782
|
...options.trigger ? { trigger: options.trigger } : {}
|
|
1401
1783
|
};
|
|
1402
1784
|
}
|
|
@@ -1414,6 +1796,196 @@ function toFlairEffectDocument(scene, options = {}) {
|
|
|
1414
1796
|
};
|
|
1415
1797
|
}
|
|
1416
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
|
+
|
|
1417
1989
|
// src/animation-editor/controller.ts
|
|
1418
1990
|
var BLANK = { width: 800, height: 450, background: "#ffffff", nodes: [] };
|
|
1419
1991
|
var AnimationEditorController = (_class = class {
|
|
@@ -1436,7 +2008,7 @@ var AnimationEditorController = (_class = class {
|
|
|
1436
2008
|
this.issues = checkMotionScene(this.scene);
|
|
1437
2009
|
this.readOnly = _nullishCoalesce(options.readOnly, () => ( false));
|
|
1438
2010
|
this.onChange = options.onChange;
|
|
1439
|
-
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")) });
|
|
1440
2012
|
this.state = this.computeState();
|
|
1441
2013
|
this.connect();
|
|
1442
2014
|
}
|
|
@@ -1481,7 +2053,7 @@ var AnimationEditorController = (_class = class {
|
|
|
1481
2053
|
time: p.time,
|
|
1482
2054
|
playing: p.playing,
|
|
1483
2055
|
loop: p.loop,
|
|
1484
|
-
currentShot: _nullishCoalesce(_optionalChain([motionShotAt, 'call',
|
|
2056
|
+
currentShot: _nullishCoalesce(_optionalChain([motionShotAt, 'call', _53 => _53(timeline, p.time), 'optionalAccess', _54 => _54.id]), () => ( null)),
|
|
1485
2057
|
selection: this.selection,
|
|
1486
2058
|
readOnly: this.readOnly,
|
|
1487
2059
|
error: this.error,
|
|
@@ -1520,7 +2092,7 @@ var AnimationEditorController = (_class = class {
|
|
|
1520
2092
|
this.applying = false;
|
|
1521
2093
|
}
|
|
1522
2094
|
this.notify();
|
|
1523
|
-
_optionalChain([this, 'access',
|
|
2095
|
+
_optionalChain([this, 'access', _55 => _55.onChange, 'optionalCall', _56 => _56(scene)]);
|
|
1524
2096
|
}
|
|
1525
2097
|
commit(label, next) {
|
|
1526
2098
|
const before = this.scene;
|
|
@@ -1660,10 +2232,29 @@ var MS = (what) => ({ type: "number", minimum: 0, description: `${what} in milli
|
|
|
1660
2232
|
var FRACTION = (what) => ({ type: "number", minimum: 0, maximum: 1, description: what });
|
|
1661
2233
|
var POINT = (what) => object({ x: { type: "number", description: "Document units." }, y: { type: "number", description: "Document units." } }, ["x", "y"], what);
|
|
1662
2234
|
var EASE = {
|
|
1663
|
-
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.`,
|
|
1664
2236
|
anyOf: [
|
|
1665
|
-
{ type: "string", enum: [...
|
|
1666
|
-
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
|
+
)
|
|
1667
2258
|
]
|
|
1668
2259
|
};
|
|
1669
2260
|
var BASE = {
|
|
@@ -1673,26 +2264,36 @@ var BASE = {
|
|
|
1673
2264
|
stagger: MS("Start each further target this much later (default 0)"),
|
|
1674
2265
|
with: { type: "string", enum: ["after", "previous"], description: '"after" (default) starts when the commands before it finish; "previous" starts together with the command before.' },
|
|
1675
2266
|
ease: EASE,
|
|
1676
|
-
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.` }
|
|
1677
2271
|
};
|
|
1678
2272
|
var command = (type, description, extra = {}, required = []) => object({ type: { const: type, description }, ...BASE, ...extra }, ["type", "targets", ...required], description);
|
|
1679
2273
|
var MOTION_COMMAND_SCHEMA = {
|
|
1680
2274
|
description: `A command (one of ${MOTION_COMMAND_TYPES.join(", ")}). Commands in a shot play one after another unless "with": "previous".`,
|
|
1681
2275
|
oneOf: [
|
|
1682
2276
|
command("enter", "Brings hidden targets in; they are hidden before this shot.", {
|
|
1683
|
-
style: {
|
|
1684
|
-
|
|
1685
|
-
|
|
2277
|
+
style: {
|
|
2278
|
+
type: "string",
|
|
2279
|
+
enum: [...MOTION_ENTER_STYLES],
|
|
2280
|
+
description: "fade (default), pop (scales up past full size and settles), grow (from nothing), slide, draw (outline drawn on, then the fill fades in), zoom (settles from larger), rise (rises a short way, overshooting a touch), or a reveal through a clip over the target's box: wipe (behind a travelling edge), iris (from the centre outwards) or split (from a line through the middle). Reveals need a box with a width and a height (not a lone straight line)."
|
|
2281
|
+
},
|
|
2282
|
+
direction: { type: "string", enum: [...MOTION_DIRECTIONS], description: "For slide: direction of travel (default up). For wipe: the way the edge travels (default right, revealing from the left)." },
|
|
2283
|
+
distance: { type: "number", description: "For slide (default 40) and rise (default 20): document units." },
|
|
2284
|
+
scale: { type: "number", minimum: 0, description: "For pop (default 0.5) and zoom (default 1.6): the starting scale (1 = rest size)." },
|
|
2285
|
+
shape: { type: "string", enum: [...MOTION_IRIS_SHAPES], description: "For iris: open through a circle (default; an ellipse on a box that is not square) or a box." },
|
|
2286
|
+
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." }
|
|
1686
2287
|
}),
|
|
1687
2288
|
command("exit", "Takes targets out.", {
|
|
1688
|
-
style: { type: "string", enum: [...MOTION_EXIT_STYLES], description: "fade (default), shrink, slide or
|
|
1689
|
-
direction: { type: "string", enum: [...MOTION_DIRECTIONS], description: "For slide: direction of travel (default down)." },
|
|
2289
|
+
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)." },
|
|
2290
|
+
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)." },
|
|
1690
2291
|
distance: { type: "number", description: "For slide: document units (default 40)." }
|
|
1691
2292
|
}),
|
|
1692
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).") }),
|
|
1693
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.") }),
|
|
1694
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." } }),
|
|
1695
|
-
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"]),
|
|
1696
2297
|
command("fade", "Changes opacity.", { to: FRACTION("Opacity at the end (0\u20131).") }, ["to"]),
|
|
1697
2298
|
command("style", "Blends solid fill / stroke colours (in OKLab) and stroke width. On a group it changes every path in it.", {
|
|
1698
2299
|
fill: { type: "string", description: "CSS colour (hex, rgb(), rgba())." },
|
|
@@ -1702,12 +2303,30 @@ var MOTION_COMMAND_SCHEMA = {
|
|
|
1702
2303
|
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"]),
|
|
1703
2304
|
command("follow", "Moves targets along a path node, centre on the path.", {
|
|
1704
2305
|
path: { type: "string", minLength: 1, description: "Id of a path node (it can be hidden)." },
|
|
1705
|
-
rotate: { type: "boolean", description: "Turn with the path (default false)." },
|
|
2306
|
+
rotate: { type: "boolean", description: "Turn with the path (default false); moves and shakes made meanwhile turn with it too." },
|
|
1706
2307
|
from: FRACTION("Where to start along the path (default 0)."),
|
|
1707
2308
|
to: FRACTION("Where to end along the path (default 1).")
|
|
1708
2309
|
}, ["path"]),
|
|
1709
2310
|
command("pulse", "Scales up and back to draw the eye.", { amount: { type: "number", description: "Extra scale at the peak (default 0.15)." }, count: { type: "integer", minimum: 1, description: "Pulses (default 1)." } }),
|
|
1710
|
-
command("shake", "Shakes side to side
|
|
2311
|
+
command("shake", "Shakes side to side (or up and down), settling.", {
|
|
2312
|
+
amount: { type: "number", description: "Document units (default 8)." },
|
|
2313
|
+
count: { type: "integer", minimum: 1, description: "Shakes (default 3)." },
|
|
2314
|
+
axis: { type: "string", enum: ["horizontal", "vertical"], description: "Default horizontal." }
|
|
2315
|
+
}),
|
|
2316
|
+
command("teeter", "Rocks about the centre either way, settling.", { angle: { type: "number", description: "Degrees either way (default 8)." }, count: { type: "integer", minimum: 1, description: "Rocks (default 3)." } }),
|
|
2317
|
+
command("wave", "Bobs up and down without settling, like something floating (default 1000 ms).", { amount: { type: "number", description: "Height in document units (default 6)." }, count: { type: "integer", minimum: 1, description: "Bobs (default 2)." } }),
|
|
2318
|
+
command("wiggle", "Wanders about its place as smooth seeded noise, easing out of and back to rest (default 1500 ms). The same seed always wanders the same way.", {
|
|
2319
|
+
amount: { type: "number", description: "Up to about this far, in document units (default 6)." },
|
|
2320
|
+
frequency: { type: "number", exclusiveMinimum: 0, description: "Wanders per second, in Hz (default 3)." },
|
|
2321
|
+
octaves: { type: "integer", minimum: 1, maximum: 8, description: "Layers of finer detail (default 2)." },
|
|
2322
|
+
rotate: { type: "number", description: "Also turns about the centre by up to about this many degrees (default 0)." },
|
|
2323
|
+
seed: { type: "integer", minimum: 0, description: "Default: one picked from the target id, so each target wanders its own way." }
|
|
2324
|
+
}),
|
|
2325
|
+
command("color-pulse", "Tints solid paint toward a colour and back (mixed in OKLab). On a group it tints every path in it.", {
|
|
2326
|
+
color: { type: "string", description: 'The tint, a CSS colour (default "#f59e0b").' },
|
|
2327
|
+
paint: { type: "string", enum: ["fill", "stroke"], description: "Which paint (default fill)." },
|
|
2328
|
+
count: { type: "integer", minimum: 1, description: "Pulses (default 1)." }
|
|
2329
|
+
}),
|
|
1711
2330
|
command("effect", "Tweens a numeric parameter of a live path effect on the targets (twist angle, wiggle time, zig zag size, trim offset\u2026).", {
|
|
1712
2331
|
effect: { type: "string", minLength: 1, description: `Effect id from get_animation's element "effects".` },
|
|
1713
2332
|
param: { type: "string", minLength: 1, description: "A numeric parameter of that effect (listed with it)." },
|
|
@@ -1720,11 +2339,12 @@ var MOTION_COMMAND_SCHEMA = {
|
|
|
1720
2339
|
)
|
|
1721
2340
|
]
|
|
1722
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)." };
|
|
1723
2343
|
var CAMERA = {
|
|
1724
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.`,
|
|
1725
2345
|
anyOf: [
|
|
1726
|
-
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"]),
|
|
1727
|
-
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"])
|
|
1728
2348
|
]
|
|
1729
2349
|
};
|
|
1730
2350
|
var SHOT_FIELDS = {
|
|
@@ -1750,6 +2370,7 @@ var SETTINGS = {
|
|
|
1750
2370
|
description: { type: ["string", "null"], description: "What it shows; null clears." },
|
|
1751
2371
|
loop: { type: "string", enum: ["none", "loop", "ping-pong"], description: "Playback past the end (default none)." },
|
|
1752
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).' },
|
|
1753
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." }
|
|
1754
2375
|
};
|
|
1755
2376
|
var EDIT_OP = {
|
|
@@ -1804,7 +2425,8 @@ function describeMotionShots(scene) {
|
|
|
1804
2425
|
...shot.hold !== void 0 ? { hold: shot.hold } : {},
|
|
1805
2426
|
commands: shot.run.map((c, index) => {
|
|
1806
2427
|
const ct = timeline.commands.find((x) => x.shotIndex === i && x.command === index);
|
|
1807
|
-
|
|
2428
|
+
const primitive = motionFlairPrimitive(c);
|
|
2429
|
+
return { index, ...c, start: ct.start, end: ct.end, ...primitive ? { flairPrimitive: primitive } : {} };
|
|
1808
2430
|
})
|
|
1809
2431
|
};
|
|
1810
2432
|
});
|
|
@@ -1840,7 +2462,7 @@ function createAnimationEditorToolProvider(controller, options = {}) {
|
|
|
1840
2462
|
const getAnimation = {
|
|
1841
2463
|
name: "get_animation",
|
|
1842
2464
|
title: "Read the animation",
|
|
1843
|
-
description:
|
|
2465
|
+
description: `Returns the vector animation: its name, artwork size, length (ms), loop mode, the artwork's elements (id, type, name, parent, bounds, solid paints, live effects with their numeric parameters), every shot with its commands and their start/end times (and, where Flair has one, the Flair motion primitive a command plays as "flairPrimitive", e.g. "entrance.pop"), Flair controls, and a summary of issues. Use the ids it returns with the write tools.`,
|
|
1844
2466
|
inputSchema: object({ includeElements: { type: "boolean", description: "Include the artwork's elements (default true)." } }),
|
|
1845
2467
|
annotations: { title: "Read the animation", ...READ },
|
|
1846
2468
|
handler: ({ includeElements = true }) => {
|
|
@@ -1927,16 +2549,20 @@ function createAnimationEditorToolProvider(controller, options = {}) {
|
|
|
1927
2549
|
}
|
|
1928
2550
|
};
|
|
1929
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.";
|
|
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.";
|
|
1930
2554
|
const exportFlairMotion = {
|
|
1931
2555
|
name: "export_flair_motion",
|
|
1932
2556
|
title: "Export as Flair vector motion",
|
|
1933
2557
|
description: "Returns the animation as a Flair SvgMotion ({ svg, animate }) that Flair's SvgPlayer plays, plus its hold, how many changes were sampled into linear steps, and notes on anything not carried over.",
|
|
1934
2558
|
inputSchema: object({
|
|
1935
2559
|
fps: { type: "number", minimum: 1, maximum: 120, description: "Sampling rate for changes Flair cannot express exactly (default: the scene's fps, else 30)." },
|
|
1936
|
-
curves: { type: "boolean", description: CURVES_DESCRIPTION }
|
|
2560
|
+
curves: { type: "boolean", description: CURVES_DESCRIPTION },
|
|
2561
|
+
behaviors: { type: "boolean", description: BEHAVIORS_DESCRIPTION },
|
|
2562
|
+
flair: { type: "string", pattern: "^\\d+\\.\\d+(\\.\\d+)?$", description: FLAIR_DESCRIPTION }
|
|
1937
2563
|
}),
|
|
1938
2564
|
annotations: { title: "Export as Flair vector motion", ...READ },
|
|
1939
|
-
handler: ({ fps, curves }) => controller.toFlairMotion({ ...fps ? { fps } : {}, ...curves === void 0 ? {} : { curves } })
|
|
2565
|
+
handler: ({ fps, curves, behaviors, flair }) => controller.toFlairMotion({ ...fps ? { fps } : {}, ...curves === void 0 ? {} : { curves }, ...behaviors === void 0 ? {} : { behaviors }, ...flair ? { flair } : {} })
|
|
1940
2566
|
};
|
|
1941
2567
|
const exportFlairEffect = {
|
|
1942
2568
|
name: "export_flair_effect",
|
|
@@ -1948,7 +2574,9 @@ function createAnimationEditorToolProvider(controller, options = {}) {
|
|
|
1948
2574
|
description: { type: "string", description: "One sentence (default: the scene's description)." },
|
|
1949
2575
|
category: { type: "string", minLength: 1, description: "Flair catalog category, e.g. icon, loader, ui, data, text, morph (default icon)." },
|
|
1950
2576
|
tags: { type: "array", items: { type: "string" }, description: "Search tags (default none)." },
|
|
1951
|
-
curves: { type: "boolean", description: CURVES_DESCRIPTION }
|
|
2577
|
+
curves: { type: "boolean", description: CURVES_DESCRIPTION },
|
|
2578
|
+
behaviors: { type: "boolean", description: BEHAVIORS_DESCRIPTION },
|
|
2579
|
+
flair: { type: "string", pattern: "^\\d+\\.\\d+(\\.\\d+)?$", description: FLAIR_DESCRIPTION }
|
|
1952
2580
|
}),
|
|
1953
2581
|
annotations: { title: "Export as a Flair effect", ...READ },
|
|
1954
2582
|
handler: (args) => controller.toFlairEffect(args)
|
|
@@ -1962,13 +2590,41 @@ function createAnimationEditorToolProvider(controller, options = {}) {
|
|
|
1962
2590
|
handler: ({ svg }) => {
|
|
1963
2591
|
let artwork;
|
|
1964
2592
|
try {
|
|
1965
|
-
artwork =
|
|
2593
|
+
artwork = _chunkYJLCBA7Vcjs.parseVectorSvg.call(void 0, svg);
|
|
1966
2594
|
} catch (error) {
|
|
1967
2595
|
throw new Error(`The SVG could not be read: ${error.message}. Nothing was changed.`);
|
|
1968
2596
|
}
|
|
1969
2597
|
return apply({ op: "set_artwork", artwork }, "Replace artwork", (s) => ({ elements: describeMotionElements(s) }));
|
|
1970
2598
|
}
|
|
1971
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
|
+
};
|
|
1972
2628
|
const insertShots = {
|
|
1973
2629
|
name: "insert_shots",
|
|
1974
2630
|
title: "Add shots",
|
|
@@ -2015,7 +2671,7 @@ function createAnimationEditorToolProvider(controller, options = {}) {
|
|
|
2015
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"]),
|
|
2016
2672
|
annotations: { title: "Add commands to a shot", ...WRITE },
|
|
2017
2673
|
handler: ({ shot, commands, at }) => {
|
|
2018
|
-
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));
|
|
2019
2675
|
const first = _nullishCoalesce(at, () => ( before));
|
|
2020
2676
|
return apply({ op: "add_commands", shot, commands, at }, "Add commands", (s) => {
|
|
2021
2677
|
const shots = describeMotionShots(s);
|
|
@@ -2051,7 +2707,7 @@ function createAnimationEditorToolProvider(controller, options = {}) {
|
|
|
2051
2707
|
const setSettings = {
|
|
2052
2708
|
name: "set_animation_settings",
|
|
2053
2709
|
title: "Change animation settings",
|
|
2054
|
-
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.",
|
|
2055
2711
|
inputSchema: object(SETTINGS),
|
|
2056
2712
|
annotations: { title: "Change animation settings", ...WRITE_IDEMPOTENT },
|
|
2057
2713
|
handler: (args) => apply({ op: "set_settings", ...args }, "Change settings")
|
|
@@ -2130,7 +2786,7 @@ function createAnimationEditorToolProvider(controller, options = {}) {
|
|
|
2130
2786
|
}
|
|
2131
2787
|
};
|
|
2132
2788
|
const reads = [getAnimation, getShot, findElements, checkAnimation, renderFrame, exportFlairMotion, exportFlairEffect, seekAnimation];
|
|
2133
|
-
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];
|
|
2134
2790
|
return {
|
|
2135
2791
|
id: _nullishCoalesce(options.id, () => ( "animation")),
|
|
2136
2792
|
namespace: options.namespace,
|
|
@@ -2160,12 +2816,19 @@ How it plays
|
|
|
2160
2816
|
|
|
2161
2817
|
Commands
|
|
2162
2818
|
- enter (fade, pop, grow, slide, draw) and exit (fade, shrink, slide, undraw) bring things in and out.
|
|
2819
|
+
- 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.
|
|
2163
2820
|
- 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.
|
|
2164
|
-
-
|
|
2821
|
+
- enter also has zoom (settles from larger) and rise (rises a short way, overshooting a touch); pop and zoom take a starting "scale".
|
|
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).
|
|
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.
|
|
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.
|
|
2165
2828
|
- effect tweens a numeric parameter of a live path effect already on the node (twist angle, wiggle time, zig zag size).
|
|
2166
2829
|
|
|
2167
2830
|
Working method
|
|
2168
|
-
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).
|
|
2169
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).
|
|
2170
2833
|
3. check_animation and fix every error and warning; render_animation_frame (shot + "end") to look at the result.
|
|
2171
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.
|
|
@@ -2235,5 +2898,20 @@ Keep motion purposeful: one thing moving at a time reads best, ease-out for arri
|
|
|
2235
2898
|
|
|
2236
2899
|
|
|
2237
2900
|
|
|
2238
|
-
|
|
2901
|
+
|
|
2902
|
+
|
|
2903
|
+
|
|
2904
|
+
|
|
2905
|
+
|
|
2906
|
+
|
|
2907
|
+
|
|
2908
|
+
|
|
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;
|
|
2239
2917
|
//# sourceMappingURL=index.cjs.map
|