sketchmark 2.0.0 → 2.1.1
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/README.md +274 -188
- package/bin/editor-ui.cjs +2285 -0
- package/bin/preview-ui.cjs +74 -0
- package/bin/sketchmark.cjs +648 -2008
- package/dist/src/animatable.d.ts +21 -0
- package/dist/src/animatable.js +439 -0
- package/dist/src/builders/index.d.ts +1 -11
- package/dist/src/builders/index.js +1 -19
- package/dist/src/diagnostics.js +1 -64
- package/dist/src/edit.d.ts +27 -0
- package/dist/src/edit.js +162 -0
- package/dist/src/index.d.ts +4 -13
- package/dist/src/index.js +4 -13
- package/dist/src/keyframes.d.ts +48 -0
- package/dist/src/keyframes.js +182 -0
- package/dist/src/motion.d.ts +4 -0
- package/dist/src/motion.js +262 -0
- package/dist/src/normalize.js +120 -151
- package/dist/src/presets/characters.d.ts +15 -0
- package/dist/src/presets/characters.js +113 -0
- package/dist/src/presets/compose.d.ts +5 -0
- package/dist/src/presets/compose.js +80 -0
- package/dist/src/presets/effects.d.ts +40 -0
- package/dist/src/presets/effects.js +79 -0
- package/dist/src/presets/helpers.d.ts +33 -0
- package/dist/src/presets/helpers.js +165 -0
- package/dist/src/presets/index.d.ts +9 -0
- package/dist/src/presets/index.js +48 -0
- package/dist/src/presets/motions.d.ts +33 -0
- package/dist/src/presets/motions.js +75 -0
- package/dist/src/presets/scenes.d.ts +35 -0
- package/dist/src/presets/scenes.js +134 -0
- package/dist/src/presets/shapes.d.ts +71 -0
- package/dist/src/presets/shapes.js +96 -0
- package/dist/src/presets/transitions.d.ts +29 -0
- package/dist/src/presets/transitions.js +113 -0
- package/dist/src/presets/types.d.ts +34 -0
- package/dist/src/presets/types.js +2 -0
- package/dist/src/render/html.js +1 -4
- package/dist/src/render/svg.d.ts +2 -2
- package/dist/src/render/svg.js +86 -82
- package/dist/src/render/three-html.js +67 -113
- package/dist/src/scenes.js +1 -0
- package/dist/src/schema.js +218 -280
- package/dist/src/shapes/builtins.js +11 -47
- package/dist/src/shapes/common.js +12 -11
- package/dist/src/shapes/registry.d.ts +0 -1
- package/dist/src/shapes/registry.js +0 -4
- package/dist/src/shapes/types.d.ts +1 -3
- package/dist/src/types.d.ts +57 -288
- package/dist/src/utils.d.ts +2 -11
- package/dist/src/utils.js +13 -70
- package/dist/src/validate.js +321 -275
- package/dist/tests/run.js +576 -510
- package/package.json +46 -52
- package/schema/visual.schema.json +1086 -930
package/dist/src/edit.js
ADDED
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.listElementReferences = listElementReferences;
|
|
4
|
+
exports.findElementById = findElementById;
|
|
5
|
+
exports.setElementProperty = setElementProperty;
|
|
6
|
+
exports.setTimelineKeyframe = setTimelineKeyframe;
|
|
7
|
+
exports.removeTimelineKeyframe = removeTimelineKeyframe;
|
|
8
|
+
exports.listTimelineTracks = listTimelineTracks;
|
|
9
|
+
exports.roundedRectClipPath = roundedRectClipPath;
|
|
10
|
+
exports.imageRoundedClip = imageRoundedClip;
|
|
11
|
+
const animatable_1 = require("./animatable");
|
|
12
|
+
const utils_1 = require("./utils");
|
|
13
|
+
const validate_1 = require("./validate");
|
|
14
|
+
function listElementReferences(document) {
|
|
15
|
+
const out = [];
|
|
16
|
+
visitElements(document.elements ?? [], (element, path, depth) => {
|
|
17
|
+
if (element.id)
|
|
18
|
+
out.push({ id: element.id, type: element.type, depth, path });
|
|
19
|
+
});
|
|
20
|
+
return out;
|
|
21
|
+
}
|
|
22
|
+
function findElementById(document, id) {
|
|
23
|
+
let found;
|
|
24
|
+
visitElements(document.elements ?? [], (element) => {
|
|
25
|
+
if (!found && element.id === id)
|
|
26
|
+
found = element;
|
|
27
|
+
});
|
|
28
|
+
return found;
|
|
29
|
+
}
|
|
30
|
+
function setElementProperty(document, id, property, value) {
|
|
31
|
+
const next = (0, utils_1.clone)(document);
|
|
32
|
+
const element = requireElement(next, id);
|
|
33
|
+
applyProperty(element, property, value);
|
|
34
|
+
assertValid(next);
|
|
35
|
+
return next;
|
|
36
|
+
}
|
|
37
|
+
function setTimelineKeyframe(document, id, property, time, value, options = {}) {
|
|
38
|
+
var _a, _b;
|
|
39
|
+
if (!(0, utils_1.isFiniteNumber)(time) || time < 0)
|
|
40
|
+
throw new Error("Keyframe time must be a non-negative finite number.");
|
|
41
|
+
const next = (0, utils_1.clone)(document);
|
|
42
|
+
const element = requireElement(next, id);
|
|
43
|
+
element.timeline ?? (element.timeline = {});
|
|
44
|
+
(_a = element.timeline).tracks ?? (_a.tracks = {});
|
|
45
|
+
const track = ((_b = element.timeline.tracks)[property] ?? (_b[property] = { keyframes: [] }));
|
|
46
|
+
if (options.ease && !track.ease)
|
|
47
|
+
track.ease = options.ease;
|
|
48
|
+
if (options.curve && !track.curve)
|
|
49
|
+
track.curve = (0, utils_1.clone)(options.curve);
|
|
50
|
+
const frame = makeKeyframe(time, value, options);
|
|
51
|
+
const existing = track.keyframes.findIndex((item) => keyframeTime(item) === time);
|
|
52
|
+
if (existing >= 0)
|
|
53
|
+
track.keyframes[existing] = mergeKeyframe(track.keyframes[existing], frame);
|
|
54
|
+
else
|
|
55
|
+
track.keyframes.push(frame);
|
|
56
|
+
track.keyframes.sort((left, right) => keyframeTime(left) - keyframeTime(right));
|
|
57
|
+
assertValid(next);
|
|
58
|
+
return next;
|
|
59
|
+
}
|
|
60
|
+
function removeTimelineKeyframe(document, id, property, time) {
|
|
61
|
+
const next = (0, utils_1.clone)(document);
|
|
62
|
+
const element = requireElement(next, id);
|
|
63
|
+
const track = element.timeline?.tracks?.[property];
|
|
64
|
+
if (!track)
|
|
65
|
+
return next;
|
|
66
|
+
track.keyframes = track.keyframes.filter((frame) => keyframeTime(frame) !== time);
|
|
67
|
+
if (!track.keyframes.length && element.timeline?.tracks)
|
|
68
|
+
delete element.timeline.tracks[property];
|
|
69
|
+
if (element.timeline?.tracks && !Object.keys(element.timeline.tracks).length)
|
|
70
|
+
delete element.timeline.tracks;
|
|
71
|
+
if (element.timeline && !element.timeline.start && !element.timeline.end && !element.timeline.tracks)
|
|
72
|
+
delete element.timeline;
|
|
73
|
+
assertValid(next);
|
|
74
|
+
return next;
|
|
75
|
+
}
|
|
76
|
+
function listTimelineTracks(document, id) {
|
|
77
|
+
const element = findElementById(document, id);
|
|
78
|
+
if (!element?.timeline?.tracks)
|
|
79
|
+
return [];
|
|
80
|
+
return Object.entries(element.timeline.tracks).map(([property, track]) => ({
|
|
81
|
+
property,
|
|
82
|
+
keyframes: (0, utils_1.clone)(track.keyframes),
|
|
83
|
+
...(track.curve ? { curve: (0, utils_1.clone)(track.curve) } : {}),
|
|
84
|
+
...(track.ease ? { ease: track.ease } : {})
|
|
85
|
+
}));
|
|
86
|
+
}
|
|
87
|
+
function roundedRectClipPath(x, y, width, height, radius = 0) {
|
|
88
|
+
const left = finiteOrZero(x);
|
|
89
|
+
const top = finiteOrZero(y);
|
|
90
|
+
const w = Math.max(0, finiteOrZero(width));
|
|
91
|
+
const h = Math.max(0, finiteOrZero(height));
|
|
92
|
+
const r = Math.min(Math.max(0, finiteOrZero(radius)), w / 2, h / 2);
|
|
93
|
+
const right = left + w;
|
|
94
|
+
const bottom = top + h;
|
|
95
|
+
if (r <= 0)
|
|
96
|
+
return `M ${left} ${top} H ${right} V ${bottom} H ${left} Z`;
|
|
97
|
+
return [
|
|
98
|
+
`M ${left + r} ${top}`,
|
|
99
|
+
`H ${right - r}`,
|
|
100
|
+
`Q ${right} ${top} ${right} ${top + r}`,
|
|
101
|
+
`V ${bottom - r}`,
|
|
102
|
+
`Q ${right} ${bottom} ${right - r} ${bottom}`,
|
|
103
|
+
`H ${left + r}`,
|
|
104
|
+
`Q ${left} ${bottom} ${left} ${bottom - r}`,
|
|
105
|
+
`V ${top + r}`,
|
|
106
|
+
`Q ${left} ${top} ${left + r} ${top}`,
|
|
107
|
+
"Z"
|
|
108
|
+
].join(" ");
|
|
109
|
+
}
|
|
110
|
+
function imageRoundedClip(element, radius = 0) {
|
|
111
|
+
return { type: "path", d: roundedRectClipPath(element.x, element.y, element.width, element.height, radius) };
|
|
112
|
+
}
|
|
113
|
+
function visitElements(elements, visit, prefix = [], depth = 0) {
|
|
114
|
+
for (const [index, element] of elements.entries()) {
|
|
115
|
+
const path = [...prefix, index];
|
|
116
|
+
visit(element, path, depth);
|
|
117
|
+
if (element.type === "group")
|
|
118
|
+
visitElements(element.children, visit, path, depth + 1);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
function requireElement(document, id) {
|
|
122
|
+
const element = findElementById(document, id);
|
|
123
|
+
if (!element)
|
|
124
|
+
throw new Error(`Unknown element '${id}'.`);
|
|
125
|
+
return element;
|
|
126
|
+
}
|
|
127
|
+
function applyProperty(element, property, value) {
|
|
128
|
+
(0, animatable_1.applyPropertyValue)(element, property, value);
|
|
129
|
+
}
|
|
130
|
+
function makeKeyframe(time, value, options) {
|
|
131
|
+
return {
|
|
132
|
+
time,
|
|
133
|
+
value: (0, utils_1.clone)(value),
|
|
134
|
+
...(options.in ? { in: (0, utils_1.clone)(options.in) } : {}),
|
|
135
|
+
...(options.out ?? options.curve ? { out: (0, utils_1.clone)(options.out ?? options.curve) } : {}),
|
|
136
|
+
...(options.interpolation ? { interpolation: (0, utils_1.clone)(options.interpolation) } : {})
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
function mergeKeyframe(existing, next) {
|
|
140
|
+
if (Array.isArray(existing))
|
|
141
|
+
return next;
|
|
142
|
+
return {
|
|
143
|
+
...existing,
|
|
144
|
+
...next,
|
|
145
|
+
in: next.in ?? existing.in,
|
|
146
|
+
out: next.out ?? existing.out,
|
|
147
|
+
interpolation: next.interpolation ?? existing.interpolation
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
function keyframeTime(frame) {
|
|
151
|
+
return Array.isArray(frame) ? frame[0] : frame.time;
|
|
152
|
+
}
|
|
153
|
+
function assertValid(document) {
|
|
154
|
+
const result = (0, validate_1.validateVisualDocument)(document);
|
|
155
|
+
if (!result.ok) {
|
|
156
|
+
const first = result.issues[0];
|
|
157
|
+
throw new Error(first ? `${first.path}: ${first.message}` : "Invalid visual document.");
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
function finiteOrZero(value) {
|
|
161
|
+
return (0, utils_1.isFiniteNumber)(value) ? value : 0;
|
|
162
|
+
}
|
package/dist/src/index.d.ts
CHANGED
|
@@ -1,19 +1,10 @@
|
|
|
1
1
|
export * from "./types";
|
|
2
2
|
export * from "./validate";
|
|
3
3
|
export * from "./normalize";
|
|
4
|
-
export * from "./kernel";
|
|
5
|
-
export * from "./schema";
|
|
6
|
-
export * from "./patch";
|
|
7
|
-
export * from "./compounds";
|
|
8
|
-
export * from "./project";
|
|
9
|
-
export * from "./scenes";
|
|
10
|
-
export * from "./sequences";
|
|
11
|
-
export * from "./deck";
|
|
12
4
|
export * from "./diagnostics";
|
|
5
|
+
export * from "./schema";
|
|
6
|
+
export * from "./keyframes";
|
|
7
|
+
export * from "./edit";
|
|
8
|
+
export * from "./animatable";
|
|
13
9
|
export * from "./render/svg";
|
|
14
10
|
export * from "./render/html";
|
|
15
|
-
export * from "./render/three-html";
|
|
16
|
-
export * from "./render/three-preview-svg";
|
|
17
|
-
export * from "./render/raw-three";
|
|
18
|
-
export * from "./export";
|
|
19
|
-
export * from "./player";
|
package/dist/src/index.js
CHANGED
|
@@ -17,19 +17,10 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
17
17
|
__exportStar(require("./types"), exports);
|
|
18
18
|
__exportStar(require("./validate"), exports);
|
|
19
19
|
__exportStar(require("./normalize"), exports);
|
|
20
|
-
__exportStar(require("./kernel"), exports);
|
|
21
|
-
__exportStar(require("./schema"), exports);
|
|
22
|
-
__exportStar(require("./patch"), exports);
|
|
23
|
-
__exportStar(require("./compounds"), exports);
|
|
24
|
-
__exportStar(require("./project"), exports);
|
|
25
|
-
__exportStar(require("./scenes"), exports);
|
|
26
|
-
__exportStar(require("./sequences"), exports);
|
|
27
|
-
__exportStar(require("./deck"), exports);
|
|
28
20
|
__exportStar(require("./diagnostics"), exports);
|
|
21
|
+
__exportStar(require("./schema"), exports);
|
|
22
|
+
__exportStar(require("./keyframes"), exports);
|
|
23
|
+
__exportStar(require("./edit"), exports);
|
|
24
|
+
__exportStar(require("./animatable"), exports);
|
|
29
25
|
__exportStar(require("./render/svg"), exports);
|
|
30
26
|
__exportStar(require("./render/html"), exports);
|
|
31
|
-
__exportStar(require("./render/three-html"), exports);
|
|
32
|
-
__exportStar(require("./render/three-preview-svg"), exports);
|
|
33
|
-
__exportStar(require("./render/raw-three"), exports);
|
|
34
|
-
__exportStar(require("./export"), exports);
|
|
35
|
-
__exportStar(require("./player"), exports);
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import type { MotionValue, Point2, TimelineCurve, VisualDocument } from "./types";
|
|
2
|
+
export type TimelineCurvePresetName = "linear" | "ease-in" | "ease-out" | "ease-in-out" | "hold";
|
|
3
|
+
export type KeyframeOffsetMap = Record<string, number | Record<string, number>>;
|
|
4
|
+
export interface KeyframePropertySpec {
|
|
5
|
+
value: MotionValue;
|
|
6
|
+
curve?: TimelineCurve;
|
|
7
|
+
ease?: TimelineCurvePresetName | string;
|
|
8
|
+
offset?: number;
|
|
9
|
+
in?: TimelineCurve;
|
|
10
|
+
out?: TimelineCurve;
|
|
11
|
+
interpolation?: TimelineCurve;
|
|
12
|
+
}
|
|
13
|
+
export type KeyframePropertyState = MotionValue | KeyframePropertySpec;
|
|
14
|
+
export interface KeyframeElementState {
|
|
15
|
+
position?: Point2 | KeyframePropertySpec;
|
|
16
|
+
x?: number | KeyframePropertySpec;
|
|
17
|
+
y?: number | KeyframePropertySpec;
|
|
18
|
+
rotation?: KeyframePropertyState;
|
|
19
|
+
scale?: KeyframePropertyState;
|
|
20
|
+
scaleX?: KeyframePropertyState;
|
|
21
|
+
scaleY?: KeyframePropertyState;
|
|
22
|
+
opacity?: KeyframePropertyState;
|
|
23
|
+
fill?: KeyframePropertyState;
|
|
24
|
+
stroke?: KeyframePropertyState;
|
|
25
|
+
strokeWidth?: KeyframePropertyState;
|
|
26
|
+
drawStart?: KeyframePropertyState;
|
|
27
|
+
drawEnd?: KeyframePropertyState;
|
|
28
|
+
dashOffset?: KeyframePropertyState;
|
|
29
|
+
[property: string]: KeyframePropertyState | undefined;
|
|
30
|
+
}
|
|
31
|
+
export interface KeyframeState {
|
|
32
|
+
time: number;
|
|
33
|
+
set?: Record<string, KeyframeElementState>;
|
|
34
|
+
elements?: Record<string, KeyframeElementState>;
|
|
35
|
+
curve?: TimelineCurve;
|
|
36
|
+
ease?: string;
|
|
37
|
+
offsets?: KeyframeOffsetMap;
|
|
38
|
+
}
|
|
39
|
+
export interface CompileKeyframeStateOptions {
|
|
40
|
+
validate?: boolean;
|
|
41
|
+
includeBaseFrame?: boolean;
|
|
42
|
+
baseTime?: number;
|
|
43
|
+
defaultCurve?: TimelineCurve;
|
|
44
|
+
defaultEase?: string;
|
|
45
|
+
offsets?: KeyframeOffsetMap;
|
|
46
|
+
}
|
|
47
|
+
export declare function timelineCurvePreset(name: TimelineCurvePresetName | string): TimelineCurve | undefined;
|
|
48
|
+
export declare function compileKeyframeStates(document: VisualDocument, states: KeyframeState[], options?: CompileKeyframeStateOptions): VisualDocument;
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.timelineCurvePreset = timelineCurvePreset;
|
|
4
|
+
exports.compileKeyframeStates = compileKeyframeStates;
|
|
5
|
+
const animatable_1 = require("./animatable");
|
|
6
|
+
const utils_1 = require("./utils");
|
|
7
|
+
const validate_1 = require("./validate");
|
|
8
|
+
function timelineCurvePreset(name) {
|
|
9
|
+
switch (name) {
|
|
10
|
+
case "linear":
|
|
11
|
+
return { type: "graph", points: [[0, 0], [1, 1]] };
|
|
12
|
+
case "ease-in":
|
|
13
|
+
return { type: "cubicBezier", x1: 0.42, y1: 0, x2: 1, y2: 1 };
|
|
14
|
+
case "ease-out":
|
|
15
|
+
return { type: "cubicBezier", x1: 0, y1: 0, x2: 0.58, y2: 1 };
|
|
16
|
+
case "ease-in-out":
|
|
17
|
+
return { type: "cubicBezier", x1: 0.42, y1: 0, x2: 0.58, y2: 1 };
|
|
18
|
+
case "hold":
|
|
19
|
+
return { type: "hold" };
|
|
20
|
+
default:
|
|
21
|
+
return undefined;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
function compileKeyframeStates(document, states, options = {}) {
|
|
25
|
+
if (options.validate !== false) {
|
|
26
|
+
const result = (0, validate_1.validateVisualDocument)(document);
|
|
27
|
+
if (!result.ok) {
|
|
28
|
+
const first = result.issues[0];
|
|
29
|
+
throw new Error(first ? `${first.path}: ${first.message}` : "Invalid visual document.");
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
const next = (0, utils_1.clone)(document);
|
|
33
|
+
const elements = (0, utils_1.flattenElements)(next.elements ?? []);
|
|
34
|
+
const byId = new Map(elements.filter((element) => element.id).map((element) => [element.id, element]));
|
|
35
|
+
const sorted = states.slice().sort((left, right) => left.time - right.time);
|
|
36
|
+
for (const state of sorted) {
|
|
37
|
+
if (!(0, utils_1.isFiniteNumber)(state.time) || state.time < 0)
|
|
38
|
+
throw new Error("Keyframe state time must be a non-negative finite number.");
|
|
39
|
+
const set = state.set ?? state.elements ?? {};
|
|
40
|
+
for (const [id, values] of Object.entries(set)) {
|
|
41
|
+
const element = byId.get(id);
|
|
42
|
+
if (!element)
|
|
43
|
+
throw new Error(`Cannot keyframe unknown element '${id}'.`);
|
|
44
|
+
for (const entry of normalizedStateEntries(element, values)) {
|
|
45
|
+
const offset = timingOffset(options.offsets, state.offsets, id, entry.property, entry.offset);
|
|
46
|
+
const time = state.time + offset;
|
|
47
|
+
if (!(0, utils_1.isFiniteNumber)(time) || time < 0)
|
|
48
|
+
throw new Error(`Keyframe time for '${id}.${entry.property}' must be a non-negative finite number after offsets.`);
|
|
49
|
+
const ease = entry.ease ?? state.ease ?? options.defaultEase;
|
|
50
|
+
const out = entry.out ?? entry.curve ?? state.curve ?? options.defaultCurve ?? timelineCurvePreset(ease ?? "");
|
|
51
|
+
addTrackKeyframe(element, entry.property, entry.value, time, {
|
|
52
|
+
in: entry.in,
|
|
53
|
+
out,
|
|
54
|
+
interpolation: entry.interpolation,
|
|
55
|
+
ease: out || entry.in || entry.interpolation ? undefined : ease,
|
|
56
|
+
includeBaseFrame: options.includeBaseFrame !== false,
|
|
57
|
+
baseTime: options.baseTime ?? 0
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
sortTimelineTracks(next.elements ?? []);
|
|
63
|
+
return next;
|
|
64
|
+
}
|
|
65
|
+
function normalizedStateEntries(element, state) {
|
|
66
|
+
const entries = [];
|
|
67
|
+
const position = state.position;
|
|
68
|
+
const hasX = state.x !== undefined;
|
|
69
|
+
const hasY = state.y !== undefined;
|
|
70
|
+
if (position !== undefined) {
|
|
71
|
+
const parsed = parsePropertyState(element, "position", position);
|
|
72
|
+
if (!(0, utils_1.isPoint2)(parsed.value))
|
|
73
|
+
throw new Error(`Element '${element.id ?? "(unnamed)"}' position keyframe must be [x,y].`);
|
|
74
|
+
entries.push({ property: "position", ...parsed });
|
|
75
|
+
}
|
|
76
|
+
else if (hasX && hasY && (0, utils_1.isFiniteNumber)(state.x) && (0, utils_1.isFiniteNumber)(state.y)) {
|
|
77
|
+
entries.push({ property: "position", value: [state.x, state.y] });
|
|
78
|
+
}
|
|
79
|
+
else {
|
|
80
|
+
if (hasX && state.x !== undefined)
|
|
81
|
+
entries.push({ property: "x", ...parsePropertyState(element, "x", state.x) });
|
|
82
|
+
if (hasY && state.y !== undefined)
|
|
83
|
+
entries.push({ property: "y", ...parsePropertyState(element, "y", state.y) });
|
|
84
|
+
}
|
|
85
|
+
for (const [property, value] of Object.entries(state)) {
|
|
86
|
+
if (property === "position" || property === "x" || property === "y" || value === undefined)
|
|
87
|
+
continue;
|
|
88
|
+
entries.push({ property, ...parsePropertyState(element, property, value) });
|
|
89
|
+
}
|
|
90
|
+
return entries;
|
|
91
|
+
}
|
|
92
|
+
function parsePropertyState(element, property, state) {
|
|
93
|
+
if (isPropertySpec(state)) {
|
|
94
|
+
if (!(0, animatable_1.isTimelineValue)(state.value))
|
|
95
|
+
throw new Error(`Element '${element.id ?? "(unnamed)"}' property '${property}' must be a timeline value.`);
|
|
96
|
+
return {
|
|
97
|
+
value: (0, utils_1.clone)(state.value),
|
|
98
|
+
...(state.curve ? { curve: (0, utils_1.clone)(state.curve) } : {}),
|
|
99
|
+
...(state.ease ? { ease: state.ease } : {}),
|
|
100
|
+
...(state.offset !== undefined ? { offset: state.offset } : {}),
|
|
101
|
+
...(state.in ? { in: (0, utils_1.clone)(state.in) } : {}),
|
|
102
|
+
...(state.out ? { out: (0, utils_1.clone)(state.out) } : {}),
|
|
103
|
+
...(state.interpolation ? { interpolation: (0, utils_1.clone)(state.interpolation) } : {})
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
if (!(0, animatable_1.isTimelineValue)(state))
|
|
107
|
+
throw new Error(`Element '${element.id ?? "(unnamed)"}' property '${property}' must be a timeline value.`);
|
|
108
|
+
return { value: (0, utils_1.clone)(state) };
|
|
109
|
+
}
|
|
110
|
+
function addTrackKeyframe(element, property, value, time, options) {
|
|
111
|
+
var _a;
|
|
112
|
+
element.timeline ?? (element.timeline = {});
|
|
113
|
+
(_a = element.timeline).tracks ?? (_a.tracks = {});
|
|
114
|
+
const tracks = element.timeline.tracks;
|
|
115
|
+
const track = (tracks[property] ?? (tracks[property] = { keyframes: [] }));
|
|
116
|
+
if (options.ease && !track.ease)
|
|
117
|
+
track.ease = options.ease;
|
|
118
|
+
if (options.includeBaseFrame && time !== options.baseTime && !track.keyframes.some((frame) => keyframeTime(frame) === options.baseTime)) {
|
|
119
|
+
const base = baseValue(element, property);
|
|
120
|
+
if (base !== undefined)
|
|
121
|
+
track.keyframes.push(makeKeyframe(options.baseTime, base, { out: options.out ?? options.interpolation }));
|
|
122
|
+
}
|
|
123
|
+
const next = makeKeyframe(time, value, { in: options.in, out: options.out, interpolation: options.interpolation });
|
|
124
|
+
const existingIndex = track.keyframes.findIndex((frame) => keyframeTime(frame) === time);
|
|
125
|
+
if (existingIndex >= 0)
|
|
126
|
+
track.keyframes[existingIndex] = mergeKeyframe(track.keyframes[existingIndex], next);
|
|
127
|
+
else
|
|
128
|
+
track.keyframes.push(next);
|
|
129
|
+
}
|
|
130
|
+
function baseValue(element, property) {
|
|
131
|
+
return (0, animatable_1.baseValueForProperty)(element, property);
|
|
132
|
+
}
|
|
133
|
+
function sortTimelineTracks(elements) {
|
|
134
|
+
for (const element of elements) {
|
|
135
|
+
for (const track of Object.values(element.timeline?.tracks ?? {})) {
|
|
136
|
+
track.keyframes.sort((left, right) => keyframeTime(left) - keyframeTime(right));
|
|
137
|
+
}
|
|
138
|
+
if (element.type === "group")
|
|
139
|
+
sortTimelineTracks(element.children);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
function makeKeyframe(time, value, options = {}) {
|
|
143
|
+
return {
|
|
144
|
+
time,
|
|
145
|
+
value: (0, utils_1.clone)(value),
|
|
146
|
+
...(options.in ? { in: (0, utils_1.clone)(options.in) } : {}),
|
|
147
|
+
...(options.out ? { out: (0, utils_1.clone)(options.out) } : {}),
|
|
148
|
+
...(options.interpolation ? { interpolation: (0, utils_1.clone)(options.interpolation) } : {})
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
function mergeKeyframe(existing, next) {
|
|
152
|
+
if (Array.isArray(existing))
|
|
153
|
+
return next;
|
|
154
|
+
return {
|
|
155
|
+
...existing,
|
|
156
|
+
...next,
|
|
157
|
+
in: next.in ?? existing.in,
|
|
158
|
+
out: next.out ?? existing.out,
|
|
159
|
+
interpolation: next.interpolation ?? existing.interpolation
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
function keyframeTime(frame) {
|
|
163
|
+
return Array.isArray(frame) ? frame[0] : frame.time;
|
|
164
|
+
}
|
|
165
|
+
function timingOffset(global, local, id, property, own) {
|
|
166
|
+
const value = offsetFromMap(global, id, property) + offsetFromMap(local, id, property) + Number(own ?? 0);
|
|
167
|
+
if (!(0, utils_1.isFiniteNumber)(value))
|
|
168
|
+
throw new Error(`Keyframe offset for '${id}.${property}' must be finite.`);
|
|
169
|
+
return value;
|
|
170
|
+
}
|
|
171
|
+
function offsetFromMap(map, id, property) {
|
|
172
|
+
const entry = map?.[id];
|
|
173
|
+
if (entry === undefined)
|
|
174
|
+
return 0;
|
|
175
|
+
if ((0, utils_1.isFiniteNumber)(entry))
|
|
176
|
+
return entry;
|
|
177
|
+
const value = entry[property];
|
|
178
|
+
return (0, utils_1.isFiniteNumber)(value) ? value : 0;
|
|
179
|
+
}
|
|
180
|
+
function isPropertySpec(value) {
|
|
181
|
+
return !!value && typeof value === "object" && !Array.isArray(value) && "value" in value;
|
|
182
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { MotionDriver, MotionExpressionInput, MotionValue, VisualDocument, VisualElement } from "./types";
|
|
2
|
+
export declare function applyMotionDrivers(document: VisualDocument, time: number): VisualDocument;
|
|
3
|
+
export declare function evaluateDriver(driver: MotionDriver, elements: VisualElement[], time: number): MotionValue | undefined;
|
|
4
|
+
export declare function evaluateExpression(input: MotionExpressionInput, elements: VisualElement[], time: number): MotionValue;
|