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