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