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