motion 11.14.4 → 11.16.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.
Files changed (68) hide show
  1. package/README.md +3 -19
  2. package/dist/cjs/index.js +1795 -1724
  3. package/dist/cjs/mini.js +215 -185
  4. package/dist/cjs/react-client.js +3713 -3699
  5. package/dist/cjs/react-m.js +3 -2
  6. package/dist/cjs/react-mini.js +215 -185
  7. package/dist/es/framer-motion/dist/es/animation/animate/index.mjs +2 -1
  8. package/dist/es/framer-motion/dist/es/animation/animate/resolve-subjects.mjs +1 -0
  9. package/dist/es/framer-motion/dist/es/animation/animate/single-value.mjs +1 -1
  10. package/dist/es/framer-motion/dist/es/animation/animators/AcceleratedAnimation.mjs +7 -7
  11. package/dist/es/framer-motion/dist/es/animation/animators/MainThreadAnimation.mjs +8 -8
  12. package/dist/es/framer-motion/dist/es/animation/animators/utils/can-animate.mjs +1 -1
  13. package/dist/es/framer-motion/dist/es/animation/animators/waapi/NativeAnimation.mjs +26 -89
  14. package/dist/es/framer-motion/dist/es/animation/animators/waapi/animate-elements.mjs +3 -3
  15. package/dist/es/framer-motion/dist/es/animation/animators/waapi/animate-style.mjs +2 -1
  16. package/dist/es/framer-motion/dist/es/animation/animators/waapi/index.mjs +2 -1
  17. package/dist/es/framer-motion/dist/es/animation/animators/waapi/utils/supports-partial-keyframes.mjs +2 -1
  18. package/dist/es/framer-motion/dist/es/animation/animators/waapi/utils/supports-waapi.mjs +2 -1
  19. package/dist/es/framer-motion/dist/es/animation/generators/spring/find.mjs +1 -1
  20. package/dist/es/framer-motion/dist/es/animation/generators/spring/index.mjs +9 -6
  21. package/dist/es/framer-motion/dist/es/animation/interfaces/motion-value.mjs +7 -6
  22. package/dist/es/framer-motion/dist/es/animation/interfaces/visual-element-target.mjs +4 -3
  23. package/dist/es/framer-motion/dist/es/animation/sequence/create.mjs +32 -6
  24. package/dist/es/framer-motion/dist/es/animation/sequence/utils/calc-repeat-duration.mjs +5 -0
  25. package/dist/es/framer-motion/dist/es/animation/sequence/utils/normalize-times.mjs +13 -0
  26. package/dist/es/framer-motion/dist/es/easing/utils/map.mjs +4 -4
  27. package/dist/es/framer-motion/dist/es/events/event-info.mjs +1 -0
  28. package/dist/es/framer-motion/dist/es/frameloop/render-step.mjs +3 -2
  29. package/dist/es/framer-motion/dist/es/gestures/drag/utils/constraints.mjs +2 -1
  30. package/dist/es/framer-motion/dist/es/gestures/hover.mjs +1 -0
  31. package/dist/es/framer-motion/dist/es/gestures/pan/PanSession.mjs +5 -4
  32. package/dist/es/framer-motion/dist/es/gestures/press.mjs +1 -0
  33. package/dist/es/framer-motion/dist/es/projection/animation/mix-values.mjs +3 -3
  34. package/dist/es/framer-motion/dist/es/projection/node/create-projection-node.mjs +15 -15
  35. package/dist/es/framer-motion/dist/es/render/dom/resize/handle-element.mjs +1 -0
  36. package/dist/es/framer-motion/dist/es/render/dom/scroll/index.mjs +3 -3
  37. package/dist/es/framer-motion/dist/es/render/dom/scroll/info.mjs +2 -1
  38. package/dist/es/framer-motion/dist/es/render/dom/viewport/index.mjs +1 -0
  39. package/dist/es/framer-motion/dist/es/render/utils/motion-values.mjs +1 -1
  40. package/dist/es/framer-motion/dist/es/utils/delay.mjs +2 -1
  41. package/dist/es/framer-motion/dist/es/utils/interpolate.mjs +3 -3
  42. package/dist/es/framer-motion/dist/es/utils/offsets/fill.mjs +2 -1
  43. package/dist/es/framer-motion/dist/es/value/index.mjs +2 -2
  44. package/dist/es/framer-motion/dist/es/value/use-spring.mjs +3 -3
  45. package/dist/es/motion/lib/index.mjs +8 -8
  46. package/dist/es/motion/lib/react.mjs +8 -8
  47. package/dist/es/{framer-motion/dist/es/animation/GroupPlaybackControls.mjs → motion-dom/dist/es/animation/controls/BaseGroup.mjs} +6 -5
  48. package/dist/es/motion-dom/dist/es/animation/controls/Group.mjs +13 -0
  49. package/dist/es/{framer-motion/dist/es/easing → motion-dom/dist/es/animation/generators}/utils/create-generator-easing.mjs +6 -3
  50. package/dist/es/motion-dom/dist/es/animation/waapi/NativeAnimationControls.mjs +85 -0
  51. package/dist/es/{framer-motion/dist/es/animation/animators/waapi → motion-dom/dist/es/animation/waapi/utils}/easing.mjs +3 -3
  52. package/dist/es/{framer-motion/dist/es/animation/animators → motion-dom/dist/es/animation}/waapi/utils/linear.mjs +2 -1
  53. package/dist/es/{framer-motion/dist/es/animation/animators/waapi/utils/supports-linear-easing.mjs → motion-dom/dist/es/utils/supports/linear-easing.mjs} +1 -1
  54. package/dist/es/{framer-motion/dist/es/animation/animators/waapi/utils/memo-supports.mjs → motion-dom/dist/es/utils/supports/memo.mjs} +3 -2
  55. package/dist/es/motion-dom/dist/es/utils/supports/scroll-timeline.mjs +6 -0
  56. package/dist/motion.dev.js +1795 -1724
  57. package/dist/motion.js +1 -1
  58. package/package.json +3 -3
  59. package/dist/es/framer-motion/dist/es/render/dom/scroll/supports.mjs +0 -5
  60. /package/dist/es/{framer-motion → motion-dom}/dist/es/animation/generators/utils/calc-duration.mjs +0 -0
  61. /package/dist/es/{framer-motion → motion-dom}/dist/es/animation/generators/utils/is-generator.mjs +0 -0
  62. /package/dist/es/{framer-motion → motion-dom}/dist/es/animation/utils/get-value-transition.mjs +0 -0
  63. /package/dist/es/{framer-motion/dist/es/animation/animators → motion-dom/dist/es/animation}/waapi/utils/attach-timeline.mjs +0 -0
  64. /package/dist/es/{framer-motion/dist/es/easing → motion-dom/dist/es}/utils/is-bezier-definition.mjs +0 -0
  65. /package/dist/es/{framer-motion/dist/es/animation/animators/waapi/utils/supports-flags.mjs → motion-dom/dist/es/utils/supports/flags.mjs} +0 -0
  66. /package/dist/es/{framer-motion/dist/es/utils → motion-utils/dist/es}/memo.mjs +0 -0
  67. /package/dist/es/{framer-motion/dist/es/utils → motion-utils/dist/es}/progress.mjs +0 -0
  68. /package/dist/es/{framer-motion/dist/es/utils → motion-utils/dist/es}/time-conversion.mjs +0 -0
package/dist/cjs/index.js CHANGED
@@ -19,1456 +19,1710 @@ if (process.env.NODE_ENV !== "production") {
19
19
  };
20
20
  }
21
21
 
22
- const isDragging = {
23
- x: false,
24
- y: false,
25
- };
26
- function isDragActive() {
27
- return isDragging.y;
22
+ function memo(callback) {
23
+ let result;
24
+ return () => {
25
+ if (result === undefined)
26
+ result = callback();
27
+ return result;
28
+ };
28
29
  }
29
30
 
30
- function resolveElements(elementOrSelector, scope, selectorCache) {
31
- var _a;
32
- if (elementOrSelector instanceof Element) {
33
- return [elementOrSelector];
34
- }
35
- else if (typeof elementOrSelector === "string") {
36
- let root = document;
37
- if (scope) {
38
- // TODO: Refactor to utils package
39
- // invariant(
40
- // Boolean(scope.current),
41
- // "Scope provided, but no element detected."
42
- // )
43
- root = scope.current;
44
- }
45
- const elements = (_a = selectorCache === null || selectorCache === void 0 ? void 0 : selectorCache[elementOrSelector]) !== null && _a !== void 0 ? _a : root.querySelectorAll(elementOrSelector);
46
- return elements ? Array.from(elements) : [];
47
- }
48
- return Array.from(elementOrSelector);
49
- }
31
+ /*
32
+ Progress within given range
50
33
 
51
- function addUniqueItem(arr, item) {
52
- if (arr.indexOf(item) === -1)
53
- arr.push(item);
54
- }
55
- function removeItem(arr, item) {
56
- const index = arr.indexOf(item);
57
- if (index > -1)
58
- arr.splice(index, 1);
59
- }
34
+ Given a lower limit and an upper limit, we return the progress
35
+ (expressed as a number 0-1) represented by the given value, and
36
+ limit that progress to within 0-1.
60
37
 
61
- class SubscriptionManager {
62
- constructor() {
63
- this.subscriptions = [];
38
+ @param [number]: Lower limit
39
+ @param [number]: Upper limit
40
+ @param [number]: Value to find progress within given range
41
+ @return [number]: Progress of value within range as expressed 0-1
42
+ */
43
+ const progress = (from, to, value) => {
44
+ const toFromDifference = to - from;
45
+ return toFromDifference === 0 ? 1 : (value - from) / toFromDifference;
46
+ };
47
+
48
+ /**
49
+ * Converts seconds to milliseconds
50
+ *
51
+ * @param seconds - Time in seconds.
52
+ * @return milliseconds - Converted time in milliseconds.
53
+ */
54
+ const secondsToMilliseconds = (seconds) => seconds * 1000;
55
+ const millisecondsToSeconds = (milliseconds) => milliseconds / 1000;
56
+
57
+ const supportsScrollTimeline = memo(() => window.ScrollTimeline !== undefined);
58
+
59
+ class BaseGroupPlaybackControls {
60
+ constructor(animations) {
61
+ // Bound to accomodate common `return animation.stop` pattern
62
+ this.stop = () => this.runAll("stop");
63
+ this.animations = animations.filter(Boolean);
64
64
  }
65
- add(handler) {
66
- addUniqueItem(this.subscriptions, handler);
67
- return () => removeItem(this.subscriptions, handler);
65
+ get finished() {
66
+ // Support for new finished Promise and legacy thennable API
67
+ return Promise.all(this.animations.map((animation) => "finished" in animation ? animation.finished : animation));
68
68
  }
69
- notify(a, b, c) {
70
- const numSubscriptions = this.subscriptions.length;
71
- if (!numSubscriptions)
72
- return;
73
- if (numSubscriptions === 1) {
74
- /**
75
- * If there's only a single handler we can just call it without invoking a loop.
76
- */
77
- this.subscriptions[0](a, b, c);
69
+ /**
70
+ * TODO: Filter out cancelled or stopped animations before returning
71
+ */
72
+ getAll(propName) {
73
+ return this.animations[0][propName];
74
+ }
75
+ setAll(propName, newValue) {
76
+ for (let i = 0; i < this.animations.length; i++) {
77
+ this.animations[i][propName] = newValue;
78
78
  }
79
- else {
80
- for (let i = 0; i < numSubscriptions; i++) {
81
- /**
82
- * Check whether the handler exists before firing as it's possible
83
- * the subscriptions were modified during this loop running.
84
- */
85
- const handler = this.subscriptions[i];
86
- handler && handler(a, b, c);
79
+ }
80
+ attachTimeline(timeline, fallback) {
81
+ const subscriptions = this.animations.map((animation) => {
82
+ if (supportsScrollTimeline() && animation.attachTimeline) {
83
+ return animation.attachTimeline(timeline);
84
+ }
85
+ else {
86
+ return fallback(animation);
87
87
  }
88
+ });
89
+ return () => {
90
+ subscriptions.forEach((cancel, i) => {
91
+ cancel && cancel();
92
+ this.animations[i].stop();
93
+ });
94
+ };
95
+ }
96
+ get time() {
97
+ return this.getAll("time");
98
+ }
99
+ set time(time) {
100
+ this.setAll("time", time);
101
+ }
102
+ get speed() {
103
+ return this.getAll("speed");
104
+ }
105
+ set speed(speed) {
106
+ this.setAll("speed", speed);
107
+ }
108
+ get startTime() {
109
+ return this.getAll("startTime");
110
+ }
111
+ get duration() {
112
+ let max = 0;
113
+ for (let i = 0; i < this.animations.length; i++) {
114
+ max = Math.max(max, this.animations[i].duration);
88
115
  }
116
+ return max;
89
117
  }
90
- getSize() {
91
- return this.subscriptions.length;
118
+ runAll(methodName) {
119
+ this.animations.forEach((controls) => controls[methodName]());
92
120
  }
93
- clear() {
94
- this.subscriptions.length = 0;
121
+ flatten() {
122
+ this.runAll("flatten");
123
+ }
124
+ play() {
125
+ this.runAll("play");
126
+ }
127
+ pause() {
128
+ this.runAll("pause");
129
+ }
130
+ cancel() {
131
+ this.runAll("cancel");
132
+ }
133
+ complete() {
134
+ this.runAll("complete");
95
135
  }
96
136
  }
97
137
 
98
- /*
99
- Convert velocity into velocity per second
100
-
101
- @param [number]: Unit per frame
102
- @param [number]: Frame duration in ms
103
- */
104
- function velocityPerSecond(velocity, frameDuration) {
105
- return frameDuration ? velocity * (1000 / frameDuration) : 0;
138
+ /**
139
+ * TODO: This is a temporary class to support the legacy
140
+ * thennable API
141
+ */
142
+ class GroupPlaybackControls extends BaseGroupPlaybackControls {
143
+ then(onResolve, onReject) {
144
+ return Promise.all(this.animations).then(onResolve).catch(onReject);
145
+ }
106
146
  }
107
147
 
108
- const warned = new Set();
109
- function warnOnce(condition, message, element) {
110
- if (condition || warned.has(message))
111
- return;
112
- console.warn(message);
113
- if (element)
114
- console.warn(element);
115
- warned.add(message);
148
+ function getValueTransition$1(transition, key) {
149
+ return transition
150
+ ? transition[key] ||
151
+ transition["default"] ||
152
+ transition
153
+ : undefined;
116
154
  }
117
155
 
118
- const MotionGlobalConfig = {
119
- skipAnimations: false,
120
- useManualTiming: false,
121
- };
156
+ /**
157
+ * Implement a practical max duration for keyframe generation
158
+ * to prevent infinite loops
159
+ */
160
+ const maxGeneratorDuration = 20000;
161
+ function calcGeneratorDuration(generator) {
162
+ let duration = 0;
163
+ const timeStep = 50;
164
+ let state = generator.next(duration);
165
+ while (!state.done && duration < maxGeneratorDuration) {
166
+ duration += timeStep;
167
+ state = generator.next(duration);
168
+ }
169
+ return duration >= maxGeneratorDuration ? Infinity : duration;
170
+ }
122
171
 
123
- function createRenderStep(runNextFrame) {
124
- /**
125
- * We create and reuse two queues, one to queue jobs for the current frame
126
- * and one for the next. We reuse to avoid triggering GC after x frames.
127
- */
128
- let thisFrame = new Set();
129
- let nextFrame = new Set();
130
- /**
131
- * Track whether we're currently processing jobs in this step. This way
132
- * we can decide whether to schedule new jobs for this frame or next.
133
- */
134
- let isProcessing = false;
135
- let flushNextFrame = false;
136
- /**
137
- * A set of processes which were marked keepAlive when scheduled.
138
- */
139
- const toKeepAlive = new WeakSet();
140
- let latestFrameData = {
141
- delta: 0.0,
142
- timestamp: 0.0,
143
- isProcessing: false,
172
+ /**
173
+ * Create a progress => progress easing function from a generator.
174
+ */
175
+ function createGeneratorEasing(options, scale = 100, createGenerator) {
176
+ const generator = createGenerator({ ...options, keyframes: [0, scale] });
177
+ const duration = Math.min(calcGeneratorDuration(generator), maxGeneratorDuration);
178
+ return {
179
+ type: "keyframes",
180
+ ease: (progress) => {
181
+ return generator.next(duration * progress).value / scale;
182
+ },
183
+ duration: millisecondsToSeconds(duration),
144
184
  };
145
- function triggerCallback(callback) {
146
- if (toKeepAlive.has(callback)) {
147
- step.schedule(callback);
148
- runNextFrame();
149
- }
150
- callback(latestFrameData);
185
+ }
186
+
187
+ function isGenerator(type) {
188
+ return typeof type === "function";
189
+ }
190
+
191
+ function attachTimeline(animation, timeline) {
192
+ animation.timeline = timeline;
193
+ animation.onfinish = null;
194
+ }
195
+
196
+ class NativeAnimationControls {
197
+ constructor(animation) {
198
+ this.animation = animation;
151
199
  }
152
- const step = {
153
- /**
154
- * Schedule a process to run on the next frame.
155
- */
156
- schedule: (callback, keepAlive = false, immediate = false) => {
157
- const addToCurrentFrame = immediate && isProcessing;
158
- const queue = addToCurrentFrame ? thisFrame : nextFrame;
159
- if (keepAlive)
160
- toKeepAlive.add(callback);
161
- if (!queue.has(callback))
162
- queue.add(callback);
163
- return callback;
164
- },
165
- /**
166
- * Cancel the provided callback from running on the next frame.
167
- */
168
- cancel: (callback) => {
169
- nextFrame.delete(callback);
170
- toKeepAlive.delete(callback);
171
- },
172
- /**
173
- * Execute all schedule callbacks.
174
- */
175
- process: (frameData) => {
176
- latestFrameData = frameData;
177
- /**
178
- * If we're already processing we've probably been triggered by a flushSync
179
- * inside an existing process. Instead of executing, mark flushNextFrame
180
- * as true and ensure we flush the following frame at the end of this one.
181
- */
182
- if (isProcessing) {
183
- flushNextFrame = true;
184
- return;
185
- }
186
- isProcessing = true;
187
- [thisFrame, nextFrame] = [nextFrame, thisFrame];
188
- // Clear the next frame queue
189
- nextFrame.clear();
190
- // Execute this frame
191
- thisFrame.forEach(triggerCallback);
192
- isProcessing = false;
193
- if (flushNextFrame) {
194
- flushNextFrame = false;
195
- step.process(frameData);
196
- }
197
- },
198
- };
199
- return step;
200
- }
201
-
202
- const stepsOrder = [
203
- "read", // Read
204
- "resolveKeyframes", // Write/Read/Write/Read
205
- "update", // Compute
206
- "preRender", // Compute
207
- "render", // Write
208
- "postRender", // Compute
209
- ];
210
- const maxElapsed$1 = 40;
211
- function createRenderBatcher(scheduleNextBatch, allowKeepAlive) {
212
- let runNextFrame = false;
213
- let useDefaultElapsed = true;
214
- const state = {
215
- delta: 0.0,
216
- timestamp: 0.0,
217
- isProcessing: false,
218
- };
219
- const flagRunNextFrame = () => (runNextFrame = true);
220
- const steps = stepsOrder.reduce((acc, key) => {
221
- acc[key] = createRenderStep(flagRunNextFrame);
222
- return acc;
223
- }, {});
224
- const { read, resolveKeyframes, update, preRender, render, postRender } = steps;
225
- const processBatch = () => {
226
- const timestamp = performance.now();
227
- runNextFrame = false;
228
- state.delta = useDefaultElapsed
229
- ? 1000 / 60
230
- : Math.max(Math.min(timestamp - state.timestamp, maxElapsed$1), 1);
231
- state.timestamp = timestamp;
232
- state.isProcessing = true;
233
- // Unrolled render loop for better per-frame performance
234
- read.process(state);
235
- resolveKeyframes.process(state);
236
- update.process(state);
237
- preRender.process(state);
238
- render.process(state);
239
- postRender.process(state);
240
- state.isProcessing = false;
241
- if (runNextFrame && allowKeepAlive) {
242
- useDefaultElapsed = false;
243
- scheduleNextBatch(processBatch);
244
- }
245
- };
246
- const wake = () => {
247
- runNextFrame = true;
248
- useDefaultElapsed = true;
249
- if (!state.isProcessing) {
250
- scheduleNextBatch(processBatch);
251
- }
252
- };
253
- const schedule = stepsOrder.reduce((acc, key) => {
254
- const step = steps[key];
255
- acc[key] = (process, keepAlive = false, immediate = false) => {
256
- if (!runNextFrame)
257
- wake();
258
- return step.schedule(process, keepAlive, immediate);
259
- };
260
- return acc;
261
- }, {});
262
- const cancel = (process) => {
263
- for (let i = 0; i < stepsOrder.length; i++) {
264
- steps[stepsOrder[i]].cancel(process);
265
- }
266
- };
267
- return { schedule, cancel, state, steps };
268
- }
269
-
270
- const { schedule: frame, cancel: cancelFrame, state: frameData, steps: frameSteps, } = createRenderBatcher(typeof requestAnimationFrame !== "undefined" ? requestAnimationFrame : noop, true);
271
-
272
- let now;
273
- function clearTime() {
274
- now = undefined;
275
- }
276
- /**
277
- * An eventloop-synchronous alternative to performance.now().
278
- *
279
- * Ensures that time measurements remain consistent within a synchronous context.
280
- * Usually calling performance.now() twice within the same synchronous context
281
- * will return different values which isn't useful for animations when we're usually
282
- * trying to sync animations to the same frame.
283
- */
284
- const time = {
285
- now: () => {
286
- if (now === undefined) {
287
- time.set(frameData.isProcessing || MotionGlobalConfig.useManualTiming
288
- ? frameData.timestamp
289
- : performance.now());
200
+ get duration() {
201
+ var _a, _b, _c;
202
+ const durationInMs = ((_b = (_a = this.animation) === null || _a === void 0 ? void 0 : _a.effect) === null || _b === void 0 ? void 0 : _b.getComputedTiming().duration) ||
203
+ ((_c = this.options) === null || _c === void 0 ? void 0 : _c.duration) ||
204
+ 300;
205
+ return millisecondsToSeconds(Number(durationInMs));
206
+ }
207
+ get time() {
208
+ var _a;
209
+ if (this.animation) {
210
+ return millisecondsToSeconds(((_a = this.animation) === null || _a === void 0 ? void 0 : _a.currentTime) || 0);
290
211
  }
291
- return now;
292
- },
293
- set: (newTime) => {
294
- now = newTime;
295
- queueMicrotask(clearTime);
296
- },
297
- };
298
-
299
- /**
300
- * Maximum time between the value of two frames, beyond which we
301
- * assume the velocity has since been 0.
302
- */
303
- const MAX_VELOCITY_DELTA = 30;
304
- const isFloat = (value) => {
305
- return !isNaN(parseFloat(value));
306
- };
307
- /**
308
- * `MotionValue` is used to track the state and velocity of motion values.
309
- *
310
- * @public
311
- */
312
- class MotionValue {
313
- /**
314
- * @param init - The initiating value
315
- * @param config - Optional configuration options
316
- *
317
- * - `transformer`: A function to transform incoming values with.
318
- *
319
- * @internal
320
- */
321
- constructor(init, options = {}) {
322
- /**
323
- * This will be replaced by the build step with the latest version number.
324
- * When MotionValues are provided to motion components, warn if versions are mixed.
325
- */
326
- this.version = "11.14.4";
327
- /**
328
- * Tracks whether this value can output a velocity. Currently this is only true
329
- * if the value is numerical, but we might be able to widen the scope here and support
330
- * other value types.
331
- *
332
- * @internal
333
- */
334
- this.canTrackVelocity = null;
335
- /**
336
- * An object containing a SubscriptionManager for each active event.
337
- */
338
- this.events = {};
339
- this.updateAndNotify = (v, render = true) => {
340
- const currentTime = time.now();
341
- /**
342
- * If we're updating the value during another frame or eventloop
343
- * than the previous frame, then the we set the previous frame value
344
- * to current.
345
- */
346
- if (this.updatedAt !== currentTime) {
347
- this.setPrevFrameValue();
348
- }
349
- this.prev = this.current;
350
- this.setCurrent(v);
351
- // Update update subscribers
352
- if (this.current !== this.prev && this.events.change) {
353
- this.events.change.notify(this.current);
354
- }
355
- // Update render subscribers
356
- if (render && this.events.renderRequest) {
357
- this.events.renderRequest.notify(this.current);
358
- }
359
- };
360
- this.hasAnimated = false;
361
- this.setCurrent(init);
362
- this.owner = options.owner;
212
+ return 0;
363
213
  }
364
- setCurrent(current) {
365
- this.current = current;
366
- this.updatedAt = time.now();
367
- if (this.canTrackVelocity === null && current !== undefined) {
368
- this.canTrackVelocity = isFloat(this.current);
214
+ set time(newTime) {
215
+ if (this.animation) {
216
+ this.animation.currentTime = secondsToMilliseconds(newTime);
369
217
  }
370
218
  }
371
- setPrevFrameValue(prevFrameValue = this.current) {
372
- this.prevFrameValue = prevFrameValue;
373
- this.prevUpdatedAt = this.updatedAt;
219
+ get speed() {
220
+ return this.animation ? this.animation.playbackRate : 1;
374
221
  }
375
- /**
376
- * Adds a function that will be notified when the `MotionValue` is updated.
377
- *
378
- * It returns a function that, when called, will cancel the subscription.
379
- *
380
- * When calling `onChange` inside a React component, it should be wrapped with the
381
- * `useEffect` hook. As it returns an unsubscribe function, this should be returned
382
- * from the `useEffect` function to ensure you don't add duplicate subscribers..
383
- *
384
- * ```jsx
385
- * export const MyComponent = () => {
386
- * const x = useMotionValue(0)
387
- * const y = useMotionValue(0)
388
- * const opacity = useMotionValue(1)
389
- *
390
- * useEffect(() => {
391
- * function updateOpacity() {
392
- * const maxXY = Math.max(x.get(), y.get())
393
- * const newOpacity = transform(maxXY, [0, 100], [1, 0])
394
- * opacity.set(newOpacity)
395
- * }
396
- *
397
- * const unsubscribeX = x.on("change", updateOpacity)
398
- * const unsubscribeY = y.on("change", updateOpacity)
399
- *
400
- * return () => {
401
- * unsubscribeX()
402
- * unsubscribeY()
403
- * }
404
- * }, [])
405
- *
406
- * return <motion.div style={{ x }} />
407
- * }
408
- * ```
409
- *
410
- * @param subscriber - A function that receives the latest value.
411
- * @returns A function that, when called, will cancel this subscription.
412
- *
413
- * @deprecated
414
- */
415
- onChange(subscription) {
416
- if (process.env.NODE_ENV !== "production") {
417
- warnOnce(false, `value.onChange(callback) is deprecated. Switch to value.on("change", callback).`);
222
+ set speed(newSpeed) {
223
+ if (this.animation) {
224
+ this.animation.playbackRate = newSpeed;
418
225
  }
419
- return this.on("change", subscription);
420
226
  }
421
- on(eventName, callback) {
422
- if (!this.events[eventName]) {
423
- this.events[eventName] = new SubscriptionManager();
227
+ get state() {
228
+ return this.animation ? this.animation.playState : "finished";
229
+ }
230
+ get startTime() {
231
+ return this.animation ? this.animation.startTime : null;
232
+ }
233
+ get finished() {
234
+ return this.animation ? this.animation.finished : Promise.resolve();
235
+ }
236
+ play() {
237
+ this.animation && this.animation.play();
238
+ }
239
+ pause() {
240
+ this.animation && this.animation.pause();
241
+ }
242
+ stop() {
243
+ if (!this.animation ||
244
+ this.state === "idle" ||
245
+ this.state === "finished") {
246
+ return;
424
247
  }
425
- const unsubscribe = this.events[eventName].add(callback);
426
- if (eventName === "change") {
427
- return () => {
428
- unsubscribe();
429
- /**
430
- * If we have no more change listeners by the start
431
- * of the next frame, stop active animations.
432
- */
433
- frame.read(() => {
434
- if (!this.events.change.getSize()) {
435
- this.stop();
436
- }
437
- });
438
- };
248
+ if (this.animation.commitStyles) {
249
+ this.animation.commitStyles();
439
250
  }
440
- return unsubscribe;
251
+ this.cancel();
441
252
  }
442
- clearListeners() {
443
- for (const eventManagers in this.events) {
444
- this.events[eventManagers].clear();
253
+ flatten() {
254
+ var _a;
255
+ if (!this.animation)
256
+ return;
257
+ (_a = this.animation.effect) === null || _a === void 0 ? void 0 : _a.updateTiming({ easing: "linear" });
258
+ }
259
+ attachTimeline(timeline) {
260
+ if (this.animation)
261
+ attachTimeline(this.animation, timeline);
262
+ return noop;
263
+ }
264
+ complete() {
265
+ this.animation && this.animation.finish();
266
+ }
267
+ cancel() {
268
+ try {
269
+ this.animation && this.animation.cancel();
445
270
  }
271
+ catch (e) { }
272
+ }
273
+ }
274
+
275
+ const isBezierDefinition = (easing) => Array.isArray(easing) && typeof easing[0] === "number";
276
+
277
+ /**
278
+ * Add the ability for test suites to manually set support flags
279
+ * to better test more environments.
280
+ */
281
+ const supportsFlags = {
282
+ linearEasing: undefined,
283
+ };
284
+
285
+ function memoSupports(callback, supportsFlag) {
286
+ const memoized = memo(callback);
287
+ return () => { var _a; return (_a = supportsFlags[supportsFlag]) !== null && _a !== void 0 ? _a : memoized(); };
288
+ }
289
+
290
+ const supportsLinearEasing = /*@__PURE__*/ memoSupports(() => {
291
+ try {
292
+ document
293
+ .createElement("div")
294
+ .animate({ opacity: 0 }, { easing: "linear(0, 1)" });
295
+ }
296
+ catch (e) {
297
+ return false;
446
298
  }
447
- /**
448
- * Attaches a passive effect to the `MotionValue`.
449
- *
450
- * @internal
451
- */
452
- attach(passiveEffect, stopPassiveEffect) {
453
- this.passiveEffect = passiveEffect;
454
- this.stopPassiveEffect = stopPassiveEffect;
299
+ return true;
300
+ }, "linearEasing");
301
+
302
+ const generateLinearEasing = (easing, duration, // as milliseconds
303
+ resolution = 10 // as milliseconds
304
+ ) => {
305
+ let points = "";
306
+ const numPoints = Math.max(Math.round(duration / resolution), 2);
307
+ for (let i = 0; i < numPoints; i++) {
308
+ points += easing(progress(0, numPoints - 1, i)) + ", ";
455
309
  }
456
- /**
457
- * Sets the state of the `MotionValue`.
458
- *
459
- * @remarks
460
- *
461
- * ```jsx
462
- * const x = useMotionValue(0)
463
- * x.set(10)
464
- * ```
465
- *
466
- * @param latest - Latest value to set.
467
- * @param render - Whether to notify render subscribers. Defaults to `true`
468
- *
469
- * @public
470
- */
471
- set(v, render = true) {
472
- if (!render || !this.passiveEffect) {
473
- this.updateAndNotify(v, render);
474
- }
475
- else {
476
- this.passiveEffect(v, this.updateAndNotify);
477
- }
310
+ return `linear(${points.substring(0, points.length - 2)})`;
311
+ };
312
+
313
+ function isWaapiSupportedEasing(easing) {
314
+ return Boolean((typeof easing === "function" && supportsLinearEasing()) ||
315
+ !easing ||
316
+ (typeof easing === "string" &&
317
+ (easing in supportedWaapiEasing || supportsLinearEasing())) ||
318
+ isBezierDefinition(easing) ||
319
+ (Array.isArray(easing) && easing.every(isWaapiSupportedEasing)));
320
+ }
321
+ const cubicBezierAsString = ([a, b, c, d]) => `cubic-bezier(${a}, ${b}, ${c}, ${d})`;
322
+ const supportedWaapiEasing = {
323
+ linear: "linear",
324
+ ease: "ease",
325
+ easeIn: "ease-in",
326
+ easeOut: "ease-out",
327
+ easeInOut: "ease-in-out",
328
+ circIn: /*@__PURE__*/ cubicBezierAsString([0, 0.65, 0.55, 1]),
329
+ circOut: /*@__PURE__*/ cubicBezierAsString([0.55, 0, 1, 0.45]),
330
+ backIn: /*@__PURE__*/ cubicBezierAsString([0.31, 0.01, 0.66, -0.59]),
331
+ backOut: /*@__PURE__*/ cubicBezierAsString([0.33, 1.53, 0.69, 0.99]),
332
+ };
333
+ function mapEasingToNativeEasing(easing, duration) {
334
+ if (!easing) {
335
+ return undefined;
478
336
  }
479
- setWithVelocity(prev, current, delta) {
480
- this.set(current);
481
- this.prev = undefined;
482
- this.prevFrameValue = prev;
483
- this.prevUpdatedAt = this.updatedAt - delta;
337
+ else if (typeof easing === "function" && supportsLinearEasing()) {
338
+ return generateLinearEasing(easing, duration);
484
339
  }
485
- /**
486
- * Set the state of the `MotionValue`, stopping any active animations,
487
- * effects, and resets velocity to `0`.
488
- */
489
- jump(v, endAnimation = true) {
490
- this.updateAndNotify(v);
491
- this.prev = v;
492
- this.prevUpdatedAt = this.prevFrameValue = undefined;
493
- endAnimation && this.stop();
494
- if (this.stopPassiveEffect)
495
- this.stopPassiveEffect();
340
+ else if (isBezierDefinition(easing)) {
341
+ return cubicBezierAsString(easing);
496
342
  }
497
- /**
498
- * Returns the latest state of `MotionValue`
499
- *
500
- * @returns - The latest state of `MotionValue`
501
- *
502
- * @public
503
- */
504
- get() {
505
- return this.current;
343
+ else if (Array.isArray(easing)) {
344
+ return easing.map((segmentEasing) => mapEasingToNativeEasing(segmentEasing, duration) ||
345
+ supportedWaapiEasing.easeOut);
506
346
  }
507
- /**
508
- * @public
509
- */
510
- getPrevious() {
511
- return this.prev;
347
+ else {
348
+ return supportedWaapiEasing[easing];
512
349
  }
513
- /**
514
- * Returns the latest velocity of `MotionValue`
515
- *
516
- * @returns - The latest velocity of `MotionValue`. Returns `0` if the state is non-numerical.
517
- *
518
- * @public
519
- */
520
- getVelocity() {
521
- const currentTime = time.now();
522
- if (!this.canTrackVelocity ||
523
- this.prevFrameValue === undefined ||
524
- currentTime - this.updatedAt > MAX_VELOCITY_DELTA) {
525
- return 0;
350
+ }
351
+
352
+ const isDragging = {
353
+ x: false,
354
+ y: false,
355
+ };
356
+ function isDragActive() {
357
+ return isDragging.y;
358
+ }
359
+
360
+ function resolveElements(elementOrSelector, scope, selectorCache) {
361
+ var _a;
362
+ if (elementOrSelector instanceof Element) {
363
+ return [elementOrSelector];
364
+ }
365
+ else if (typeof elementOrSelector === "string") {
366
+ let root = document;
367
+ if (scope) {
368
+ // TODO: Refactor to utils package
369
+ // invariant(
370
+ // Boolean(scope.current),
371
+ // "Scope provided, but no element detected."
372
+ // )
373
+ root = scope.current;
526
374
  }
527
- const delta = Math.min(this.updatedAt - this.prevUpdatedAt, MAX_VELOCITY_DELTA);
528
- // Casts because of parseFloat's poor typing
529
- return velocityPerSecond(parseFloat(this.current) -
530
- parseFloat(this.prevFrameValue), delta);
375
+ const elements = (_a = selectorCache === null || selectorCache === void 0 ? void 0 : selectorCache[elementOrSelector]) !== null && _a !== void 0 ? _a : root.querySelectorAll(elementOrSelector);
376
+ return elements ? Array.from(elements) : [];
531
377
  }
378
+ return Array.from(elementOrSelector);
379
+ }
380
+
381
+ const clamp = (min, max, v) => {
382
+ if (v > max)
383
+ return max;
384
+ if (v < min)
385
+ return min;
386
+ return v;
387
+ };
388
+
389
+ /*
390
+ Convert velocity into velocity per second
391
+
392
+ @param [number]: Unit per frame
393
+ @param [number]: Frame duration in ms
394
+ */
395
+ function velocityPerSecond(velocity, frameDuration) {
396
+ return frameDuration ? velocity * (1000 / frameDuration) : 0;
397
+ }
398
+
399
+ const velocitySampleDuration = 5; // ms
400
+ function calcGeneratorVelocity(resolveValue, t, current) {
401
+ const prevT = Math.max(t - velocitySampleDuration, 0);
402
+ return velocityPerSecond(current - resolveValue(prevT), t - prevT);
403
+ }
404
+
405
+ const springDefaults = {
406
+ // Default spring physics
407
+ stiffness: 100,
408
+ damping: 10,
409
+ mass: 1.0,
410
+ velocity: 0.0,
411
+ // Default duration/bounce-based options
412
+ duration: 800, // in ms
413
+ bounce: 0.3,
414
+ visualDuration: 0.3, // in seconds
415
+ // Rest thresholds
416
+ restSpeed: {
417
+ granular: 0.01,
418
+ default: 2,
419
+ },
420
+ restDelta: {
421
+ granular: 0.005,
422
+ default: 0.5,
423
+ },
424
+ // Limits
425
+ minDuration: 0.01, // in seconds
426
+ maxDuration: 10.0, // in seconds
427
+ minDamping: 0.05,
428
+ maxDamping: 1,
429
+ };
430
+
431
+ const safeMin = 0.001;
432
+ function findSpring({ duration = springDefaults.duration, bounce = springDefaults.bounce, velocity = springDefaults.velocity, mass = springDefaults.mass, }) {
433
+ let envelope;
434
+ let derivative;
435
+ warning(duration <= secondsToMilliseconds(springDefaults.maxDuration), "Spring duration must be 10 seconds or less");
436
+ let dampingRatio = 1 - bounce;
532
437
  /**
533
- * Registers a new animation to control this `MotionValue`. Only one
534
- * animation can drive a `MotionValue` at one time.
535
- *
536
- * ```jsx
537
- * value.start()
538
- * ```
539
- *
540
- * @param animation - A function that starts the provided animation
541
- *
542
- * @internal
438
+ * Restrict dampingRatio and duration to within acceptable ranges.
543
439
  */
544
- start(startAnimation) {
545
- this.stop();
546
- return new Promise((resolve) => {
547
- this.hasAnimated = true;
548
- this.animation = startAnimation(resolve);
549
- if (this.events.animationStart) {
550
- this.events.animationStart.notify();
551
- }
552
- }).then(() => {
553
- if (this.events.animationComplete) {
554
- this.events.animationComplete.notify();
555
- }
556
- this.clearAnimation();
557
- });
440
+ dampingRatio = clamp(springDefaults.minDamping, springDefaults.maxDamping, dampingRatio);
441
+ duration = clamp(springDefaults.minDuration, springDefaults.maxDuration, millisecondsToSeconds(duration));
442
+ if (dampingRatio < 1) {
443
+ /**
444
+ * Underdamped spring
445
+ */
446
+ envelope = (undampedFreq) => {
447
+ const exponentialDecay = undampedFreq * dampingRatio;
448
+ const delta = exponentialDecay * duration;
449
+ const a = exponentialDecay - velocity;
450
+ const b = calcAngularFreq(undampedFreq, dampingRatio);
451
+ const c = Math.exp(-delta);
452
+ return safeMin - (a / b) * c;
453
+ };
454
+ derivative = (undampedFreq) => {
455
+ const exponentialDecay = undampedFreq * dampingRatio;
456
+ const delta = exponentialDecay * duration;
457
+ const d = delta * velocity + velocity;
458
+ const e = Math.pow(dampingRatio, 2) * Math.pow(undampedFreq, 2) * duration;
459
+ const f = Math.exp(-delta);
460
+ const g = calcAngularFreq(Math.pow(undampedFreq, 2), dampingRatio);
461
+ const factor = -envelope(undampedFreq) + safeMin > 0 ? -1 : 1;
462
+ return (factor * ((d - e) * f)) / g;
463
+ };
558
464
  }
559
- /**
560
- * Stop the currently active animation.
561
- *
562
- * @public
563
- */
564
- stop() {
565
- if (this.animation) {
566
- this.animation.stop();
567
- if (this.events.animationCancel) {
568
- this.events.animationCancel.notify();
569
- }
570
- }
571
- this.clearAnimation();
465
+ else {
466
+ /**
467
+ * Critically-damped spring
468
+ */
469
+ envelope = (undampedFreq) => {
470
+ const a = Math.exp(-undampedFreq * duration);
471
+ const b = (undampedFreq - velocity) * duration + 1;
472
+ return -safeMin + a * b;
473
+ };
474
+ derivative = (undampedFreq) => {
475
+ const a = Math.exp(-undampedFreq * duration);
476
+ const b = (velocity - undampedFreq) * (duration * duration);
477
+ return a * b;
478
+ };
572
479
  }
573
- /**
574
- * Returns `true` if this value is currently animating.
575
- *
576
- * @public
577
- */
578
- isAnimating() {
579
- return !!this.animation;
480
+ const initialGuess = 5 / duration;
481
+ const undampedFreq = approximateRoot(envelope, derivative, initialGuess);
482
+ duration = secondsToMilliseconds(duration);
483
+ if (isNaN(undampedFreq)) {
484
+ return {
485
+ stiffness: springDefaults.stiffness,
486
+ damping: springDefaults.damping,
487
+ duration,
488
+ };
580
489
  }
581
- clearAnimation() {
582
- delete this.animation;
490
+ else {
491
+ const stiffness = Math.pow(undampedFreq, 2) * mass;
492
+ return {
493
+ stiffness,
494
+ damping: dampingRatio * 2 * Math.sqrt(mass * stiffness),
495
+ duration,
496
+ };
583
497
  }
584
- /**
585
- * Destroy and clean up subscribers to this `MotionValue`.
586
- *
587
- * The `MotionValue` hooks like `useMotionValue` and `useTransform` automatically
588
- * handle the lifecycle of the returned `MotionValue`, so this method is only necessary if you've manually
589
- * created a `MotionValue` via the `motionValue` function.
590
- *
591
- * @public
592
- */
593
- destroy() {
594
- this.clearListeners();
595
- this.stop();
596
- if (this.stopPassiveEffect) {
597
- this.stopPassiveEffect();
598
- }
498
+ }
499
+ const rootIterations = 12;
500
+ function approximateRoot(envelope, derivative, initialGuess) {
501
+ let result = initialGuess;
502
+ for (let i = 1; i < rootIterations; i++) {
503
+ result = result - envelope(result) / derivative(result);
599
504
  }
505
+ return result;
600
506
  }
601
- function motionValue(init, options) {
602
- return new MotionValue(init, options);
507
+ function calcAngularFreq(undampedFreq, dampingRatio) {
508
+ return undampedFreq * Math.sqrt(1 - dampingRatio * dampingRatio);
603
509
  }
604
510
 
605
- function memo(callback) {
606
- let result;
607
- return () => {
608
- if (result === undefined)
609
- result = callback();
610
- return result;
611
- };
511
+ const durationKeys = ["duration", "bounce"];
512
+ const physicsKeys = ["stiffness", "damping", "mass"];
513
+ function isSpringType(options, keys) {
514
+ return keys.some((key) => options[key] !== undefined);
612
515
  }
613
-
614
- const supportsScrollTimeline = memo(() => window.ScrollTimeline !== undefined);
615
-
616
- class GroupPlaybackControls {
617
- constructor(animations) {
618
- // Bound to accomodate common `return animation.stop` pattern
619
- this.stop = () => this.runAll("stop");
620
- this.animations = animations.filter(Boolean);
621
- }
622
- then(onResolve, onReject) {
623
- return Promise.all(this.animations).then(onResolve).catch(onReject);
516
+ function getSpringOptions(options) {
517
+ let springOptions = {
518
+ velocity: springDefaults.velocity,
519
+ stiffness: springDefaults.stiffness,
520
+ damping: springDefaults.damping,
521
+ mass: springDefaults.mass,
522
+ isResolvedFromDuration: false,
523
+ ...options,
524
+ };
525
+ // stiffness/damping/mass overrides duration/bounce
526
+ if (!isSpringType(options, physicsKeys) &&
527
+ isSpringType(options, durationKeys)) {
528
+ if (options.visualDuration) {
529
+ const visualDuration = options.visualDuration;
530
+ const root = (2 * Math.PI) / (visualDuration * 1.2);
531
+ const stiffness = root * root;
532
+ const damping = 2 *
533
+ clamp(0.05, 1, 1 - (options.bounce || 0)) *
534
+ Math.sqrt(stiffness);
535
+ springOptions = {
536
+ ...springOptions,
537
+ mass: springDefaults.mass,
538
+ stiffness,
539
+ damping,
540
+ };
541
+ }
542
+ else {
543
+ const derived = findSpring(options);
544
+ springOptions = {
545
+ ...springOptions,
546
+ ...derived,
547
+ mass: springDefaults.mass,
548
+ };
549
+ springOptions.isResolvedFromDuration = true;
550
+ }
624
551
  }
552
+ return springOptions;
553
+ }
554
+ function spring(optionsOrVisualDuration = springDefaults.visualDuration, bounce = springDefaults.bounce) {
555
+ const options = typeof optionsOrVisualDuration !== "object"
556
+ ? {
557
+ visualDuration: optionsOrVisualDuration,
558
+ keyframes: [0, 1],
559
+ bounce,
560
+ }
561
+ : optionsOrVisualDuration;
562
+ let { restSpeed, restDelta } = options;
563
+ const origin = options.keyframes[0];
564
+ const target = options.keyframes[options.keyframes.length - 1];
625
565
  /**
626
- * TODO: Filter out cancelled or stopped animations before returning
566
+ * This is the Iterator-spec return value. We ensure it's mutable rather than using a generator
567
+ * to reduce GC during animation.
627
568
  */
628
- getAll(propName) {
629
- return this.animations[0][propName];
630
- }
631
- setAll(propName, newValue) {
632
- for (let i = 0; i < this.animations.length; i++) {
633
- this.animations[i][propName] = newValue;
634
- }
635
- }
636
- attachTimeline(timeline, fallback) {
637
- const subscriptions = this.animations.map((animation) => {
638
- if (supportsScrollTimeline() && animation.attachTimeline) {
639
- return animation.attachTimeline(timeline);
640
- }
641
- else {
642
- return fallback(animation);
643
- }
644
- });
645
- return () => {
646
- subscriptions.forEach((cancel, i) => {
647
- cancel && cancel();
648
- this.animations[i].stop();
649
- });
569
+ const state = { done: false, value: origin };
570
+ const { stiffness, damping, mass, duration, velocity, isResolvedFromDuration, } = getSpringOptions({
571
+ ...options,
572
+ velocity: -millisecondsToSeconds(options.velocity || 0),
573
+ });
574
+ const initialVelocity = velocity || 0.0;
575
+ const dampingRatio = damping / (2 * Math.sqrt(stiffness * mass));
576
+ const initialDelta = target - origin;
577
+ const undampedAngularFreq = millisecondsToSeconds(Math.sqrt(stiffness / mass));
578
+ /**
579
+ * If we're working on a granular scale, use smaller defaults for determining
580
+ * when the spring is finished.
581
+ *
582
+ * These defaults have been selected emprically based on what strikes a good
583
+ * ratio between feeling good and finishing as soon as changes are imperceptible.
584
+ */
585
+ const isGranularScale = Math.abs(initialDelta) < 5;
586
+ restSpeed || (restSpeed = isGranularScale
587
+ ? springDefaults.restSpeed.granular
588
+ : springDefaults.restSpeed.default);
589
+ restDelta || (restDelta = isGranularScale
590
+ ? springDefaults.restDelta.granular
591
+ : springDefaults.restDelta.default);
592
+ let resolveSpring;
593
+ if (dampingRatio < 1) {
594
+ const angularFreq = calcAngularFreq(undampedAngularFreq, dampingRatio);
595
+ // Underdamped spring
596
+ resolveSpring = (t) => {
597
+ const envelope = Math.exp(-dampingRatio * undampedAngularFreq * t);
598
+ return (target -
599
+ envelope *
600
+ (((initialVelocity +
601
+ dampingRatio * undampedAngularFreq * initialDelta) /
602
+ angularFreq) *
603
+ Math.sin(angularFreq * t) +
604
+ initialDelta * Math.cos(angularFreq * t)));
650
605
  };
651
606
  }
652
- get time() {
653
- return this.getAll("time");
654
- }
655
- set time(time) {
656
- this.setAll("time", time);
657
- }
658
- get speed() {
659
- return this.getAll("speed");
660
- }
661
- set speed(speed) {
662
- this.setAll("speed", speed);
663
- }
664
- get startTime() {
665
- return this.getAll("startTime");
666
- }
667
- get duration() {
668
- let max = 0;
669
- for (let i = 0; i < this.animations.length; i++) {
670
- max = Math.max(max, this.animations[i].duration);
671
- }
672
- return max;
673
- }
674
- runAll(methodName) {
675
- this.animations.forEach((controls) => controls[methodName]());
676
- }
677
- flatten() {
678
- this.runAll("flatten");
679
- }
680
- play() {
681
- this.runAll("play");
682
- }
683
- pause() {
684
- this.runAll("pause");
685
- }
686
- cancel() {
687
- this.runAll("cancel");
607
+ else if (dampingRatio === 1) {
608
+ // Critically damped spring
609
+ resolveSpring = (t) => target -
610
+ Math.exp(-undampedAngularFreq * t) *
611
+ (initialDelta +
612
+ (initialVelocity + undampedAngularFreq * initialDelta) * t);
688
613
  }
689
- complete() {
690
- this.runAll("complete");
614
+ else {
615
+ // Overdamped spring
616
+ const dampedAngularFreq = undampedAngularFreq * Math.sqrt(dampingRatio * dampingRatio - 1);
617
+ resolveSpring = (t) => {
618
+ const envelope = Math.exp(-dampingRatio * undampedAngularFreq * t);
619
+ // When performing sinh or cosh values can hit Infinity so we cap them here
620
+ const freqForT = Math.min(dampedAngularFreq * t, 300);
621
+ return (target -
622
+ (envelope *
623
+ ((initialVelocity +
624
+ dampingRatio * undampedAngularFreq * initialDelta) *
625
+ Math.sinh(freqForT) +
626
+ dampedAngularFreq *
627
+ initialDelta *
628
+ Math.cosh(freqForT))) /
629
+ dampedAngularFreq);
630
+ };
691
631
  }
632
+ const generator = {
633
+ calculatedDuration: isResolvedFromDuration ? duration || null : null,
634
+ next: (t) => {
635
+ const current = resolveSpring(t);
636
+ if (!isResolvedFromDuration) {
637
+ let currentVelocity = 0.0;
638
+ /**
639
+ * We only need to calculate velocity for under-damped springs
640
+ * as over- and critically-damped springs can't overshoot, so
641
+ * checking only for displacement is enough.
642
+ */
643
+ if (dampingRatio < 1) {
644
+ currentVelocity =
645
+ t === 0
646
+ ? secondsToMilliseconds(initialVelocity)
647
+ : calcGeneratorVelocity(resolveSpring, t, current);
648
+ }
649
+ const isBelowVelocityThreshold = Math.abs(currentVelocity) <= restSpeed;
650
+ const isBelowDisplacementThreshold = Math.abs(target - current) <= restDelta;
651
+ state.done =
652
+ isBelowVelocityThreshold && isBelowDisplacementThreshold;
653
+ }
654
+ else {
655
+ state.done = t >= duration;
656
+ }
657
+ state.value = state.done ? target : current;
658
+ return state;
659
+ },
660
+ toString: () => {
661
+ const calculatedDuration = Math.min(calcGeneratorDuration(generator), maxGeneratorDuration);
662
+ const easing = generateLinearEasing((progress) => generator.next(calculatedDuration * progress).value, calculatedDuration, 30);
663
+ return calculatedDuration + "ms " + easing;
664
+ },
665
+ };
666
+ return generator;
667
+ }
668
+
669
+ const wrap = (min, max, v) => {
670
+ const rangeSize = max - min;
671
+ return ((((v - min) % rangeSize) + rangeSize) % rangeSize) + min;
672
+ };
673
+
674
+ const isEasingArray = (ease) => {
675
+ return Array.isArray(ease) && typeof ease[0] !== "number";
676
+ };
677
+
678
+ function getEasingForSegment(easing, i) {
679
+ return isEasingArray(easing) ? easing[wrap(0, easing.length, i)] : easing;
692
680
  }
693
681
 
694
682
  /*
695
- Progress within given range
683
+ Value in range from progress
696
684
 
697
- Given a lower limit and an upper limit, we return the progress
698
- (expressed as a number 0-1) represented by the given value, and
699
- limit that progress to within 0-1.
685
+ Given a lower limit and an upper limit, we return the value within
686
+ that range as expressed by progress (usually a number from 0 to 1)
700
687
 
701
- @param [number]: Lower limit
702
- @param [number]: Upper limit
703
- @param [number]: Value to find progress within given range
704
- @return [number]: Progress of value within range as expressed 0-1
688
+ So progress = 0.5 would change
689
+
690
+ from -------- to
691
+
692
+ to
693
+
694
+ from ---- to
695
+
696
+ E.g. from = 10, to = 20, progress = 0.5 => 15
697
+
698
+ @param [number]: Lower limit of range
699
+ @param [number]: Upper limit of range
700
+ @param [number]: The progress between lower and upper limits expressed 0-1
701
+ @return [number]: Value as calculated from progress within range (not limited within range)
705
702
  */
706
- const progress = (from, to, value) => {
707
- const toFromDifference = to - from;
708
- return toFromDifference === 0 ? 1 : (value - from) / toFromDifference;
703
+ const mixNumber$1 = (from, to, progress) => {
704
+ return from + (to - from) * progress;
709
705
  };
710
706
 
711
- const generateLinearEasing = (easing, duration, // as milliseconds
712
- resolution = 10 // as milliseconds
713
- ) => {
714
- let points = "";
715
- const numPoints = Math.max(Math.round(duration / resolution), 2);
716
- for (let i = 0; i < numPoints; i++) {
717
- points += easing(progress(0, numPoints - 1, i)) + ", ";
707
+ function fillOffset(offset, remaining) {
708
+ const min = offset[offset.length - 1];
709
+ for (let i = 1; i <= remaining; i++) {
710
+ const offsetProgress = progress(0, remaining, i);
711
+ offset.push(mixNumber$1(min, 1, offsetProgress));
718
712
  }
719
- return `linear(${points.substring(0, points.length - 2)})`;
720
- };
721
-
722
- /**
723
- * Converts seconds to milliseconds
724
- *
725
- * @param seconds - Time in seconds.
726
- * @return milliseconds - Converted time in milliseconds.
727
- */
728
- const secondsToMilliseconds = (seconds) => seconds * 1000;
729
- const millisecondsToSeconds = (milliseconds) => milliseconds / 1000;
713
+ }
730
714
 
731
- const velocitySampleDuration = 5; // ms
732
- function calcGeneratorVelocity(resolveValue, t, current) {
733
- const prevT = Math.max(t - velocitySampleDuration, 0);
734
- return velocityPerSecond(current - resolveValue(prevT), t - prevT);
715
+ function defaultOffset$1(arr) {
716
+ const offset = [0];
717
+ fillOffset(offset, arr.length - 1);
718
+ return offset;
735
719
  }
736
720
 
737
- const clamp = (min, max, v) => {
738
- if (v > max)
739
- return max;
740
- if (v < min)
741
- return min;
742
- return v;
743
- };
721
+ const isMotionValue = (value) => Boolean(value && value.getVelocity);
744
722
 
745
- const springDefaults = {
746
- // Default spring physics
747
- stiffness: 100,
748
- damping: 10,
749
- mass: 1.0,
750
- velocity: 0.0,
751
- // Default duration/bounce-based options
752
- duration: 800, // in ms
753
- bounce: 0.3,
754
- visualDuration: 0.3, // in seconds
755
- // Rest thresholds
756
- restSpeed: {
757
- granular: 0.01,
758
- default: 2,
759
- },
760
- restDelta: {
761
- granular: 0.005,
762
- default: 0.5,
763
- },
764
- // Limits
765
- minDuration: 0.01, // in seconds
766
- maxDuration: 10.0, // in seconds
767
- minDamping: 0.05,
768
- maxDamping: 1,
769
- };
723
+ function isDOMKeyframes(keyframes) {
724
+ return typeof keyframes === "object" && !Array.isArray(keyframes);
725
+ }
770
726
 
771
- const safeMin = 0.001;
772
- function findSpring({ duration = springDefaults.duration, bounce = springDefaults.bounce, velocity = springDefaults.velocity, mass = springDefaults.mass, }) {
773
- let envelope;
774
- let derivative;
775
- warning(duration <= secondsToMilliseconds(springDefaults.maxDuration), "Spring duration must be 10 seconds or less");
776
- let dampingRatio = 1 - bounce;
777
- /**
778
- * Restrict dampingRatio and duration to within acceptable ranges.
779
- */
780
- dampingRatio = clamp(springDefaults.minDamping, springDefaults.maxDamping, dampingRatio);
781
- duration = clamp(springDefaults.minDuration, springDefaults.maxDuration, millisecondsToSeconds(duration));
782
- if (dampingRatio < 1) {
783
- /**
784
- * Underdamped spring
785
- */
786
- envelope = (undampedFreq) => {
787
- const exponentialDecay = undampedFreq * dampingRatio;
788
- const delta = exponentialDecay * duration;
789
- const a = exponentialDecay - velocity;
790
- const b = calcAngularFreq(undampedFreq, dampingRatio);
791
- const c = Math.exp(-delta);
792
- return safeMin - (a / b) * c;
793
- };
794
- derivative = (undampedFreq) => {
795
- const exponentialDecay = undampedFreq * dampingRatio;
796
- const delta = exponentialDecay * duration;
797
- const d = delta * velocity + velocity;
798
- const e = Math.pow(dampingRatio, 2) * Math.pow(undampedFreq, 2) * duration;
799
- const f = Math.exp(-delta);
800
- const g = calcAngularFreq(Math.pow(undampedFreq, 2), dampingRatio);
801
- const factor = -envelope(undampedFreq) + safeMin > 0 ? -1 : 1;
802
- return (factor * ((d - e) * f)) / g;
803
- };
727
+ function resolveSubjects(subject, keyframes, scope, selectorCache) {
728
+ if (typeof subject === "string" && isDOMKeyframes(keyframes)) {
729
+ return resolveElements(subject, scope, selectorCache);
730
+ }
731
+ else if (subject instanceof NodeList) {
732
+ return Array.from(subject);
733
+ }
734
+ else if (Array.isArray(subject)) {
735
+ return subject;
804
736
  }
805
737
  else {
806
- /**
807
- * Critically-damped spring
808
- */
809
- envelope = (undampedFreq) => {
810
- const a = Math.exp(-undampedFreq * duration);
811
- const b = (undampedFreq - velocity) * duration + 1;
812
- return -safeMin + a * b;
813
- };
814
- derivative = (undampedFreq) => {
815
- const a = Math.exp(-undampedFreq * duration);
816
- const b = (velocity - undampedFreq) * (duration * duration);
817
- return a * b;
818
- };
738
+ return [subject];
819
739
  }
820
- const initialGuess = 5 / duration;
821
- const undampedFreq = approximateRoot(envelope, derivative, initialGuess);
822
- duration = secondsToMilliseconds(duration);
823
- if (isNaN(undampedFreq)) {
824
- return {
825
- stiffness: springDefaults.stiffness,
826
- damping: springDefaults.damping,
827
- duration,
828
- };
740
+ }
741
+
742
+ function calculateRepeatDuration(duration, repeat, _repeatDelay) {
743
+ return duration * (repeat + 1);
744
+ }
745
+
746
+ /**
747
+ * Given a absolute or relative time definition and current/prev time state of the sequence,
748
+ * calculate an absolute time for the next keyframes.
749
+ */
750
+ function calcNextTime(current, next, prev, labels) {
751
+ var _a;
752
+ if (typeof next === "number") {
753
+ return next;
754
+ }
755
+ else if (next.startsWith("-") || next.startsWith("+")) {
756
+ return Math.max(0, current + parseFloat(next));
757
+ }
758
+ else if (next === "<") {
759
+ return prev;
829
760
  }
830
761
  else {
831
- const stiffness = Math.pow(undampedFreq, 2) * mass;
832
- return {
833
- stiffness,
834
- damping: dampingRatio * 2 * Math.sqrt(mass * stiffness),
835
- duration,
836
- };
762
+ return (_a = labels.get(next)) !== null && _a !== void 0 ? _a : current;
837
763
  }
838
764
  }
839
- const rootIterations = 12;
840
- function approximateRoot(envelope, derivative, initialGuess) {
841
- let result = initialGuess;
842
- for (let i = 1; i < rootIterations; i++) {
843
- result = result - envelope(result) / derivative(result);
765
+
766
+ function addUniqueItem(arr, item) {
767
+ if (arr.indexOf(item) === -1)
768
+ arr.push(item);
769
+ }
770
+ function removeItem(arr, item) {
771
+ const index = arr.indexOf(item);
772
+ if (index > -1)
773
+ arr.splice(index, 1);
774
+ }
775
+
776
+ function eraseKeyframes(sequence, startTime, endTime) {
777
+ for (let i = 0; i < sequence.length; i++) {
778
+ const keyframe = sequence[i];
779
+ if (keyframe.at > startTime && keyframe.at < endTime) {
780
+ removeItem(sequence, keyframe);
781
+ // If we remove this item we have to push the pointer back one
782
+ i--;
783
+ }
844
784
  }
845
- return result;
846
785
  }
847
- function calcAngularFreq(undampedFreq, dampingRatio) {
848
- return undampedFreq * Math.sqrt(1 - dampingRatio * dampingRatio);
786
+ function addKeyframes(sequence, keyframes, easing, offset, startTime, endTime) {
787
+ /**
788
+ * Erase every existing value between currentTime and targetTime,
789
+ * this will essentially splice this timeline into any currently
790
+ * defined ones.
791
+ */
792
+ eraseKeyframes(sequence, startTime, endTime);
793
+ for (let i = 0; i < keyframes.length; i++) {
794
+ sequence.push({
795
+ value: keyframes[i],
796
+ at: mixNumber$1(startTime, endTime, offset[i]),
797
+ easing: getEasingForSegment(easing, i),
798
+ });
799
+ }
849
800
  }
850
801
 
851
802
  /**
852
- * Implement a practical max duration for keyframe generation
853
- * to prevent infinite loops
803
+ * Take an array of times that represent repeated keyframes. For instance
804
+ * if we have original times of [0, 0.5, 1] then our repeated times will
805
+ * be [0, 0.5, 1, 1, 1.5, 2]. Loop over the times and scale them back
806
+ * down to a 0-1 scale.
854
807
  */
855
- const maxGeneratorDuration = 20000;
856
- function calcGeneratorDuration(generator) {
857
- let duration = 0;
858
- const timeStep = 50;
859
- let state = generator.next(duration);
860
- while (!state.done && duration < maxGeneratorDuration) {
861
- duration += timeStep;
862
- state = generator.next(duration);
808
+ function normalizeTimes(times, repeat) {
809
+ for (let i = 0; i < times.length; i++) {
810
+ times[i] = times[i] / (repeat + 1);
863
811
  }
864
- return duration >= maxGeneratorDuration ? Infinity : duration;
865
812
  }
866
813
 
867
- const durationKeys = ["duration", "bounce"];
868
- const physicsKeys = ["stiffness", "damping", "mass"];
869
- function isSpringType(options, keys) {
870
- return keys.some((key) => options[key] !== undefined);
814
+ function compareByTime(a, b) {
815
+ if (a.at === b.at) {
816
+ if (a.value === null)
817
+ return 1;
818
+ if (b.value === null)
819
+ return -1;
820
+ return 0;
821
+ }
822
+ else {
823
+ return a.at - b.at;
824
+ }
871
825
  }
872
- function getSpringOptions(options) {
873
- let springOptions = {
874
- velocity: springDefaults.velocity,
875
- stiffness: springDefaults.stiffness,
876
- damping: springDefaults.damping,
877
- mass: springDefaults.mass,
878
- isResolvedFromDuration: false,
879
- ...options,
880
- };
881
- // stiffness/damping/mass overrides duration/bounce
882
- if (!isSpringType(options, physicsKeys) &&
883
- isSpringType(options, durationKeys)) {
884
- if (options.visualDuration) {
885
- const visualDuration = options.visualDuration;
886
- const root = (2 * Math.PI) / (visualDuration * 1.2);
887
- const stiffness = root * root;
888
- const damping = 2 * clamp(0.05, 1, 1 - options.bounce) * Math.sqrt(stiffness);
889
- springOptions = {
890
- ...springOptions,
891
- mass: springDefaults.mass,
892
- stiffness,
893
- damping,
894
- };
826
+
827
+ const defaultSegmentEasing = "easeInOut";
828
+ const MAX_REPEAT = 20;
829
+ function createAnimationsFromSequence(sequence, { defaultTransition = {}, ...sequenceTransition } = {}, scope, generators) {
830
+ const defaultDuration = defaultTransition.duration || 0.3;
831
+ const animationDefinitions = new Map();
832
+ const sequences = new Map();
833
+ const elementCache = {};
834
+ const timeLabels = new Map();
835
+ let prevTime = 0;
836
+ let currentTime = 0;
837
+ let totalDuration = 0;
838
+ /**
839
+ * Build the timeline by mapping over the sequence array and converting
840
+ * the definitions into keyframes and offsets with absolute time values.
841
+ * These will later get converted into relative offsets in a second pass.
842
+ */
843
+ for (let i = 0; i < sequence.length; i++) {
844
+ const segment = sequence[i];
845
+ /**
846
+ * If this is a timeline label, mark it and skip the rest of this iteration.
847
+ */
848
+ if (typeof segment === "string") {
849
+ timeLabels.set(segment, currentTime);
850
+ continue;
851
+ }
852
+ else if (!Array.isArray(segment)) {
853
+ timeLabels.set(segment.name, calcNextTime(currentTime, segment.at, prevTime, timeLabels));
854
+ continue;
855
+ }
856
+ let [subject, keyframes, transition = {}] = segment;
857
+ /**
858
+ * If a relative or absolute time value has been specified we need to resolve
859
+ * it in relation to the currentTime.
860
+ */
861
+ if (transition.at !== undefined) {
862
+ currentTime = calcNextTime(currentTime, transition.at, prevTime, timeLabels);
863
+ }
864
+ /**
865
+ * Keep track of the maximum duration in this definition. This will be
866
+ * applied to currentTime once the definition has been parsed.
867
+ */
868
+ let maxDuration = 0;
869
+ const resolveValueSequence = (valueKeyframes, valueTransition, valueSequence, elementIndex = 0, numSubjects = 0) => {
870
+ const valueKeyframesAsList = keyframesAsList(valueKeyframes);
871
+ const { delay = 0, times = defaultOffset$1(valueKeyframesAsList), type = "keyframes", repeat, repeatType, repeatDelay = 0, ...remainingTransition } = valueTransition;
872
+ let { ease = defaultTransition.ease || "easeOut", duration } = valueTransition;
873
+ /**
874
+ * Resolve stagger() if defined.
875
+ */
876
+ const calculatedDelay = typeof delay === "function"
877
+ ? delay(elementIndex, numSubjects)
878
+ : delay;
879
+ /**
880
+ * If this animation should and can use a spring, generate a spring easing function.
881
+ */
882
+ const numKeyframes = valueKeyframesAsList.length;
883
+ const createGenerator = isGenerator(type)
884
+ ? type
885
+ : generators === null || generators === void 0 ? void 0 : generators[type];
886
+ if (numKeyframes <= 2 && createGenerator) {
887
+ /**
888
+ * As we're creating an easing function from a spring,
889
+ * ideally we want to generate it using the real distance
890
+ * between the two keyframes. However this isn't always
891
+ * possible - in these situations we use 0-100.
892
+ */
893
+ let absoluteDelta = 100;
894
+ if (numKeyframes === 2 &&
895
+ isNumberKeyframesArray(valueKeyframesAsList)) {
896
+ const delta = valueKeyframesAsList[1] - valueKeyframesAsList[0];
897
+ absoluteDelta = Math.abs(delta);
898
+ }
899
+ const springTransition = { ...remainingTransition };
900
+ if (duration !== undefined) {
901
+ springTransition.duration = secondsToMilliseconds(duration);
902
+ }
903
+ const springEasing = createGeneratorEasing(springTransition, absoluteDelta, createGenerator);
904
+ ease = springEasing.ease;
905
+ duration = springEasing.duration;
906
+ }
907
+ duration !== null && duration !== void 0 ? duration : (duration = defaultDuration);
908
+ const startTime = currentTime + calculatedDelay;
909
+ /**
910
+ * If there's only one time offset of 0, fill in a second with length 1
911
+ */
912
+ if (times.length === 1 && times[0] === 0) {
913
+ times[1] = 1;
914
+ }
915
+ /**
916
+ * Fill out if offset if fewer offsets than keyframes
917
+ */
918
+ const remainder = times.length - valueKeyframesAsList.length;
919
+ remainder > 0 && fillOffset(times, remainder);
920
+ /**
921
+ * If only one value has been set, ie [1], push a null to the start of
922
+ * the keyframe array. This will let us mark a keyframe at this point
923
+ * that will later be hydrated with the previous value.
924
+ */
925
+ valueKeyframesAsList.length === 1 &&
926
+ valueKeyframesAsList.unshift(null);
927
+ /**
928
+ * Handle repeat options
929
+ */
930
+ if (repeat) {
931
+ exports.invariant(repeat < MAX_REPEAT, "Repeat count too high, must be less than 20");
932
+ duration = calculateRepeatDuration(duration, repeat);
933
+ const originalKeyframes = [...valueKeyframesAsList];
934
+ const originalTimes = [...times];
935
+ ease = Array.isArray(ease) ? [...ease] : [ease];
936
+ const originalEase = [...ease];
937
+ for (let repeatIndex = 0; repeatIndex < repeat; repeatIndex++) {
938
+ valueKeyframesAsList.push(...originalKeyframes);
939
+ for (let keyframeIndex = 0; keyframeIndex < originalKeyframes.length; keyframeIndex++) {
940
+ times.push(originalTimes[keyframeIndex] + (repeatIndex + 1));
941
+ ease.push(keyframeIndex === 0
942
+ ? "linear"
943
+ : getEasingForSegment(originalEase, keyframeIndex - 1));
944
+ }
945
+ }
946
+ normalizeTimes(times, repeat);
947
+ }
948
+ const targetTime = startTime + duration;
949
+ /**
950
+ * Add keyframes, mapping offsets to absolute time.
951
+ */
952
+ addKeyframes(valueSequence, valueKeyframesAsList, ease, times, startTime, targetTime);
953
+ maxDuration = Math.max(calculatedDelay + duration, maxDuration);
954
+ totalDuration = Math.max(targetTime, totalDuration);
955
+ };
956
+ if (isMotionValue(subject)) {
957
+ const subjectSequence = getSubjectSequence(subject, sequences);
958
+ resolveValueSequence(keyframes, transition, getValueSequence("default", subjectSequence));
895
959
  }
896
960
  else {
897
- const derived = findSpring(options);
898
- springOptions = {
899
- ...springOptions,
900
- ...derived,
901
- mass: springDefaults.mass,
961
+ const subjects = resolveSubjects(subject, keyframes, scope, elementCache);
962
+ const numSubjects = subjects.length;
963
+ /**
964
+ * For every element in this segment, process the defined values.
965
+ */
966
+ for (let subjectIndex = 0; subjectIndex < numSubjects; subjectIndex++) {
967
+ /**
968
+ * Cast necessary, but we know these are of this type
969
+ */
970
+ keyframes = keyframes;
971
+ transition = transition;
972
+ const thisSubject = subjects[subjectIndex];
973
+ const subjectSequence = getSubjectSequence(thisSubject, sequences);
974
+ for (const key in keyframes) {
975
+ resolveValueSequence(keyframes[key], getValueTransition(transition, key), getValueSequence(key, subjectSequence), subjectIndex, numSubjects);
976
+ }
977
+ }
978
+ }
979
+ prevTime = currentTime;
980
+ currentTime += maxDuration;
981
+ }
982
+ /**
983
+ * For every element and value combination create a new animation.
984
+ */
985
+ sequences.forEach((valueSequences, element) => {
986
+ for (const key in valueSequences) {
987
+ const valueSequence = valueSequences[key];
988
+ /**
989
+ * Arrange all the keyframes in ascending time order.
990
+ */
991
+ valueSequence.sort(compareByTime);
992
+ const keyframes = [];
993
+ const valueOffset = [];
994
+ const valueEasing = [];
995
+ /**
996
+ * For each keyframe, translate absolute times into
997
+ * relative offsets based on the total duration of the timeline.
998
+ */
999
+ for (let i = 0; i < valueSequence.length; i++) {
1000
+ const { at, value, easing } = valueSequence[i];
1001
+ keyframes.push(value);
1002
+ valueOffset.push(progress(0, totalDuration, at));
1003
+ valueEasing.push(easing || "easeOut");
1004
+ }
1005
+ /**
1006
+ * If the first keyframe doesn't land on offset: 0
1007
+ * provide one by duplicating the initial keyframe. This ensures
1008
+ * it snaps to the first keyframe when the animation starts.
1009
+ */
1010
+ if (valueOffset[0] !== 0) {
1011
+ valueOffset.unshift(0);
1012
+ keyframes.unshift(keyframes[0]);
1013
+ valueEasing.unshift(defaultSegmentEasing);
1014
+ }
1015
+ /**
1016
+ * If the last keyframe doesn't land on offset: 1
1017
+ * provide one with a null wildcard value. This will ensure it
1018
+ * stays static until the end of the animation.
1019
+ */
1020
+ if (valueOffset[valueOffset.length - 1] !== 1) {
1021
+ valueOffset.push(1);
1022
+ keyframes.push(null);
1023
+ }
1024
+ if (!animationDefinitions.has(element)) {
1025
+ animationDefinitions.set(element, {
1026
+ keyframes: {},
1027
+ transition: {},
1028
+ });
1029
+ }
1030
+ const definition = animationDefinitions.get(element);
1031
+ definition.keyframes[key] = keyframes;
1032
+ definition.transition[key] = {
1033
+ ...defaultTransition,
1034
+ duration: totalDuration,
1035
+ ease: valueEasing,
1036
+ times: valueOffset,
1037
+ ...sequenceTransition,
902
1038
  };
903
- springOptions.isResolvedFromDuration = true;
904
1039
  }
905
- }
906
- return springOptions;
1040
+ });
1041
+ return animationDefinitions;
907
1042
  }
908
- function spring(optionsOrVisualDuration = springDefaults.visualDuration, bounce = springDefaults.bounce) {
909
- const options = typeof optionsOrVisualDuration !== "object"
1043
+ function getSubjectSequence(subject, sequences) {
1044
+ !sequences.has(subject) && sequences.set(subject, {});
1045
+ return sequences.get(subject);
1046
+ }
1047
+ function getValueSequence(name, sequences) {
1048
+ if (!sequences[name])
1049
+ sequences[name] = [];
1050
+ return sequences[name];
1051
+ }
1052
+ function keyframesAsList(keyframes) {
1053
+ return Array.isArray(keyframes) ? keyframes : [keyframes];
1054
+ }
1055
+ function getValueTransition(transition, key) {
1056
+ return transition && transition[key]
910
1057
  ? {
911
- visualDuration: optionsOrVisualDuration,
912
- keyframes: [0, 1],
913
- bounce,
1058
+ ...transition,
1059
+ ...transition[key],
914
1060
  }
915
- : optionsOrVisualDuration;
916
- let { restSpeed, restDelta } = options;
917
- const origin = options.keyframes[0];
918
- const target = options.keyframes[options.keyframes.length - 1];
1061
+ : { ...transition };
1062
+ }
1063
+ const isNumber = (keyframe) => typeof keyframe === "number";
1064
+ const isNumberKeyframesArray = (keyframes) => keyframes.every(isNumber);
1065
+
1066
+ const visualElementStore = new WeakMap();
1067
+
1068
+ /**
1069
+ * Generate a list of every possible transform key.
1070
+ */
1071
+ const transformPropOrder = [
1072
+ "transformPerspective",
1073
+ "x",
1074
+ "y",
1075
+ "z",
1076
+ "translateX",
1077
+ "translateY",
1078
+ "translateZ",
1079
+ "scale",
1080
+ "scaleX",
1081
+ "scaleY",
1082
+ "rotate",
1083
+ "rotateX",
1084
+ "rotateY",
1085
+ "rotateZ",
1086
+ "skew",
1087
+ "skewX",
1088
+ "skewY",
1089
+ ];
1090
+ /**
1091
+ * A quick lookup for transform props.
1092
+ */
1093
+ const transformProps = new Set(transformPropOrder);
1094
+
1095
+ const isKeyframesTarget = (v) => {
1096
+ return Array.isArray(v);
1097
+ };
1098
+
1099
+ const resolveFinalValueInKeyframes = (v) => {
1100
+ // TODO maybe throw if v.length - 1 is placeholder token?
1101
+ return isKeyframesTarget(v) ? v[v.length - 1] || 0 : v;
1102
+ };
1103
+
1104
+ const MotionGlobalConfig = {
1105
+ skipAnimations: false,
1106
+ useManualTiming: false,
1107
+ };
1108
+
1109
+ function createRenderStep(runNextFrame) {
919
1110
  /**
920
- * This is the Iterator-spec return value. We ensure it's mutable rather than using a generator
921
- * to reduce GC during animation.
1111
+ * We create and reuse two queues, one to queue jobs for the current frame
1112
+ * and one for the next. We reuse to avoid triggering GC after x frames.
922
1113
  */
923
- const state = { done: false, value: origin };
924
- const { stiffness, damping, mass, duration, velocity, isResolvedFromDuration, } = getSpringOptions({
925
- ...options,
926
- velocity: -millisecondsToSeconds(options.velocity || 0),
927
- });
928
- const initialVelocity = velocity || 0.0;
929
- const dampingRatio = damping / (2 * Math.sqrt(stiffness * mass));
930
- const initialDelta = target - origin;
931
- const undampedAngularFreq = millisecondsToSeconds(Math.sqrt(stiffness / mass));
1114
+ let thisFrame = new Set();
1115
+ let nextFrame = new Set();
932
1116
  /**
933
- * If we're working on a granular scale, use smaller defaults for determining
934
- * when the spring is finished.
935
- *
936
- * These defaults have been selected emprically based on what strikes a good
937
- * ratio between feeling good and finishing as soon as changes are imperceptible.
1117
+ * Track whether we're currently processing jobs in this step. This way
1118
+ * we can decide whether to schedule new jobs for this frame or next.
938
1119
  */
939
- const isGranularScale = Math.abs(initialDelta) < 5;
940
- restSpeed || (restSpeed = isGranularScale
941
- ? springDefaults.restSpeed.granular
942
- : springDefaults.restSpeed.default);
943
- restDelta || (restDelta = isGranularScale
944
- ? springDefaults.restDelta.granular
945
- : springDefaults.restDelta.default);
946
- let resolveSpring;
947
- if (dampingRatio < 1) {
948
- const angularFreq = calcAngularFreq(undampedAngularFreq, dampingRatio);
949
- // Underdamped spring
950
- resolveSpring = (t) => {
951
- const envelope = Math.exp(-dampingRatio * undampedAngularFreq * t);
952
- return (target -
953
- envelope *
954
- (((initialVelocity +
955
- dampingRatio * undampedAngularFreq * initialDelta) /
956
- angularFreq) *
957
- Math.sin(angularFreq * t) +
958
- initialDelta * Math.cos(angularFreq * t)));
959
- };
960
- }
961
- else if (dampingRatio === 1) {
962
- // Critically damped spring
963
- resolveSpring = (t) => target -
964
- Math.exp(-undampedAngularFreq * t) *
965
- (initialDelta +
966
- (initialVelocity + undampedAngularFreq * initialDelta) * t);
967
- }
968
- else {
969
- // Overdamped spring
970
- const dampedAngularFreq = undampedAngularFreq * Math.sqrt(dampingRatio * dampingRatio - 1);
971
- resolveSpring = (t) => {
972
- const envelope = Math.exp(-dampingRatio * undampedAngularFreq * t);
973
- // When performing sinh or cosh values can hit Infinity so we cap them here
974
- const freqForT = Math.min(dampedAngularFreq * t, 300);
975
- return (target -
976
- (envelope *
977
- ((initialVelocity +
978
- dampingRatio * undampedAngularFreq * initialDelta) *
979
- Math.sinh(freqForT) +
980
- dampedAngularFreq *
981
- initialDelta *
982
- Math.cosh(freqForT))) /
983
- dampedAngularFreq);
984
- };
1120
+ let isProcessing = false;
1121
+ let flushNextFrame = false;
1122
+ /**
1123
+ * A set of processes which were marked keepAlive when scheduled.
1124
+ */
1125
+ const toKeepAlive = new WeakSet();
1126
+ let latestFrameData = {
1127
+ delta: 0.0,
1128
+ timestamp: 0.0,
1129
+ isProcessing: false,
1130
+ };
1131
+ function triggerCallback(callback) {
1132
+ if (toKeepAlive.has(callback)) {
1133
+ step.schedule(callback);
1134
+ runNextFrame();
1135
+ }
1136
+ callback(latestFrameData);
985
1137
  }
986
- const generator = {
987
- calculatedDuration: isResolvedFromDuration ? duration || null : null,
988
- next: (t) => {
989
- const current = resolveSpring(t);
990
- if (!isResolvedFromDuration) {
991
- let currentVelocity = 0.0;
992
- /**
993
- * We only need to calculate velocity for under-damped springs
994
- * as over- and critically-damped springs can't overshoot, so
995
- * checking only for displacement is enough.
996
- */
997
- if (dampingRatio < 1) {
998
- currentVelocity =
999
- t === 0
1000
- ? secondsToMilliseconds(initialVelocity)
1001
- : calcGeneratorVelocity(resolveSpring, t, current);
1002
- }
1003
- const isBelowVelocityThreshold = Math.abs(currentVelocity) <= restSpeed;
1004
- const isBelowDisplacementThreshold = Math.abs(target - current) <= restDelta;
1005
- state.done =
1006
- isBelowVelocityThreshold && isBelowDisplacementThreshold;
1007
- }
1008
- else {
1009
- state.done = t >= duration;
1010
- }
1011
- state.value = state.done ? target : current;
1012
- return state;
1138
+ const step = {
1139
+ /**
1140
+ * Schedule a process to run on the next frame.
1141
+ */
1142
+ schedule: (callback, keepAlive = false, immediate = false) => {
1143
+ const addToCurrentFrame = immediate && isProcessing;
1144
+ const queue = addToCurrentFrame ? thisFrame : nextFrame;
1145
+ if (keepAlive)
1146
+ toKeepAlive.add(callback);
1147
+ if (!queue.has(callback))
1148
+ queue.add(callback);
1149
+ return callback;
1013
1150
  },
1014
- toString: () => {
1015
- const calculatedDuration = Math.min(calcGeneratorDuration(generator), maxGeneratorDuration);
1016
- const easing = generateLinearEasing((progress) => generator.next(calculatedDuration * progress).value, calculatedDuration, 30);
1017
- return calculatedDuration + "ms " + easing;
1151
+ /**
1152
+ * Cancel the provided callback from running on the next frame.
1153
+ */
1154
+ cancel: (callback) => {
1155
+ nextFrame.delete(callback);
1156
+ toKeepAlive.delete(callback);
1157
+ },
1158
+ /**
1159
+ * Execute all schedule callbacks.
1160
+ */
1161
+ process: (frameData) => {
1162
+ latestFrameData = frameData;
1163
+ /**
1164
+ * If we're already processing we've probably been triggered by a flushSync
1165
+ * inside an existing process. Instead of executing, mark flushNextFrame
1166
+ * as true and ensure we flush the following frame at the end of this one.
1167
+ */
1168
+ if (isProcessing) {
1169
+ flushNextFrame = true;
1170
+ return;
1171
+ }
1172
+ isProcessing = true;
1173
+ [thisFrame, nextFrame] = [nextFrame, thisFrame];
1174
+ // Execute this frame
1175
+ thisFrame.forEach(triggerCallback);
1176
+ // Clear the frame so no callbacks remain. This is to avoid
1177
+ // memory leaks should this render step not run for a while.
1178
+ thisFrame.clear();
1179
+ isProcessing = false;
1180
+ if (flushNextFrame) {
1181
+ flushNextFrame = false;
1182
+ step.process(frameData);
1183
+ }
1018
1184
  },
1019
1185
  };
1020
- return generator;
1186
+ return step;
1021
1187
  }
1022
1188
 
1023
- /**
1024
- * Create a progress => progress easing function from a generator.
1025
- */
1026
- function createGeneratorEasing(options, scale = 100, createGenerator) {
1027
- const generator = createGenerator({ ...options, keyframes: [0, scale] });
1028
- const duration = Math.min(calcGeneratorDuration(generator), maxGeneratorDuration);
1029
- return {
1030
- type: "keyframes",
1031
- ease: (progress) => generator.next(duration * progress).value / scale,
1032
- duration: millisecondsToSeconds(duration),
1189
+ const stepsOrder = [
1190
+ "read", // Read
1191
+ "resolveKeyframes", // Write/Read/Write/Read
1192
+ "update", // Compute
1193
+ "preRender", // Compute
1194
+ "render", // Write
1195
+ "postRender", // Compute
1196
+ ];
1197
+ const maxElapsed$1 = 40;
1198
+ function createRenderBatcher(scheduleNextBatch, allowKeepAlive) {
1199
+ let runNextFrame = false;
1200
+ let useDefaultElapsed = true;
1201
+ const state = {
1202
+ delta: 0.0,
1203
+ timestamp: 0.0,
1204
+ isProcessing: false,
1033
1205
  };
1206
+ const flagRunNextFrame = () => (runNextFrame = true);
1207
+ const steps = stepsOrder.reduce((acc, key) => {
1208
+ acc[key] = createRenderStep(flagRunNextFrame);
1209
+ return acc;
1210
+ }, {});
1211
+ const { read, resolveKeyframes, update, preRender, render, postRender } = steps;
1212
+ const processBatch = () => {
1213
+ const timestamp = performance.now();
1214
+ runNextFrame = false;
1215
+ state.delta = useDefaultElapsed
1216
+ ? 1000 / 60
1217
+ : Math.max(Math.min(timestamp - state.timestamp, maxElapsed$1), 1);
1218
+ state.timestamp = timestamp;
1219
+ state.isProcessing = true;
1220
+ // Unrolled render loop for better per-frame performance
1221
+ read.process(state);
1222
+ resolveKeyframes.process(state);
1223
+ update.process(state);
1224
+ preRender.process(state);
1225
+ render.process(state);
1226
+ postRender.process(state);
1227
+ state.isProcessing = false;
1228
+ if (runNextFrame && allowKeepAlive) {
1229
+ useDefaultElapsed = false;
1230
+ scheduleNextBatch(processBatch);
1231
+ }
1232
+ };
1233
+ const wake = () => {
1234
+ runNextFrame = true;
1235
+ useDefaultElapsed = true;
1236
+ if (!state.isProcessing) {
1237
+ scheduleNextBatch(processBatch);
1238
+ }
1239
+ };
1240
+ const schedule = stepsOrder.reduce((acc, key) => {
1241
+ const step = steps[key];
1242
+ acc[key] = (process, keepAlive = false, immediate = false) => {
1243
+ if (!runNextFrame)
1244
+ wake();
1245
+ return step.schedule(process, keepAlive, immediate);
1246
+ };
1247
+ return acc;
1248
+ }, {});
1249
+ const cancel = (process) => {
1250
+ for (let i = 0; i < stepsOrder.length; i++) {
1251
+ steps[stepsOrder[i]].cancel(process);
1252
+ }
1253
+ };
1254
+ return { schedule, cancel, state, steps };
1034
1255
  }
1035
1256
 
1036
- /*
1037
- Value in range from progress
1038
-
1039
- Given a lower limit and an upper limit, we return the value within
1040
- that range as expressed by progress (usually a number from 0 to 1)
1041
-
1042
- So progress = 0.5 would change
1043
-
1044
- from -------- to
1045
-
1046
- to
1047
-
1048
- from ---- to
1049
-
1050
- E.g. from = 10, to = 20, progress = 0.5 => 15
1051
-
1052
- @param [number]: Lower limit of range
1053
- @param [number]: Upper limit of range
1054
- @param [number]: The progress between lower and upper limits expressed 0-1
1055
- @return [number]: Value as calculated from progress within range (not limited within range)
1056
- */
1057
- const mixNumber$1 = (from, to, progress) => {
1058
- return from + (to - from) * progress;
1059
- };
1060
-
1061
- function fillOffset(offset, remaining) {
1062
- const min = offset[offset.length - 1];
1063
- for (let i = 1; i <= remaining; i++) {
1064
- const offsetProgress = progress(0, remaining, i);
1065
- offset.push(mixNumber$1(min, 1, offsetProgress));
1066
- }
1067
- }
1068
-
1069
- function defaultOffset$1(arr) {
1070
- const offset = [0];
1071
- fillOffset(offset, arr.length - 1);
1072
- return offset;
1073
- }
1074
-
1075
- const isMotionValue = (value) => Boolean(value && value.getVelocity);
1076
-
1077
- function isDOMKeyframes(keyframes) {
1078
- return typeof keyframes === "object" && !Array.isArray(keyframes);
1079
- }
1080
-
1081
- function resolveSubjects(subject, keyframes, scope, selectorCache) {
1082
- if (typeof subject === "string" && isDOMKeyframes(keyframes)) {
1083
- return resolveElements(subject, scope, selectorCache);
1084
- }
1085
- else if (subject instanceof NodeList) {
1086
- return Array.from(subject);
1087
- }
1088
- else if (Array.isArray(subject)) {
1089
- return subject;
1090
- }
1091
- else {
1092
- return [subject];
1093
- }
1094
- }
1257
+ const { schedule: frame, cancel: cancelFrame, state: frameData, steps: frameSteps, } = createRenderBatcher(typeof requestAnimationFrame !== "undefined" ? requestAnimationFrame : noop, true);
1095
1258
 
1096
- function isGenerator(type) {
1097
- return typeof type === "function";
1259
+ let now;
1260
+ function clearTime() {
1261
+ now = undefined;
1098
1262
  }
1099
-
1100
1263
  /**
1101
- * Given a absolute or relative time definition and current/prev time state of the sequence,
1102
- * calculate an absolute time for the next keyframes.
1264
+ * An eventloop-synchronous alternative to performance.now().
1265
+ *
1266
+ * Ensures that time measurements remain consistent within a synchronous context.
1267
+ * Usually calling performance.now() twice within the same synchronous context
1268
+ * will return different values which isn't useful for animations when we're usually
1269
+ * trying to sync animations to the same frame.
1103
1270
  */
1104
- function calcNextTime(current, next, prev, labels) {
1105
- var _a;
1106
- if (typeof next === "number") {
1107
- return next;
1108
- }
1109
- else if (next.startsWith("-") || next.startsWith("+")) {
1110
- return Math.max(0, current + parseFloat(next));
1111
- }
1112
- else if (next === "<") {
1113
- return prev;
1114
- }
1115
- else {
1116
- return (_a = labels.get(next)) !== null && _a !== void 0 ? _a : current;
1117
- }
1118
- }
1119
-
1120
- const wrap = (min, max, v) => {
1121
- const rangeSize = max - min;
1122
- return ((((v - min) % rangeSize) + rangeSize) % rangeSize) + min;
1123
- };
1124
-
1125
- const isEasingArray = (ease) => {
1126
- return Array.isArray(ease) && typeof ease[0] !== "number";
1271
+ const time = {
1272
+ now: () => {
1273
+ if (now === undefined) {
1274
+ time.set(frameData.isProcessing || MotionGlobalConfig.useManualTiming
1275
+ ? frameData.timestamp
1276
+ : performance.now());
1277
+ }
1278
+ return now;
1279
+ },
1280
+ set: (newTime) => {
1281
+ now = newTime;
1282
+ queueMicrotask(clearTime);
1283
+ },
1127
1284
  };
1128
1285
 
1129
- function getEasingForSegment(easing, i) {
1130
- return isEasingArray(easing) ? easing[wrap(0, easing.length, i)] : easing;
1131
- }
1132
-
1133
- function eraseKeyframes(sequence, startTime, endTime) {
1134
- for (let i = 0; i < sequence.length; i++) {
1135
- const keyframe = sequence[i];
1136
- if (keyframe.at > startTime && keyframe.at < endTime) {
1137
- removeItem(sequence, keyframe);
1138
- // If we remove this item we have to push the pointer back one
1139
- i--;
1140
- }
1286
+ class SubscriptionManager {
1287
+ constructor() {
1288
+ this.subscriptions = [];
1141
1289
  }
1142
- }
1143
- function addKeyframes(sequence, keyframes, easing, offset, startTime, endTime) {
1144
- /**
1145
- * Erase every existing value between currentTime and targetTime,
1146
- * this will essentially splice this timeline into any currently
1147
- * defined ones.
1148
- */
1149
- eraseKeyframes(sequence, startTime, endTime);
1150
- for (let i = 0; i < keyframes.length; i++) {
1151
- sequence.push({
1152
- value: keyframes[i],
1153
- at: mixNumber$1(startTime, endTime, offset[i]),
1154
- easing: getEasingForSegment(easing, i),
1155
- });
1290
+ add(handler) {
1291
+ addUniqueItem(this.subscriptions, handler);
1292
+ return () => removeItem(this.subscriptions, handler);
1156
1293
  }
1157
- }
1158
-
1159
- function compareByTime(a, b) {
1160
- if (a.at === b.at) {
1161
- if (a.value === null)
1162
- return 1;
1163
- if (b.value === null)
1164
- return -1;
1165
- return 0;
1294
+ notify(a, b, c) {
1295
+ const numSubscriptions = this.subscriptions.length;
1296
+ if (!numSubscriptions)
1297
+ return;
1298
+ if (numSubscriptions === 1) {
1299
+ /**
1300
+ * If there's only a single handler we can just call it without invoking a loop.
1301
+ */
1302
+ this.subscriptions[0](a, b, c);
1303
+ }
1304
+ else {
1305
+ for (let i = 0; i < numSubscriptions; i++) {
1306
+ /**
1307
+ * Check whether the handler exists before firing as it's possible
1308
+ * the subscriptions were modified during this loop running.
1309
+ */
1310
+ const handler = this.subscriptions[i];
1311
+ handler && handler(a, b, c);
1312
+ }
1313
+ }
1166
1314
  }
1167
- else {
1168
- return a.at - b.at;
1315
+ getSize() {
1316
+ return this.subscriptions.length;
1317
+ }
1318
+ clear() {
1319
+ this.subscriptions.length = 0;
1169
1320
  }
1170
1321
  }
1171
1322
 
1172
- const defaultSegmentEasing = "easeInOut";
1173
- function createAnimationsFromSequence(sequence, { defaultTransition = {}, ...sequenceTransition } = {}, scope, generators) {
1174
- const defaultDuration = defaultTransition.duration || 0.3;
1175
- const animationDefinitions = new Map();
1176
- const sequences = new Map();
1177
- const elementCache = {};
1178
- const timeLabels = new Map();
1179
- let prevTime = 0;
1180
- let currentTime = 0;
1181
- let totalDuration = 0;
1323
+ const warned = new Set();
1324
+ function warnOnce(condition, message, element) {
1325
+ if (condition || warned.has(message))
1326
+ return;
1327
+ console.warn(message);
1328
+ if (element)
1329
+ console.warn(element);
1330
+ warned.add(message);
1331
+ }
1332
+
1333
+ /**
1334
+ * Maximum time between the value of two frames, beyond which we
1335
+ * assume the velocity has since been 0.
1336
+ */
1337
+ const MAX_VELOCITY_DELTA = 30;
1338
+ const isFloat = (value) => {
1339
+ return !isNaN(parseFloat(value));
1340
+ };
1341
+ /**
1342
+ * `MotionValue` is used to track the state and velocity of motion values.
1343
+ *
1344
+ * @public
1345
+ */
1346
+ class MotionValue {
1182
1347
  /**
1183
- * Build the timeline by mapping over the sequence array and converting
1184
- * the definitions into keyframes and offsets with absolute time values.
1185
- * These will later get converted into relative offsets in a second pass.
1348
+ * @param init - The initiating value
1349
+ * @param config - Optional configuration options
1350
+ *
1351
+ * - `transformer`: A function to transform incoming values with.
1352
+ *
1353
+ * @internal
1186
1354
  */
1187
- for (let i = 0; i < sequence.length; i++) {
1188
- const segment = sequence[i];
1355
+ constructor(init, options = {}) {
1189
1356
  /**
1190
- * If this is a timeline label, mark it and skip the rest of this iteration.
1357
+ * This will be replaced by the build step with the latest version number.
1358
+ * When MotionValues are provided to motion components, warn if versions are mixed.
1191
1359
  */
1192
- if (typeof segment === "string") {
1193
- timeLabels.set(segment, currentTime);
1194
- continue;
1195
- }
1196
- else if (!Array.isArray(segment)) {
1197
- timeLabels.set(segment.name, calcNextTime(currentTime, segment.at, prevTime, timeLabels));
1198
- continue;
1199
- }
1200
- let [subject, keyframes, transition = {}] = segment;
1360
+ this.version = "11.16.0";
1201
1361
  /**
1202
- * If a relative or absolute time value has been specified we need to resolve
1203
- * it in relation to the currentTime.
1362
+ * Tracks whether this value can output a velocity. Currently this is only true
1363
+ * if the value is numerical, but we might be able to widen the scope here and support
1364
+ * other value types.
1365
+ *
1366
+ * @internal
1204
1367
  */
1205
- if (transition.at !== undefined) {
1206
- currentTime = calcNextTime(currentTime, transition.at, prevTime, timeLabels);
1207
- }
1368
+ this.canTrackVelocity = null;
1208
1369
  /**
1209
- * Keep track of the maximum duration in this definition. This will be
1210
- * applied to currentTime once the definition has been parsed.
1370
+ * An object containing a SubscriptionManager for each active event.
1211
1371
  */
1212
- let maxDuration = 0;
1213
- const resolveValueSequence = (valueKeyframes, valueTransition, valueSequence, elementIndex = 0, numSubjects = 0) => {
1214
- const valueKeyframesAsList = keyframesAsList(valueKeyframes);
1215
- const { delay = 0, times = defaultOffset$1(valueKeyframesAsList), type = "keyframes", ...remainingTransition } = valueTransition;
1216
- let { ease = defaultTransition.ease || "easeOut", duration } = valueTransition;
1217
- /**
1218
- * Resolve stagger() if defined.
1219
- */
1220
- const calculatedDelay = typeof delay === "function"
1221
- ? delay(elementIndex, numSubjects)
1222
- : delay;
1372
+ this.events = {};
1373
+ this.updateAndNotify = (v, render = true) => {
1374
+ const currentTime = time.now();
1223
1375
  /**
1224
- * If this animation should and can use a spring, generate a spring easing function.
1376
+ * If we're updating the value during another frame or eventloop
1377
+ * than the previous frame, then the we set the previous frame value
1378
+ * to current.
1225
1379
  */
1226
- const numKeyframes = valueKeyframesAsList.length;
1227
- const createGenerator = isGenerator(type)
1228
- ? type
1229
- : generators === null || generators === void 0 ? void 0 : generators[type];
1230
- if (numKeyframes <= 2 && createGenerator) {
1231
- /**
1232
- * As we're creating an easing function from a spring,
1233
- * ideally we want to generate it using the real distance
1234
- * between the two keyframes. However this isn't always
1235
- * possible - in these situations we use 0-100.
1236
- */
1237
- let absoluteDelta = 100;
1238
- if (numKeyframes === 2 &&
1239
- isNumberKeyframesArray(valueKeyframesAsList)) {
1240
- const delta = valueKeyframesAsList[1] - valueKeyframesAsList[0];
1241
- absoluteDelta = Math.abs(delta);
1242
- }
1243
- const springTransition = { ...remainingTransition };
1244
- if (duration !== undefined) {
1245
- springTransition.duration = secondsToMilliseconds(duration);
1246
- }
1247
- const springEasing = createGeneratorEasing(springTransition, absoluteDelta, createGenerator);
1248
- ease = springEasing.ease;
1249
- duration = springEasing.duration;
1380
+ if (this.updatedAt !== currentTime) {
1381
+ this.setPrevFrameValue();
1250
1382
  }
1251
- duration !== null && duration !== void 0 ? duration : (duration = defaultDuration);
1252
- const startTime = currentTime + calculatedDelay;
1253
- const targetTime = startTime + duration;
1254
- /**
1255
- * If there's only one time offset of 0, fill in a second with length 1
1256
- */
1257
- if (times.length === 1 && times[0] === 0) {
1258
- times[1] = 1;
1383
+ this.prev = this.current;
1384
+ this.setCurrent(v);
1385
+ // Update update subscribers
1386
+ if (this.current !== this.prev && this.events.change) {
1387
+ this.events.change.notify(this.current);
1388
+ }
1389
+ // Update render subscribers
1390
+ if (render && this.events.renderRequest) {
1391
+ this.events.renderRequest.notify(this.current);
1259
1392
  }
1260
- /**
1261
- * Fill out if offset if fewer offsets than keyframes
1262
- */
1263
- const remainder = times.length - valueKeyframesAsList.length;
1264
- remainder > 0 && fillOffset(times, remainder);
1265
- /**
1266
- * If only one value has been set, ie [1], push a null to the start of
1267
- * the keyframe array. This will let us mark a keyframe at this point
1268
- * that will later be hydrated with the previous value.
1269
- */
1270
- valueKeyframesAsList.length === 1 &&
1271
- valueKeyframesAsList.unshift(null);
1272
- /**
1273
- * Add keyframes, mapping offsets to absolute time.
1274
- */
1275
- addKeyframes(valueSequence, valueKeyframesAsList, ease, times, startTime, targetTime);
1276
- maxDuration = Math.max(calculatedDelay + duration, maxDuration);
1277
- totalDuration = Math.max(targetTime, totalDuration);
1278
1393
  };
1279
- if (isMotionValue(subject)) {
1280
- const subjectSequence = getSubjectSequence(subject, sequences);
1281
- resolveValueSequence(keyframes, transition, getValueSequence("default", subjectSequence));
1394
+ this.hasAnimated = false;
1395
+ this.setCurrent(init);
1396
+ this.owner = options.owner;
1397
+ }
1398
+ setCurrent(current) {
1399
+ this.current = current;
1400
+ this.updatedAt = time.now();
1401
+ if (this.canTrackVelocity === null && current !== undefined) {
1402
+ this.canTrackVelocity = isFloat(this.current);
1403
+ }
1404
+ }
1405
+ setPrevFrameValue(prevFrameValue = this.current) {
1406
+ this.prevFrameValue = prevFrameValue;
1407
+ this.prevUpdatedAt = this.updatedAt;
1408
+ }
1409
+ /**
1410
+ * Adds a function that will be notified when the `MotionValue` is updated.
1411
+ *
1412
+ * It returns a function that, when called, will cancel the subscription.
1413
+ *
1414
+ * When calling `onChange` inside a React component, it should be wrapped with the
1415
+ * `useEffect` hook. As it returns an unsubscribe function, this should be returned
1416
+ * from the `useEffect` function to ensure you don't add duplicate subscribers..
1417
+ *
1418
+ * ```jsx
1419
+ * export const MyComponent = () => {
1420
+ * const x = useMotionValue(0)
1421
+ * const y = useMotionValue(0)
1422
+ * const opacity = useMotionValue(1)
1423
+ *
1424
+ * useEffect(() => {
1425
+ * function updateOpacity() {
1426
+ * const maxXY = Math.max(x.get(), y.get())
1427
+ * const newOpacity = transform(maxXY, [0, 100], [1, 0])
1428
+ * opacity.set(newOpacity)
1429
+ * }
1430
+ *
1431
+ * const unsubscribeX = x.on("change", updateOpacity)
1432
+ * const unsubscribeY = y.on("change", updateOpacity)
1433
+ *
1434
+ * return () => {
1435
+ * unsubscribeX()
1436
+ * unsubscribeY()
1437
+ * }
1438
+ * }, [])
1439
+ *
1440
+ * return <motion.div style={{ x }} />
1441
+ * }
1442
+ * ```
1443
+ *
1444
+ * @param subscriber - A function that receives the latest value.
1445
+ * @returns A function that, when called, will cancel this subscription.
1446
+ *
1447
+ * @deprecated
1448
+ */
1449
+ onChange(subscription) {
1450
+ if (process.env.NODE_ENV !== "production") {
1451
+ warnOnce(false, `value.onChange(callback) is deprecated. Switch to value.on("change", callback).`);
1452
+ }
1453
+ return this.on("change", subscription);
1454
+ }
1455
+ on(eventName, callback) {
1456
+ if (!this.events[eventName]) {
1457
+ this.events[eventName] = new SubscriptionManager();
1458
+ }
1459
+ const unsubscribe = this.events[eventName].add(callback);
1460
+ if (eventName === "change") {
1461
+ return () => {
1462
+ unsubscribe();
1463
+ /**
1464
+ * If we have no more change listeners by the start
1465
+ * of the next frame, stop active animations.
1466
+ */
1467
+ frame.read(() => {
1468
+ if (!this.events.change.getSize()) {
1469
+ this.stop();
1470
+ }
1471
+ });
1472
+ };
1473
+ }
1474
+ return unsubscribe;
1475
+ }
1476
+ clearListeners() {
1477
+ for (const eventManagers in this.events) {
1478
+ this.events[eventManagers].clear();
1479
+ }
1480
+ }
1481
+ /**
1482
+ * Attaches a passive effect to the `MotionValue`.
1483
+ *
1484
+ * @internal
1485
+ */
1486
+ attach(passiveEffect, stopPassiveEffect) {
1487
+ this.passiveEffect = passiveEffect;
1488
+ this.stopPassiveEffect = stopPassiveEffect;
1489
+ }
1490
+ /**
1491
+ * Sets the state of the `MotionValue`.
1492
+ *
1493
+ * @remarks
1494
+ *
1495
+ * ```jsx
1496
+ * const x = useMotionValue(0)
1497
+ * x.set(10)
1498
+ * ```
1499
+ *
1500
+ * @param latest - Latest value to set.
1501
+ * @param render - Whether to notify render subscribers. Defaults to `true`
1502
+ *
1503
+ * @public
1504
+ */
1505
+ set(v, render = true) {
1506
+ if (!render || !this.passiveEffect) {
1507
+ this.updateAndNotify(v, render);
1282
1508
  }
1283
1509
  else {
1284
- const subjects = resolveSubjects(subject, keyframes, scope, elementCache);
1285
- const numSubjects = subjects.length;
1286
- /**
1287
- * For every element in this segment, process the defined values.
1288
- */
1289
- for (let subjectIndex = 0; subjectIndex < numSubjects; subjectIndex++) {
1290
- /**
1291
- * Cast necessary, but we know these are of this type
1292
- */
1293
- keyframes = keyframes;
1294
- transition = transition;
1295
- const thisSubject = subjects[subjectIndex];
1296
- const subjectSequence = getSubjectSequence(thisSubject, sequences);
1297
- for (const key in keyframes) {
1298
- resolveValueSequence(keyframes[key], getValueTransition$1(transition, key), getValueSequence(key, subjectSequence), subjectIndex, numSubjects);
1299
- }
1300
- }
1510
+ this.passiveEffect(v, this.updateAndNotify);
1301
1511
  }
1302
- prevTime = currentTime;
1303
- currentTime += maxDuration;
1512
+ }
1513
+ setWithVelocity(prev, current, delta) {
1514
+ this.set(current);
1515
+ this.prev = undefined;
1516
+ this.prevFrameValue = prev;
1517
+ this.prevUpdatedAt = this.updatedAt - delta;
1304
1518
  }
1305
1519
  /**
1306
- * For every element and value combination create a new animation.
1520
+ * Set the state of the `MotionValue`, stopping any active animations,
1521
+ * effects, and resets velocity to `0`.
1307
1522
  */
1308
- sequences.forEach((valueSequences, element) => {
1309
- for (const key in valueSequences) {
1310
- const valueSequence = valueSequences[key];
1311
- /**
1312
- * Arrange all the keyframes in ascending time order.
1313
- */
1314
- valueSequence.sort(compareByTime);
1315
- const keyframes = [];
1316
- const valueOffset = [];
1317
- const valueEasing = [];
1318
- /**
1319
- * For each keyframe, translate absolute times into
1320
- * relative offsets based on the total duration of the timeline.
1321
- */
1322
- for (let i = 0; i < valueSequence.length; i++) {
1323
- const { at, value, easing } = valueSequence[i];
1324
- keyframes.push(value);
1325
- valueOffset.push(progress(0, totalDuration, at));
1326
- valueEasing.push(easing || "easeOut");
1327
- }
1328
- /**
1329
- * If the first keyframe doesn't land on offset: 0
1330
- * provide one by duplicating the initial keyframe. This ensures
1331
- * it snaps to the first keyframe when the animation starts.
1332
- */
1333
- if (valueOffset[0] !== 0) {
1334
- valueOffset.unshift(0);
1335
- keyframes.unshift(keyframes[0]);
1336
- valueEasing.unshift(defaultSegmentEasing);
1523
+ jump(v, endAnimation = true) {
1524
+ this.updateAndNotify(v);
1525
+ this.prev = v;
1526
+ this.prevUpdatedAt = this.prevFrameValue = undefined;
1527
+ endAnimation && this.stop();
1528
+ if (this.stopPassiveEffect)
1529
+ this.stopPassiveEffect();
1530
+ }
1531
+ /**
1532
+ * Returns the latest state of `MotionValue`
1533
+ *
1534
+ * @returns - The latest state of `MotionValue`
1535
+ *
1536
+ * @public
1537
+ */
1538
+ get() {
1539
+ return this.current;
1540
+ }
1541
+ /**
1542
+ * @public
1543
+ */
1544
+ getPrevious() {
1545
+ return this.prev;
1546
+ }
1547
+ /**
1548
+ * Returns the latest velocity of `MotionValue`
1549
+ *
1550
+ * @returns - The latest velocity of `MotionValue`. Returns `0` if the state is non-numerical.
1551
+ *
1552
+ * @public
1553
+ */
1554
+ getVelocity() {
1555
+ const currentTime = time.now();
1556
+ if (!this.canTrackVelocity ||
1557
+ this.prevFrameValue === undefined ||
1558
+ currentTime - this.updatedAt > MAX_VELOCITY_DELTA) {
1559
+ return 0;
1560
+ }
1561
+ const delta = Math.min(this.updatedAt - this.prevUpdatedAt, MAX_VELOCITY_DELTA);
1562
+ // Casts because of parseFloat's poor typing
1563
+ return velocityPerSecond(parseFloat(this.current) -
1564
+ parseFloat(this.prevFrameValue), delta);
1565
+ }
1566
+ /**
1567
+ * Registers a new animation to control this `MotionValue`. Only one
1568
+ * animation can drive a `MotionValue` at one time.
1569
+ *
1570
+ * ```jsx
1571
+ * value.start()
1572
+ * ```
1573
+ *
1574
+ * @param animation - A function that starts the provided animation
1575
+ *
1576
+ * @internal
1577
+ */
1578
+ start(startAnimation) {
1579
+ this.stop();
1580
+ return new Promise((resolve) => {
1581
+ this.hasAnimated = true;
1582
+ this.animation = startAnimation(resolve);
1583
+ if (this.events.animationStart) {
1584
+ this.events.animationStart.notify();
1337
1585
  }
1338
- /**
1339
- * If the last keyframe doesn't land on offset: 1
1340
- * provide one with a null wildcard value. This will ensure it
1341
- * stays static until the end of the animation.
1342
- */
1343
- if (valueOffset[valueOffset.length - 1] !== 1) {
1344
- valueOffset.push(1);
1345
- keyframes.push(null);
1586
+ }).then(() => {
1587
+ if (this.events.animationComplete) {
1588
+ this.events.animationComplete.notify();
1346
1589
  }
1347
- if (!animationDefinitions.has(element)) {
1348
- animationDefinitions.set(element, {
1349
- keyframes: {},
1350
- transition: {},
1351
- });
1590
+ this.clearAnimation();
1591
+ });
1592
+ }
1593
+ /**
1594
+ * Stop the currently active animation.
1595
+ *
1596
+ * @public
1597
+ */
1598
+ stop() {
1599
+ if (this.animation) {
1600
+ this.animation.stop();
1601
+ if (this.events.animationCancel) {
1602
+ this.events.animationCancel.notify();
1352
1603
  }
1353
- const definition = animationDefinitions.get(element);
1354
- definition.keyframes[key] = keyframes;
1355
- definition.transition[key] = {
1356
- ...defaultTransition,
1357
- duration: totalDuration,
1358
- ease: valueEasing,
1359
- times: valueOffset,
1360
- ...sequenceTransition,
1361
- };
1362
1604
  }
1605
+ this.clearAnimation();
1606
+ }
1607
+ /**
1608
+ * Returns `true` if this value is currently animating.
1609
+ *
1610
+ * @public
1611
+ */
1612
+ isAnimating() {
1613
+ return !!this.animation;
1614
+ }
1615
+ clearAnimation() {
1616
+ delete this.animation;
1617
+ }
1618
+ /**
1619
+ * Destroy and clean up subscribers to this `MotionValue`.
1620
+ *
1621
+ * The `MotionValue` hooks like `useMotionValue` and `useTransform` automatically
1622
+ * handle the lifecycle of the returned `MotionValue`, so this method is only necessary if you've manually
1623
+ * created a `MotionValue` via the `motionValue` function.
1624
+ *
1625
+ * @public
1626
+ */
1627
+ destroy() {
1628
+ this.clearListeners();
1629
+ this.stop();
1630
+ if (this.stopPassiveEffect) {
1631
+ this.stopPassiveEffect();
1632
+ }
1633
+ }
1634
+ }
1635
+ function motionValue(init, options) {
1636
+ return new MotionValue(init, options);
1637
+ }
1638
+
1639
+ function getValueState(visualElement) {
1640
+ const state = [{}, {}];
1641
+ visualElement === null || visualElement === void 0 ? void 0 : visualElement.values.forEach((value, key) => {
1642
+ state[0][key] = value.get();
1643
+ state[1][key] = value.getVelocity();
1363
1644
  });
1364
- return animationDefinitions;
1645
+ return state;
1365
1646
  }
1366
- function getSubjectSequence(subject, sequences) {
1367
- !sequences.has(subject) && sequences.set(subject, {});
1368
- return sequences.get(subject);
1647
+ function resolveVariantFromProps(props, definition, custom, visualElement) {
1648
+ /**
1649
+ * If the variant definition is a function, resolve.
1650
+ */
1651
+ if (typeof definition === "function") {
1652
+ const [current, velocity] = getValueState(visualElement);
1653
+ definition = definition(custom !== undefined ? custom : props.custom, current, velocity);
1654
+ }
1655
+ /**
1656
+ * If the variant definition is a variant label, or
1657
+ * the function returned a variant label, resolve.
1658
+ */
1659
+ if (typeof definition === "string") {
1660
+ definition = props.variants && props.variants[definition];
1661
+ }
1662
+ /**
1663
+ * At this point we've resolved both functions and variant labels,
1664
+ * but the resolved variant label might itself have been a function.
1665
+ * If so, resolve. This can only have returned a valid target object.
1666
+ */
1667
+ if (typeof definition === "function") {
1668
+ const [current, velocity] = getValueState(visualElement);
1669
+ definition = definition(custom !== undefined ? custom : props.custom, current, velocity);
1670
+ }
1671
+ return definition;
1369
1672
  }
1370
- function getValueSequence(name, sequences) {
1371
- if (!sequences[name])
1372
- sequences[name] = [];
1373
- return sequences[name];
1673
+
1674
+ function resolveVariant(visualElement, definition, custom) {
1675
+ const props = visualElement.getProps();
1676
+ return resolveVariantFromProps(props, definition, custom !== undefined ? custom : props.custom, visualElement);
1374
1677
  }
1375
- function keyframesAsList(keyframes) {
1376
- return Array.isArray(keyframes) ? keyframes : [keyframes];
1678
+
1679
+ /**
1680
+ * Set VisualElement's MotionValue, creating a new MotionValue for it if
1681
+ * it doesn't exist.
1682
+ */
1683
+ function setMotionValue(visualElement, key, value) {
1684
+ if (visualElement.hasValue(key)) {
1685
+ visualElement.getValue(key).set(value);
1686
+ }
1687
+ else {
1688
+ visualElement.addValue(key, motionValue(value));
1689
+ }
1377
1690
  }
1378
- function getValueTransition$1(transition, key) {
1379
- return transition && transition[key]
1380
- ? {
1381
- ...transition,
1382
- ...transition[key],
1383
- }
1384
- : { ...transition };
1691
+ function setTarget(visualElement, definition) {
1692
+ const resolved = resolveVariant(visualElement, definition);
1693
+ let { transitionEnd = {}, transition = {}, ...target } = resolved || {};
1694
+ target = { ...target, ...transitionEnd };
1695
+ for (const key in target) {
1696
+ const value = resolveFinalValueInKeyframes(target[key]);
1697
+ setMotionValue(visualElement, key, value);
1698
+ }
1385
1699
  }
1386
- const isNumber = (keyframe) => typeof keyframe === "number";
1387
- const isNumberKeyframesArray = (keyframes) => keyframes.every(isNumber);
1388
1700
 
1389
- const visualElementStore = new WeakMap();
1701
+ function isWillChangeMotionValue(value) {
1702
+ return Boolean(isMotionValue(value) && value.add);
1703
+ }
1390
1704
 
1391
- /**
1392
- * Generate a list of every possible transform key.
1393
- */
1394
- const transformPropOrder = [
1395
- "transformPerspective",
1396
- "x",
1397
- "y",
1398
- "z",
1399
- "translateX",
1400
- "translateY",
1401
- "translateZ",
1402
- "scale",
1403
- "scaleX",
1404
- "scaleY",
1405
- "rotate",
1406
- "rotateX",
1407
- "rotateY",
1408
- "rotateZ",
1409
- "skew",
1410
- "skewX",
1411
- "skewY",
1412
- ];
1413
- /**
1414
- * A quick lookup for transform props.
1415
- */
1416
- const transformProps = new Set(transformPropOrder);
1705
+ function addValueToWillChange(visualElement, key) {
1706
+ const willChange = visualElement.getValue("willChange");
1707
+ /**
1708
+ * It could be that a user has set willChange to a regular MotionValue,
1709
+ * in which case we can't add the value to it.
1710
+ */
1711
+ if (isWillChangeMotionValue(willChange)) {
1712
+ return willChange.add(key);
1713
+ }
1714
+ }
1417
1715
 
1418
- const underDampedSpring = {
1419
- type: "spring",
1420
- stiffness: 500,
1421
- damping: 25,
1422
- restSpeed: 10,
1423
- };
1424
- const criticallyDampedSpring = (target) => ({
1425
- type: "spring",
1426
- stiffness: 550,
1427
- damping: target === 0 ? 2 * Math.sqrt(550) : 30,
1428
- restSpeed: 10,
1429
- });
1430
- const keyframesTransition = {
1431
- type: "keyframes",
1432
- duration: 0.8,
1433
- };
1434
1716
  /**
1435
- * Default easing curve is a slightly shallower version of
1436
- * the default browser easing curve.
1717
+ * Convert camelCase to dash-case properties.
1437
1718
  */
1438
- const ease = {
1439
- type: "keyframes",
1440
- ease: [0.25, 0.1, 0.35, 1],
1441
- duration: 0.3,
1442
- };
1443
- const getDefaultTransition = (valueKey, { keyframes }) => {
1444
- if (keyframes.length > 2) {
1445
- return keyframesTransition;
1446
- }
1447
- else if (transformProps.has(valueKey)) {
1448
- return valueKey.startsWith("scale")
1449
- ? criticallyDampedSpring(keyframes[1])
1450
- : underDampedSpring;
1451
- }
1452
- return ease;
1453
- };
1719
+ const camelToDash = (str) => str.replace(/([a-z])([A-Z])/gu, "$1-$2").toLowerCase();
1454
1720
 
1455
- function getValueTransition(transition, key) {
1456
- return transition
1457
- ? transition[key] ||
1458
- transition["default"] ||
1459
- transition
1460
- : undefined;
1461
- }
1721
+ const optimizedAppearDataId = "framerAppearId";
1722
+ const optimizedAppearDataAttribute = "data-" + camelToDash(optimizedAppearDataId);
1462
1723
 
1463
- const isNotNull = (value) => value !== null;
1464
- function getFinalKeyframe(keyframes, { repeat, repeatType = "loop" }, finalKeyframe) {
1465
- const resolvedKeyframes = keyframes.filter(isNotNull);
1466
- const index = repeat && repeatType !== "loop" && repeat % 2 === 1
1467
- ? 0
1468
- : resolvedKeyframes.length - 1;
1469
- return !index || finalKeyframe === undefined
1470
- ? resolvedKeyframes[index]
1471
- : finalKeyframe;
1724
+ function getOptimisedAppearId(visualElement) {
1725
+ return visualElement.props[optimizedAppearDataAttribute];
1472
1726
  }
1473
1727
 
1474
1728
  /*
@@ -2452,6 +2706,17 @@ function canAnimate(keyframes, name, type, velocity) {
2452
2706
  ((type === "spring" || isGenerator(type)) && velocity));
2453
2707
  }
2454
2708
 
2709
+ const isNotNull = (value) => value !== null;
2710
+ function getFinalKeyframe(keyframes, { repeat, repeatType = "loop" }, finalKeyframe) {
2711
+ const resolvedKeyframes = keyframes.filter(isNotNull);
2712
+ const index = repeat && repeatType !== "loop" && repeat % 2 === 1
2713
+ ? 0
2714
+ : resolvedKeyframes.length - 1;
2715
+ return !index || finalKeyframe === undefined
2716
+ ? resolvedKeyframes[index]
2717
+ : finalKeyframe;
2718
+ }
2719
+
2455
2720
  /**
2456
2721
  * Maximum time allowed between an animation being created and it being
2457
2722
  * resolved for us to use the latter as the start time.
@@ -2542,152 +2807,25 @@ class BaseAnimation {
2542
2807
  };
2543
2808
  this.onPostResolved();
2544
2809
  }
2545
- onPostResolved() { }
2546
- /**
2547
- * Allows the returned animation to be awaited or promise-chained. Currently
2548
- * resolves when the animation finishes at all but in a future update could/should
2549
- * reject if its cancels.
2550
- */
2551
- then(resolve, reject) {
2552
- return this.currentFinishedPromise.then(resolve, reject);
2553
- }
2554
- flatten() {
2555
- this.options.type = "keyframes";
2556
- this.options.ease = "linear";
2557
- }
2558
- updateFinishedPromise() {
2559
- this.currentFinishedPromise = new Promise((resolve) => {
2560
- this.resolveFinishedPromise = resolve;
2561
- });
2562
- }
2563
- }
2564
-
2565
- function inertia({ keyframes, velocity = 0.0, power = 0.8, timeConstant = 325, bounceDamping = 10, bounceStiffness = 500, modifyTarget, min, max, restDelta = 0.5, restSpeed, }) {
2566
- const origin = keyframes[0];
2567
- const state = {
2568
- done: false,
2569
- value: origin,
2570
- };
2571
- const isOutOfBounds = (v) => (min !== undefined && v < min) || (max !== undefined && v > max);
2572
- const nearestBoundary = (v) => {
2573
- if (min === undefined)
2574
- return max;
2575
- if (max === undefined)
2576
- return min;
2577
- return Math.abs(min - v) < Math.abs(max - v) ? min : max;
2578
- };
2579
- let amplitude = power * velocity;
2580
- const ideal = origin + amplitude;
2581
- const target = modifyTarget === undefined ? ideal : modifyTarget(ideal);
2582
- /**
2583
- * If the target has changed we need to re-calculate the amplitude, otherwise
2584
- * the animation will start from the wrong position.
2585
- */
2586
- if (target !== ideal)
2587
- amplitude = target - origin;
2588
- const calcDelta = (t) => -amplitude * Math.exp(-t / timeConstant);
2589
- const calcLatest = (t) => target + calcDelta(t);
2590
- const applyFriction = (t) => {
2591
- const delta = calcDelta(t);
2592
- const latest = calcLatest(t);
2593
- state.done = Math.abs(delta) <= restDelta;
2594
- state.value = state.done ? target : latest;
2595
- };
2596
- /**
2597
- * Ideally this would resolve for t in a stateless way, we could
2598
- * do that by always precalculating the animation but as we know
2599
- * this will be done anyway we can assume that spring will
2600
- * be discovered during that.
2601
- */
2602
- let timeReachedBoundary;
2603
- let spring$1;
2604
- const checkCatchBoundary = (t) => {
2605
- if (!isOutOfBounds(state.value))
2606
- return;
2607
- timeReachedBoundary = t;
2608
- spring$1 = spring({
2609
- keyframes: [state.value, nearestBoundary(state.value)],
2610
- velocity: calcGeneratorVelocity(calcLatest, t, state.value), // TODO: This should be passing * 1000
2611
- damping: bounceDamping,
2612
- stiffness: bounceStiffness,
2613
- restDelta,
2614
- restSpeed,
2615
- });
2616
- };
2617
- checkCatchBoundary(0);
2618
- return {
2619
- calculatedDuration: null,
2620
- next: (t) => {
2621
- /**
2622
- * We need to resolve the friction to figure out if we need a
2623
- * spring but we don't want to do this twice per frame. So here
2624
- * we flag if we updated for this frame and later if we did
2625
- * we can skip doing it again.
2626
- */
2627
- let hasUpdatedFrame = false;
2628
- if (!spring$1 && timeReachedBoundary === undefined) {
2629
- hasUpdatedFrame = true;
2630
- applyFriction(t);
2631
- checkCatchBoundary(t);
2632
- }
2633
- /**
2634
- * If we have a spring and the provided t is beyond the moment the friction
2635
- * animation crossed the min/max boundary, use the spring.
2636
- */
2637
- if (timeReachedBoundary !== undefined && t >= timeReachedBoundary) {
2638
- return spring$1.next(t - timeReachedBoundary);
2639
- }
2640
- else {
2641
- !hasUpdatedFrame && applyFriction(t);
2642
- return state;
2643
- }
2644
- },
2645
- };
2646
- }
2647
-
2648
- const easeIn = /*@__PURE__*/ cubicBezier(0.42, 0, 1, 1);
2649
- const easeOut = /*@__PURE__*/ cubicBezier(0, 0, 0.58, 1);
2650
- const easeInOut = /*@__PURE__*/ cubicBezier(0.42, 0, 0.58, 1);
2651
-
2652
- const isBezierDefinition = (easing) => Array.isArray(easing) && typeof easing[0] === "number";
2653
-
2654
- const easingLookup = {
2655
- linear: noop,
2656
- easeIn,
2657
- easeInOut,
2658
- easeOut,
2659
- circIn,
2660
- circInOut,
2661
- circOut,
2662
- backIn,
2663
- backInOut,
2664
- backOut,
2665
- anticipate,
2666
- };
2667
- const easingDefinitionToFunction = (definition) => {
2668
- if (isBezierDefinition(definition)) {
2669
- // If cubic bezier definition, create bezier curve
2670
- exports.invariant(definition.length === 4, `Cubic bezier arrays must contain four numerical values.`);
2671
- const [x1, y1, x2, y2] = definition;
2672
- return cubicBezier(x1, y1, x2, y2);
2673
- }
2674
- else if (typeof definition === "string") {
2675
- // Else lookup from table
2676
- exports.invariant(easingLookup[definition] !== undefined, `Invalid easing type '${definition}'`);
2677
- return easingLookup[definition];
2810
+ onPostResolved() { }
2811
+ /**
2812
+ * Allows the returned animation to be awaited or promise-chained. Currently
2813
+ * resolves when the animation finishes at all but in a future update could/should
2814
+ * reject if its cancels.
2815
+ */
2816
+ then(resolve, reject) {
2817
+ return this.currentFinishedPromise.then(resolve, reject);
2678
2818
  }
2679
- return definition;
2680
- };
2681
-
2682
- /**
2683
- * Pipe
2684
- * Compose other transformers to run linearily
2685
- * pipe(min(20), max(40))
2686
- * @param {...functions} transformers
2687
- * @return {function}
2688
- */
2689
- const combineFunctions = (a, b) => (v) => b(a(v));
2690
- const pipe = (...transformers) => transformers.reduce(combineFunctions);
2819
+ flatten() {
2820
+ this.options.type = "keyframes";
2821
+ this.options.ease = "linear";
2822
+ }
2823
+ updateFinishedPromise() {
2824
+ this.currentFinishedPromise = new Promise((resolve) => {
2825
+ this.resolveFinishedPromise = resolve;
2826
+ });
2827
+ }
2828
+ }
2691
2829
 
2692
2830
  // Adapted from https://gist.github.com/mjackson/5311256
2693
2831
  function hueToRgb(p, q, t) {
@@ -2772,6 +2910,16 @@ const mixColor = (from, to) => {
2772
2910
  };
2773
2911
  };
2774
2912
 
2913
+ /**
2914
+ * Pipe
2915
+ * Compose other transformers to run linearily
2916
+ * pipe(min(20), max(40))
2917
+ * @param {...functions} transformers
2918
+ * @return {function}
2919
+ */
2920
+ const combineFunctions = (a, b) => (v) => b(a(v));
2921
+ const pipe = (...transformers) => transformers.reduce(combineFunctions);
2922
+
2775
2923
  const invisibleValues = new Set(["none", "hidden"]);
2776
2924
  /**
2777
2925
  * Returns a function that, when provided a progress value between 0 and 1,
@@ -2880,6 +3028,121 @@ function mix(from, to, p) {
2880
3028
  return mixer(from, to);
2881
3029
  }
2882
3030
 
3031
+ function inertia({ keyframes, velocity = 0.0, power = 0.8, timeConstant = 325, bounceDamping = 10, bounceStiffness = 500, modifyTarget, min, max, restDelta = 0.5, restSpeed, }) {
3032
+ const origin = keyframes[0];
3033
+ const state = {
3034
+ done: false,
3035
+ value: origin,
3036
+ };
3037
+ const isOutOfBounds = (v) => (min !== undefined && v < min) || (max !== undefined && v > max);
3038
+ const nearestBoundary = (v) => {
3039
+ if (min === undefined)
3040
+ return max;
3041
+ if (max === undefined)
3042
+ return min;
3043
+ return Math.abs(min - v) < Math.abs(max - v) ? min : max;
3044
+ };
3045
+ let amplitude = power * velocity;
3046
+ const ideal = origin + amplitude;
3047
+ const target = modifyTarget === undefined ? ideal : modifyTarget(ideal);
3048
+ /**
3049
+ * If the target has changed we need to re-calculate the amplitude, otherwise
3050
+ * the animation will start from the wrong position.
3051
+ */
3052
+ if (target !== ideal)
3053
+ amplitude = target - origin;
3054
+ const calcDelta = (t) => -amplitude * Math.exp(-t / timeConstant);
3055
+ const calcLatest = (t) => target + calcDelta(t);
3056
+ const applyFriction = (t) => {
3057
+ const delta = calcDelta(t);
3058
+ const latest = calcLatest(t);
3059
+ state.done = Math.abs(delta) <= restDelta;
3060
+ state.value = state.done ? target : latest;
3061
+ };
3062
+ /**
3063
+ * Ideally this would resolve for t in a stateless way, we could
3064
+ * do that by always precalculating the animation but as we know
3065
+ * this will be done anyway we can assume that spring will
3066
+ * be discovered during that.
3067
+ */
3068
+ let timeReachedBoundary;
3069
+ let spring$1;
3070
+ const checkCatchBoundary = (t) => {
3071
+ if (!isOutOfBounds(state.value))
3072
+ return;
3073
+ timeReachedBoundary = t;
3074
+ spring$1 = spring({
3075
+ keyframes: [state.value, nearestBoundary(state.value)],
3076
+ velocity: calcGeneratorVelocity(calcLatest, t, state.value), // TODO: This should be passing * 1000
3077
+ damping: bounceDamping,
3078
+ stiffness: bounceStiffness,
3079
+ restDelta,
3080
+ restSpeed,
3081
+ });
3082
+ };
3083
+ checkCatchBoundary(0);
3084
+ return {
3085
+ calculatedDuration: null,
3086
+ next: (t) => {
3087
+ /**
3088
+ * We need to resolve the friction to figure out if we need a
3089
+ * spring but we don't want to do this twice per frame. So here
3090
+ * we flag if we updated for this frame and later if we did
3091
+ * we can skip doing it again.
3092
+ */
3093
+ let hasUpdatedFrame = false;
3094
+ if (!spring$1 && timeReachedBoundary === undefined) {
3095
+ hasUpdatedFrame = true;
3096
+ applyFriction(t);
3097
+ checkCatchBoundary(t);
3098
+ }
3099
+ /**
3100
+ * If we have a spring and the provided t is beyond the moment the friction
3101
+ * animation crossed the min/max boundary, use the spring.
3102
+ */
3103
+ if (timeReachedBoundary !== undefined && t >= timeReachedBoundary) {
3104
+ return spring$1.next(t - timeReachedBoundary);
3105
+ }
3106
+ else {
3107
+ !hasUpdatedFrame && applyFriction(t);
3108
+ return state;
3109
+ }
3110
+ },
3111
+ };
3112
+ }
3113
+
3114
+ const easeIn = /*@__PURE__*/ cubicBezier(0.42, 0, 1, 1);
3115
+ const easeOut = /*@__PURE__*/ cubicBezier(0, 0, 0.58, 1);
3116
+ const easeInOut = /*@__PURE__*/ cubicBezier(0.42, 0, 0.58, 1);
3117
+
3118
+ const easingLookup = {
3119
+ linear: noop,
3120
+ easeIn,
3121
+ easeInOut,
3122
+ easeOut,
3123
+ circIn,
3124
+ circInOut,
3125
+ circOut,
3126
+ backIn,
3127
+ backInOut,
3128
+ backOut,
3129
+ anticipate,
3130
+ };
3131
+ const easingDefinitionToFunction = (definition) => {
3132
+ if (isBezierDefinition(definition)) {
3133
+ // If cubic bezier definition, create bezier curve
3134
+ exports.invariant(definition.length === 4, `Cubic bezier arrays must contain four numerical values.`);
3135
+ const [x1, y1, x2, y2] = definition;
3136
+ return cubicBezier(x1, y1, x2, y2);
3137
+ }
3138
+ else if (typeof definition === "string") {
3139
+ // Else lookup from table
3140
+ exports.invariant(easingLookup[definition] !== undefined, `Invalid easing type '${definition}'`);
3141
+ return easingLookup[definition];
3142
+ }
3143
+ return definition;
3144
+ };
3145
+
2883
3146
  function createMixers(output, ease, customMixer) {
2884
3147
  const mixers = [];
2885
3148
  const mixerFactory = customMixer || mix;
@@ -3391,70 +3654,6 @@ const acceleratedValues = new Set([
3391
3654
  // "background-color"
3392
3655
  ]);
3393
3656
 
3394
- /**
3395
- * Add the ability for test suites to manually set support flags
3396
- * to better test more environments.
3397
- */
3398
- const supportsFlags = {
3399
- linearEasing: undefined,
3400
- };
3401
-
3402
- function memoSupports(callback, supportsFlag) {
3403
- const memoized = memo(callback);
3404
- return () => { var _a; return (_a = supportsFlags[supportsFlag]) !== null && _a !== void 0 ? _a : memoized(); };
3405
- }
3406
-
3407
- const supportsLinearEasing = /*@__PURE__*/ memoSupports(() => {
3408
- try {
3409
- document
3410
- .createElement("div")
3411
- .animate({ opacity: 0 }, { easing: "linear(0, 1)" });
3412
- }
3413
- catch (e) {
3414
- return false;
3415
- }
3416
- return true;
3417
- }, "linearEasing");
3418
-
3419
- function isWaapiSupportedEasing(easing) {
3420
- return Boolean((typeof easing === "function" && supportsLinearEasing()) ||
3421
- !easing ||
3422
- (typeof easing === "string" &&
3423
- (easing in supportedWaapiEasing || supportsLinearEasing())) ||
3424
- isBezierDefinition(easing) ||
3425
- (Array.isArray(easing) && easing.every(isWaapiSupportedEasing)));
3426
- }
3427
- const cubicBezierAsString = ([a, b, c, d]) => `cubic-bezier(${a}, ${b}, ${c}, ${d})`;
3428
- const supportedWaapiEasing = {
3429
- linear: "linear",
3430
- ease: "ease",
3431
- easeIn: "ease-in",
3432
- easeOut: "ease-out",
3433
- easeInOut: "ease-in-out",
3434
- circIn: /*@__PURE__*/ cubicBezierAsString([0, 0.65, 0.55, 1]),
3435
- circOut: /*@__PURE__*/ cubicBezierAsString([0.55, 0, 1, 0.45]),
3436
- backIn: /*@__PURE__*/ cubicBezierAsString([0.31, 0.01, 0.66, -0.59]),
3437
- backOut: /*@__PURE__*/ cubicBezierAsString([0.33, 1.53, 0.69, 0.99]),
3438
- };
3439
- function mapEasingToNativeEasing(easing, duration) {
3440
- if (!easing) {
3441
- return undefined;
3442
- }
3443
- else if (typeof easing === "function" && supportsLinearEasing()) {
3444
- return generateLinearEasing(easing, duration);
3445
- }
3446
- else if (isBezierDefinition(easing)) {
3447
- return cubicBezierAsString(easing);
3448
- }
3449
- else if (Array.isArray(easing)) {
3450
- return easing.map((segmentEasing) => mapEasingToNativeEasing(segmentEasing, duration) ||
3451
- supportedWaapiEasing.easeOut);
3452
- }
3453
- else {
3454
- return supportedWaapiEasing[easing];
3455
- }
3456
- }
3457
-
3458
3657
  function startWaapiAnimation(element, valueName, keyframes, { delay = 0, duration = 300, repeat = 0, repeatType = "loop", ease = "easeInOut", times, } = {}) {
3459
3658
  const keyframeOptions = { [valueName]: keyframes };
3460
3659
  if (times)
@@ -3475,11 +3674,6 @@ function startWaapiAnimation(element, valueName, keyframes, { delay = 0, duratio
3475
3674
  });
3476
3675
  }
3477
3676
 
3478
- function attachTimeline(animation, timeline) {
3479
- animation.timeline = timeline;
3480
- animation.onfinish = null;
3481
- }
3482
-
3483
3677
  const supportsWaapi = /*@__PURE__*/ memo(() => Object.hasOwnProperty.call(Element.prototype, "animate"));
3484
3678
 
3485
3679
  /**
@@ -3780,7 +3974,44 @@ class AcceleratedAnimation extends BaseAnimation {
3780
3974
  damping !== 0 &&
3781
3975
  type !== "inertia");
3782
3976
  }
3783
- }
3977
+ }
3978
+
3979
+ const underDampedSpring = {
3980
+ type: "spring",
3981
+ stiffness: 500,
3982
+ damping: 25,
3983
+ restSpeed: 10,
3984
+ };
3985
+ const criticallyDampedSpring = (target) => ({
3986
+ type: "spring",
3987
+ stiffness: 550,
3988
+ damping: target === 0 ? 2 * Math.sqrt(550) : 30,
3989
+ restSpeed: 10,
3990
+ });
3991
+ const keyframesTransition = {
3992
+ type: "keyframes",
3993
+ duration: 0.8,
3994
+ };
3995
+ /**
3996
+ * Default easing curve is a slightly shallower version of
3997
+ * the default browser easing curve.
3998
+ */
3999
+ const ease = {
4000
+ type: "keyframes",
4001
+ ease: [0.25, 0.1, 0.35, 1],
4002
+ duration: 0.3,
4003
+ };
4004
+ const getDefaultTransition = (valueKey, { keyframes }) => {
4005
+ if (keyframes.length > 2) {
4006
+ return keyframesTransition;
4007
+ }
4008
+ else if (transformProps.has(valueKey)) {
4009
+ return valueKey.startsWith("scale")
4010
+ ? criticallyDampedSpring(keyframes[1])
4011
+ : underDampedSpring;
4012
+ }
4013
+ return ease;
4014
+ };
3784
4015
 
3785
4016
  /**
3786
4017
  * Decide whether a transition is defined on a given Transition.
@@ -3792,7 +4023,7 @@ function isTransitionDefined({ when, delay: _delay, delayChildren, staggerChildr
3792
4023
  }
3793
4024
 
3794
4025
  const animateMotionValue = (name, value, target, transition = {}, element, isHandoff) => (onComplete) => {
3795
- const valueTransition = getValueTransition(transition, name) || {};
4026
+ const valueTransition = getValueTransition$1(transition, name) || {};
3796
4027
  /**
3797
4028
  * Most transition values are currently completely overwritten by value-specific
3798
4029
  * transitions. In the future it'd be nicer to blend these transitions. But for now
@@ -3885,104 +4116,6 @@ const animateMotionValue = (name, value, target, transition = {}, element, isHan
3885
4116
  }
3886
4117
  };
3887
4118
 
3888
- const isKeyframesTarget = (v) => {
3889
- return Array.isArray(v);
3890
- };
3891
-
3892
- const resolveFinalValueInKeyframes = (v) => {
3893
- // TODO maybe throw if v.length - 1 is placeholder token?
3894
- return isKeyframesTarget(v) ? v[v.length - 1] || 0 : v;
3895
- };
3896
-
3897
- function getValueState(visualElement) {
3898
- const state = [{}, {}];
3899
- visualElement === null || visualElement === void 0 ? void 0 : visualElement.values.forEach((value, key) => {
3900
- state[0][key] = value.get();
3901
- state[1][key] = value.getVelocity();
3902
- });
3903
- return state;
3904
- }
3905
- function resolveVariantFromProps(props, definition, custom, visualElement) {
3906
- /**
3907
- * If the variant definition is a function, resolve.
3908
- */
3909
- if (typeof definition === "function") {
3910
- const [current, velocity] = getValueState(visualElement);
3911
- definition = definition(custom !== undefined ? custom : props.custom, current, velocity);
3912
- }
3913
- /**
3914
- * If the variant definition is a variant label, or
3915
- * the function returned a variant label, resolve.
3916
- */
3917
- if (typeof definition === "string") {
3918
- definition = props.variants && props.variants[definition];
3919
- }
3920
- /**
3921
- * At this point we've resolved both functions and variant labels,
3922
- * but the resolved variant label might itself have been a function.
3923
- * If so, resolve. This can only have returned a valid target object.
3924
- */
3925
- if (typeof definition === "function") {
3926
- const [current, velocity] = getValueState(visualElement);
3927
- definition = definition(custom !== undefined ? custom : props.custom, current, velocity);
3928
- }
3929
- return definition;
3930
- }
3931
-
3932
- function resolveVariant(visualElement, definition, custom) {
3933
- const props = visualElement.getProps();
3934
- return resolveVariantFromProps(props, definition, custom !== undefined ? custom : props.custom, visualElement);
3935
- }
3936
-
3937
- /**
3938
- * Set VisualElement's MotionValue, creating a new MotionValue for it if
3939
- * it doesn't exist.
3940
- */
3941
- function setMotionValue(visualElement, key, value) {
3942
- if (visualElement.hasValue(key)) {
3943
- visualElement.getValue(key).set(value);
3944
- }
3945
- else {
3946
- visualElement.addValue(key, motionValue(value));
3947
- }
3948
- }
3949
- function setTarget(visualElement, definition) {
3950
- const resolved = resolveVariant(visualElement, definition);
3951
- let { transitionEnd = {}, transition = {}, ...target } = resolved || {};
3952
- target = { ...target, ...transitionEnd };
3953
- for (const key in target) {
3954
- const value = resolveFinalValueInKeyframes(target[key]);
3955
- setMotionValue(visualElement, key, value);
3956
- }
3957
- }
3958
-
3959
- /**
3960
- * Convert camelCase to dash-case properties.
3961
- */
3962
- const camelToDash = (str) => str.replace(/([a-z])([A-Z])/gu, "$1-$2").toLowerCase();
3963
-
3964
- const optimizedAppearDataId = "framerAppearId";
3965
- const optimizedAppearDataAttribute = "data-" + camelToDash(optimizedAppearDataId);
3966
-
3967
- function getOptimisedAppearId(visualElement) {
3968
- return visualElement.props[optimizedAppearDataAttribute];
3969
- }
3970
-
3971
- function isWillChangeMotionValue(value) {
3972
- return Boolean(isMotionValue(value) && value.add);
3973
- }
3974
-
3975
- function addValueToWillChange(visualElement, key) {
3976
- const willChange = visualElement.getValue("willChange");
3977
- /**
3978
- * It could be that a user has set willChange to a regular MotionValue,
3979
- * in which case we can't add the value to it.
3980
- */
3981
- if (isWillChangeMotionValue(willChange)) {
3982
- return willChange.add(key);
3983
- }
3984
- }
3985
-
3986
4119
  /**
3987
4120
  * Decide whether we should block this animation. Previously, we achieved this
3988
4121
  * just by checking whether the key was listed in protectedKeys, but this
@@ -4013,7 +4146,7 @@ function animateTarget(visualElement, targetAndTransition, { delay = 0, transiti
4013
4146
  }
4014
4147
  const valueTransition = {
4015
4148
  delay,
4016
- ...getValueTransition(transition || {}, key),
4149
+ ...getValueTransition$1(transition || {}, key),
4017
4150
  };
4018
4151
  /**
4019
4152
  * If this is the first time a value is being animated, check
@@ -4160,7 +4293,7 @@ function updateMotionValuesFromProps(element, next, prev) {
4160
4293
  * and warn against mismatches.
4161
4294
  */
4162
4295
  if (process.env.NODE_ENV === "development") {
4163
- warnOnce(nextValue.version === "11.14.4", `Attempting to mix Motion versions ${nextValue.version} with 11.14.4 may not work as expected.`);
4296
+ warnOnce(nextValue.version === "11.16.0", `Attempting to mix Motion versions ${nextValue.version} with 11.16.0 may not work as expected.`);
4164
4297
  }
4165
4298
  }
4166
4299
  else if (isMotionValue(prevValue)) {
@@ -5291,12 +5424,9 @@ function getElementAnimationState(element) {
5291
5424
  state.set(element, animationState);
5292
5425
  return state.get(element);
5293
5426
  }
5294
- class NativeAnimation {
5427
+ class NativeAnimation extends NativeAnimationControls {
5295
5428
  constructor(element, valueName, valueKeyframes, options) {
5296
5429
  const isCSSVar = valueName.startsWith("--");
5297
- this.setValue = isCSSVar ? setCSSVar : setStyle;
5298
- this.options = options;
5299
- this.updateFinishedPromise();
5300
5430
  exports.invariant(typeof options.type !== "string", `animateMini doesn't support "type" as a string. Did you mean to import { spring } from "framer-motion"?`);
5301
5431
  const existingAnimation = getElementAnimationState(element).get(valueName);
5302
5432
  existingAnimation && existingAnimation.stop();
@@ -5321,92 +5451,32 @@ class NativeAnimation {
5321
5451
  else {
5322
5452
  options.ease = options.ease || defaultEasing;
5323
5453
  }
5324
- this.removeAnimation = () => { var _a; return (_a = state.get(element)) === null || _a === void 0 ? void 0 : _a.delete(valueName); };
5325
5454
  const onFinish = () => {
5326
- this.setValue(element, valueName, getFinalKeyframe(valueKeyframes, this.options));
5455
+ this.setValue(element, valueName, getFinalKeyframe(valueKeyframes, options));
5327
5456
  this.cancel();
5328
5457
  this.resolveFinishedPromise();
5329
5458
  };
5459
+ const init = () => {
5460
+ this.setValue = isCSSVar ? setCSSVar : setStyle;
5461
+ this.options = options;
5462
+ this.updateFinishedPromise();
5463
+ this.removeAnimation = () => { var _a; return (_a = state.get(element)) === null || _a === void 0 ? void 0 : _a.delete(valueName); };
5464
+ };
5330
5465
  if (!supportsWaapi()) {
5466
+ super();
5467
+ init();
5331
5468
  onFinish();
5332
5469
  }
5333
5470
  else {
5334
- this.animation = startWaapiAnimation(element, valueName, valueKeyframes, options);
5471
+ super(startWaapiAnimation(element, valueName, valueKeyframes, options));
5472
+ init();
5335
5473
  if (options.autoplay === false) {
5336
5474
  this.animation.pause();
5337
5475
  }
5338
5476
  this.animation.onfinish = onFinish;
5339
- if (this.pendingTimeline) {
5340
- attachTimeline(this.animation, this.pendingTimeline);
5341
- }
5342
5477
  getElementAnimationState(element).set(valueName, this);
5343
5478
  }
5344
5479
  }
5345
- get duration() {
5346
- return millisecondsToSeconds(this.options.duration || 300);
5347
- }
5348
- get time() {
5349
- var _a;
5350
- if (this.animation) {
5351
- return millisecondsToSeconds(((_a = this.animation) === null || _a === void 0 ? void 0 : _a.currentTime) || 0);
5352
- }
5353
- return 0;
5354
- }
5355
- set time(newTime) {
5356
- if (this.animation) {
5357
- this.animation.currentTime = secondsToMilliseconds(newTime);
5358
- }
5359
- }
5360
- get speed() {
5361
- return this.animation ? this.animation.playbackRate : 1;
5362
- }
5363
- set speed(newSpeed) {
5364
- if (this.animation) {
5365
- this.animation.playbackRate = newSpeed;
5366
- }
5367
- }
5368
- get state() {
5369
- return this.animation ? this.animation.playState : "finished";
5370
- }
5371
- get startTime() {
5372
- return this.animation ? this.animation.startTime : null;
5373
- }
5374
- flatten() {
5375
- var _a;
5376
- if (!this.animation)
5377
- return;
5378
- (_a = this.animation.effect) === null || _a === void 0 ? void 0 : _a.updateTiming({ easing: "linear" });
5379
- }
5380
- play() {
5381
- if (this.state === "finished") {
5382
- this.updateFinishedPromise();
5383
- }
5384
- this.animation && this.animation.play();
5385
- }
5386
- pause() {
5387
- this.animation && this.animation.pause();
5388
- }
5389
- stop() {
5390
- if (!this.animation ||
5391
- this.state === "idle" ||
5392
- this.state === "finished") {
5393
- return;
5394
- }
5395
- if (this.animation.commitStyles) {
5396
- this.animation.commitStyles();
5397
- }
5398
- this.cancel();
5399
- }
5400
- complete() {
5401
- this.animation && this.animation.finish();
5402
- }
5403
- cancel() {
5404
- this.removeAnimation();
5405
- try {
5406
- this.animation && this.animation.cancel();
5407
- }
5408
- catch (e) { }
5409
- }
5410
5480
  /**
5411
5481
  * Allows the returned animation to be awaited or promise-chained. Currently
5412
5482
  * resolves when the animation finishes at all but in a future update could/should
@@ -5420,14 +5490,15 @@ class NativeAnimation {
5420
5490
  this.resolveFinishedPromise = resolve;
5421
5491
  });
5422
5492
  }
5423
- attachTimeline(timeline) {
5424
- if (!this.animation) {
5425
- this.pendingTimeline = timeline;
5426
- }
5427
- else {
5428
- attachTimeline(this.animation, timeline);
5493
+ play() {
5494
+ if (this.state === "finished") {
5495
+ this.updateFinishedPromise();
5429
5496
  }
5430
- return noop;
5497
+ super.play();
5498
+ }
5499
+ cancel() {
5500
+ this.removeAnimation();
5501
+ super.cancel();
5431
5502
  }
5432
5503
  }
5433
5504
 
@@ -5448,7 +5519,7 @@ function animateElements(elementOrSelector, keyframes, options, scope) {
5448
5519
  for (const valueName in keyframes) {
5449
5520
  const valueKeyframes = keyframes[valueName];
5450
5521
  const valueOptions = {
5451
- ...getValueTransition(elementTransition, valueName),
5522
+ ...getValueTransition$1(elementTransition, valueName),
5452
5523
  };
5453
5524
  valueOptions.duration = valueOptions.duration
5454
5525
  ? secondsToMilliseconds(valueOptions.duration)
@@ -5468,6 +5539,21 @@ const createScopedWaapiAnimate = (scope) => {
5468
5539
  };
5469
5540
  const animateMini = /*@__PURE__*/ createScopedWaapiAnimate();
5470
5541
 
5542
+ function observeTimeline(update, timeline) {
5543
+ let prevProgress;
5544
+ const onFrame = () => {
5545
+ const { currentTime } = timeline;
5546
+ const percentage = currentTime === null ? 0 : currentTime.value;
5547
+ const progress = percentage / 100;
5548
+ if (prevProgress !== progress) {
5549
+ update(progress);
5550
+ }
5551
+ prevProgress = progress;
5552
+ };
5553
+ frame.update(onFrame, true);
5554
+ return () => cancelFrame(onFrame);
5555
+ }
5556
+
5471
5557
  const resizeHandlers = new WeakMap();
5472
5558
  let observer;
5473
5559
  function getElementSize(target, borderBoxSize) {
@@ -5926,21 +6012,6 @@ function scrollInfo(onScroll, { container = document.documentElement, ...options
5926
6012
  };
5927
6013
  }
5928
6014
 
5929
- function observeTimeline(update, timeline) {
5930
- let prevProgress;
5931
- const onFrame = () => {
5932
- const { currentTime } = timeline;
5933
- const percentage = currentTime === null ? 0 : currentTime.value;
5934
- const progress = percentage / 100;
5935
- if (prevProgress !== progress) {
5936
- update(progress);
5937
- }
5938
- prevProgress = progress;
5939
- };
5940
- frame.update(onFrame, true);
5941
- return () => cancelFrame(onFrame);
5942
- }
5943
-
5944
6015
  function scrollTimelineFallback({ source, container, axis = "y", }) {
5945
6016
  // Support legacy source argument. Deprecate later.
5946
6017
  if (source)
@@ -6097,24 +6168,6 @@ function stagger(duration = 0.1, { startDelay = 0, from = 0, ease } = {}) {
6097
6168
  };
6098
6169
  }
6099
6170
 
6100
- const isCustomValueType = (v) => {
6101
- return v && typeof v === "object" && v.mix;
6102
- };
6103
- const getMixer = (v) => (isCustomValueType(v) ? v.mix : undefined);
6104
- function transform(...args) {
6105
- const useImmediate = !Array.isArray(args[0]);
6106
- const argOffset = useImmediate ? 0 : -1;
6107
- const inputValue = args[0 + argOffset];
6108
- const inputRange = args[1 + argOffset];
6109
- const outputRange = args[2 + argOffset];
6110
- const options = args[3 + argOffset];
6111
- const interpolator = interpolate(inputRange, outputRange, {
6112
- mixer: getMixer(outputRange[0]),
6113
- ...options,
6114
- });
6115
- return useImmediate ? interpolator(inputValue) : interpolator;
6116
- }
6117
-
6118
6171
  /**
6119
6172
  * Timeout defined in ms
6120
6173
  */
@@ -6142,6 +6195,24 @@ function distance2D(a, b) {
6142
6195
  return Math.sqrt(xDelta ** 2 + yDelta ** 2);
6143
6196
  }
6144
6197
 
6198
+ const isCustomValueType = (v) => {
6199
+ return v && typeof v === "object" && v.mix;
6200
+ };
6201
+ const getMixer = (v) => (isCustomValueType(v) ? v.mix : undefined);
6202
+ function transform(...args) {
6203
+ const useImmediate = !Array.isArray(args[0]);
6204
+ const argOffset = useImmediate ? 0 : -1;
6205
+ const inputValue = args[0 + argOffset];
6206
+ const inputRange = args[1 + argOffset];
6207
+ const outputRange = args[2 + argOffset];
6208
+ const options = args[3 + argOffset];
6209
+ const interpolator = interpolate(inputRange, outputRange, {
6210
+ mixer: getMixer(outputRange[0]),
6211
+ ...options,
6212
+ });
6213
+ return useImmediate ? interpolator(inputValue) : interpolator;
6214
+ }
6215
+
6145
6216
  /**
6146
6217
  * @deprecated
6147
6218
  *