@voila.dev/motion 6.0.121 → 6.0.123

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.
@@ -1,4 +1,5 @@
1
1
  import { groupControls, thenable } from "#/animate/controls.ts";
2
+ import { planGenerator, totalDuration } from "#/animate/plan.ts";
2
3
  import type {
3
4
  AnimationControls,
4
5
  AnimationOptions,
@@ -48,6 +49,36 @@ function startOf(
48
49
  return labels.get(at) ?? cursor;
49
50
  }
50
51
 
52
+ /**
53
+ * How long a segment that waits for an earlier one will run once launched.
54
+ * Its first keyframe is only read then, but a tween's length doesn't depend
55
+ * on it, and neither does a default spring's (its rest thresholds scale with
56
+ * the distance).
57
+ */
58
+ function plannedDuration(keyframes: unknown, options: AnimationOptions) {
59
+ const channels =
60
+ typeof keyframes === "object" &&
61
+ keyframes !== null &&
62
+ !Array.isArray(keyframes)
63
+ ? Object.values(keyframes)
64
+ : [keyframes];
65
+ const { delay } = options;
66
+ return (
67
+ (typeof delay === "number" ? delay : 0) +
68
+ Math.max(
69
+ 0,
70
+ ...channels.map((channel) => {
71
+ const frames = Array.isArray(channel) ? channel : [0, channel];
72
+ const { generator } = planGenerator(
73
+ frames.map((_frame, index) => index),
74
+ options,
75
+ );
76
+ return totalDuration(generator.duration, options);
77
+ }),
78
+ )
79
+ );
80
+ }
81
+
51
82
  /**
52
83
  * Launches every segment at once, each delayed to its place on the
53
84
  * timeline, so the whole sequence shares one clock and can be scrubbed. A
@@ -62,9 +93,13 @@ export function animateSequence(
62
93
  ): AnimationControls {
63
94
  const begun = now();
64
95
  const labels = new Map<string, number>();
65
- const busy = new Map<object, Map<string, Promise<void>>>();
96
+ const busy = new Map<
97
+ object,
98
+ Map<string, { readonly finished: Promise<void>; readonly end: number }>
99
+ >();
66
100
  const children: AnimationControls[] = [];
67
101
  const waits: Promise<void>[] = [];
102
+ let end = 0;
68
103
  let cursor = 0;
69
104
  let previousStart = 0;
70
105
  for (const segment of segments) {
@@ -78,7 +113,18 @@ export function animateSequence(
78
113
  unknown,
79
114
  SegmentOptions?,
80
115
  ];
81
- const start = startOf(own.at, cursor, previousStart, labels);
116
+ const placed = startOf(own.at, cursor, previousStart, labels);
117
+ const keys =
118
+ typeof keyframes === "object" &&
119
+ keyframes !== null &&
120
+ !Array.isArray(keyframes)
121
+ ? Object.keys(keyframes)
122
+ : ["value"];
123
+ const targets = resolveTargets(target);
124
+ const before = targets.flatMap((one) =>
125
+ keys.flatMap((key) => busy.get(one)?.get(key) ?? []),
126
+ );
127
+ const start = Math.max(placed, ...before.map((earlier) => earlier.end));
82
128
  const { delay } = own;
83
129
  /** Launched late, a segment only waits for what is left of its delay. */
84
130
  function optionsAt(elapsed: number): AnimationOptions {
@@ -92,32 +138,28 @@ export function animateSequence(
92
138
  : Math.max(0, offset + (delay ?? 0)),
93
139
  };
94
140
  }
95
- const keys =
96
- typeof keyframes === "object" &&
97
- keyframes !== null &&
98
- !Array.isArray(keyframes)
99
- ? Object.keys(keyframes)
100
- : ["value"];
101
- const targets = resolveTargets(target);
102
- const before = targets.flatMap((one) =>
103
- keys.flatMap((key) => busy.get(one)?.get(key) ?? []),
104
- );
105
141
  const launch = () =>
106
142
  animateOne(target, keyframes, optionsAt((now() - begun) / 1000));
107
143
  let finished: Promise<void>;
144
+ let ends: number;
108
145
  if (before.length === 0) {
109
146
  const controls = launch();
110
147
  children.push(controls);
111
148
  finished = controls.finished;
112
- cursor = Math.max(start, controls.duration);
149
+ ends = Math.max(start, controls.duration);
113
150
  } else {
114
- finished = Promise.all(before).then(() => launch().finished);
151
+ finished = Promise.all(before.map((earlier) => earlier.finished)).then(
152
+ () => launch().finished,
153
+ );
115
154
  waits.push(finished);
155
+ ends = start + plannedDuration(keyframes, { ...options, ...own });
116
156
  }
157
+ cursor = ends;
158
+ end = Math.max(end, ends);
117
159
  for (const one of targets) {
118
160
  const byKey = busy.get(one) ?? new Map();
119
161
  busy.set(one, byKey);
120
- for (const key of keys) byKey.set(key, finished);
162
+ for (const key of keys) byKey.set(key, { finished, end: ends });
121
163
  }
122
164
  previousStart = start;
123
165
  }
@@ -125,5 +167,9 @@ export function animateSequence(
125
167
  const finished = Promise.all([group.finished, ...waits]).then(
126
168
  () => undefined,
127
169
  );
170
+ // The segments still waiting count too: they end after the ones launched.
171
+ Object.defineProperty(group, "duration", {
172
+ value: Math.max(end, group.duration),
173
+ });
128
174
  return Object.assign(group, thenable(finished));
129
175
  }