@voila.dev/chart 5.0.118 → 6.0.121
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/core/motion/motion-store.d.ts.map +1 -1
- package/dist/core/motion/motion-store.js +65 -105
- package/dist/core/motion/motion-store.js.map +3 -3
- package/dist/core/motion/points-motion.d.ts.map +1 -1
- package/dist/core/motion/points-motion.js +9 -22
- package/dist/core/motion/points-motion.js.map +2 -2
- package/dist/core/motion/strings.d.ts +4 -2
- package/dist/core/motion/strings.d.ts.map +1 -1
- package/dist/core/motion/strings.js +5 -6
- package/dist/core/motion/strings.js.map +2 -2
- package/dist/core/motion/timing.d.ts +16 -18
- package/dist/core/motion/timing.d.ts.map +1 -1
- package/dist/core/motion/timing.js +9 -8
- package/dist/core/motion/timing.js.map +2 -2
- package/dist/core/motion/tween-timing.d.ts +17 -0
- package/dist/core/motion/tween-timing.d.ts.map +1 -0
- package/dist/core/motion/tween-timing.js +22 -0
- package/dist/core/motion/tween-timing.js.map +7 -0
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +2 -2
- package/dist/react/chart.d.ts +3 -2
- package/dist/react/chart.d.ts.map +1 -1
- package/dist/react/chart.js +3 -1
- package/dist/react/chart.js.map +2 -2
- package/dist/react/use-animated-scene.d.ts.map +1 -1
- package/dist/react/use-animated-scene.js +4 -4
- package/dist/react/use-animated-scene.js.map +2 -2
- package/package.json +2 -2
- package/src/core/motion/motion-store.ts +93 -139
- package/src/core/motion/points-motion.ts +12 -24
- package/src/core/motion/strings.ts +9 -12
- package/src/core/motion/timing.ts +28 -33
- package/src/core/motion/tween-timing.ts +35 -0
- package/src/index.ts +2 -0
- package/src/react/chart.tsx +9 -4
- package/src/react/use-animated-scene.ts +6 -4
- package/dist/core/motion/leaving.d.ts +0 -12
- package/dist/core/motion/leaving.d.ts.map +0 -1
- package/dist/core/motion/leaving.js +0 -33
- package/dist/core/motion/leaving.js.map +0 -7
- package/src/core/motion/leaving.ts +0 -55
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"motion-store.d.ts","sourceRoot":"","sources":["../../../src/core/motion/motion-store.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"motion-store.d.ts","sourceRoot":"","sources":["../../../src/core/motion/motion-store.ts"],"names":[],"mappings":"AASA,OAAO,EAAE,KAAK,WAAW,EAAgB,MAAM,yBAAyB,CAAC;AACzE,OAAO,KAAK,EACX,UAAU,EAIV,MAAM,iBAAiB,CAAC;AAEzB;;;;;;;;GAQG;AACH,MAAM,WAAW,WAAW;IAC3B,QAAQ,CAAC,KAAK,EAAE,UAAU,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/C,8FAA8F;IAC9F,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;IAChC,qDAAqD;IACrD,IAAI,CAAC,KAAK,EAAE,UAAU,GAAG,IAAI,CAAC;IAC9B,KAAK,CAAC,GAAG,EAAE,MAAM,GAAG,UAAU,CAAC;IAC/B,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CAC9B;AAiGD,wBAAgB,iBAAiB,CAChC,OAAO,EAAE,UAAU,EACnB,MAAM,EAAE,WAAW,GACjB,WAAW,CA4Qb"}
|
|
@@ -1,14 +1,9 @@
|
|
|
1
|
-
import { spring, tween } from "@voila.dev/motion";
|
|
2
1
|
import { enterFrom, exitTo } from "./enter-exit.js";
|
|
3
|
-
import {
|
|
4
|
-
leavingGuides,
|
|
5
|
-
withLeaving
|
|
6
|
-
} from "./leaving.js";
|
|
7
2
|
import {
|
|
8
3
|
STRING_CHANNELS,
|
|
9
4
|
stringMixer,
|
|
10
5
|
stringOf,
|
|
11
|
-
|
|
6
|
+
withString
|
|
12
7
|
} from "./strings.js";
|
|
13
8
|
import { staggerDelay } from "./timing.js";
|
|
14
9
|
const FIELDS = {
|
|
@@ -22,20 +17,28 @@ const FIELDS = {
|
|
|
22
17
|
const OPACITY = "opacity";
|
|
23
18
|
const DRAWN = "drawn";
|
|
24
19
|
const SEPARATOR = "\0";
|
|
25
|
-
|
|
20
|
+
const GUIDE_ROLES = /* @__PURE__ */ new Set(["grid", "axis"]);
|
|
21
|
+
function first(parent) {
|
|
22
|
+
return parent === null ? void 0 : parent + SEPARATOR;
|
|
23
|
+
}
|
|
24
|
+
function walk(nodes, visit, parent = null) {
|
|
25
|
+
let after = first(parent);
|
|
26
26
|
for (const node of nodes) {
|
|
27
|
-
visit(node, nodes);
|
|
28
|
-
if (node.kind === "group") walk(node.children, visit);
|
|
27
|
+
visit(node, nodes, after);
|
|
28
|
+
if (node.kind === "group") walk(node.children, visit, node.key);
|
|
29
|
+
after = node.key;
|
|
29
30
|
}
|
|
30
31
|
}
|
|
31
32
|
function index(nodes) {
|
|
32
33
|
const byKey = /* @__PURE__ */ new Map();
|
|
33
34
|
const siblings = /* @__PURE__ */ new Map();
|
|
34
|
-
|
|
35
|
+
const afters = /* @__PURE__ */ new Map();
|
|
36
|
+
walk(nodes, (node, list, after) => {
|
|
35
37
|
byKey.set(node.key, node);
|
|
36
38
|
siblings.set(node.key, list);
|
|
39
|
+
afters.set(node.key, after);
|
|
37
40
|
});
|
|
38
|
-
return { byKey, siblings };
|
|
41
|
+
return { byKey, siblings, afters };
|
|
39
42
|
}
|
|
40
43
|
function channelKey(node, channel) {
|
|
41
44
|
return `${node}${SEPARATOR}${channel}`;
|
|
@@ -43,6 +46,9 @@ function channelKey(node, channel) {
|
|
|
43
46
|
function elapsed(motion, now) {
|
|
44
47
|
return Math.max(0, (now - motion.start) / 1e3);
|
|
45
48
|
}
|
|
49
|
+
function sample(motion, now) {
|
|
50
|
+
return motion.generator.at(elapsed(motion, now));
|
|
51
|
+
}
|
|
46
52
|
function done(motion, now) {
|
|
47
53
|
return elapsed(motion, now) >= motion.generator.duration;
|
|
48
54
|
}
|
|
@@ -51,53 +57,36 @@ function clamp(value) {
|
|
|
51
57
|
}
|
|
52
58
|
function createMotionStore(initial, timing) {
|
|
53
59
|
let target = initial;
|
|
54
|
-
const
|
|
55
|
-
const strings = /* @__PURE__ */ new Map();
|
|
56
|
-
const guides = /* @__PURE__ */ new Map();
|
|
60
|
+
const channels = /* @__PURE__ */ new Map();
|
|
57
61
|
const shapes = /* @__PURE__ */ new Map();
|
|
58
62
|
const leaving = /* @__PURE__ */ new Map();
|
|
59
|
-
function generator(from, to, velocity) {
|
|
60
|
-
return timing.type === "spring" ? spring({
|
|
61
|
-
from,
|
|
62
|
-
to,
|
|
63
|
-
velocity,
|
|
64
|
-
duration: timing.duration,
|
|
65
|
-
bounce: timing.bounce
|
|
66
|
-
}) : tween({
|
|
67
|
-
keyframes: [from, to],
|
|
68
|
-
duration: timing.duration,
|
|
69
|
-
ease: timing.easing
|
|
70
|
-
});
|
|
71
|
-
}
|
|
72
63
|
function value(node, channel, fallback, now) {
|
|
73
|
-
const motion =
|
|
74
|
-
return motion ?
|
|
64
|
+
const motion = channels.get(channelKey(node, channel));
|
|
65
|
+
return motion ? sample(motion, now).value : fallback;
|
|
75
66
|
}
|
|
76
67
|
function spring1(node, channel, from, to, start, now) {
|
|
77
68
|
const key = channelKey(node, channel);
|
|
78
|
-
const running =
|
|
79
|
-
const velocity = running ?
|
|
80
|
-
|
|
69
|
+
const running = channels.get(key);
|
|
70
|
+
const velocity = running ? sample(running, now).velocity : 0;
|
|
71
|
+
channels.delete(key);
|
|
81
72
|
if (from === to && velocity === 0) return;
|
|
82
|
-
|
|
73
|
+
channels.set(key, { generator: timing.motion(from, to, velocity), start });
|
|
83
74
|
}
|
|
84
75
|
function mixString(from, to, channel, start) {
|
|
85
76
|
const key = channelKey(to.key, channel);
|
|
86
|
-
|
|
77
|
+
channels.delete(key);
|
|
87
78
|
const was = stringOf(from, channel);
|
|
88
79
|
const goal = stringOf(to, channel);
|
|
89
80
|
if (was === void 0 || goal === void 0 || was === goal) return;
|
|
90
|
-
|
|
91
|
-
generator:
|
|
81
|
+
channels.set(key, {
|
|
82
|
+
generator: timing.motion(0, 1, 0),
|
|
92
83
|
start,
|
|
93
84
|
mix: stringMixer(to, channel, was, goal)
|
|
94
85
|
});
|
|
95
86
|
}
|
|
96
87
|
function drop(node) {
|
|
97
|
-
for (const
|
|
98
|
-
|
|
99
|
-
if (key.startsWith(node + SEPARATOR)) motions.delete(key);
|
|
100
|
-
}
|
|
88
|
+
for (const key of channels.keys()) {
|
|
89
|
+
if (key.startsWith(node + SEPARATOR)) channels.delete(key);
|
|
101
90
|
}
|
|
102
91
|
shapes.delete(node);
|
|
103
92
|
leaving.delete(node);
|
|
@@ -120,7 +109,7 @@ function createMotionStore(initial, timing) {
|
|
|
120
109
|
if (to.kind !== "path" || from.kind !== "path") return;
|
|
121
110
|
const previous = shapes.get(key);
|
|
122
111
|
shapes.delete(key);
|
|
123
|
-
|
|
112
|
+
channels.delete(channelKey(key, "d"));
|
|
124
113
|
const motion = to.motion;
|
|
125
114
|
const plan = motion && from.geometry && to.geometry ? motion.plan(from.geometry, to.geometry) : void 0;
|
|
126
115
|
if (!plan) {
|
|
@@ -129,7 +118,7 @@ function createMotionStore(initial, timing) {
|
|
|
129
118
|
}
|
|
130
119
|
for (const channel of previous?.plan.targets.keys() ?? []) {
|
|
131
120
|
if (!plan.targets.has(channel)) {
|
|
132
|
-
|
|
121
|
+
channels.delete(channelKey(key, `g${channel}`));
|
|
133
122
|
}
|
|
134
123
|
}
|
|
135
124
|
for (const [channel, goal] of plan.targets) {
|
|
@@ -155,37 +144,31 @@ function createMotionStore(initial, timing) {
|
|
|
155
144
|
let next = { ...node, ...moved };
|
|
156
145
|
const shape = shapes.get(key);
|
|
157
146
|
if (next.kind === "path" && shape) {
|
|
158
|
-
const
|
|
147
|
+
const geometry = shape.plan.build(
|
|
159
148
|
(channel) => value(key, `g${channel}`, shape.plan.targets.get(channel) ?? 0, now)
|
|
160
149
|
);
|
|
161
|
-
next = { ...next, geometry
|
|
150
|
+
next = { ...next, geometry, d: shape.motion.draw(geometry) };
|
|
162
151
|
}
|
|
163
|
-
const mixed = {};
|
|
164
152
|
for (const channel of STRING_CHANNELS) {
|
|
165
|
-
const motion =
|
|
166
|
-
if (motion) {
|
|
167
|
-
|
|
168
|
-
motion.generator.at(elapsed(motion, now)).value
|
|
169
|
-
);
|
|
153
|
+
const motion = channels.get(channelKey(key, channel));
|
|
154
|
+
if (motion?.mix) {
|
|
155
|
+
next = withString(next, channel, motion.mix(sample(motion, now).value));
|
|
170
156
|
}
|
|
171
157
|
}
|
|
172
|
-
next = withStrings(next, mixed);
|
|
173
158
|
const opacity = value(key, OPACITY, 1, now);
|
|
174
159
|
const drawn = value(key, DRAWN, 1, now);
|
|
175
160
|
if (opacity === 1 && drawn === 1) return next;
|
|
176
|
-
const geometry = next.kind === "path" ? next.geometry : void 0;
|
|
177
|
-
const length = geometry && next.kind === "path" ? next.motion?.length?.(geometry) : 0;
|
|
178
161
|
return {
|
|
179
162
|
...next,
|
|
180
163
|
paint: {
|
|
181
164
|
...next.paint,
|
|
182
165
|
opacity: (next.paint.opacity ?? 1) * clamp(opacity),
|
|
183
|
-
...drawn < 1 && geometry
|
|
166
|
+
...drawn < 1 && next.kind === "path" && next.geometry && {
|
|
184
167
|
drawn: {
|
|
185
168
|
fraction: clamp(drawn),
|
|
186
|
-
length: length ?? 0
|
|
169
|
+
length: next.motion?.length?.(next.geometry) ?? 0
|
|
187
170
|
}
|
|
188
|
-
}
|
|
171
|
+
}
|
|
189
172
|
}
|
|
190
173
|
};
|
|
191
174
|
}
|
|
@@ -198,36 +181,24 @@ function createMotionStore(initial, timing) {
|
|
|
198
181
|
place(key);
|
|
199
182
|
}
|
|
200
183
|
}
|
|
201
|
-
|
|
184
|
+
place(first(parent));
|
|
202
185
|
for (const node of list) {
|
|
203
186
|
out.push(paintNode(node, now));
|
|
204
187
|
place(node.key);
|
|
205
188
|
}
|
|
206
|
-
|
|
207
|
-
const fading = [...guides.values()].map(
|
|
208
|
-
(motion) => [
|
|
209
|
-
motion.guide,
|
|
210
|
-
Math.max(0, motion.generator.at(elapsed(motion, now)).value)
|
|
211
|
-
]
|
|
212
|
-
);
|
|
213
|
-
return withLeaving(out, parent, fading);
|
|
189
|
+
return out;
|
|
214
190
|
}
|
|
215
191
|
function prune(now) {
|
|
216
|
-
for (const
|
|
217
|
-
|
|
218
|
-
if (done(motion, now)) motions.delete(key);
|
|
219
|
-
}
|
|
192
|
+
for (const [key, motion] of channels) {
|
|
193
|
+
if (done(motion, now)) channels.delete(key);
|
|
220
194
|
}
|
|
221
195
|
const moving = new Set(
|
|
222
|
-
[...
|
|
223
|
-
(key) => key.split(SEPARATOR)[0]
|
|
224
|
-
)
|
|
196
|
+
[...channels.keys()].map((key) => key.split(SEPARATOR)[0])
|
|
225
197
|
);
|
|
226
|
-
for (const
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
if (!moving.has(key)) leaving.delete(key);
|
|
198
|
+
for (const byNode of [shapes, leaving]) {
|
|
199
|
+
for (const key of byNode.keys()) {
|
|
200
|
+
if (!moving.has(key)) byNode.delete(key);
|
|
201
|
+
}
|
|
231
202
|
}
|
|
232
203
|
}
|
|
233
204
|
return {
|
|
@@ -235,34 +206,28 @@ function createMotionStore(initial, timing) {
|
|
|
235
206
|
const before = index(target.nodes);
|
|
236
207
|
const after = index(scene.nodes);
|
|
237
208
|
function painted(key) {
|
|
238
|
-
const node = before.byKey.get(key) ?? leaving.get(key)?.node
|
|
209
|
+
const node = before.byKey.get(key) ?? leaving.get(key)?.node;
|
|
239
210
|
return node && paintNode(node, now);
|
|
240
211
|
}
|
|
241
|
-
for (const guide of leavingGuides(
|
|
242
|
-
target.nodes,
|
|
243
|
-
(key) => after.byKey.has(key)
|
|
244
|
-
)) {
|
|
245
|
-
guides.set(guide.node.key, {
|
|
246
|
-
generator: generator(value(guide.node.key, OPACITY, 1, now), 0, 0),
|
|
247
|
-
start: now,
|
|
248
|
-
guide
|
|
249
|
-
});
|
|
250
|
-
}
|
|
251
212
|
for (const key of /* @__PURE__ */ new Set([...before.byKey.keys(), ...leaving.keys()])) {
|
|
252
213
|
if (after.byKey.has(key)) continue;
|
|
253
214
|
const base = before.byKey.get(key) ?? leaving.get(key)?.node;
|
|
254
|
-
const
|
|
255
|
-
const
|
|
215
|
+
const exit = leaving.get(key);
|
|
216
|
+
const siblings = exit?.siblings ?? before.siblings.get(key) ?? [];
|
|
217
|
+
const guide = base.kind !== "group" && GUIDE_ROLES.has(base.role ?? "");
|
|
218
|
+
const goal = exitTo(base, siblings, after.byKey) ?? (guide && base);
|
|
256
219
|
const from = painted(key);
|
|
257
220
|
if (!goal || !from) {
|
|
258
221
|
drop(key);
|
|
259
222
|
continue;
|
|
260
223
|
}
|
|
261
224
|
move(from, goal, now, now);
|
|
262
|
-
|
|
225
|
+
if (guide) {
|
|
226
|
+
spring1(key, OPACITY, value(key, OPACITY, 1, now), 0, now, now);
|
|
227
|
+
}
|
|
263
228
|
leaving.set(key, {
|
|
264
229
|
node: goal,
|
|
265
|
-
after:
|
|
230
|
+
after: exit ? exit.after : before.afters.get(key),
|
|
266
231
|
siblings
|
|
267
232
|
});
|
|
268
233
|
}
|
|
@@ -274,17 +239,16 @@ function createMotionStore(initial, timing) {
|
|
|
274
239
|
if (node.kind === "group") continue;
|
|
275
240
|
const start = now + staggerDelay(timing, order.get(node.key) ?? 0, marks.length) * 1e3;
|
|
276
241
|
const old = painted(node.key);
|
|
277
|
-
leaving.delete(node.key);
|
|
242
|
+
const returning = leaving.delete(node.key);
|
|
278
243
|
const from = old ?? enterFrom(node, after.siblings.get(node.key) ?? [], before.byKey);
|
|
279
244
|
if (from) move(from, node, start, now);
|
|
280
|
-
const fading =
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
245
|
+
const fading = channels.get(channelKey(node.key, OPACITY));
|
|
246
|
+
if (!from && node.enter !== "none" || fading) {
|
|
247
|
+
const shown = fading ? Math.max(0, sample(fading, now).value) : 0;
|
|
248
|
+
if (returning) channels.delete(channelKey(node.key, OPACITY));
|
|
284
249
|
spring1(node.key, OPACITY, shown, 1, start, now);
|
|
285
250
|
}
|
|
286
251
|
}
|
|
287
|
-
for (const key of after.byKey.keys()) guides.delete(key);
|
|
288
252
|
target = scene;
|
|
289
253
|
},
|
|
290
254
|
introduce(now) {
|
|
@@ -300,22 +264,18 @@ function createMotionStore(initial, timing) {
|
|
|
300
264
|
return any;
|
|
301
265
|
},
|
|
302
266
|
snap(scene) {
|
|
303
|
-
for (const motions of [
|
|
267
|
+
for (const motions of [channels, shapes, leaving]) {
|
|
304
268
|
motions.clear();
|
|
305
269
|
}
|
|
306
270
|
target = scene;
|
|
307
271
|
},
|
|
308
272
|
frame(now) {
|
|
309
273
|
prune(now);
|
|
310
|
-
if (
|
|
274
|
+
if (channels.size === 0) return target;
|
|
311
275
|
return { ...target, nodes: paintList(target.nodes, null, now) };
|
|
312
276
|
},
|
|
313
277
|
settled(now) {
|
|
314
|
-
return [
|
|
315
|
-
...numbers.values(),
|
|
316
|
-
...strings.values(),
|
|
317
|
-
...guides.values()
|
|
318
|
-
].every((motion) => done(motion, now));
|
|
278
|
+
return [...channels.values()].every((motion) => done(motion, now));
|
|
319
279
|
}
|
|
320
280
|
};
|
|
321
281
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/core/motion/motion-store.ts"],
|
|
4
|
-
"sourcesContent": ["import { type Generator, spring, tween } from \"@voila.dev/motion\";\nimport { enterFrom, exitTo } from \"#/core/motion/enter-exit.ts\";\nimport {\n\ttype LeavingGuide,\n\tleavingGuides,\n\twithLeaving,\n} from \"#/core/motion/leaving.ts\";\nimport {\n\tSTRING_CHANNELS,\n\ttype StringChannel,\n\tstringMixer,\n\tstringOf,\n\twithStrings,\n} from \"#/core/motion/strings.ts\";\nimport { type ChartTiming, staggerDelay } from \"#/core/motion/timing.ts\";\nimport type {\n\tChartScene,\n\tGeometryMotion,\n\tGeometryPlan,\n\tSceneNode,\n} from \"#/core/types.ts\";\n\n/**\n * Every moving number of a chart, as springs keyed by node and channel. A\n * new scene retargets them: each channel starts from where it is painted, at\n * the speed it has, so an update landing mid-flight bends the motion instead\n * of kinking it. Pure and clocked from outside (milliseconds), so the SVG and\n * Canvas renderers move identically and tests step it by hand. Hit-testing\n * and focus never read it: they read the scene it moves towards, which never\n * holds the marks collapsing or the guides fading on their way out.\n */\nexport interface MotionStore {\n\tretarget(scene: ChartScene, now: number): void;\n\t/** The first render: lines that `enter: \"draw\"` trace themselves in. False when none does. */\n\tintroduce(now: number): boolean;\n\t/** Jumps to `scene`: a resize, or reduced motion. */\n\tsnap(scene: ChartScene): void;\n\tframe(now: number): ChartScene;\n\tsettled(now: number): boolean;\n}\n\ninterface Motion {\n\treadonly generator: Generator;\n\t/** Milliseconds, the delay included. */\n\treadonly start: number;\n}\n\n/** A path or a colour: a progress from 0 to 1, and the mix it reads. */\ninterface StringMotion extends Motion {\n\treadonly mix: (progress: number) => string;\n}\n\n/** A guide fading out where it stood. */\ninterface GuideMotion extends Motion {\n\treadonly guide: LeavingGuide;\n}\n\n/** A data mark collapsing on its way out: drawn after its old sibling until its springs settle. */\ninterface Leaving {\n\treadonly node: SceneNode;\n\treadonly after: string | undefined;\n\treadonly siblings: readonly SceneNode[];\n}\n\ntype Numeric = Record<string, number>;\n\nconst FIELDS: Record<SceneNode[\"kind\"], readonly string[]> = {\n\trect: [\"x\", \"y\", \"width\", \"height\"],\n\tcircle: [\"cx\", \"cy\", \"r\"],\n\tline: [\"x1\", \"y1\", \"x2\", \"y2\"],\n\ttext: [\"x\", \"y\"],\n\tpath: [],\n\tgroup: [],\n};\n\nconst OPACITY = \"opacity\";\nconst DRAWN = \"drawn\";\nconst SEPARATOR = \"\\u0000\";\n\nfunction walk(\n\tnodes: readonly SceneNode[],\n\tvisit: (node: SceneNode, siblings: readonly SceneNode[]) => void,\n) {\n\tfor (const node of nodes) {\n\t\tvisit(node, nodes);\n\t\tif (node.kind === \"group\") walk(node.children, visit);\n\t}\n}\n\nfunction index(nodes: readonly SceneNode[]) {\n\tconst byKey = new Map<string, SceneNode>();\n\tconst siblings = new Map<string, readonly SceneNode[]>();\n\twalk(nodes, (node, list) => {\n\t\tbyKey.set(node.key, node);\n\t\tsiblings.set(node.key, list);\n\t});\n\treturn { byKey, siblings };\n}\n\nfunction channelKey(node: string, channel: string): string {\n\treturn `${node}${SEPARATOR}${channel}`;\n}\n\nfunction elapsed(motion: Motion, now: number): number {\n\treturn Math.max(0, (now - motion.start) / 1000);\n}\n\nfunction done(motion: Motion, now: number): boolean {\n\treturn elapsed(motion, now) >= motion.generator.duration;\n}\n\nfunction clamp(value: number): number {\n\treturn Math.min(1, Math.max(0, value));\n}\n\nexport function createMotionStore(\n\tinitial: ChartScene,\n\ttiming: ChartTiming,\n): MotionStore {\n\tlet target = initial;\n\tconst numbers = new Map<string, Motion>();\n\tconst strings = new Map<string, StringMotion>();\n\tconst guides = new Map<string, GuideMotion>();\n\tconst shapes = new Map<\n\t\tstring,\n\t\t{ readonly plan: GeometryPlan; readonly motion: GeometryMotion }\n\t>();\n\tconst leaving = new Map<string, Leaving>();\n\n\tfunction generator(from: number, to: number, velocity: number): Generator {\n\t\treturn timing.type === \"spring\"\n\t\t\t? spring({\n\t\t\t\t\tfrom,\n\t\t\t\t\tto,\n\t\t\t\t\tvelocity,\n\t\t\t\t\tduration: timing.duration,\n\t\t\t\t\tbounce: timing.bounce,\n\t\t\t\t})\n\t\t\t: tween({\n\t\t\t\t\tkeyframes: [from, to],\n\t\t\t\t\tduration: timing.duration,\n\t\t\t\t\tease: timing.easing,\n\t\t\t\t});\n\t}\n\n\tfunction value(node: string, channel: string, fallback: number, now: number) {\n\t\tconst motion = numbers.get(channelKey(node, channel));\n\t\treturn motion ? motion.generator.at(elapsed(motion, now)).value : fallback;\n\t}\n\n\t/** Springs one channel from `from` to `to`, at the speed it already has. */\n\tfunction spring1(\n\t\tnode: string,\n\t\tchannel: string,\n\t\tfrom: number,\n\t\tto: number,\n\t\tstart: number,\n\t\tnow: number,\n\t) {\n\t\tconst key = channelKey(node, channel);\n\t\tconst running = numbers.get(key);\n\t\tconst velocity = running\n\t\t\t? running.generator.at(elapsed(running, now)).velocity\n\t\t\t: 0;\n\t\tnumbers.delete(key);\n\t\tif (from === to && velocity === 0) return;\n\t\tnumbers.set(key, { generator: generator(from, to, velocity), start });\n\t}\n\n\t/** Mixes one string channel from what is painted to what `to` asks for. */\n\tfunction mixString(\n\t\tfrom: SceneNode,\n\t\tto: SceneNode,\n\t\tchannel: StringChannel,\n\t\tstart: number,\n\t) {\n\t\tconst key = channelKey(to.key, channel);\n\t\tstrings.delete(key);\n\t\tconst was = stringOf(from, channel);\n\t\tconst goal = stringOf(to, channel);\n\t\tif (was === undefined || goal === undefined || was === goal) return;\n\t\tstrings.set(key, {\n\t\t\tgenerator: generator(0, 1, 0),\n\t\t\tstart,\n\t\t\tmix: stringMixer(to, channel, was, goal),\n\t\t});\n\t}\n\n\tfunction drop(node: string) {\n\t\tfor (const motions of [numbers, strings]) {\n\t\t\tfor (const key of motions.keys()) {\n\t\t\t\tif (key.startsWith(node + SEPARATOR)) motions.delete(key);\n\t\t\t}\n\t\t}\n\t\tshapes.delete(node);\n\t\tleaving.delete(node);\n\t}\n\n\t/** `from` is the node as painted now (or the shape it enters from), `to` where it goes. */\n\tfunction move(from: SceneNode, to: SceneNode, start: number, now: number) {\n\t\tconst key = to.key;\n\t\tfor (const field of FIELDS[to.kind]) {\n\t\t\tconst was = (from as unknown as Numeric)[field] as number;\n\t\t\tspring1(\n\t\t\t\tkey,\n\t\t\t\tfield,\n\t\t\t\twas,\n\t\t\t\t(to as unknown as Numeric)[field] as number,\n\t\t\t\tstart,\n\t\t\t\tnow,\n\t\t\t);\n\t\t}\n\t\tmixString(from, to, \"fill\", start);\n\t\tmixString(from, to, \"stroke\", start);\n\t\tif (to.kind !== \"path\" || from.kind !== \"path\") return;\n\t\tconst previous = shapes.get(key);\n\t\tshapes.delete(key);\n\t\tstrings.delete(channelKey(key, \"d\"));\n\t\tconst motion = to.motion;\n\t\tconst plan =\n\t\t\tmotion && from.geometry && to.geometry\n\t\t\t\t? motion.plan(from.geometry, to.geometry)\n\t\t\t\t: undefined;\n\t\tif (!plan) {\n\t\t\t// No meaning to move: the node's own morph, else the numbers of `d`.\n\t\t\tmixString(from, to, \"d\", start);\n\t\t\treturn;\n\t\t}\n\t\tfor (const channel of previous?.plan.targets.keys() ?? []) {\n\t\t\tif (!plan.targets.has(channel)) {\n\t\t\t\tnumbers.delete(channelKey(key, `g${channel}`));\n\t\t\t}\n\t\t}\n\t\tfor (const [channel, goal] of plan.targets) {\n\t\t\tconst was = plan.starts.get(channel) ?? goal;\n\t\t\tspring1(key, `g${channel}`, was, goal, start, now);\n\t\t}\n\t\tshapes.set(key, { plan, motion: motion as GeometryMotion });\n\t}\n\n\tfunction paintNode(node: SceneNode, now: number): SceneNode {\n\t\tif (node.kind === \"group\") {\n\t\t\treturn { ...node, children: paintList(node.children, node.key, now) };\n\t\t}\n\t\tconst key = node.key;\n\t\tconst moved: Numeric = {};\n\t\tfor (const field of FIELDS[node.kind]) {\n\t\t\tmoved[field] = value(\n\t\t\t\tkey,\n\t\t\t\tfield,\n\t\t\t\t(node as unknown as Numeric)[field] as number,\n\t\t\t\tnow,\n\t\t\t);\n\t\t}\n\t\tlet next = { ...node, ...moved } as Exclude<SceneNode, { kind: \"group\" }>;\n\t\tconst shape = shapes.get(key);\n\t\tif (next.kind === \"path\" && shape) {\n\t\t\tconst geometry = shape.plan.build((channel) =>\n\t\t\t\tvalue(key, `g${channel}`, shape.plan.targets.get(channel) ?? 0, now),\n\t\t\t);\n\t\t\tnext = { ...next, geometry, d: shape.motion.draw(geometry) };\n\t\t}\n\t\tconst mixed: Partial<Record<StringChannel, string>> = {};\n\t\tfor (const channel of STRING_CHANNELS) {\n\t\t\tconst motion = strings.get(channelKey(key, channel));\n\t\t\tif (motion) {\n\t\t\t\tmixed[channel] = motion.mix(\n\t\t\t\t\tmotion.generator.at(elapsed(motion, now)).value,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t\tnext = withStrings(next, mixed) as typeof next;\n\t\tconst opacity = value(key, OPACITY, 1, now);\n\t\tconst drawn = value(key, DRAWN, 1, now);\n\t\tif (opacity === 1 && drawn === 1) return next;\n\t\tconst geometry = next.kind === \"path\" ? next.geometry : undefined;\n\t\tconst length =\n\t\t\tgeometry && next.kind === \"path\" ? next.motion?.length?.(geometry) : 0;\n\t\treturn {\n\t\t\t...next,\n\t\t\tpaint: {\n\t\t\t\t...next.paint,\n\t\t\t\topacity: (next.paint.opacity ?? 1) * clamp(opacity),\n\t\t\t\t...(drawn < 1 && geometry\n\t\t\t\t\t? {\n\t\t\t\t\t\t\tdrawn: {\n\t\t\t\t\t\t\t\tfraction: clamp(drawn),\n\t\t\t\t\t\t\t\tlength: length ?? 0,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t}\n\t\t\t\t\t: {}),\n\t\t\t},\n\t\t} as SceneNode;\n\t}\n\n\t/**\n\t * The painted list, with the marks still collapsing after the sibling they\n\t * followed, and the guides still fading back at their places.\n\t */\n\tfunction paintList(\n\t\tlist: readonly SceneNode[],\n\t\tparent: string | null,\n\t\tnow: number,\n\t): SceneNode[] {\n\t\tconst out: SceneNode[] = [];\n\t\tfunction place(after: string | undefined) {\n\t\t\tfor (const [key, exit] of leaving) {\n\t\t\t\tif (exit.after !== after) continue;\n\t\t\t\tout.push(paintNode(exit.node, now));\n\t\t\t\tplace(key);\n\t\t\t}\n\t\t}\n\t\tif (parent === null) place(undefined);\n\t\tfor (const node of list) {\n\t\t\tout.push(paintNode(node, now));\n\t\t\tplace(node.key);\n\t\t}\n\t\tif (guides.size === 0) return out;\n\t\tconst fading = [...guides.values()].map(\n\t\t\t(motion) =>\n\t\t\t\t[\n\t\t\t\t\tmotion.guide,\n\t\t\t\t\tMath.max(0, motion.generator.at(elapsed(motion, now)).value),\n\t\t\t\t] as const,\n\t\t);\n\t\treturn withLeaving(out, parent, fading);\n\t}\n\n\tfunction prune(now: number) {\n\t\tfor (const motions of [numbers, strings, guides]) {\n\t\t\tfor (const [key, motion] of motions) {\n\t\t\t\tif (done(motion, now)) motions.delete(key);\n\t\t\t}\n\t\t}\n\t\tconst moving = new Set(\n\t\t\t[...numbers.keys(), ...strings.keys()].map(\n\t\t\t\t(key) => key.split(SEPARATOR)[0],\n\t\t\t),\n\t\t);\n\t\tfor (const key of shapes.keys()) {\n\t\t\tif (!moving.has(key)) shapes.delete(key);\n\t\t}\n\t\tfor (const key of leaving.keys()) {\n\t\t\tif (!moving.has(key)) leaving.delete(key);\n\t\t}\n\t}\n\n\treturn {\n\t\tretarget(scene, now) {\n\t\t\tconst before = index(target.nodes);\n\t\t\tconst after = index(scene.nodes);\n\t\t\tfunction painted(key: string): SceneNode | undefined {\n\t\t\t\tconst node =\n\t\t\t\t\tbefore.byKey.get(key) ??\n\t\t\t\t\tleaving.get(key)?.node ??\n\t\t\t\t\tguides.get(key)?.guide.node;\n\t\t\t\treturn node && paintNode(node, now);\n\t\t\t}\n\t\t\t// Read before the exits below drop what the guides were showing.\n\t\t\tfor (const guide of leavingGuides(target.nodes, (key) =>\n\t\t\t\tafter.byKey.has(key),\n\t\t\t)) {\n\t\t\t\tguides.set(guide.node.key, {\n\t\t\t\t\tgenerator: generator(value(guide.node.key, OPACITY, 1, now), 0, 0),\n\t\t\t\t\tstart: now,\n\t\t\t\t\tguide,\n\t\t\t\t});\n\t\t\t}\n\t\t\tfor (const key of new Set([...before.byKey.keys(), ...leaving.keys()])) {\n\t\t\t\tif (after.byKey.has(key)) continue;\n\t\t\t\tconst base = (before.byKey.get(key) ??\n\t\t\t\t\tleaving.get(key)?.node) as SceneNode;\n\t\t\t\tconst siblings =\n\t\t\t\t\tbefore.siblings.get(key) ?? leaving.get(key)?.siblings ?? [];\n\t\t\t\tconst goal = exitTo(base, siblings, after.byKey);\n\t\t\t\tconst from = painted(key);\n\t\t\t\tif (!goal || !from) {\n\t\t\t\t\tdrop(key);\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tmove(from, goal, now, now);\n\t\t\t\tconst at = siblings.findIndex((node) => node.key === key);\n\t\t\t\tleaving.set(key, {\n\t\t\t\t\tnode: goal,\n\t\t\t\t\tafter: siblings[at - 1]?.key,\n\t\t\t\t\tsiblings,\n\t\t\t\t});\n\t\t\t}\n\t\t\tconst marks = [...after.byKey.values()].filter(\n\t\t\t\t(node) => node.role === \"mark\",\n\t\t\t);\n\t\t\tconst order = new Map(marks.map((node, at) => [node.key, at]));\n\t\t\tfor (const node of after.byKey.values()) {\n\t\t\t\tif (node.kind === \"group\") continue;\n\t\t\t\tconst start =\n\t\t\t\t\tnow +\n\t\t\t\t\tstaggerDelay(timing, order.get(node.key) ?? 0, marks.length) * 1000;\n\t\t\t\tconst old = painted(node.key);\n\t\t\t\tleaving.delete(node.key);\n\t\t\t\tconst from =\n\t\t\t\t\told ??\n\t\t\t\t\tenterFrom(node, after.siblings.get(node.key) ?? [], before.byKey);\n\t\t\t\tif (from) move(from, node, start, now);\n\t\t\t\tconst fading = numbers.get(channelKey(node.key, OPACITY));\n\t\t\t\tconst returning = guides.get(node.key);\n\t\t\t\tif ((!from && node.enter !== \"none\") || fading || returning) {\n\t\t\t\t\tconst shown = returning\n\t\t\t\t\t\t? Math.max(0, returning.generator.at(elapsed(returning, now)).value)\n\t\t\t\t\t\t: fading\n\t\t\t\t\t\t\t? value(node.key, OPACITY, 1, now)\n\t\t\t\t\t\t\t: 0;\n\t\t\t\t\tspring1(node.key, OPACITY, shown, 1, start, now);\n\t\t\t\t}\n\t\t\t}\n\t\t\tfor (const key of after.byKey.keys()) guides.delete(key);\n\t\t\ttarget = scene;\n\t\t},\n\t\tintroduce(now) {\n\t\t\tlet any = false;\n\t\t\twalk(target.nodes, (node) => {\n\t\t\t\tif (node.kind !== \"path\" || node.enter !== \"draw\") return;\n\t\t\t\tif (node.geometry?.kind !== \"points\" || node.paint.strokeDasharray) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tspring1(node.key, DRAWN, 0, 1, now, now);\n\t\t\t\tany = true;\n\t\t\t});\n\t\t\treturn any;\n\t\t},\n\t\tsnap(scene) {\n\t\t\tfor (const motions of [numbers, strings, guides, shapes, leaving]) {\n\t\t\t\tmotions.clear();\n\t\t\t}\n\t\t\ttarget = scene;\n\t\t},\n\t\tframe(now) {\n\t\t\tprune(now);\n\t\t\tif (numbers.size + strings.size + guides.size === 0) return target;\n\t\t\treturn { ...target, nodes: paintList(target.nodes, null, now) };\n\t\t},\n\t\tsettled(now) {\n\t\t\treturn [\n\t\t\t\t...numbers.values(),\n\t\t\t\t...strings.values(),\n\t\t\t\t...guides.values(),\n\t\t\t].every((motion) => done(motion, now));\n\t\t},\n\t};\n}\n"],
|
|
5
|
-
"mappings": "
|
|
6
|
-
"names": [
|
|
4
|
+
"sourcesContent": ["import type { Generator } from \"@voila.dev/motion\";\nimport { enterFrom, exitTo } from \"#/core/motion/enter-exit.ts\";\nimport {\n\tSTRING_CHANNELS,\n\ttype StringChannel,\n\tstringMixer,\n\tstringOf,\n\twithString,\n} from \"#/core/motion/strings.ts\";\nimport { type ChartTiming, staggerDelay } from \"#/core/motion/timing.ts\";\nimport type {\n\tChartScene,\n\tGeometryMotion,\n\tGeometryPlan,\n\tSceneNode,\n} from \"#/core/types.ts\";\n\n/**\n * Every moving number of a chart, as springs keyed by node and channel. A\n * new scene retargets them: each channel starts from where it is painted, at\n * the speed it has, so an update landing mid-flight bends the motion instead\n * of kinking it. Pure and clocked from outside (milliseconds), so the SVG and\n * Canvas renderers move identically and tests step it by hand. Hit-testing\n * and focus never read it: they read the scene it moves towards, which never\n * holds the marks collapsing or the guides fading on their way out.\n */\nexport interface MotionStore {\n\tretarget(scene: ChartScene, now: number): void;\n\t/** The first render: lines that `enter: \"draw\"` trace themselves in. False when none does. */\n\tintroduce(now: number): boolean;\n\t/** Jumps to `scene`: a resize, or reduced motion. */\n\tsnap(scene: ChartScene): void;\n\tframe(now: number): ChartScene;\n\tsettled(now: number): boolean;\n}\n\ninterface Motion {\n\treadonly generator: Generator;\n\t/** Milliseconds, the delay included. */\n\treadonly start: number;\n\t/** A path or a colour: the generator runs a progress from 0 to 1, and this reads it. */\n\treadonly mix?: (progress: number) => string;\n}\n\n/**\n * A node on its way out, drawn after its old sibling until its springs\n * settle: a data mark collapsing, or a guide fading where it stood.\n */\ninterface Leaving {\n\treadonly node: SceneNode;\n\t/** Its old previous sibling's key, else its group's `first` marker. */\n\treadonly after: string | undefined;\n\treadonly siblings: readonly SceneNode[];\n}\n\ntype Numeric = Record<string, number>;\n\nconst FIELDS: Record<SceneNode[\"kind\"], readonly string[]> = {\n\trect: [\"x\", \"y\", \"width\", \"height\"],\n\tcircle: [\"cx\", \"cy\", \"r\"],\n\tline: [\"x1\", \"y1\", \"x2\", \"y2\"],\n\ttext: [\"x\", \"y\"],\n\tpath: [],\n\tgroup: [],\n};\n\nconst OPACITY = \"opacity\";\nconst DRAWN = \"drawn\";\nconst SEPARATOR = \"\\u0000\";\n/**\n * A tick label or a grid line vanishing at once reads as a glitch, so they\n * fade out. Data marks never linger: a bar the data no longer has would show\n * a value that isn't true.\n */\nconst GUIDE_ROLES = new Set([\"grid\", \"axis\"]);\n\n/** Where the leaving first children of the group `parent` are drawn. */\nfunction first(parent: string | null): string | undefined {\n\treturn parent === null ? undefined : parent + SEPARATOR;\n}\n\n/** Visits every node with its list and the key it is drawn after when it leaves (see `Leaving`). */\nfunction walk(\n\tnodes: readonly SceneNode[],\n\tvisit: (\n\t\tnode: SceneNode,\n\t\tsiblings: readonly SceneNode[],\n\t\tafter: string | undefined,\n\t) => void,\n\tparent: string | null = null,\n) {\n\tlet after = first(parent);\n\tfor (const node of nodes) {\n\t\tvisit(node, nodes, after);\n\t\tif (node.kind === \"group\") walk(node.children, visit, node.key);\n\t\tafter = node.key;\n\t}\n}\n\nfunction index(nodes: readonly SceneNode[]) {\n\tconst byKey = new Map<string, SceneNode>();\n\tconst siblings = new Map<string, readonly SceneNode[]>();\n\tconst afters = new Map<string, string | undefined>();\n\twalk(nodes, (node, list, after) => {\n\t\tbyKey.set(node.key, node);\n\t\tsiblings.set(node.key, list);\n\t\tafters.set(node.key, after);\n\t});\n\treturn { byKey, siblings, afters };\n}\n\nfunction channelKey(node: string, channel: string): string {\n\treturn `${node}${SEPARATOR}${channel}`;\n}\n\nfunction elapsed(motion: Motion, now: number): number {\n\treturn Math.max(0, (now - motion.start) / 1000);\n}\n\nfunction sample(motion: Motion, now: number) {\n\treturn motion.generator.at(elapsed(motion, now));\n}\n\nfunction done(motion: Motion, now: number): boolean {\n\treturn elapsed(motion, now) >= motion.generator.duration;\n}\n\nfunction clamp(value: number): number {\n\treturn Math.min(1, Math.max(0, value));\n}\n\nexport function createMotionStore(\n\tinitial: ChartScene,\n\ttiming: ChartTiming,\n): MotionStore {\n\tlet target = initial;\n\tconst channels = new Map<string, Motion>();\n\tconst shapes = new Map<\n\t\tstring,\n\t\t{ readonly plan: GeometryPlan; readonly motion: GeometryMotion }\n\t>();\n\tconst leaving = new Map<string, Leaving>();\n\n\tfunction value(node: string, channel: string, fallback: number, now: number) {\n\t\tconst motion = channels.get(channelKey(node, channel));\n\t\treturn motion ? sample(motion, now).value : fallback;\n\t}\n\n\t/** Springs one channel from `from` to `to`, at the speed it already has. */\n\tfunction spring1(\n\t\tnode: string,\n\t\tchannel: string,\n\t\tfrom: number,\n\t\tto: number,\n\t\tstart: number,\n\t\tnow: number,\n\t) {\n\t\tconst key = channelKey(node, channel);\n\t\tconst running = channels.get(key);\n\t\tconst velocity = running ? sample(running, now).velocity : 0;\n\t\tchannels.delete(key);\n\t\tif (from === to && velocity === 0) return;\n\t\tchannels.set(key, { generator: timing.motion(from, to, velocity), start });\n\t}\n\n\t/** Mixes one string channel from what is painted to what `to` asks for. */\n\tfunction mixString(\n\t\tfrom: SceneNode,\n\t\tto: SceneNode,\n\t\tchannel: StringChannel,\n\t\tstart: number,\n\t) {\n\t\tconst key = channelKey(to.key, channel);\n\t\tchannels.delete(key);\n\t\tconst was = stringOf(from, channel);\n\t\tconst goal = stringOf(to, channel);\n\t\tif (was === undefined || goal === undefined || was === goal) return;\n\t\tchannels.set(key, {\n\t\t\tgenerator: timing.motion(0, 1, 0),\n\t\t\tstart,\n\t\t\tmix: stringMixer(to, channel, was, goal),\n\t\t});\n\t}\n\n\tfunction drop(node: string) {\n\t\tfor (const key of channels.keys()) {\n\t\t\tif (key.startsWith(node + SEPARATOR)) channels.delete(key);\n\t\t}\n\t\tshapes.delete(node);\n\t\tleaving.delete(node);\n\t}\n\n\t/** `from` is the node as painted now (or the shape it enters from), `to` where it goes. */\n\tfunction move(from: SceneNode, to: SceneNode, start: number, now: number) {\n\t\tconst key = to.key;\n\t\tfor (const field of FIELDS[to.kind]) {\n\t\t\tconst was = (from as unknown as Numeric)[field] as number;\n\t\t\tspring1(\n\t\t\t\tkey,\n\t\t\t\tfield,\n\t\t\t\twas,\n\t\t\t\t(to as unknown as Numeric)[field] as number,\n\t\t\t\tstart,\n\t\t\t\tnow,\n\t\t\t);\n\t\t}\n\t\tmixString(from, to, \"fill\", start);\n\t\tmixString(from, to, \"stroke\", start);\n\t\tif (to.kind !== \"path\" || from.kind !== \"path\") return;\n\t\tconst previous = shapes.get(key);\n\t\tshapes.delete(key);\n\t\tchannels.delete(channelKey(key, \"d\"));\n\t\tconst motion = to.motion;\n\t\tconst plan =\n\t\t\tmotion && from.geometry && to.geometry\n\t\t\t\t? motion.plan(from.geometry, to.geometry)\n\t\t\t\t: undefined;\n\t\tif (!plan) {\n\t\t\t// No meaning to move: the node's own morph, else the numbers of `d`.\n\t\t\tmixString(from, to, \"d\", start);\n\t\t\treturn;\n\t\t}\n\t\tfor (const channel of previous?.plan.targets.keys() ?? []) {\n\t\t\tif (!plan.targets.has(channel)) {\n\t\t\t\tchannels.delete(channelKey(key, `g${channel}`));\n\t\t\t}\n\t\t}\n\t\tfor (const [channel, goal] of plan.targets) {\n\t\t\tconst was = plan.starts.get(channel) ?? goal;\n\t\t\tspring1(key, `g${channel}`, was, goal, start, now);\n\t\t}\n\t\tshapes.set(key, { plan, motion: motion as GeometryMotion });\n\t}\n\n\tfunction paintNode(node: SceneNode, now: number): SceneNode {\n\t\tif (node.kind === \"group\") {\n\t\t\treturn { ...node, children: paintList(node.children, node.key, now) };\n\t\t}\n\t\tconst key = node.key;\n\t\tconst moved: Numeric = {};\n\t\tfor (const field of FIELDS[node.kind]) {\n\t\t\tmoved[field] = value(\n\t\t\t\tkey,\n\t\t\t\tfield,\n\t\t\t\t(node as unknown as Numeric)[field] as number,\n\t\t\t\tnow,\n\t\t\t);\n\t\t}\n\t\tlet next = { ...node, ...moved } as Exclude<SceneNode, { kind: \"group\" }>;\n\t\tconst shape = shapes.get(key);\n\t\tif (next.kind === \"path\" && shape) {\n\t\t\tconst geometry = shape.plan.build((channel) =>\n\t\t\t\tvalue(key, `g${channel}`, shape.plan.targets.get(channel) ?? 0, now),\n\t\t\t);\n\t\t\tnext = { ...next, geometry, d: shape.motion.draw(geometry) };\n\t\t}\n\t\tfor (const channel of STRING_CHANNELS) {\n\t\t\tconst motion = channels.get(channelKey(key, channel));\n\t\t\tif (motion?.mix) {\n\t\t\t\tnext = withString(next, channel, motion.mix(sample(motion, now).value));\n\t\t\t}\n\t\t}\n\t\tconst opacity = value(key, OPACITY, 1, now);\n\t\tconst drawn = value(key, DRAWN, 1, now);\n\t\tif (opacity === 1 && drawn === 1) return next;\n\t\treturn {\n\t\t\t...next,\n\t\t\tpaint: {\n\t\t\t\t...next.paint,\n\t\t\t\topacity: (next.paint.opacity ?? 1) * clamp(opacity),\n\t\t\t\t...(drawn < 1 &&\n\t\t\t\t\tnext.kind === \"path\" &&\n\t\t\t\t\tnext.geometry && {\n\t\t\t\t\t\tdrawn: {\n\t\t\t\t\t\t\tfraction: clamp(drawn),\n\t\t\t\t\t\t\tlength: next.motion?.length?.(next.geometry) ?? 0,\n\t\t\t\t\t\t},\n\t\t\t\t\t}),\n\t\t\t},\n\t\t} as SceneNode;\n\t}\n\n\t/** The painted list, with the nodes still leaving after the sibling they followed. */\n\tfunction paintList(\n\t\tlist: readonly SceneNode[],\n\t\tparent: string | null,\n\t\tnow: number,\n\t): SceneNode[] {\n\t\tconst out: SceneNode[] = [];\n\t\tfunction place(after: string | undefined) {\n\t\t\tfor (const [key, exit] of leaving) {\n\t\t\t\tif (exit.after !== after) continue;\n\t\t\t\tout.push(paintNode(exit.node, now));\n\t\t\t\tplace(key);\n\t\t\t}\n\t\t}\n\t\tplace(first(parent));\n\t\tfor (const node of list) {\n\t\t\tout.push(paintNode(node, now));\n\t\t\tplace(node.key);\n\t\t}\n\t\treturn out;\n\t}\n\n\tfunction prune(now: number) {\n\t\tfor (const [key, motion] of channels) {\n\t\t\tif (done(motion, now)) channels.delete(key);\n\t\t}\n\t\tconst moving = new Set(\n\t\t\t[...channels.keys()].map((key) => key.split(SEPARATOR)[0]),\n\t\t);\n\t\tfor (const byNode of [shapes, leaving]) {\n\t\t\tfor (const key of byNode.keys()) {\n\t\t\t\tif (!moving.has(key)) byNode.delete(key);\n\t\t\t}\n\t\t}\n\t}\n\n\treturn {\n\t\tretarget(scene, now) {\n\t\t\tconst before = index(target.nodes);\n\t\t\tconst after = index(scene.nodes);\n\t\t\tfunction painted(key: string): SceneNode | undefined {\n\t\t\t\tconst node = before.byKey.get(key) ?? leaving.get(key)?.node;\n\t\t\t\treturn node && paintNode(node, now);\n\t\t\t}\n\t\t\tfor (const key of new Set([...before.byKey.keys(), ...leaving.keys()])) {\n\t\t\t\tif (after.byKey.has(key)) continue;\n\t\t\t\tconst base = (before.byKey.get(key) ??\n\t\t\t\t\tleaving.get(key)?.node) as SceneNode;\n\t\t\t\tconst exit = leaving.get(key);\n\t\t\t\tconst siblings = exit?.siblings ?? before.siblings.get(key) ?? [];\n\t\t\t\t// A whole guide group leaving goes at once, its lines with it.\n\t\t\t\tconst guide = base.kind !== \"group\" && GUIDE_ROLES.has(base.role ?? \"\");\n\t\t\t\tconst goal = exitTo(base, siblings, after.byKey) ?? (guide && base);\n\t\t\t\tconst from = painted(key);\n\t\t\t\tif (!goal || !from) {\n\t\t\t\t\tdrop(key);\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tmove(from, goal, now, now);\n\t\t\t\tif (guide) {\n\t\t\t\t\tspring1(key, OPACITY, value(key, OPACITY, 1, now), 0, now, now);\n\t\t\t\t}\n\t\t\t\tleaving.set(key, {\n\t\t\t\t\tnode: goal,\n\t\t\t\t\tafter: exit ? exit.after : before.afters.get(key),\n\t\t\t\t\tsiblings,\n\t\t\t\t});\n\t\t\t}\n\t\t\tconst marks = [...after.byKey.values()].filter(\n\t\t\t\t(node) => node.role === \"mark\",\n\t\t\t);\n\t\t\tconst order = new Map(marks.map((node, at) => [node.key, at]));\n\t\t\tfor (const node of after.byKey.values()) {\n\t\t\t\tif (node.kind === \"group\") continue;\n\t\t\t\tconst start =\n\t\t\t\t\tnow +\n\t\t\t\t\tstaggerDelay(timing, order.get(node.key) ?? 0, marks.length) * 1000;\n\t\t\t\tconst old = painted(node.key);\n\t\t\t\tconst returning = leaving.delete(node.key);\n\t\t\t\tconst from =\n\t\t\t\t\told ??\n\t\t\t\t\tenterFrom(node, after.siblings.get(node.key) ?? [], before.byKey);\n\t\t\t\tif (from) move(from, node, start, now);\n\t\t\t\tconst fading = channels.get(channelKey(node.key, OPACITY));\n\t\t\t\tif ((!from && node.enter !== \"none\") || fading) {\n\t\t\t\t\tconst shown = fading ? Math.max(0, sample(fading, now).value) : 0;\n\t\t\t\t\t// A guide coming back starts from rest: its fade-out speed points the wrong way.\n\t\t\t\t\tif (returning) channels.delete(channelKey(node.key, OPACITY));\n\t\t\t\t\tspring1(node.key, OPACITY, shown, 1, start, now);\n\t\t\t\t}\n\t\t\t}\n\t\t\ttarget = scene;\n\t\t},\n\t\tintroduce(now) {\n\t\t\tlet any = false;\n\t\t\twalk(target.nodes, (node) => {\n\t\t\t\tif (node.kind !== \"path\" || node.enter !== \"draw\") return;\n\t\t\t\tif (node.geometry?.kind !== \"points\" || node.paint.strokeDasharray) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tspring1(node.key, DRAWN, 0, 1, now, now);\n\t\t\t\tany = true;\n\t\t\t});\n\t\t\treturn any;\n\t\t},\n\t\tsnap(scene) {\n\t\t\tfor (const motions of [channels, shapes, leaving]) {\n\t\t\t\tmotions.clear();\n\t\t\t}\n\t\t\ttarget = scene;\n\t\t},\n\t\tframe(now) {\n\t\t\tprune(now);\n\t\t\tif (channels.size === 0) return target;\n\t\t\treturn { ...target, nodes: paintList(target.nodes, null, now) };\n\t\t},\n\t\tsettled(now) {\n\t\t\treturn [...channels.values()].every((motion) => done(motion, now));\n\t\t},\n\t};\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
7
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"points-motion.d.ts","sourceRoot":"","sources":["../../../src/core/motion/points-motion.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACX,cAAc,EAGd,aAAa,EAEb,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"points-motion.d.ts","sourceRoot":"","sources":["../../../src/core/motion/points-motion.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACX,cAAc,EAGd,aAAa,EAEb,MAAM,iBAAiB,CAAC;AAWzB,6DAA6D;AAC7D,wBAAgB,UAAU,CAAC,QAAQ,EAAE,aAAa,GAAG,MAAM,CAW1D;AA0ID,uEAAuE;AACvE,eAAO,MAAM,aAAa,EAAE,cAQ3B,CAAC"}
|
|
@@ -1,32 +1,25 @@
|
|
|
1
1
|
import { areaPath, linePath, polygonPath } from "../paths.js";
|
|
2
2
|
const POINT_FIELDS = ["x", "y", "x0", "y0"];
|
|
3
|
+
function lowerEdge(point) {
|
|
4
|
+
return { ...point, x: point.x0 ?? point.x, y: point.y0 ?? point.y };
|
|
5
|
+
}
|
|
3
6
|
function pointsPath(geometry) {
|
|
4
7
|
if (geometry.kind !== "points") return "";
|
|
5
8
|
const { runs, shape, curve } = geometry;
|
|
6
9
|
if (shape === "polygon") return polygonPath(runs[0] ?? []);
|
|
7
10
|
return runs.map(
|
|
8
|
-
(run) => shape === "area" ? areaPath(
|
|
9
|
-
run,
|
|
10
|
-
run.map((point) => ({
|
|
11
|
-
x: point.x0 ?? point.x,
|
|
12
|
-
y: point.y0 ?? point.y
|
|
13
|
-
})),
|
|
14
|
-
curve
|
|
15
|
-
) : linePath(run, curve)
|
|
11
|
+
(run) => shape === "area" ? areaPath(run, run.map(lowerEdge), curve) : linePath(run, curve)
|
|
16
12
|
).join("");
|
|
17
13
|
}
|
|
18
14
|
function channel(key, field) {
|
|
19
15
|
return `${key}${field}`;
|
|
20
16
|
}
|
|
21
|
-
function fields(point, into) {
|
|
17
|
+
function fields(key, point, into) {
|
|
22
18
|
for (const field of POINT_FIELDS) {
|
|
23
19
|
const value = point[field];
|
|
24
|
-
if (value !== void 0) into.set(channel(
|
|
20
|
+
if (value !== void 0) into.set(channel(key, field), value);
|
|
25
21
|
}
|
|
26
22
|
}
|
|
27
|
-
function as(key, point) {
|
|
28
|
-
return { ...point, key };
|
|
29
|
-
}
|
|
30
23
|
function neighbour(order, index, has) {
|
|
31
24
|
for (let at = index - 1; at >= 0; at -= 1) {
|
|
32
25
|
const found = has.get(order[at]);
|
|
@@ -71,8 +64,8 @@ function pointsPlan(from, to) {
|
|
|
71
64
|
const target = next.get(key) ?? neighbour(order, at, next);
|
|
72
65
|
const start = old.get(key) ?? neighbour(order, at, old) ?? target;
|
|
73
66
|
if (target === void 0 || start === void 0) continue;
|
|
74
|
-
fields(
|
|
75
|
-
fields(
|
|
67
|
+
fields(key, start, starts);
|
|
68
|
+
fields(key, target, targets);
|
|
76
69
|
}
|
|
77
70
|
return order;
|
|
78
71
|
});
|
|
@@ -117,13 +110,7 @@ function collapsed(node) {
|
|
|
117
110
|
...node,
|
|
118
111
|
geometry: {
|
|
119
112
|
...geometry,
|
|
120
|
-
runs: geometry.runs.map(
|
|
121
|
-
(run) => run.map((point) => ({
|
|
122
|
-
...point,
|
|
123
|
-
x: point.x0 ?? point.x,
|
|
124
|
-
y: point.y0 ?? point.y
|
|
125
|
-
}))
|
|
126
|
-
)
|
|
113
|
+
runs: geometry.runs.map((run) => run.map(lowerEdge))
|
|
127
114
|
}
|
|
128
115
|
};
|
|
129
116
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/core/motion/points-motion.ts"],
|
|
4
|
-
"sourcesContent": ["import { areaPath, linePath, polygonPath } from \"#/core/paths.ts\";\nimport type {\n\tGeometryMotion,\n\tGeometryPlan,\n\tGeometryPoint,\n\tSceneGeometry,\n\tScenePath,\n} from \"#/core/types.ts\";\n\ntype Points = Extract<SceneGeometry, { kind: \"points\" }>;\n\nconst POINT_FIELDS = [\"x\", \"y\", \"x0\", \"y0\"] as const;\n\n/** A line, an area or a radar outline through its points. */\nexport function pointsPath(geometry: SceneGeometry): string {\n\tif (geometry.kind !== \"points\") return \"\";\n\tconst { runs, shape, curve } = geometry;\n\tif (shape === \"polygon\") return polygonPath(runs[0] ?? []);\n\treturn runs\n\t\t.map((run) =>\n\t\t\tshape === \"area\"\n\t\t\t\t? areaPath(
|
|
5
|
-
"mappings": "AAAA,SAAS,UAAU,UAAU,mBAAmB;AAWhD,MAAM,eAAe,CAAC,KAAK,KAAK,MAAM,IAAI;
|
|
4
|
+
"sourcesContent": ["import { areaPath, linePath, polygonPath } from \"#/core/paths.ts\";\nimport type {\n\tGeometryMotion,\n\tGeometryPlan,\n\tGeometryPoint,\n\tSceneGeometry,\n\tScenePath,\n} from \"#/core/types.ts\";\n\ntype Points = Extract<SceneGeometry, { kind: \"points\" }>;\n\nconst POINT_FIELDS = [\"x\", \"y\", \"x0\", \"y0\"] as const;\n\n/** Where `point` sits on its area's lower edge. */\nfunction lowerEdge(point: GeometryPoint): GeometryPoint {\n\treturn { ...point, x: point.x0 ?? point.x, y: point.y0 ?? point.y };\n}\n\n/** A line, an area or a radar outline through its points. */\nexport function pointsPath(geometry: SceneGeometry): string {\n\tif (geometry.kind !== \"points\") return \"\";\n\tconst { runs, shape, curve } = geometry;\n\tif (shape === \"polygon\") return polygonPath(runs[0] ?? []);\n\treturn runs\n\t\t.map((run) =>\n\t\t\tshape === \"area\"\n\t\t\t\t? areaPath(run, run.map(lowerEdge), curve)\n\t\t\t\t: linePath(run, curve),\n\t\t)\n\t\t.join(\"\");\n}\n\nfunction channel(key: string, field: string): string {\n\treturn `${key}\\u0001${field}`;\n}\n\n/** `point`'s numbers under `key`'s channel names: `point` may be a neighbour that `key` enters from or folds into. */\nfunction fields(key: string, point: GeometryPoint, into: Map<string, number>) {\n\tfor (const field of POINT_FIELDS) {\n\t\tconst value = point[field];\n\t\tif (value !== undefined) into.set(channel(key, field), value);\n\t}\n}\n\n/** The nearest of `order` around `index` that `has` knows: looking back first, then ahead. */\nfunction neighbour(\n\torder: readonly string[],\n\tindex: number,\n\thas: ReadonlyMap<string, GeometryPoint>,\n): GeometryPoint | undefined {\n\tfor (let at = index - 1; at >= 0; at -= 1) {\n\t\tconst found = has.get(order[at] as string);\n\t\tif (found) return found;\n\t}\n\tfor (let at = index + 1; at < order.length; at += 1) {\n\t\tconst found = has.get(order[at] as string);\n\t\tif (found) return found;\n\t}\n\treturn undefined;\n}\n\n/** The new keys, with the ones that left kept after the old point before them. */\nfunction mergeOrder(\n\tbefore: readonly string[],\n\tafter: readonly string[],\n): string[] {\n\tconst merged = [...after];\n\tconst known = new Set(after);\n\tfor (const [index, key] of before.entries()) {\n\t\tif (known.has(key)) continue;\n\t\tconst previous = before[index - 1];\n\t\tmerged.splice(\n\t\t\tprevious === undefined ? 0 : merged.indexOf(previous) + 1,\n\t\t\t0,\n\t\t\tkey,\n\t\t);\n\t\tknown.add(key);\n\t}\n\treturn merged;\n}\n\n/**\n * Points keyed by x. A point that appears enters from its nearest old\n * neighbour (a month added on the right slides out of the last one); a\n * point that leaves folds into its nearest new neighbour. Undefined when the\n * runs don't line up: the caller falls back to tweening the path.\n */\nfunction pointsPlan(from: Points, to: Points): GeometryPlan | undefined {\n\tif (from.runs.length !== to.runs.length || from.shape !== to.shape)\n\t\treturn undefined;\n\tconst starts = new Map<string, number>();\n\tconst targets = new Map<string, number>();\n\tconst orders = to.runs.map((run, index): string[] | undefined => {\n\t\tconst old = new Map(\n\t\t\t(from.runs[index] ?? []).map((point) => [point.key, point]),\n\t\t);\n\t\tconst next = new Map(run.map((point) => [point.key, point]));\n\t\t// Two points under one key can't be told apart: tween the path instead.\n\t\tif (\n\t\t\told.size !== (from.runs[index]?.length ?? 0) ||\n\t\t\tnext.size !== run.length\n\t\t) {\n\t\t\treturn undefined;\n\t\t}\n\t\tconst order = mergeOrder([...old.keys()], [...next.keys()]);\n\t\tfor (const [at, key] of order.entries()) {\n\t\t\tconst target = next.get(key) ?? neighbour(order, at, next);\n\t\t\tconst start = old.get(key) ?? neighbour(order, at, old) ?? target;\n\t\t\tif (target === undefined || start === undefined) continue;\n\t\t\tfields(key, start, starts);\n\t\t\tfields(key, target, targets);\n\t\t}\n\t\treturn order;\n\t});\n\tif (orders.some((order) => order === undefined)) return undefined;\n\treturn {\n\t\tstarts,\n\t\ttargets,\n\t\tbuild(value) {\n\t\t\tconst area = to.runs.some((run) => run[0]?.y0 !== undefined);\n\t\t\treturn {\n\t\t\t\t...to,\n\t\t\t\truns: (orders as string[][]).map((order) =>\n\t\t\t\t\torder.map((key) => ({\n\t\t\t\t\t\tkey,\n\t\t\t\t\t\tx: value(channel(key, \"x\")),\n\t\t\t\t\t\ty: value(channel(key, \"y\")),\n\t\t\t\t\t\t...(area\n\t\t\t\t\t\t\t? { x0: value(channel(key, \"x0\")), y0: value(channel(key, \"y0\")) }\n\t\t\t\t\t\t\t: {}),\n\t\t\t\t\t})),\n\t\t\t\t),\n\t\t\t};\n\t\t},\n\t};\n}\n\n/** A line's length along its points: what Canvas dashes against to trace it in. */\nfunction pointsLength(geometry: SceneGeometry): number {\n\tif (geometry.kind !== \"points\") return 0;\n\tlet length = 0;\n\tfor (const run of geometry.runs) {\n\t\tfor (let index = 1; index < run.length; index += 1) {\n\t\t\tconst a = run[index - 1] as GeometryPoint;\n\t\t\tconst b = run[index] as GeometryPoint;\n\t\t\tlength += Math.hypot(b.x - a.x, b.y - a.y);\n\t\t}\n\t}\n\t// A curve runs a little longer than its chords; the dash must cover it all.\n\treturn length * 1.1;\n}\n\n/** An area, or the line over it, grows from its lower edge. */\nfunction collapsed(node: ScenePath): ScenePath | undefined {\n\tconst geometry = node.geometry;\n\t// A line over an area carries the area's lower edge too, so the two grow as one.\n\tif (geometry?.kind !== \"points\" || geometry.runs[0]?.[0]?.y0 === undefined) {\n\t\treturn undefined;\n\t}\n\treturn {\n\t\t...node,\n\t\tgeometry: {\n\t\t\t...geometry,\n\t\t\truns: geometry.runs.map((run) => run.map(lowerEdge)),\n\t\t},\n\t};\n}\n\n/** Lines, areas and radar outlines: points keyed by x (or by axis). */\nexport const POINTS_MOTION: GeometryMotion = {\n\tplan: (from, to) =>\n\t\tfrom.kind === \"points\" && to.kind === \"points\"\n\t\t\t? pointsPlan(from, to)\n\t\t\t: undefined,\n\tdraw: pointsPath,\n\tlength: pointsLength,\n\tenter: (node) => (node.enter === \"grow\" ? collapsed(node) : undefined),\n};\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -13,6 +13,8 @@ export declare function colourBetween(from: string, to: string, progress: number
|
|
|
13
13
|
export declare function stringOf(node: SceneNode | undefined, channel: StringChannel): string | undefined;
|
|
14
14
|
/** The mix from what was painted to what `node` asks for. */
|
|
15
15
|
export declare function stringMixer(node: SceneNode, channel: StringChannel, from: string, to: string): (progress: number) => string;
|
|
16
|
-
/** `node` with
|
|
17
|
-
export declare function
|
|
16
|
+
/** `node` with one string channel set to its moving value. */
|
|
17
|
+
export declare function withString<Node extends Exclude<SceneNode, {
|
|
18
|
+
kind: "group";
|
|
19
|
+
}>>(node: Node, channel: StringChannel, value: string): Node;
|
|
18
20
|
//# sourceMappingURL=strings.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"strings.d.ts","sourceRoot":"","sources":["../../../src/core/motion/strings.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAEjD,6EAA6E;AAC7E,eAAO,MAAM,eAAe,YAAI,GAAG,EAAE,MAAM,EAAE,QAAQ,CAAU,CAAC;AAEhE,MAAM,MAAM,aAAa,GAAG,CAAC,OAAO,eAAe,CAAC,CAAC,MAAM,CAAC,CAAC;AAK7D;;;;;GAKG;AACH,wBAAgB,aAAa,CAC5B,IAAI,EAAE,MAAM,EACZ,EAAE,EAAE,MAAM,EACV,QAAQ,EAAE,MAAM,GACd,MAAM,CAKR;AAED,iDAAiD;AACjD,wBAAgB,QAAQ,CACvB,IAAI,EAAE,SAAS,GAAG,SAAS,EAC3B,OAAO,EAAE,aAAa,GACpB,MAAM,GAAG,SAAS,CAOpB;AAED,6DAA6D;AAC7D,wBAAgB,WAAW,CAC1B,IAAI,EAAE,SAAS,EACf,OAAO,EAAE,aAAa,EACtB,IAAI,EAAE,MAAM,EACZ,EAAE,EAAE,MAAM,GACR,CAAC,QAAQ,EAAE,MAAM,KAAK,MAAM,CAM9B;AAED
|
|
1
|
+
{"version":3,"file":"strings.d.ts","sourceRoot":"","sources":["../../../src/core/motion/strings.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAEjD,6EAA6E;AAC7E,eAAO,MAAM,eAAe,YAAI,GAAG,EAAE,MAAM,EAAE,QAAQ,CAAU,CAAC;AAEhE,MAAM,MAAM,aAAa,GAAG,CAAC,OAAO,eAAe,CAAC,CAAC,MAAM,CAAC,CAAC;AAK7D;;;;;GAKG;AACH,wBAAgB,aAAa,CAC5B,IAAI,EAAE,MAAM,EACZ,EAAE,EAAE,MAAM,EACV,QAAQ,EAAE,MAAM,GACd,MAAM,CAKR;AAED,iDAAiD;AACjD,wBAAgB,QAAQ,CACvB,IAAI,EAAE,SAAS,GAAG,SAAS,EAC3B,OAAO,EAAE,aAAa,GACpB,MAAM,GAAG,SAAS,CAOpB;AAED,6DAA6D;AAC7D,wBAAgB,WAAW,CAC1B,IAAI,EAAE,SAAS,EACf,OAAO,EAAE,aAAa,EACtB,IAAI,EAAE,MAAM,EACZ,EAAE,EAAE,MAAM,GACR,CAAC,QAAQ,EAAE,MAAM,KAAK,MAAM,CAM9B;AAED,8DAA8D;AAC9D,wBAAgB,UAAU,CAAC,IAAI,SAAS,OAAO,CAAC,SAAS,EAAE;IAAE,IAAI,EAAE,OAAO,CAAA;CAAE,CAAC,EAC5E,IAAI,EAAE,IAAI,EACV,OAAO,EAAE,aAAa,EACtB,KAAK,EAAE,MAAM,GACX,IAAI,CAIN"}
|
|
@@ -18,17 +18,16 @@ function stringMixer(node, channel, from, to) {
|
|
|
18
18
|
const morph = node.kind === "path" ? node.morph : void 0;
|
|
19
19
|
return morph ? morph(from, to) : (progress) => tweenPath(from, to, progress);
|
|
20
20
|
}
|
|
21
|
-
function
|
|
22
|
-
if (
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
return Object.keys(colours).length === 0 ? shaped : { ...shaped, paint: { ...shaped.paint, ...colours } };
|
|
21
|
+
function withString(node, channel, value) {
|
|
22
|
+
if (channel !== "d")
|
|
23
|
+
return { ...node, paint: { ...node.paint, [channel]: value } };
|
|
24
|
+
return node.kind === "path" ? { ...node, d: value } : node;
|
|
26
25
|
}
|
|
27
26
|
export {
|
|
28
27
|
STRING_CHANNELS,
|
|
29
28
|
colourBetween,
|
|
30
29
|
stringMixer,
|
|
31
30
|
stringOf,
|
|
32
|
-
|
|
31
|
+
withString
|
|
33
32
|
};
|
|
34
33
|
//# sourceMappingURL=strings.js.map
|