@pixerate/editor 0.63.0 → 0.64.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/THIRD_PARTY_NOTICES.md +1 -0
- package/dist/animation-editor/index.cjs +322 -7
- package/dist/animation-editor/index.cjs.map +1 -1
- package/dist/animation-editor/index.d.cts +85 -1
- package/dist/animation-editor/index.d.ts +85 -1
- package/dist/animation-editor/index.js +316 -1
- package/dist/animation-editor/index.js.map +1 -1
- package/package.json +1 -1
|
@@ -1215,6 +1215,90 @@ declare function fromFlairSvgMotion(input: FlairSvgMotion, options?: FlairImport
|
|
|
1215
1215
|
/** A Flair catalog `VectorEffect` as a scene: its motion, name, description, colour controls and presets. */
|
|
1216
1216
|
declare function fromFlairVectorEffect(effect: FlairVectorEffect, options?: FlairImportOptions): FlairImportResult;
|
|
1217
1217
|
|
|
1218
|
+
/**
|
|
1219
|
+
* Export to Lottie (Bodymovin JSON, v5): the scene as one shape layer whose groups mirror the
|
|
1220
|
+
* artwork. Lottie players (lottie-web, rlottie, the iOS and Android libraries) then play it.
|
|
1221
|
+
*
|
|
1222
|
+
* - Each node is an outer group carrying its animated pose (anchor at its centre, position,
|
|
1223
|
+
* scale, rotation, opacity) around an inner group carrying its own transform. Paths keep their
|
|
1224
|
+
* own transform in their points.
|
|
1225
|
+
* - Paths become `sh` shapes with solid fills (`fl`) and strokes (`st`). Drawing on becomes trim
|
|
1226
|
+
* paths (`tm`), and colours, stroke width and fill opacity animate on the fill and stroke.
|
|
1227
|
+
* - The camera becomes the transform of a root group.
|
|
1228
|
+
* - A change is written as keyframes with Lottie's bezier easing when a cubic-bezier ease (run
|
|
1229
|
+
* backwards for a mirrored repeat) is all that drives that property over that stretch.
|
|
1230
|
+
* Everything else is sampled at `fps` into linear keyframes: springs, elastic, behaviours,
|
|
1231
|
+
* stepped time, geometric zooms, additive layers, follows and camera moves.
|
|
1232
|
+
*
|
|
1233
|
+
* Gradients, dashes, reveal clips and skews of groups are not carried over; `notes` says so.
|
|
1234
|
+
*/
|
|
1235
|
+
/** A Lottie property: static (`a: 0`) or keyframed (`a: 1`). */
|
|
1236
|
+
type LottieValue = {
|
|
1237
|
+
a: 0;
|
|
1238
|
+
k: number | number[];
|
|
1239
|
+
} | {
|
|
1240
|
+
a: 1;
|
|
1241
|
+
k: LottieKeyframe[];
|
|
1242
|
+
};
|
|
1243
|
+
interface LottieKeyframe {
|
|
1244
|
+
/** Frame. */
|
|
1245
|
+
t: number;
|
|
1246
|
+
/** Value from this key on. */
|
|
1247
|
+
s: number[];
|
|
1248
|
+
/** Out tangent of the ease to the next key (`x1`, `y1`). */
|
|
1249
|
+
o?: {
|
|
1250
|
+
x: number[];
|
|
1251
|
+
y: number[];
|
|
1252
|
+
};
|
|
1253
|
+
/** In tangent of the ease to the next key (`x2`, `y2`). */
|
|
1254
|
+
i?: {
|
|
1255
|
+
x: number[];
|
|
1256
|
+
y: number[];
|
|
1257
|
+
};
|
|
1258
|
+
/** Hold until the next key. */
|
|
1259
|
+
h?: 1;
|
|
1260
|
+
}
|
|
1261
|
+
/** A Lottie shape item (group, path, fill, stroke, trim, transform): plain JSON. */
|
|
1262
|
+
type LottieShape = {
|
|
1263
|
+
ty: string;
|
|
1264
|
+
nm?: string;
|
|
1265
|
+
[key: string]: unknown;
|
|
1266
|
+
};
|
|
1267
|
+
/** A Lottie animation: plain Bodymovin JSON. */
|
|
1268
|
+
interface LottieAnimation {
|
|
1269
|
+
v: string;
|
|
1270
|
+
fr: number;
|
|
1271
|
+
ip: number;
|
|
1272
|
+
op: number;
|
|
1273
|
+
w: number;
|
|
1274
|
+
h: number;
|
|
1275
|
+
nm: string;
|
|
1276
|
+
ddd: 0;
|
|
1277
|
+
assets: [];
|
|
1278
|
+
layers: Record<string, unknown>[];
|
|
1279
|
+
/** One marker per shot (`cm` its label, else its id). */
|
|
1280
|
+
markers: {
|
|
1281
|
+
tm: number;
|
|
1282
|
+
cm: string;
|
|
1283
|
+
dr: number;
|
|
1284
|
+
}[];
|
|
1285
|
+
}
|
|
1286
|
+
interface LottieExportOptions {
|
|
1287
|
+
/** Frame rate and sampling rate (default: the scene's `fps`, else 30). */
|
|
1288
|
+
fps?: number;
|
|
1289
|
+
/** Decimal places (default 3). */
|
|
1290
|
+
precision?: number;
|
|
1291
|
+
}
|
|
1292
|
+
interface LottieExport {
|
|
1293
|
+
lottie: LottieAnimation;
|
|
1294
|
+
/** Keyframes that sample changes Lottie cannot ease the same way. */
|
|
1295
|
+
sampled: number;
|
|
1296
|
+
/** What could not be carried over, in sentences. */
|
|
1297
|
+
notes: string[];
|
|
1298
|
+
}
|
|
1299
|
+
/** A scene as a Lottie animation. */
|
|
1300
|
+
declare function toLottie(scene: MotionScene, options?: LottieExportOptions): LottieExport;
|
|
1301
|
+
|
|
1218
1302
|
interface AnimationEditorOptions {
|
|
1219
1303
|
/** The scene to open (default: an empty one over a blank 800 × 450 artwork). */
|
|
1220
1304
|
scene?: MotionScene;
|
|
@@ -1766,4 +1850,4 @@ declare function createAnimationEditorToolProvider(controller: AnimationEditorCo
|
|
|
1766
1850
|
*/
|
|
1767
1851
|
declare const ANIMATION_EDITOR_AGENT_INSTRUCTIONS = "You animate vector artwork. A scene is artwork (vector paths and groups, each with an id) plus shots.\n\nHow it plays\n- Shots play in order. A shot optionally moves the camera, plays its commands, then holds (default 600 ms).\n- Commands in a shot play one after another; give \"with\": \"previous\" to start one together with the command before, \"delay\" to wait, \"stagger\" to offset several targets.\n- Timing comes from the content: each command has a default length (enter 600 ms, move 700 ms, draw follows the path length). Only set \"duration\" to change the pace.\n- Anything that enters later is hidden until it enters. Things that never enter are visible from the start.\n- Every frame is a pure function of time: the same scene always plays the same frames.\n\nCommands\n- enter (fade, pop, grow, slide, draw) and exit (fade, shrink, slide, undraw) bring things in and out.\n- 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.\n- 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.\n- enter also has zoom (settles from larger) and rise (rises a short way, overshooting a touch); pop and zoom take a starting \"scale\".\n- morph turns a shape into another node's shape (keep the target hidden); match does the same for a whole group, part by part (Smart Animate: draw the end state as a second, hidden group whose parts share names with the first); follow moves along a path node (with \"rotate\", moves made meanwhile turn with the path).\n- 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).\n- Emphasis stacks on other motion: a shake during a slide shakes as it slides. Give move, rotate or scale \"blend\": \"add\" to stack them too (what they add stays); \"blend\": \"replace\" makes a change take over instead.\n- 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.\n- Springs (\"spring-gentle\", \"spring-quick\", \"spring-bouncy\", \"spring-slow\" or { \"type\": \"spring\", \u2026 }) feel physical; leave \"duration\" out and the spring sets it.\n- To time shots to music or a voiceover: set a \"beat\" grid or \"narration\" word timings with set_animation_settings, read them with get_timing_grid, and give a shot \"at\": { \"beat\": n } or { \"word\": i }. The shot before holds until the cue; check_animation warns when a cue is missed.\n- \"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.\n- 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.\n- effect tweens a numeric parameter of a live path effect already on the node (twist angle, wiggle time, zig zag size).\n\nWorking method\n1. 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).\n2. 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).\n3. To hit a length, fit_animation (keep the shots whose pace matters). check_animation and fix every error and warning; render_animation_frame (shot + \"end\") to look at the result.\n4. To make a reusable Flair effect, set_animation_controls (colour controls stand for every use of their default colour) and export_flair_effect.\n\nKeep motion purposeful: one thing moving at a time reads best, ease-out for arrivals, ease-in for exits, and hold long enough to see each result.";
|
|
1768
1852
|
|
|
1769
|
-
export { ANIMATION_EDITOR_AGENT_INSTRUCTIONS, AnimationEditorController, type AnimationEditorOptions, type AnimationEditorSelection, type AnimationEditorState, type AnimationEditorToolProviderOptions, FLAIR_EASES, FLAIR_EXPORT_VERSION, type FlairEase, type FlairEaseName, type FlairEffectDocument, type FlairEffectOptions, type FlairExportOptions, type FlairImportOptions, type FlairImportResult, type FlairMotionExport, type FlairSpringEase, type FlairSvgAnimation, type FlairSvgMotion, type FlairVectorEffect, MOTION_ADDITIVE_PROPERTIES, MOTION_BLENDS, MOTION_CAMERA_MS, MOTION_COMMAND_SCHEMA, MOTION_COMMAND_TYPES, MOTION_DEFAULT_DURATIONS, MOTION_DEFAULT_HOLD, MOTION_DIRECTIONS, MOTION_DRAW_EFFECT_ID, MOTION_DRAW_MS_PER_UNIT, MOTION_DRAW_RANGE, MOTION_EASE_IN_OUT, MOTION_EASE_OUT, MOTION_EDIT_OPS, MOTION_ENTER_STYLES, MOTION_EXIT_STYLES, MOTION_IRIS_SHAPES, MOTION_ISSUE_CODES, MOTION_MIN_CHANGE_MS, MOTION_REPEAT_MODES, MOTION_SPEED_RANGE, MOTION_SPLIT_ORIENTATIONS, type MotionBeatGrid, type MotionCamera, type MotionClipShape, type MotionColorPulseCommand, type MotionCommand, type MotionCommandBase, type MotionCommandTiming, type MotionCommandType, type MotionControl, type MotionCue, type MotionDirection, type MotionDrawCommand, type MotionEdit, type MotionEditOp, type MotionEditResult, type MotionEffectCommand, type MotionEnterCommand, type MotionEnterStyle, type MotionExitCommand, type MotionExitStyle, type MotionFadeCommand, type MotionFit, type MotionFitOptions, type MotionFollowCommand, type MotionFrame, type MotionFrameOptions, type MotionIrisShape, type MotionIssue, type MotionIssueCode, type MotionIssueSeverity, type MotionMatch, type MotionMatchCommand, type MotionMorphCommand, type MotionMoveCommand, type MotionNarration, type MotionNodeEntry, type MotionPreset, type MotionProperty, type MotionPulseCommand, type MotionRect, type MotionReveal, type MotionRotateCommand, type MotionScaleCommand, type MotionScene, MotionSceneFormatError, type MotionSegment, type MotionShakeCommand, type MotionShot, type MotionShotTiming, type MotionSplitOrientation, type MotionStyleCommand, type MotionSvgOptions, type MotionTeeterCommand, type MotionTimeline, type MotionValue, type MotionWaitCommand, type MotionWaveCommand, type MotionWiggleCommand, activeSegment, applyMotionControlValues, applyMotionEdits, checkMotionScene, commandShapeProblems, compileMotionScene, createAnimationEditorToolProvider, createMotionScene, describeMotionElements, describeMotionShots, fitMotionRect, fitMotionScene, flairAtLeast, flairCurveFor, flairEaseFor, freshShotId, fromFlairSvgMotion, fromFlairVectorEffect, indexMotionNodes, interpolateMotionValue, isMotionCommand, isMotionShot, matchMotionNodes, motionAddTrackKey, motionBeatAt, motionBeatTime, motionClipAttrs, motionCommandDuration, motionCommandRepeat, motionCueTime, motionCycleDuration, motionFlairPrimitive, motionFollowRoute, motionFrameAt, motionFrameToSvg, motionIssue, motionIssueKey, motionIssueLine, motionNodeClips, motionNodeOffset, motionPathLength, motionRestValue, motionRevealClip, motionRevealPadding, motionSceneShapeProblems, motionSceneToJson, motionShotAt, motionShotTimeScale, motionTrackKey, namedWithSuggestion, parseMotionScene, resolveMotionCamera, segmentProgress, segmentValue, shotShapeProblems, suggestName, toFlairEffectDocument, toFlairSvgMotion, toFlairVectorEffect, trackValue, withMotionLayers, withViewBox };
|
|
1853
|
+
export { ANIMATION_EDITOR_AGENT_INSTRUCTIONS, AnimationEditorController, type AnimationEditorOptions, type AnimationEditorSelection, type AnimationEditorState, type AnimationEditorToolProviderOptions, FLAIR_EASES, FLAIR_EXPORT_VERSION, type FlairEase, type FlairEaseName, type FlairEffectDocument, type FlairEffectOptions, type FlairExportOptions, type FlairImportOptions, type FlairImportResult, type FlairMotionExport, type FlairSpringEase, type FlairSvgAnimation, type FlairSvgMotion, type FlairVectorEffect, type LottieAnimation, type LottieExport, type LottieExportOptions, type LottieKeyframe, type LottieShape, type LottieValue, MOTION_ADDITIVE_PROPERTIES, MOTION_BLENDS, MOTION_CAMERA_MS, MOTION_COMMAND_SCHEMA, MOTION_COMMAND_TYPES, MOTION_DEFAULT_DURATIONS, MOTION_DEFAULT_HOLD, MOTION_DIRECTIONS, MOTION_DRAW_EFFECT_ID, MOTION_DRAW_MS_PER_UNIT, MOTION_DRAW_RANGE, MOTION_EASE_IN_OUT, MOTION_EASE_OUT, MOTION_EDIT_OPS, MOTION_ENTER_STYLES, MOTION_EXIT_STYLES, MOTION_IRIS_SHAPES, MOTION_ISSUE_CODES, MOTION_MIN_CHANGE_MS, MOTION_REPEAT_MODES, MOTION_SPEED_RANGE, MOTION_SPLIT_ORIENTATIONS, type MotionBeatGrid, type MotionCamera, type MotionClipShape, type MotionColorPulseCommand, type MotionCommand, type MotionCommandBase, type MotionCommandTiming, type MotionCommandType, type MotionControl, type MotionCue, type MotionDirection, type MotionDrawCommand, type MotionEdit, type MotionEditOp, type MotionEditResult, type MotionEffectCommand, type MotionEnterCommand, type MotionEnterStyle, type MotionExitCommand, type MotionExitStyle, type MotionFadeCommand, type MotionFit, type MotionFitOptions, type MotionFollowCommand, type MotionFrame, type MotionFrameOptions, type MotionIrisShape, type MotionIssue, type MotionIssueCode, type MotionIssueSeverity, type MotionMatch, type MotionMatchCommand, type MotionMorphCommand, type MotionMoveCommand, type MotionNarration, type MotionNodeEntry, type MotionPreset, type MotionProperty, type MotionPulseCommand, type MotionRect, type MotionReveal, type MotionRotateCommand, type MotionScaleCommand, type MotionScene, MotionSceneFormatError, type MotionSegment, type MotionShakeCommand, type MotionShot, type MotionShotTiming, type MotionSplitOrientation, type MotionStyleCommand, type MotionSvgOptions, type MotionTeeterCommand, type MotionTimeline, type MotionValue, type MotionWaitCommand, type MotionWaveCommand, type MotionWiggleCommand, activeSegment, applyMotionControlValues, applyMotionEdits, checkMotionScene, commandShapeProblems, compileMotionScene, createAnimationEditorToolProvider, createMotionScene, describeMotionElements, describeMotionShots, fitMotionRect, fitMotionScene, flairAtLeast, flairCurveFor, flairEaseFor, freshShotId, fromFlairSvgMotion, fromFlairVectorEffect, indexMotionNodes, interpolateMotionValue, isMotionCommand, isMotionShot, matchMotionNodes, motionAddTrackKey, motionBeatAt, motionBeatTime, motionClipAttrs, motionCommandDuration, motionCommandRepeat, motionCueTime, motionCycleDuration, motionFlairPrimitive, motionFollowRoute, motionFrameAt, motionFrameToSvg, motionIssue, motionIssueKey, motionIssueLine, motionNodeClips, motionNodeOffset, motionPathLength, motionRestValue, motionRevealClip, motionRevealPadding, motionSceneShapeProblems, motionSceneToJson, motionShotAt, motionShotTimeScale, motionTrackKey, namedWithSuggestion, parseMotionScene, resolveMotionCamera, segmentProgress, segmentValue, shotShapeProblems, suggestName, toFlairEffectDocument, toFlairSvgMotion, toFlairVectorEffect, toLottie, trackValue, withMotionLayers, withViewBox };
|
|
@@ -1215,6 +1215,90 @@ declare function fromFlairSvgMotion(input: FlairSvgMotion, options?: FlairImport
|
|
|
1215
1215
|
/** A Flair catalog `VectorEffect` as a scene: its motion, name, description, colour controls and presets. */
|
|
1216
1216
|
declare function fromFlairVectorEffect(effect: FlairVectorEffect, options?: FlairImportOptions): FlairImportResult;
|
|
1217
1217
|
|
|
1218
|
+
/**
|
|
1219
|
+
* Export to Lottie (Bodymovin JSON, v5): the scene as one shape layer whose groups mirror the
|
|
1220
|
+
* artwork. Lottie players (lottie-web, rlottie, the iOS and Android libraries) then play it.
|
|
1221
|
+
*
|
|
1222
|
+
* - Each node is an outer group carrying its animated pose (anchor at its centre, position,
|
|
1223
|
+
* scale, rotation, opacity) around an inner group carrying its own transform. Paths keep their
|
|
1224
|
+
* own transform in their points.
|
|
1225
|
+
* - Paths become `sh` shapes with solid fills (`fl`) and strokes (`st`). Drawing on becomes trim
|
|
1226
|
+
* paths (`tm`), and colours, stroke width and fill opacity animate on the fill and stroke.
|
|
1227
|
+
* - The camera becomes the transform of a root group.
|
|
1228
|
+
* - A change is written as keyframes with Lottie's bezier easing when a cubic-bezier ease (run
|
|
1229
|
+
* backwards for a mirrored repeat) is all that drives that property over that stretch.
|
|
1230
|
+
* Everything else is sampled at `fps` into linear keyframes: springs, elastic, behaviours,
|
|
1231
|
+
* stepped time, geometric zooms, additive layers, follows and camera moves.
|
|
1232
|
+
*
|
|
1233
|
+
* Gradients, dashes, reveal clips and skews of groups are not carried over; `notes` says so.
|
|
1234
|
+
*/
|
|
1235
|
+
/** A Lottie property: static (`a: 0`) or keyframed (`a: 1`). */
|
|
1236
|
+
type LottieValue = {
|
|
1237
|
+
a: 0;
|
|
1238
|
+
k: number | number[];
|
|
1239
|
+
} | {
|
|
1240
|
+
a: 1;
|
|
1241
|
+
k: LottieKeyframe[];
|
|
1242
|
+
};
|
|
1243
|
+
interface LottieKeyframe {
|
|
1244
|
+
/** Frame. */
|
|
1245
|
+
t: number;
|
|
1246
|
+
/** Value from this key on. */
|
|
1247
|
+
s: number[];
|
|
1248
|
+
/** Out tangent of the ease to the next key (`x1`, `y1`). */
|
|
1249
|
+
o?: {
|
|
1250
|
+
x: number[];
|
|
1251
|
+
y: number[];
|
|
1252
|
+
};
|
|
1253
|
+
/** In tangent of the ease to the next key (`x2`, `y2`). */
|
|
1254
|
+
i?: {
|
|
1255
|
+
x: number[];
|
|
1256
|
+
y: number[];
|
|
1257
|
+
};
|
|
1258
|
+
/** Hold until the next key. */
|
|
1259
|
+
h?: 1;
|
|
1260
|
+
}
|
|
1261
|
+
/** A Lottie shape item (group, path, fill, stroke, trim, transform): plain JSON. */
|
|
1262
|
+
type LottieShape = {
|
|
1263
|
+
ty: string;
|
|
1264
|
+
nm?: string;
|
|
1265
|
+
[key: string]: unknown;
|
|
1266
|
+
};
|
|
1267
|
+
/** A Lottie animation: plain Bodymovin JSON. */
|
|
1268
|
+
interface LottieAnimation {
|
|
1269
|
+
v: string;
|
|
1270
|
+
fr: number;
|
|
1271
|
+
ip: number;
|
|
1272
|
+
op: number;
|
|
1273
|
+
w: number;
|
|
1274
|
+
h: number;
|
|
1275
|
+
nm: string;
|
|
1276
|
+
ddd: 0;
|
|
1277
|
+
assets: [];
|
|
1278
|
+
layers: Record<string, unknown>[];
|
|
1279
|
+
/** One marker per shot (`cm` its label, else its id). */
|
|
1280
|
+
markers: {
|
|
1281
|
+
tm: number;
|
|
1282
|
+
cm: string;
|
|
1283
|
+
dr: number;
|
|
1284
|
+
}[];
|
|
1285
|
+
}
|
|
1286
|
+
interface LottieExportOptions {
|
|
1287
|
+
/** Frame rate and sampling rate (default: the scene's `fps`, else 30). */
|
|
1288
|
+
fps?: number;
|
|
1289
|
+
/** Decimal places (default 3). */
|
|
1290
|
+
precision?: number;
|
|
1291
|
+
}
|
|
1292
|
+
interface LottieExport {
|
|
1293
|
+
lottie: LottieAnimation;
|
|
1294
|
+
/** Keyframes that sample changes Lottie cannot ease the same way. */
|
|
1295
|
+
sampled: number;
|
|
1296
|
+
/** What could not be carried over, in sentences. */
|
|
1297
|
+
notes: string[];
|
|
1298
|
+
}
|
|
1299
|
+
/** A scene as a Lottie animation. */
|
|
1300
|
+
declare function toLottie(scene: MotionScene, options?: LottieExportOptions): LottieExport;
|
|
1301
|
+
|
|
1218
1302
|
interface AnimationEditorOptions {
|
|
1219
1303
|
/** The scene to open (default: an empty one over a blank 800 × 450 artwork). */
|
|
1220
1304
|
scene?: MotionScene;
|
|
@@ -1766,4 +1850,4 @@ declare function createAnimationEditorToolProvider(controller: AnimationEditorCo
|
|
|
1766
1850
|
*/
|
|
1767
1851
|
declare const ANIMATION_EDITOR_AGENT_INSTRUCTIONS = "You animate vector artwork. A scene is artwork (vector paths and groups, each with an id) plus shots.\n\nHow it plays\n- Shots play in order. A shot optionally moves the camera, plays its commands, then holds (default 600 ms).\n- Commands in a shot play one after another; give \"with\": \"previous\" to start one together with the command before, \"delay\" to wait, \"stagger\" to offset several targets.\n- Timing comes from the content: each command has a default length (enter 600 ms, move 700 ms, draw follows the path length). Only set \"duration\" to change the pace.\n- Anything that enters later is hidden until it enters. Things that never enter are visible from the start.\n- Every frame is a pure function of time: the same scene always plays the same frames.\n\nCommands\n- enter (fade, pop, grow, slide, draw) and exit (fade, shrink, slide, undraw) bring things in and out.\n- 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.\n- 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.\n- enter also has zoom (settles from larger) and rise (rises a short way, overshooting a touch); pop and zoom take a starting \"scale\".\n- morph turns a shape into another node's shape (keep the target hidden); match does the same for a whole group, part by part (Smart Animate: draw the end state as a second, hidden group whose parts share names with the first); follow moves along a path node (with \"rotate\", moves made meanwhile turn with the path).\n- 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).\n- Emphasis stacks on other motion: a shake during a slide shakes as it slides. Give move, rotate or scale \"blend\": \"add\" to stack them too (what they add stays); \"blend\": \"replace\" makes a change take over instead.\n- 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.\n- Springs (\"spring-gentle\", \"spring-quick\", \"spring-bouncy\", \"spring-slow\" or { \"type\": \"spring\", \u2026 }) feel physical; leave \"duration\" out and the spring sets it.\n- To time shots to music or a voiceover: set a \"beat\" grid or \"narration\" word timings with set_animation_settings, read them with get_timing_grid, and give a shot \"at\": { \"beat\": n } or { \"word\": i }. The shot before holds until the cue; check_animation warns when a cue is missed.\n- \"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.\n- 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.\n- effect tweens a numeric parameter of a live path effect already on the node (twist angle, wiggle time, zig zag size).\n\nWorking method\n1. 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).\n2. 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).\n3. To hit a length, fit_animation (keep the shots whose pace matters). check_animation and fix every error and warning; render_animation_frame (shot + \"end\") to look at the result.\n4. To make a reusable Flair effect, set_animation_controls (colour controls stand for every use of their default colour) and export_flair_effect.\n\nKeep motion purposeful: one thing moving at a time reads best, ease-out for arrivals, ease-in for exits, and hold long enough to see each result.";
|
|
1768
1852
|
|
|
1769
|
-
export { ANIMATION_EDITOR_AGENT_INSTRUCTIONS, AnimationEditorController, type AnimationEditorOptions, type AnimationEditorSelection, type AnimationEditorState, type AnimationEditorToolProviderOptions, FLAIR_EASES, FLAIR_EXPORT_VERSION, type FlairEase, type FlairEaseName, type FlairEffectDocument, type FlairEffectOptions, type FlairExportOptions, type FlairImportOptions, type FlairImportResult, type FlairMotionExport, type FlairSpringEase, type FlairSvgAnimation, type FlairSvgMotion, type FlairVectorEffect, MOTION_ADDITIVE_PROPERTIES, MOTION_BLENDS, MOTION_CAMERA_MS, MOTION_COMMAND_SCHEMA, MOTION_COMMAND_TYPES, MOTION_DEFAULT_DURATIONS, MOTION_DEFAULT_HOLD, MOTION_DIRECTIONS, MOTION_DRAW_EFFECT_ID, MOTION_DRAW_MS_PER_UNIT, MOTION_DRAW_RANGE, MOTION_EASE_IN_OUT, MOTION_EASE_OUT, MOTION_EDIT_OPS, MOTION_ENTER_STYLES, MOTION_EXIT_STYLES, MOTION_IRIS_SHAPES, MOTION_ISSUE_CODES, MOTION_MIN_CHANGE_MS, MOTION_REPEAT_MODES, MOTION_SPEED_RANGE, MOTION_SPLIT_ORIENTATIONS, type MotionBeatGrid, type MotionCamera, type MotionClipShape, type MotionColorPulseCommand, type MotionCommand, type MotionCommandBase, type MotionCommandTiming, type MotionCommandType, type MotionControl, type MotionCue, type MotionDirection, type MotionDrawCommand, type MotionEdit, type MotionEditOp, type MotionEditResult, type MotionEffectCommand, type MotionEnterCommand, type MotionEnterStyle, type MotionExitCommand, type MotionExitStyle, type MotionFadeCommand, type MotionFit, type MotionFitOptions, type MotionFollowCommand, type MotionFrame, type MotionFrameOptions, type MotionIrisShape, type MotionIssue, type MotionIssueCode, type MotionIssueSeverity, type MotionMatch, type MotionMatchCommand, type MotionMorphCommand, type MotionMoveCommand, type MotionNarration, type MotionNodeEntry, type MotionPreset, type MotionProperty, type MotionPulseCommand, type MotionRect, type MotionReveal, type MotionRotateCommand, type MotionScaleCommand, type MotionScene, MotionSceneFormatError, type MotionSegment, type MotionShakeCommand, type MotionShot, type MotionShotTiming, type MotionSplitOrientation, type MotionStyleCommand, type MotionSvgOptions, type MotionTeeterCommand, type MotionTimeline, type MotionValue, type MotionWaitCommand, type MotionWaveCommand, type MotionWiggleCommand, activeSegment, applyMotionControlValues, applyMotionEdits, checkMotionScene, commandShapeProblems, compileMotionScene, createAnimationEditorToolProvider, createMotionScene, describeMotionElements, describeMotionShots, fitMotionRect, fitMotionScene, flairAtLeast, flairCurveFor, flairEaseFor, freshShotId, fromFlairSvgMotion, fromFlairVectorEffect, indexMotionNodes, interpolateMotionValue, isMotionCommand, isMotionShot, matchMotionNodes, motionAddTrackKey, motionBeatAt, motionBeatTime, motionClipAttrs, motionCommandDuration, motionCommandRepeat, motionCueTime, motionCycleDuration, motionFlairPrimitive, motionFollowRoute, motionFrameAt, motionFrameToSvg, motionIssue, motionIssueKey, motionIssueLine, motionNodeClips, motionNodeOffset, motionPathLength, motionRestValue, motionRevealClip, motionRevealPadding, motionSceneShapeProblems, motionSceneToJson, motionShotAt, motionShotTimeScale, motionTrackKey, namedWithSuggestion, parseMotionScene, resolveMotionCamera, segmentProgress, segmentValue, shotShapeProblems, suggestName, toFlairEffectDocument, toFlairSvgMotion, toFlairVectorEffect, trackValue, withMotionLayers, withViewBox };
|
|
1853
|
+
export { ANIMATION_EDITOR_AGENT_INSTRUCTIONS, AnimationEditorController, type AnimationEditorOptions, type AnimationEditorSelection, type AnimationEditorState, type AnimationEditorToolProviderOptions, FLAIR_EASES, FLAIR_EXPORT_VERSION, type FlairEase, type FlairEaseName, type FlairEffectDocument, type FlairEffectOptions, type FlairExportOptions, type FlairImportOptions, type FlairImportResult, type FlairMotionExport, type FlairSpringEase, type FlairSvgAnimation, type FlairSvgMotion, type FlairVectorEffect, type LottieAnimation, type LottieExport, type LottieExportOptions, type LottieKeyframe, type LottieShape, type LottieValue, MOTION_ADDITIVE_PROPERTIES, MOTION_BLENDS, MOTION_CAMERA_MS, MOTION_COMMAND_SCHEMA, MOTION_COMMAND_TYPES, MOTION_DEFAULT_DURATIONS, MOTION_DEFAULT_HOLD, MOTION_DIRECTIONS, MOTION_DRAW_EFFECT_ID, MOTION_DRAW_MS_PER_UNIT, MOTION_DRAW_RANGE, MOTION_EASE_IN_OUT, MOTION_EASE_OUT, MOTION_EDIT_OPS, MOTION_ENTER_STYLES, MOTION_EXIT_STYLES, MOTION_IRIS_SHAPES, MOTION_ISSUE_CODES, MOTION_MIN_CHANGE_MS, MOTION_REPEAT_MODES, MOTION_SPEED_RANGE, MOTION_SPLIT_ORIENTATIONS, type MotionBeatGrid, type MotionCamera, type MotionClipShape, type MotionColorPulseCommand, type MotionCommand, type MotionCommandBase, type MotionCommandTiming, type MotionCommandType, type MotionControl, type MotionCue, type MotionDirection, type MotionDrawCommand, type MotionEdit, type MotionEditOp, type MotionEditResult, type MotionEffectCommand, type MotionEnterCommand, type MotionEnterStyle, type MotionExitCommand, type MotionExitStyle, type MotionFadeCommand, type MotionFit, type MotionFitOptions, type MotionFollowCommand, type MotionFrame, type MotionFrameOptions, type MotionIrisShape, type MotionIssue, type MotionIssueCode, type MotionIssueSeverity, type MotionMatch, type MotionMatchCommand, type MotionMorphCommand, type MotionMoveCommand, type MotionNarration, type MotionNodeEntry, type MotionPreset, type MotionProperty, type MotionPulseCommand, type MotionRect, type MotionReveal, type MotionRotateCommand, type MotionScaleCommand, type MotionScene, MotionSceneFormatError, type MotionSegment, type MotionShakeCommand, type MotionShot, type MotionShotTiming, type MotionSplitOrientation, type MotionStyleCommand, type MotionSvgOptions, type MotionTeeterCommand, type MotionTimeline, type MotionValue, type MotionWaitCommand, type MotionWaveCommand, type MotionWiggleCommand, activeSegment, applyMotionControlValues, applyMotionEdits, checkMotionScene, commandShapeProblems, compileMotionScene, createAnimationEditorToolProvider, createMotionScene, describeMotionElements, describeMotionShots, fitMotionRect, fitMotionScene, flairAtLeast, flairCurveFor, flairEaseFor, freshShotId, fromFlairSvgMotion, fromFlairVectorEffect, indexMotionNodes, interpolateMotionValue, isMotionCommand, isMotionShot, matchMotionNodes, motionAddTrackKey, motionBeatAt, motionBeatTime, motionClipAttrs, motionCommandDuration, motionCommandRepeat, motionCueTime, motionCycleDuration, motionFlairPrimitive, motionFollowRoute, motionFrameAt, motionFrameToSvg, motionIssue, motionIssueKey, motionIssueLine, motionNodeClips, motionNodeOffset, motionPathLength, motionRestValue, motionRevealClip, motionRevealPadding, motionSceneShapeProblems, motionSceneToJson, motionShotAt, motionShotTimeScale, motionTrackKey, namedWithSuggestion, parseMotionScene, resolveMotionCamera, segmentProgress, segmentValue, shotShapeProblems, suggestName, toFlairEffectDocument, toFlairSvgMotion, toFlairVectorEffect, toLottie, trackValue, withMotionLayers, withViewBox };
|
|
@@ -23,6 +23,7 @@ import {
|
|
|
23
23
|
bezierPathLength,
|
|
24
24
|
bezierPathPointAtLength,
|
|
25
25
|
bezierPathToSvg,
|
|
26
|
+
toBezierPath,
|
|
26
27
|
transformBezierPath
|
|
27
28
|
} from "../chunk-GTMJ4Y3S.js";
|
|
28
29
|
import {
|
|
@@ -2183,6 +2184,311 @@ function fromFlairVectorEffect(effect, options = {}) {
|
|
|
2183
2184
|
return { scene, notes, issues: checkMotionScene(scene) };
|
|
2184
2185
|
}
|
|
2185
2186
|
|
|
2187
|
+
// src/animation-editor/lottie.ts
|
|
2188
|
+
var STATIC_TR = () => ({
|
|
2189
|
+
ty: "tr",
|
|
2190
|
+
p: { a: 0, k: [0, 0] },
|
|
2191
|
+
a: { a: 0, k: [0, 0] },
|
|
2192
|
+
s: { a: 0, k: [100, 100] },
|
|
2193
|
+
r: { a: 0, k: 0 },
|
|
2194
|
+
o: { a: 0, k: 100 },
|
|
2195
|
+
sk: { a: 0, k: 0 },
|
|
2196
|
+
sa: { a: 0, k: 0 }
|
|
2197
|
+
});
|
|
2198
|
+
function rgba(color) {
|
|
2199
|
+
const hex = typeof color === "string" ? normalizeTokenColor(color) : null;
|
|
2200
|
+
if (!hex) return null;
|
|
2201
|
+
const n = (i) => parseInt(hex.slice(i, i + 2), 16) / 255;
|
|
2202
|
+
return [n(1), n(3), n(5), hex.length >= 9 ? parseInt(hex.slice(7, 9), 16) / 255 : 1];
|
|
2203
|
+
}
|
|
2204
|
+
function lottieContours(path, round2) {
|
|
2205
|
+
const out = [];
|
|
2206
|
+
let cur = null;
|
|
2207
|
+
let px = 0;
|
|
2208
|
+
let py = 0;
|
|
2209
|
+
const add = (x, y, inX, inY) => {
|
|
2210
|
+
cur.v.push([x, y]);
|
|
2211
|
+
cur.i.push([inX - x, inY - y]);
|
|
2212
|
+
cur.o.push([0, 0]);
|
|
2213
|
+
px = x;
|
|
2214
|
+
py = y;
|
|
2215
|
+
};
|
|
2216
|
+
for (const cmd of path) {
|
|
2217
|
+
if (cmd.type === "M") {
|
|
2218
|
+
if (cur) out.push(cur);
|
|
2219
|
+
cur = { c: false, v: [], i: [], o: [] };
|
|
2220
|
+
add(cmd.x, cmd.y, cmd.x, cmd.y);
|
|
2221
|
+
} else if (!cur) continue;
|
|
2222
|
+
else if (cmd.type === "L") add(cmd.x, cmd.y, cmd.x, cmd.y);
|
|
2223
|
+
else if (cmd.type === "Q") {
|
|
2224
|
+
const c1 = [px + 2 / 3 * (cmd.x1 - px), py + 2 / 3 * (cmd.y1 - py)];
|
|
2225
|
+
const c2 = [cmd.x + 2 / 3 * (cmd.x1 - cmd.x), cmd.y + 2 / 3 * (cmd.y1 - cmd.y)];
|
|
2226
|
+
cur.o[cur.o.length - 1] = [c1[0] - px, c1[1] - py];
|
|
2227
|
+
add(cmd.x, cmd.y, c2[0], c2[1]);
|
|
2228
|
+
} else if (cmd.type === "C") {
|
|
2229
|
+
cur.o[cur.o.length - 1] = [cmd.x1 - px, cmd.y1 - py];
|
|
2230
|
+
add(cmd.x, cmd.y, cmd.x2, cmd.y2);
|
|
2231
|
+
} else if (cmd.type === "Z") {
|
|
2232
|
+
cur.c = true;
|
|
2233
|
+
const n = cur.v.length;
|
|
2234
|
+
if (n > 1 && Math.abs(cur.v[n - 1][0] - cur.v[0][0]) < 1e-9 && Math.abs(cur.v[n - 1][1] - cur.v[0][1]) < 1e-9) {
|
|
2235
|
+
cur.i[0] = cur.i[n - 1];
|
|
2236
|
+
cur.v.pop();
|
|
2237
|
+
cur.i.pop();
|
|
2238
|
+
cur.o.pop();
|
|
2239
|
+
}
|
|
2240
|
+
out.push(cur);
|
|
2241
|
+
cur = null;
|
|
2242
|
+
}
|
|
2243
|
+
}
|
|
2244
|
+
if (cur) out.push(cur);
|
|
2245
|
+
return out.map((c) => ({ c: c.c, v: c.v.map((p) => p.map(round2)), i: c.i.map((p) => p.map(round2)), o: c.o.map((p) => p.map(round2)) }));
|
|
2246
|
+
}
|
|
2247
|
+
function staticParts(m) {
|
|
2248
|
+
const sx = Math.hypot(m[0], m[1]);
|
|
2249
|
+
if (!(sx > 1e-12)) return null;
|
|
2250
|
+
const sy = (m[0] * m[3] - m[1] * m[2]) / sx;
|
|
2251
|
+
if (Math.abs(m[2] + m[1] / sx * sy) > 1e-6 || Math.abs(m[3] - m[0] / sx * sy) > 1e-6) return null;
|
|
2252
|
+
return { p: [m[4], m[5]], s: [sx * 100, sy * 100], r: Math.atan2(m[1], m[0]) * 180 / Math.PI };
|
|
2253
|
+
}
|
|
2254
|
+
function toLottie(scene, options = {}) {
|
|
2255
|
+
const fps = options.fps ?? scene.fps ?? 30;
|
|
2256
|
+
const precision = options.precision ?? 3;
|
|
2257
|
+
const round2 = (n) => Math.round(n * 10 ** precision) / 10 ** precision;
|
|
2258
|
+
const frame = (ms) => round2(ms * fps / 1e3);
|
|
2259
|
+
const timeline = compileMotionScene(scene);
|
|
2260
|
+
const nodes = indexMotionNodes(scene.artwork.nodes);
|
|
2261
|
+
const notes = [];
|
|
2262
|
+
let sampled = 0;
|
|
2263
|
+
const track = (id, p) => timeline.tracks.get(motionTrackKey(id, p)) ?? [];
|
|
2264
|
+
const layers = (id, p) => timeline.tracks.get(`${motionTrackKey(id, p)}\0add`) ?? [];
|
|
2265
|
+
const end = timeline.duration;
|
|
2266
|
+
const curveOf = (s) => {
|
|
2267
|
+
if (s.behavior || s.steps || s.geometric) return void 0;
|
|
2268
|
+
const e = s.reverse ? reverseCubicBezier(s.ease) : resolveEasing(s.ease);
|
|
2269
|
+
if (!e || e === "hold" || e.type !== "cubic-bezier") return void 0;
|
|
2270
|
+
return [e.x1, e.y1, e.x2, e.y2];
|
|
2271
|
+
};
|
|
2272
|
+
const animate = (segments, value, exact = true) => {
|
|
2273
|
+
const rest = value(0).map(round2);
|
|
2274
|
+
if (!segments.length) return { a: 0, k: rest.length === 1 ? rest[0] : rest };
|
|
2275
|
+
const keys = [];
|
|
2276
|
+
const key = (t, v, ease) => {
|
|
2277
|
+
const k = { t: frame(t), s: v.map(round2) };
|
|
2278
|
+
if (ease === "hold") k.h = 1;
|
|
2279
|
+
else {
|
|
2280
|
+
const [x1, y1, x2, y2] = ease === "linear" || !ease ? [0, 0, 1, 1] : ease;
|
|
2281
|
+
k.o = { x: [round2(x1)], y: [round2(y1)] };
|
|
2282
|
+
k.i = { x: [round2(x2)], y: [round2(y2)] };
|
|
2283
|
+
}
|
|
2284
|
+
if (keys.length && keys[keys.length - 1].t === k.t) keys[keys.length - 1] = k;
|
|
2285
|
+
else keys.push(k);
|
|
2286
|
+
};
|
|
2287
|
+
const times = [.../* @__PURE__ */ new Set([0, ...segments.flatMap((s) => [s.start, s.end]), end])].filter((t) => t >= 0 && t <= end).sort((a, b) => a - b);
|
|
2288
|
+
for (let n = 0; n + 1 < times.length; n++) {
|
|
2289
|
+
const a = times[n];
|
|
2290
|
+
const b = times[n + 1];
|
|
2291
|
+
const active = segments.filter((s) => s.start < b && s.end > a);
|
|
2292
|
+
if (!active.length) {
|
|
2293
|
+
key(a, value(a), "hold");
|
|
2294
|
+
continue;
|
|
2295
|
+
}
|
|
2296
|
+
const first = active[0];
|
|
2297
|
+
const curve = exact && active.every((s) => s.start === a && s.end === b && JSON.stringify(curveOf(s)) === JSON.stringify(curveOf(first))) ? curveOf(first) : void 0;
|
|
2298
|
+
if (curve) {
|
|
2299
|
+
key(a, value(a), curve);
|
|
2300
|
+
continue;
|
|
2301
|
+
}
|
|
2302
|
+
const count = Math.max(1, Math.ceil((b - a) * fps / 1e3));
|
|
2303
|
+
for (let k = 0; k < count; k++) {
|
|
2304
|
+
const t = a + (b - a) * k / count;
|
|
2305
|
+
key(t, value(t), "linear");
|
|
2306
|
+
sampled++;
|
|
2307
|
+
}
|
|
2308
|
+
}
|
|
2309
|
+
for (const s of segments) if (s.end <= s.start) key(s.start, value(s.start), "hold");
|
|
2310
|
+
keys.sort((x, y) => x.t - y.t);
|
|
2311
|
+
key(end, value(end), "hold");
|
|
2312
|
+
const last = keys[keys.length - 1];
|
|
2313
|
+
delete last.o;
|
|
2314
|
+
delete last.i;
|
|
2315
|
+
delete last.h;
|
|
2316
|
+
return { a: 1, k: keys };
|
|
2317
|
+
};
|
|
2318
|
+
const poseAt = (entry, t) => {
|
|
2319
|
+
const o = motionNodeOffset(entry, timeline, t, nodes);
|
|
2320
|
+
if (!o) return { p: [entry.center.x, entry.center.y], s: [100, 100], r: 0 };
|
|
2321
|
+
const m = o.matrix;
|
|
2322
|
+
const sx = Math.hypot(m[0], m[1]);
|
|
2323
|
+
const sy = sx > 1e-12 ? (m[0] * m[3] - m[1] * m[2]) / sx : 0;
|
|
2324
|
+
const c = entry.center;
|
|
2325
|
+
return { p: [m[0] * c.x + m[2] * c.y + m[4], m[1] * c.x + m[3] * c.y + m[5]], s: [sx * 100, sy * 100], r: Math.atan2(m[1], m[0]) * 180 / Math.PI };
|
|
2326
|
+
};
|
|
2327
|
+
const poseTransform = (entry) => {
|
|
2328
|
+
const id = entry.node.id;
|
|
2329
|
+
const tr = STATIC_TR();
|
|
2330
|
+
const c = [entry.center.x, entry.center.y].map(round2);
|
|
2331
|
+
const own = (p) => track(id, p);
|
|
2332
|
+
const layered = ["x", "y", "rotation", "scaleX", "scaleY"].some((p) => layers(id, p).length);
|
|
2333
|
+
const moving = ["x", "y", "rotation", "scaleX", "scaleY", "follow"].some((p) => own(p).length) || layered;
|
|
2334
|
+
if (moving) {
|
|
2335
|
+
const mixed = own("follow").length > 0 || layered;
|
|
2336
|
+
tr.a = { a: 0, k: c };
|
|
2337
|
+
tr.p = animate(mixed ? [...own("x"), ...own("y"), ...own("follow"), ...layers(id, "x"), ...layers(id, "y")] : [...own("x"), ...own("y")], (t) => poseAt(entry, t).p, !mixed);
|
|
2338
|
+
tr.r = animate(mixed ? [...own("rotation"), ...own("follow"), ...layers(id, "rotation")] : own("rotation"), (t) => [poseAt(entry, t).r], !mixed);
|
|
2339
|
+
tr.s = animate(mixed ? [...own("scaleX"), ...own("scaleY"), ...layers(id, "scaleX"), ...layers(id, "scaleY")] : [...own("scaleX"), ...own("scaleY")], (t) => poseAt(entry, t).s, !mixed);
|
|
2340
|
+
if (tr.p.a === 0) tr.p = { a: 0, k: poseAt(entry, 0).p.map(round2) };
|
|
2341
|
+
}
|
|
2342
|
+
const opacity = own("opacity");
|
|
2343
|
+
const rest = entry.node.opacity ?? 1;
|
|
2344
|
+
tr.o = opacity.length ? animate(opacity, (t) => [Math.min(1, Math.max(0, Number(trackValue(opacity, t) ?? rest))) * 100]) : { a: 0, k: round2(rest * 100) };
|
|
2345
|
+
return tr;
|
|
2346
|
+
};
|
|
2347
|
+
const owner = (path, p) => {
|
|
2348
|
+
const entry = nodes.get(path.id);
|
|
2349
|
+
if (!entry) return void 0;
|
|
2350
|
+
return [...entry.ancestors, path.id].find((id) => timeline.tracks.has(motionTrackKey(id, p)));
|
|
2351
|
+
};
|
|
2352
|
+
const paintOf = (paint, what, id) => {
|
|
2353
|
+
if (paint === void 0 || paint === null || paint === "none") return null;
|
|
2354
|
+
const c = rgba(paint);
|
|
2355
|
+
if (!c) notes.push(`"${id}": its ${what} is a gradient, which is not exported; it is left unpainted.`);
|
|
2356
|
+
return c;
|
|
2357
|
+
};
|
|
2358
|
+
const pathShapes = (path) => {
|
|
2359
|
+
const items = [];
|
|
2360
|
+
const shapeSegments = [...timeline.tracks.entries()].filter(([k]) => k.startsWith(`${path.id}\0`) && (k.endsWith("\0shape") || k.includes("\0effect:"))).flatMap(([, v]) => v);
|
|
2361
|
+
const geometryAt = (t) => {
|
|
2362
|
+
if (!shapeSegments.length) return vectorPathWorldBezier(path);
|
|
2363
|
+
const now = indexMotionNodes(motionFrameAt(scene, t, { draw: false, offsets: false }).document.nodes).get(path.id)?.node;
|
|
2364
|
+
return now && now.type === "path" ? vectorPathWorldBezier(expandVectorAppearances([now])[0]) : vectorPathWorldBezier(path);
|
|
2365
|
+
};
|
|
2366
|
+
const rest = lottieContours(toBezierPath(geometryAt(0)), round2);
|
|
2367
|
+
rest.forEach((contour, n) => {
|
|
2368
|
+
let ks = { a: 0, k: contour };
|
|
2369
|
+
if (shapeSegments.length) {
|
|
2370
|
+
const keys = [];
|
|
2371
|
+
let fits = true;
|
|
2372
|
+
const count = Math.max(1, Math.ceil(end * fps / 1e3));
|
|
2373
|
+
for (let k = 0; k <= count && fits; k++) {
|
|
2374
|
+
const t = end * k / count;
|
|
2375
|
+
const c = lottieContours(toBezierPath(geometryAt(t)), round2)[n];
|
|
2376
|
+
if (!c || c.v.length !== contour.v.length || c.c !== contour.c) fits = false;
|
|
2377
|
+
else keys.push({ t: frame(t), s: [c], ...k < count ? { o: { x: [0], y: [0] }, i: { x: [1], y: [1] } } : {} });
|
|
2378
|
+
}
|
|
2379
|
+
if (fits) {
|
|
2380
|
+
ks = { a: 1, k: keys };
|
|
2381
|
+
sampled += keys.length;
|
|
2382
|
+
} else notes.push(`"${path.id}": its morph changes how many points the outline has, which Lottie cannot tween; it shows its starting shape.`);
|
|
2383
|
+
}
|
|
2384
|
+
items.push({ ty: "sh", nm: `${path.id} ${n + 1}`, ks });
|
|
2385
|
+
});
|
|
2386
|
+
const fillFrom = owner(path, "fill");
|
|
2387
|
+
const strokeFrom = owner(path, "stroke");
|
|
2388
|
+
const widthFrom = owner(path, "strokeWidth");
|
|
2389
|
+
const fadeFrom = owner(path, "fillOpacity");
|
|
2390
|
+
const drawFrom = owner(path, "drawEnd");
|
|
2391
|
+
if (drawFrom) {
|
|
2392
|
+
const draw = track(drawFrom, "drawEnd");
|
|
2393
|
+
items.push({ ty: "tm", nm: "Draw on", s: { a: 0, k: 0 }, e: animate(draw, (t) => [Math.min(1, Math.max(0, Number(trackValue(draw, t) ?? 1))) * 100]), o: { a: 0, k: 0 }, m: 1 });
|
|
2394
|
+
}
|
|
2395
|
+
if (owner(path, "drawStart")) notes.push(`"${path.id}": drawing from a start other than 0 is not exported.`);
|
|
2396
|
+
const stroke = paintOf(path.stroke, "stroke", path.id);
|
|
2397
|
+
if (stroke || strokeFrom) {
|
|
2398
|
+
const colour = strokeFrom ? track(strokeFrom, "stroke") : [];
|
|
2399
|
+
items.push({
|
|
2400
|
+
ty: "st",
|
|
2401
|
+
nm: "Stroke",
|
|
2402
|
+
c: colour.length ? animate(colour, (t) => rgba(trackValue(colour, t)) ?? stroke ?? [0, 0, 0, 1]) : { a: 0, k: (stroke ?? [0, 0, 0, 1]).map(round2) },
|
|
2403
|
+
o: { a: 0, k: 100 },
|
|
2404
|
+
w: widthFrom ? animate(track(widthFrom, "strokeWidth"), (t) => [Math.max(0, Number(trackValue(track(widthFrom, "strokeWidth"), t) ?? path.strokeWidth ?? 1))]) : { a: 0, k: round2(path.strokeWidth ?? 1) },
|
|
2405
|
+
lc: path.strokeLinecap === "round" ? 2 : path.strokeLinecap === "square" ? 3 : 1,
|
|
2406
|
+
lj: path.strokeLinejoin === "round" ? 2 : path.strokeLinejoin === "bevel" ? 3 : 1,
|
|
2407
|
+
ml: 4
|
|
2408
|
+
});
|
|
2409
|
+
if (path.strokeDasharray?.length) notes.push(`"${path.id}": its dashes are not exported; the stroke is solid.`);
|
|
2410
|
+
if (path.strokeAlign && path.strokeAlign !== "center") notes.push(`"${path.id}": its stroke is drawn centred (Lottie has no inside or outside strokes).`);
|
|
2411
|
+
}
|
|
2412
|
+
const fill = paintOf(path.fill, "fill", path.id);
|
|
2413
|
+
if (fill || fillFrom) {
|
|
2414
|
+
const colour = fillFrom ? track(fillFrom, "fill") : [];
|
|
2415
|
+
const fade = fadeFrom ? track(fadeFrom, "fillOpacity") : [];
|
|
2416
|
+
items.push({
|
|
2417
|
+
ty: "fl",
|
|
2418
|
+
nm: "Fill",
|
|
2419
|
+
c: colour.length ? animate(colour, (t) => rgba(trackValue(colour, t)) ?? fill ?? [0, 0, 0, 1]) : { a: 0, k: (fill ?? [0, 0, 0, 1]).map(round2) },
|
|
2420
|
+
o: fade.length ? animate(fade, (t) => [Math.min(1, Math.max(0, Number(trackValue(fade, t) ?? 1))) * 100]) : { a: 0, k: 100 },
|
|
2421
|
+
r: path.fillRule === "evenodd" ? 2 : 1
|
|
2422
|
+
});
|
|
2423
|
+
}
|
|
2424
|
+
return items;
|
|
2425
|
+
};
|
|
2426
|
+
const nodeGroup = (node) => {
|
|
2427
|
+
const entry = nodes.get(node.id);
|
|
2428
|
+
if (timeline.tracks.has(motionTrackKey(node.id, "clipRect")) || timeline.tracks.has(motionTrackKey(node.id, "clipCircle")))
|
|
2429
|
+
notes.push(`"${node.id}": its wipe, iris or split reveal is not exported (Lottie masks belong to layers); it fades with its opacity only.`);
|
|
2430
|
+
let inner;
|
|
2431
|
+
const innerTr = STATIC_TR();
|
|
2432
|
+
if (node.type === "path") inner = [...pathShapes(node)];
|
|
2433
|
+
else {
|
|
2434
|
+
inner = [...node.children].reverse().map(nodeGroup);
|
|
2435
|
+
const m = nodeMatrix(node);
|
|
2436
|
+
if (m !== IDENTITY_MATRIX) {
|
|
2437
|
+
const parts = staticParts(m);
|
|
2438
|
+
if (!parts) notes.push(`"${node.id}": its transform skews, which is not exported; it is drawn without the skew.`);
|
|
2439
|
+
else {
|
|
2440
|
+
innerTr.p = { a: 0, k: parts.p.map(round2) };
|
|
2441
|
+
innerTr.s = { a: 0, k: parts.s.map(round2) };
|
|
2442
|
+
innerTr.r = { a: 0, k: round2(parts.r) };
|
|
2443
|
+
}
|
|
2444
|
+
}
|
|
2445
|
+
}
|
|
2446
|
+
const body = { ty: "gr", nm: `${node.id} (own)`, it: [...inner, innerTr] };
|
|
2447
|
+
return { ty: "gr", nm: node.name ?? node.id, ...node.visible === false ? { hd: true } : {}, it: [body, poseTransform(entry)] };
|
|
2448
|
+
};
|
|
2449
|
+
const artwork = { ...scene.artwork, nodes: expandVectorAppearances(scene.artwork.nodes) };
|
|
2450
|
+
const content = [...artwork.nodes].reverse().map(nodeGroup);
|
|
2451
|
+
if (artwork.background && rgba(artwork.background))
|
|
2452
|
+
content.push({ ty: "gr", nm: "Background", it: [{ ty: "rc", nm: "Background", p: { a: 0, k: [artwork.width / 2, artwork.height / 2] }, s: { a: 0, k: [artwork.width, artwork.height] }, r: { a: 0, k: 0 } }, { ty: "fl", c: { a: 0, k: rgba(artwork.background).map(round2) }, o: { a: 0, k: 100 }, r: 1 }, STATIC_TR()] });
|
|
2453
|
+
const camera = track("", "camera");
|
|
2454
|
+
const root = STATIC_TR();
|
|
2455
|
+
if (camera.length) {
|
|
2456
|
+
const box = (t) => trackValue(camera, t) ?? [0, 0, artwork.width, artwork.height];
|
|
2457
|
+
root.a = animate(camera, (t) => box(t).slice(0, 2), false);
|
|
2458
|
+
root.s = animate(camera, (t) => [artwork.width / box(t)[2] * 100, artwork.height / box(t)[3] * 100], false);
|
|
2459
|
+
}
|
|
2460
|
+
const op = Math.max(1, Math.ceil(end * fps / 1e3));
|
|
2461
|
+
const lottie = {
|
|
2462
|
+
v: "5.7.4",
|
|
2463
|
+
fr: fps,
|
|
2464
|
+
ip: 0,
|
|
2465
|
+
op,
|
|
2466
|
+
w: artwork.width,
|
|
2467
|
+
h: artwork.height,
|
|
2468
|
+
nm: scene.name,
|
|
2469
|
+
ddd: 0,
|
|
2470
|
+
assets: [],
|
|
2471
|
+
layers: [
|
|
2472
|
+
{
|
|
2473
|
+
ddd: 0,
|
|
2474
|
+
ind: 1,
|
|
2475
|
+
ty: 4,
|
|
2476
|
+
nm: scene.name,
|
|
2477
|
+
sr: 1,
|
|
2478
|
+
ks: { o: { a: 0, k: 100 }, r: { a: 0, k: 0 }, p: { a: 0, k: [0, 0, 0] }, a: { a: 0, k: [0, 0, 0] }, s: { a: 0, k: [100, 100, 100] } },
|
|
2479
|
+
ao: 0,
|
|
2480
|
+
shapes: [{ ty: "gr", nm: "Camera", it: [...content, root] }],
|
|
2481
|
+
ip: 0,
|
|
2482
|
+
op,
|
|
2483
|
+
st: 0,
|
|
2484
|
+
bm: 0
|
|
2485
|
+
}
|
|
2486
|
+
],
|
|
2487
|
+
markers: timeline.shots.map((s) => ({ tm: frame(s.start), cm: s.label ?? s.id, dr: frame(s.end - s.start) }))
|
|
2488
|
+
};
|
|
2489
|
+
return { lottie, sampled, notes: [...new Set(notes)] };
|
|
2490
|
+
}
|
|
2491
|
+
|
|
2186
2492
|
// src/animation-editor/controller.ts
|
|
2187
2493
|
var BLANK = { width: 800, height: 450, background: "#ffffff", nodes: [] };
|
|
2188
2494
|
var AnimationEditorController = class {
|
|
@@ -2827,6 +3133,14 @@ function createAnimationEditorToolProvider(controller, options = {}) {
|
|
|
2827
3133
|
annotations: { title: "Export as Flair vector motion", ...READ },
|
|
2828
3134
|
handler: ({ fps, curves, behaviors, flair }) => controller.toFlairMotion({ ...fps ? { fps } : {}, ...curves === void 0 ? {} : { curves }, ...behaviors === void 0 ? {} : { behaviors }, ...flair ? { flair } : {} })
|
|
2829
3135
|
};
|
|
3136
|
+
const exportLottie = {
|
|
3137
|
+
name: "export_lottie",
|
|
3138
|
+
title: "Export as Lottie",
|
|
3139
|
+
description: "Returns the animation as Lottie (Bodymovin JSON, v5) for lottie-web and the mobile Lottie players: one shape layer whose groups mirror the artwork, a marker per shot, eased changes as keyframes with bezier easing and the rest sampled. Also returns how many keyframes were sampled and notes on anything not carried over (gradients, dashes, reveal clips, morphs that change point counts).",
|
|
3140
|
+
inputSchema: object({ fps: { type: "number", minimum: 1, maximum: 120, description: "Frame rate (default: the scene's fps, else 30)." } }),
|
|
3141
|
+
annotations: { title: "Export as Lottie", ...READ },
|
|
3142
|
+
handler: ({ fps }) => toLottie(scene(), fps ? { fps } : {})
|
|
3143
|
+
};
|
|
2830
3144
|
const exportFlairEffect = {
|
|
2831
3145
|
name: "export_flair_effect",
|
|
2832
3146
|
title: "Export as a Flair effect",
|
|
@@ -3069,7 +3383,7 @@ function createAnimationEditorToolProvider(controller, options = {}) {
|
|
|
3069
3383
|
return { redone, ...redone ? { label } : {} };
|
|
3070
3384
|
}
|
|
3071
3385
|
};
|
|
3072
|
-
const reads = [getAnimation, getShot, findElements, getTimingGrid, checkAnimation, renderFrame, exportFlairMotion, exportFlairEffect, seekAnimation];
|
|
3386
|
+
const reads = [getAnimation, getShot, findElements, getTimingGrid, checkAnimation, renderFrame, exportFlairMotion, exportFlairEffect, exportLottie, seekAnimation];
|
|
3073
3387
|
const writes = [setArtworkSvg, fitAnimation, importFlairEffect, insertShots, updateShot, deleteShots, moveShot, addCommands, updateCommand, deleteCommands, setSettings, setControls, editAnimation, writeAnimation, undo, redo];
|
|
3074
3388
|
return {
|
|
3075
3389
|
id: options.id ?? "animation",
|
|
@@ -3207,6 +3521,7 @@ export {
|
|
|
3207
3521
|
toFlairEffectDocument,
|
|
3208
3522
|
toFlairSvgMotion,
|
|
3209
3523
|
toFlairVectorEffect,
|
|
3524
|
+
toLottie,
|
|
3210
3525
|
trackValue,
|
|
3211
3526
|
withMotionLayers,
|
|
3212
3527
|
withViewBox
|