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