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