framer-motion 12.7.3 → 12.7.5-alpha.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 +1 -1
- package/dist/cjs/client.js +1 -1
- package/dist/cjs/{create-DwAwaNot.js → create-C7kXmWbI.js} +99 -2828
- package/dist/cjs/dom-mini.js +82 -66
- package/dist/cjs/dom.js +264 -3000
- package/dist/cjs/index.js +163 -218
- package/dist/cjs/m.js +13 -170
- package/dist/cjs/mini.js +77 -9
- package/dist/dom-mini.js +1 -1
- package/dist/dom.d.ts +5 -94
- package/dist/dom.js +1 -1
- package/dist/es/animation/animate/sequence.mjs +1 -1
- package/dist/es/animation/animators/waapi/animate-elements.mjs +78 -10
- package/dist/es/animation/interfaces/motion-value.mjs +11 -30
- package/dist/es/animation/interfaces/visual-element-target.mjs +1 -2
- package/dist/es/animation/optimized-appear/store-id.mjs +1 -1
- package/dist/es/animation/sequence/create.mjs +2 -5
- package/dist/es/animation/sequence/utils/edit.mjs +2 -3
- package/dist/es/animation/utils/default-transitions.mjs +1 -1
- package/dist/es/animation/utils/stagger.mjs +1 -1
- package/dist/es/components/AnimatePresence/PresenceChild.mjs +26 -23
- package/dist/es/components/Reorder/utils/check-reorder.mjs +1 -1
- package/dist/es/dom.mjs +2 -18
- package/dist/es/gestures/drag/VisualElementDragControls.mjs +1 -3
- package/dist/es/gestures/drag/utils/constraints.mjs +2 -3
- package/dist/es/gestures/focus.mjs +1 -1
- package/dist/es/gestures/pan/PanSession.mjs +1 -2
- package/dist/es/index.mjs +3 -24
- package/dist/es/motion/utils/is-forced-motion-value.mjs +1 -1
- package/dist/es/projection/animation/mix-values.mjs +2 -4
- package/dist/es/projection/geometry/delta-apply.mjs +1 -1
- package/dist/es/projection/geometry/delta-calc.mjs +1 -1
- package/dist/es/projection/geometry/delta-remove.mjs +1 -2
- package/dist/es/projection/node/create-projection-node.mjs +3 -7
- package/dist/es/projection/styles/scale-border-radius.mjs +1 -1
- package/dist/es/projection/styles/scale-box-shadow.mjs +1 -2
- package/dist/es/projection/styles/scale-correction.mjs +1 -1
- package/dist/es/projection.mjs +1 -3
- package/dist/es/render/VisualElement.mjs +2 -9
- package/dist/es/render/dom/DOMVisualElement.mjs +1 -1
- package/dist/es/render/dom/scroll/attach-animation.mjs +17 -0
- package/dist/es/render/dom/scroll/attach-function.mjs +23 -0
- package/dist/es/render/dom/scroll/index.mjs +6 -82
- package/dist/es/render/dom/scroll/offsets/index.mjs +2 -3
- package/dist/es/render/dom/scroll/utils/get-timeline.mjs +29 -0
- package/dist/es/render/html/HTMLVisualElement.mjs +1 -3
- package/dist/es/render/html/utils/build-styles.mjs +1 -4
- package/dist/es/render/html/utils/build-transform.mjs +1 -3
- package/dist/es/render/svg/SVGVisualElement.mjs +1 -3
- package/dist/es/render/svg/config-motion.mjs +1 -2
- package/dist/es/render/svg/utils/path.mjs +1 -1
- package/dist/es/render/svg/utils/scrape-motion-values.mjs +1 -1
- package/dist/es/render/svg/utils/transform-origin.mjs +1 -1
- package/dist/es/render/utils/motion-values.mjs +1 -1
- package/dist/es/utils/delay.mjs +1 -1
- package/dist/es/utils/transform.mjs +1 -1
- package/dist/es/utils/use-cycle.mjs +1 -1
- package/dist/es/utils/use-instant-transition.mjs +4 -4
- package/dist/es/value/use-spring.mjs +2 -3
- package/dist/es/value/use-will-change/get-will-change-name.mjs +1 -2
- package/dist/framer-motion.dev.js +3881 -3419
- package/dist/framer-motion.js +1 -1
- package/dist/m.d.ts +3 -50
- package/dist/mini.js +1 -1
- package/dist/size-rollup-animate.js +1 -1
- package/dist/size-rollup-dom-animation-assets.js +1 -1
- package/dist/size-rollup-dom-animation.js +1 -1
- package/dist/size-rollup-dom-max-assets.js +1 -1
- package/dist/size-rollup-dom-max.js +1 -1
- package/dist/size-rollup-m.js +1 -1
- package/dist/size-rollup-motion.js +1 -1
- package/dist/size-rollup-scroll.js +1 -1
- package/dist/size-rollup-waapi-animate.js +1 -1
- package/dist/types/client.d.ts +4 -3
- package/dist/types/index.d.ts +56 -351
- package/dist/{types.d-B50aGbjN.d.ts → types.d-B1Voffvi.d.ts} +3 -138
- package/package.json +9 -9
- package/dist/es/animation/animators/AcceleratedAnimation.mjs +0 -319
- package/dist/es/animation/animators/BaseAnimation.mjs +0 -120
- package/dist/es/animation/animators/MainThreadAnimation.mjs +0 -394
- package/dist/es/animation/animators/drivers/driver-frameloop.mjs +0 -16
- package/dist/es/animation/animators/utils/accelerated-values.mjs +0 -14
- package/dist/es/animation/animators/utils/can-animate.mjs +0 -42
- package/dist/es/animation/animators/waapi/utils/supports-waapi.mjs +0 -5
- package/dist/es/animation/generators/inertia.mjs +0 -87
- package/dist/es/animation/generators/keyframes.mjs +0 -51
- package/dist/es/animation/generators/spring/defaults.mjs +0 -27
- package/dist/es/animation/generators/spring/find.mjs +0 -85
- package/dist/es/animation/generators/spring/index.mjs +0 -174
- package/dist/es/animation/generators/utils/velocity.mjs +0 -9
- package/dist/es/animation/utils/is-animatable.mjs +0 -30
- package/dist/es/animation/utils/is-none.mjs +0 -15
- package/dist/es/easing/anticipate.mjs +0 -5
- package/dist/es/easing/back.mjs +0 -9
- package/dist/es/easing/circ.mjs +0 -8
- package/dist/es/easing/cubic-bezier.mjs +0 -51
- package/dist/es/easing/ease.mjs +0 -7
- package/dist/es/easing/modifiers/mirror.mjs +0 -5
- package/dist/es/easing/modifiers/reverse.mjs +0 -5
- package/dist/es/easing/steps.mjs +0 -15
- package/dist/es/easing/utils/get-easing-for-segment.mjs +0 -8
- package/dist/es/easing/utils/is-easing-array.mjs +0 -5
- package/dist/es/easing/utils/map.mjs +0 -37
- package/dist/es/render/dom/DOMKeyframesResolver.mjs +0 -130
- package/dist/es/render/dom/scroll/observe.mjs +0 -18
- package/dist/es/render/dom/utils/css-variables-conversion.mjs +0 -42
- package/dist/es/render/dom/utils/is-css-variable.mjs +0 -15
- package/dist/es/render/dom/utils/unit-conversion.mjs +0 -36
- package/dist/es/render/dom/value-types/animatable-none.mjs +0 -15
- package/dist/es/render/dom/value-types/defaults.mjs +0 -30
- package/dist/es/render/dom/value-types/dimensions.mjs +0 -15
- package/dist/es/render/dom/value-types/find.mjs +0 -15
- package/dist/es/render/dom/value-types/get-as-type.mjs +0 -10
- package/dist/es/render/dom/value-types/number-browser.mjs +0 -41
- package/dist/es/render/dom/value-types/number.mjs +0 -18
- package/dist/es/render/dom/value-types/test.mjs +0 -6
- package/dist/es/render/dom/value-types/transform.mjs +0 -31
- package/dist/es/render/dom/value-types/type-auto.mjs +0 -9
- package/dist/es/render/dom/value-types/type-int.mjs +0 -8
- package/dist/es/render/html/utils/keys-position.mjs +0 -13
- package/dist/es/render/html/utils/keys-transform.mjs +0 -28
- package/dist/es/render/html/utils/make-none-animatable.mjs +0 -30
- package/dist/es/render/html/utils/parse-transform.mjs +0 -83
- package/dist/es/render/utils/KeyframesResolver.mjs +0 -163
- package/dist/es/utils/clamp.mjs +0 -9
- package/dist/es/utils/hsla-to-rgba.mjs +0 -42
- package/dist/es/utils/interpolate.mjs +0 -76
- package/dist/es/utils/is-numerical-string.mjs +0 -6
- package/dist/es/utils/is-zero-value-string.mjs +0 -6
- package/dist/es/utils/mix/color.mjs +0 -47
- package/dist/es/utils/mix/complex.mjs +0 -93
- package/dist/es/utils/mix/immediate.mjs +0 -5
- package/dist/es/utils/mix/index.mjs +0 -14
- package/dist/es/utils/mix/number.mjs +0 -26
- package/dist/es/utils/mix/visibility.mjs +0 -16
- package/dist/es/utils/offsets/default.mjs +0 -9
- package/dist/es/utils/offsets/fill.mjs +0 -12
- package/dist/es/utils/offsets/time.mjs +0 -5
- package/dist/es/utils/pipe.mjs +0 -11
- package/dist/es/utils/use-instant-transition-state.mjs +0 -5
- package/dist/es/utils/wrap.mjs +0 -6
- package/dist/es/value/types/color/hex.mjs +0 -40
- package/dist/es/value/types/color/hsla.mjs +0 -22
- package/dist/es/value/types/color/index.mjs +0 -27
- package/dist/es/value/types/color/rgba.mjs +0 -25
- package/dist/es/value/types/color/utils.mjs +0 -29
- package/dist/es/value/types/complex/filter.mjs +0 -30
- package/dist/es/value/types/complex/index.mjs +0 -91
- package/dist/es/value/types/numbers/index.mjs +0 -17
- package/dist/es/value/types/numbers/units.mjs +0 -17
- package/dist/es/value/types/utils/color-regex.mjs +0 -3
- package/dist/es/value/types/utils/float-regex.mjs +0 -3
- package/dist/es/value/types/utils/is-nullish.mjs +0 -5
- package/dist/es/value/types/utils/sanitize.mjs +0 -5
- package/dist/es/value/types/utils/single-color-regex.mjs +0 -3
|
@@ -107,31 +107,6 @@ function isPresent(context) {
|
|
|
107
107
|
return context === null ? true : context.isPresent;
|
|
108
108
|
}
|
|
109
109
|
|
|
110
|
-
/*
|
|
111
|
-
Value in range from progress
|
|
112
|
-
|
|
113
|
-
Given a lower limit and an upper limit, we return the value within
|
|
114
|
-
that range as expressed by progress (usually a number from 0 to 1)
|
|
115
|
-
|
|
116
|
-
So progress = 0.5 would change
|
|
117
|
-
|
|
118
|
-
from -------- to
|
|
119
|
-
|
|
120
|
-
to
|
|
121
|
-
|
|
122
|
-
from ---- to
|
|
123
|
-
|
|
124
|
-
E.g. from = 10, to = 20, progress = 0.5 => 15
|
|
125
|
-
|
|
126
|
-
@param [number]: Lower limit of range
|
|
127
|
-
@param [number]: Upper limit of range
|
|
128
|
-
@param [number]: The progress between lower and upper limits expressed 0-1
|
|
129
|
-
@return [number]: Value as calculated from progress within range (not limited within range)
|
|
130
|
-
*/
|
|
131
|
-
const mixNumber$1 = (from, to, progress) => {
|
|
132
|
-
return from + (to - from) * progress;
|
|
133
|
-
};
|
|
134
|
-
|
|
135
110
|
const SCALE_PRECISION = 0.0001;
|
|
136
111
|
const SCALE_MIN = 1 - SCALE_PRECISION;
|
|
137
112
|
const SCALE_MAX = 1 + SCALE_PRECISION;
|
|
@@ -146,10 +121,10 @@ function isNear(value, target, maxDistance) {
|
|
|
146
121
|
}
|
|
147
122
|
function calcAxisDelta(delta, source, target, origin = 0.5) {
|
|
148
123
|
delta.origin = origin;
|
|
149
|
-
delta.originPoint = mixNumber
|
|
124
|
+
delta.originPoint = motionDom.mixNumber(source.min, source.max, delta.origin);
|
|
150
125
|
delta.scale = calcLength(target) / calcLength(source);
|
|
151
126
|
delta.translate =
|
|
152
|
-
mixNumber
|
|
127
|
+
motionDom.mixNumber(target.min, target.max, delta.origin) - delta.originPoint;
|
|
153
128
|
if ((delta.scale >= SCALE_MIN && delta.scale <= SCALE_MAX) ||
|
|
154
129
|
isNaN(delta.scale)) {
|
|
155
130
|
delta.scale = 1.0;
|
|
@@ -158,2674 +133,40 @@ function calcAxisDelta(delta, source, target, origin = 0.5) {
|
|
|
158
133
|
delta.translate <= TRANSLATE_MAX) ||
|
|
159
134
|
isNaN(delta.translate)) {
|
|
160
135
|
delta.translate = 0.0;
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
function calcBoxDelta(delta, source, target, origin) {
|
|
164
|
-
calcAxisDelta(delta.x, source.x, target.x, origin ? origin.originX : undefined);
|
|
165
|
-
calcAxisDelta(delta.y, source.y, target.y, origin ? origin.originY : undefined);
|
|
166
|
-
}
|
|
167
|
-
function calcRelativeAxis(target, relative, parent) {
|
|
168
|
-
target.min = parent.min + relative.min;
|
|
169
|
-
target.max = target.min + calcLength(relative);
|
|
170
|
-
}
|
|
171
|
-
function calcRelativeBox(target, relative, parent) {
|
|
172
|
-
calcRelativeAxis(target.x, relative.x, parent.x);
|
|
173
|
-
calcRelativeAxis(target.y, relative.y, parent.y);
|
|
174
|
-
}
|
|
175
|
-
function calcRelativeAxisPosition(target, layout, parent) {
|
|
176
|
-
target.min = layout.min - parent.min;
|
|
177
|
-
target.max = target.min + calcLength(layout);
|
|
178
|
-
}
|
|
179
|
-
function calcRelativePosition(target, layout, parent) {
|
|
180
|
-
calcRelativeAxisPosition(target.x, layout.x, parent.x);
|
|
181
|
-
calcRelativeAxisPosition(target.y, layout.y, parent.y);
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
const isMotionValue = (value) => Boolean(value && value.getVelocity);
|
|
185
|
-
|
|
186
|
-
const instantAnimationState = {
|
|
187
|
-
current: false,
|
|
188
|
-
};
|
|
189
|
-
|
|
190
|
-
/*
|
|
191
|
-
Bezier function generator
|
|
192
|
-
This has been modified from Gaëtan Renaudeau's BezierEasing
|
|
193
|
-
https://github.com/gre/bezier-easing/blob/master/src/index.js
|
|
194
|
-
https://github.com/gre/bezier-easing/blob/master/LICENSE
|
|
195
|
-
|
|
196
|
-
I've removed the newtonRaphsonIterate algo because in benchmarking it
|
|
197
|
-
wasn't noticiably faster than binarySubdivision, indeed removing it
|
|
198
|
-
usually improved times, depending on the curve.
|
|
199
|
-
I also removed the lookup table, as for the added bundle size and loop we're
|
|
200
|
-
only cutting ~4 or so subdivision iterations. I bumped the max iterations up
|
|
201
|
-
to 12 to compensate and this still tended to be faster for no perceivable
|
|
202
|
-
loss in accuracy.
|
|
203
|
-
Usage
|
|
204
|
-
const easeOut = cubicBezier(.17,.67,.83,.67);
|
|
205
|
-
const x = easeOut(0.5); // returns 0.627...
|
|
206
|
-
*/
|
|
207
|
-
// Returns x(t) given t, x1, and x2, or y(t) given t, y1, and y2.
|
|
208
|
-
const calcBezier = (t, a1, a2) => (((1.0 - 3.0 * a2 + 3.0 * a1) * t + (3.0 * a2 - 6.0 * a1)) * t + 3.0 * a1) *
|
|
209
|
-
t;
|
|
210
|
-
const subdivisionPrecision = 0.0000001;
|
|
211
|
-
const subdivisionMaxIterations = 12;
|
|
212
|
-
function binarySubdivide(x, lowerBound, upperBound, mX1, mX2) {
|
|
213
|
-
let currentX;
|
|
214
|
-
let currentT;
|
|
215
|
-
let i = 0;
|
|
216
|
-
do {
|
|
217
|
-
currentT = lowerBound + (upperBound - lowerBound) / 2.0;
|
|
218
|
-
currentX = calcBezier(currentT, mX1, mX2) - x;
|
|
219
|
-
if (currentX > 0.0) {
|
|
220
|
-
upperBound = currentT;
|
|
221
|
-
}
|
|
222
|
-
else {
|
|
223
|
-
lowerBound = currentT;
|
|
224
|
-
}
|
|
225
|
-
} while (Math.abs(currentX) > subdivisionPrecision &&
|
|
226
|
-
++i < subdivisionMaxIterations);
|
|
227
|
-
return currentT;
|
|
228
|
-
}
|
|
229
|
-
function cubicBezier(mX1, mY1, mX2, mY2) {
|
|
230
|
-
// If this is a linear gradient, return linear easing
|
|
231
|
-
if (mX1 === mY1 && mX2 === mY2)
|
|
232
|
-
return motionUtils.noop;
|
|
233
|
-
const getTForX = (aX) => binarySubdivide(aX, 0, 1, mX1, mX2);
|
|
234
|
-
// If animation is at start/end, return t without easing
|
|
235
|
-
return (t) => t === 0 || t === 1 ? t : calcBezier(getTForX(t), mY1, mY2);
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
// Accepts an easing function and returns a new one that outputs mirrored values for
|
|
239
|
-
// the second half of the animation. Turns easeIn into easeInOut.
|
|
240
|
-
const mirrorEasing = (easing) => (p) => p <= 0.5 ? easing(2 * p) / 2 : (2 - easing(2 * (1 - p))) / 2;
|
|
241
|
-
|
|
242
|
-
// Accepts an easing function and returns a new one that outputs reversed values.
|
|
243
|
-
// Turns easeIn into easeOut.
|
|
244
|
-
const reverseEasing = (easing) => (p) => 1 - easing(1 - p);
|
|
245
|
-
|
|
246
|
-
const backOut = /*@__PURE__*/ cubicBezier(0.33, 1.53, 0.69, 0.99);
|
|
247
|
-
const backIn = /*@__PURE__*/ reverseEasing(backOut);
|
|
248
|
-
const backInOut = /*@__PURE__*/ mirrorEasing(backIn);
|
|
249
|
-
|
|
250
|
-
const anticipate = (p) => (p *= 2) < 1 ? 0.5 * backIn(p) : 0.5 * (2 - Math.pow(2, -10 * (p - 1)));
|
|
251
|
-
|
|
252
|
-
const circIn = (p) => 1 - Math.sin(Math.acos(p));
|
|
253
|
-
const circOut = reverseEasing(circIn);
|
|
254
|
-
const circInOut = mirrorEasing(circIn);
|
|
255
|
-
|
|
256
|
-
/**
|
|
257
|
-
* Check if the value is a zero value string like "0px" or "0%"
|
|
258
|
-
*/
|
|
259
|
-
const isZeroValueString = (v) => /^0[^.\s]+$/u.test(v);
|
|
260
|
-
|
|
261
|
-
function isNone(value) {
|
|
262
|
-
if (typeof value === "number") {
|
|
263
|
-
return value === 0;
|
|
264
|
-
}
|
|
265
|
-
else if (value !== null) {
|
|
266
|
-
return value === "none" || value === "0" || isZeroValueString(value);
|
|
267
|
-
}
|
|
268
|
-
else {
|
|
269
|
-
return true;
|
|
270
|
-
}
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
/**
|
|
274
|
-
* Generate a list of every possible transform key.
|
|
275
|
-
*/
|
|
276
|
-
const transformPropOrder = [
|
|
277
|
-
"transformPerspective",
|
|
278
|
-
"x",
|
|
279
|
-
"y",
|
|
280
|
-
"z",
|
|
281
|
-
"translateX",
|
|
282
|
-
"translateY",
|
|
283
|
-
"translateZ",
|
|
284
|
-
"scale",
|
|
285
|
-
"scaleX",
|
|
286
|
-
"scaleY",
|
|
287
|
-
"rotate",
|
|
288
|
-
"rotateX",
|
|
289
|
-
"rotateY",
|
|
290
|
-
"rotateZ",
|
|
291
|
-
"skew",
|
|
292
|
-
"skewX",
|
|
293
|
-
"skewY",
|
|
294
|
-
];
|
|
295
|
-
/**
|
|
296
|
-
* A quick lookup for transform props.
|
|
297
|
-
*/
|
|
298
|
-
const transformProps = new Set(transformPropOrder);
|
|
299
|
-
|
|
300
|
-
const positionalKeys = new Set([
|
|
301
|
-
"width",
|
|
302
|
-
"height",
|
|
303
|
-
"top",
|
|
304
|
-
"left",
|
|
305
|
-
"right",
|
|
306
|
-
"bottom",
|
|
307
|
-
...transformPropOrder,
|
|
308
|
-
]);
|
|
309
|
-
|
|
310
|
-
const clamp = (min, max, v) => {
|
|
311
|
-
if (v > max)
|
|
312
|
-
return max;
|
|
313
|
-
if (v < min)
|
|
314
|
-
return min;
|
|
315
|
-
return v;
|
|
316
|
-
};
|
|
317
|
-
|
|
318
|
-
const number = {
|
|
319
|
-
test: (v) => typeof v === "number",
|
|
320
|
-
parse: parseFloat,
|
|
321
|
-
transform: (v) => v,
|
|
322
|
-
};
|
|
323
|
-
const alpha = {
|
|
324
|
-
...number,
|
|
325
|
-
transform: (v) => clamp(0, 1, v),
|
|
326
|
-
};
|
|
327
|
-
const scale = {
|
|
328
|
-
...number,
|
|
329
|
-
default: 1,
|
|
330
|
-
};
|
|
331
|
-
|
|
332
|
-
// If this number is a decimal, make it just five decimal places
|
|
333
|
-
// to avoid exponents
|
|
334
|
-
const sanitize = (v) => Math.round(v * 100000) / 100000;
|
|
335
|
-
|
|
336
|
-
const floatRegex = /-?(?:\d+(?:\.\d+)?|\.\d+)/gu;
|
|
337
|
-
|
|
338
|
-
function isNullish(v) {
|
|
339
|
-
return v == null;
|
|
340
|
-
}
|
|
341
|
-
|
|
342
|
-
const singleColorRegex = /^(?:#[\da-f]{3,8}|(?:rgb|hsl)a?\((?:-?[\d.]+%?[,\s]+){2}-?[\d.]+%?\s*(?:[,/]\s*)?(?:\b\d+(?:\.\d+)?|\.\d+)?%?\))$/iu;
|
|
343
|
-
|
|
344
|
-
/**
|
|
345
|
-
* Returns true if the provided string is a color, ie rgba(0,0,0,0) or #000,
|
|
346
|
-
* but false if a number or multiple colors
|
|
347
|
-
*/
|
|
348
|
-
const isColorString = (type, testProp) => (v) => {
|
|
349
|
-
return Boolean((typeof v === "string" &&
|
|
350
|
-
singleColorRegex.test(v) &&
|
|
351
|
-
v.startsWith(type)) ||
|
|
352
|
-
(testProp &&
|
|
353
|
-
!isNullish(v) &&
|
|
354
|
-
Object.prototype.hasOwnProperty.call(v, testProp)));
|
|
355
|
-
};
|
|
356
|
-
const splitColor = (aName, bName, cName) => (v) => {
|
|
357
|
-
if (typeof v !== "string")
|
|
358
|
-
return v;
|
|
359
|
-
const [a, b, c, alpha] = v.match(floatRegex);
|
|
360
|
-
return {
|
|
361
|
-
[aName]: parseFloat(a),
|
|
362
|
-
[bName]: parseFloat(b),
|
|
363
|
-
[cName]: parseFloat(c),
|
|
364
|
-
alpha: alpha !== undefined ? parseFloat(alpha) : 1,
|
|
365
|
-
};
|
|
366
|
-
};
|
|
367
|
-
|
|
368
|
-
const clampRgbUnit = (v) => clamp(0, 255, v);
|
|
369
|
-
const rgbUnit = {
|
|
370
|
-
...number,
|
|
371
|
-
transform: (v) => Math.round(clampRgbUnit(v)),
|
|
372
|
-
};
|
|
373
|
-
const rgba = {
|
|
374
|
-
test: /*@__PURE__*/ isColorString("rgb", "red"),
|
|
375
|
-
parse: /*@__PURE__*/ splitColor("red", "green", "blue"),
|
|
376
|
-
transform: ({ red, green, blue, alpha: alpha$1 = 1 }) => "rgba(" +
|
|
377
|
-
rgbUnit.transform(red) +
|
|
378
|
-
", " +
|
|
379
|
-
rgbUnit.transform(green) +
|
|
380
|
-
", " +
|
|
381
|
-
rgbUnit.transform(blue) +
|
|
382
|
-
", " +
|
|
383
|
-
sanitize(alpha.transform(alpha$1)) +
|
|
384
|
-
")",
|
|
385
|
-
};
|
|
386
|
-
|
|
387
|
-
function parseHex(v) {
|
|
388
|
-
let r = "";
|
|
389
|
-
let g = "";
|
|
390
|
-
let b = "";
|
|
391
|
-
let a = "";
|
|
392
|
-
// If we have 6 characters, ie #FF0000
|
|
393
|
-
if (v.length > 5) {
|
|
394
|
-
r = v.substring(1, 3);
|
|
395
|
-
g = v.substring(3, 5);
|
|
396
|
-
b = v.substring(5, 7);
|
|
397
|
-
a = v.substring(7, 9);
|
|
398
|
-
// Or we have 3 characters, ie #F00
|
|
399
|
-
}
|
|
400
|
-
else {
|
|
401
|
-
r = v.substring(1, 2);
|
|
402
|
-
g = v.substring(2, 3);
|
|
403
|
-
b = v.substring(3, 4);
|
|
404
|
-
a = v.substring(4, 5);
|
|
405
|
-
r += r;
|
|
406
|
-
g += g;
|
|
407
|
-
b += b;
|
|
408
|
-
a += a;
|
|
409
|
-
}
|
|
410
|
-
return {
|
|
411
|
-
red: parseInt(r, 16),
|
|
412
|
-
green: parseInt(g, 16),
|
|
413
|
-
blue: parseInt(b, 16),
|
|
414
|
-
alpha: a ? parseInt(a, 16) / 255 : 1,
|
|
415
|
-
};
|
|
416
|
-
}
|
|
417
|
-
const hex = {
|
|
418
|
-
test: /*@__PURE__*/ isColorString("#"),
|
|
419
|
-
parse: parseHex,
|
|
420
|
-
transform: rgba.transform,
|
|
421
|
-
};
|
|
422
|
-
|
|
423
|
-
const createUnitType = (unit) => ({
|
|
424
|
-
test: (v) => typeof v === "string" && v.endsWith(unit) && v.split(" ").length === 1,
|
|
425
|
-
parse: parseFloat,
|
|
426
|
-
transform: (v) => `${v}${unit}`,
|
|
427
|
-
});
|
|
428
|
-
const degrees = /*@__PURE__*/ createUnitType("deg");
|
|
429
|
-
const percent = /*@__PURE__*/ createUnitType("%");
|
|
430
|
-
const px = /*@__PURE__*/ createUnitType("px");
|
|
431
|
-
const vh = /*@__PURE__*/ createUnitType("vh");
|
|
432
|
-
const vw = /*@__PURE__*/ createUnitType("vw");
|
|
433
|
-
const progressPercentage = {
|
|
434
|
-
...percent,
|
|
435
|
-
parse: (v) => percent.parse(v) / 100,
|
|
436
|
-
transform: (v) => percent.transform(v * 100),
|
|
437
|
-
};
|
|
438
|
-
|
|
439
|
-
const hsla = {
|
|
440
|
-
test: /*@__PURE__*/ isColorString("hsl", "hue"),
|
|
441
|
-
parse: /*@__PURE__*/ splitColor("hue", "saturation", "lightness"),
|
|
442
|
-
transform: ({ hue, saturation, lightness, alpha: alpha$1 = 1 }) => {
|
|
443
|
-
return ("hsla(" +
|
|
444
|
-
Math.round(hue) +
|
|
445
|
-
", " +
|
|
446
|
-
percent.transform(sanitize(saturation)) +
|
|
447
|
-
", " +
|
|
448
|
-
percent.transform(sanitize(lightness)) +
|
|
449
|
-
", " +
|
|
450
|
-
sanitize(alpha.transform(alpha$1)) +
|
|
451
|
-
")");
|
|
452
|
-
},
|
|
453
|
-
};
|
|
454
|
-
|
|
455
|
-
const color = {
|
|
456
|
-
test: (v) => rgba.test(v) || hex.test(v) || hsla.test(v),
|
|
457
|
-
parse: (v) => {
|
|
458
|
-
if (rgba.test(v)) {
|
|
459
|
-
return rgba.parse(v);
|
|
460
|
-
}
|
|
461
|
-
else if (hsla.test(v)) {
|
|
462
|
-
return hsla.parse(v);
|
|
463
|
-
}
|
|
464
|
-
else {
|
|
465
|
-
return hex.parse(v);
|
|
466
|
-
}
|
|
467
|
-
},
|
|
468
|
-
transform: (v) => {
|
|
469
|
-
return typeof v === "string"
|
|
470
|
-
? v
|
|
471
|
-
: v.hasOwnProperty("red")
|
|
472
|
-
? rgba.transform(v)
|
|
473
|
-
: hsla.transform(v);
|
|
474
|
-
},
|
|
475
|
-
};
|
|
476
|
-
|
|
477
|
-
const colorRegex = /(?:#[\da-f]{3,8}|(?:rgb|hsl)a?\((?:-?[\d.]+%?[,\s]+){2}-?[\d.]+%?\s*(?:[,/]\s*)?(?:\b\d+(?:\.\d+)?|\.\d+)?%?\))/giu;
|
|
478
|
-
|
|
479
|
-
function test(v) {
|
|
480
|
-
return (isNaN(v) &&
|
|
481
|
-
typeof v === "string" &&
|
|
482
|
-
(v.match(floatRegex)?.length || 0) +
|
|
483
|
-
(v.match(colorRegex)?.length || 0) >
|
|
484
|
-
0);
|
|
485
|
-
}
|
|
486
|
-
const NUMBER_TOKEN = "number";
|
|
487
|
-
const COLOR_TOKEN = "color";
|
|
488
|
-
const VAR_TOKEN = "var";
|
|
489
|
-
const VAR_FUNCTION_TOKEN = "var(";
|
|
490
|
-
const SPLIT_TOKEN = "${}";
|
|
491
|
-
// this regex consists of the `singleCssVariableRegex|rgbHSLValueRegex|digitRegex`
|
|
492
|
-
const complexRegex = /var\s*\(\s*--(?:[\w-]+\s*|[\w-]+\s*,(?:\s*[^)(\s]|\s*\((?:[^)(]|\([^)(]*\))*\))+\s*)\)|#[\da-f]{3,8}|(?:rgb|hsl)a?\((?:-?[\d.]+%?[,\s]+){2}-?[\d.]+%?\s*(?:[,/]\s*)?(?:\b\d+(?:\.\d+)?|\.\d+)?%?\)|-?(?:\d+(?:\.\d+)?|\.\d+)/giu;
|
|
493
|
-
function analyseComplexValue(value) {
|
|
494
|
-
const originalValue = value.toString();
|
|
495
|
-
const values = [];
|
|
496
|
-
const indexes = {
|
|
497
|
-
color: [],
|
|
498
|
-
number: [],
|
|
499
|
-
var: [],
|
|
500
|
-
};
|
|
501
|
-
const types = [];
|
|
502
|
-
let i = 0;
|
|
503
|
-
const tokenised = originalValue.replace(complexRegex, (parsedValue) => {
|
|
504
|
-
if (color.test(parsedValue)) {
|
|
505
|
-
indexes.color.push(i);
|
|
506
|
-
types.push(COLOR_TOKEN);
|
|
507
|
-
values.push(color.parse(parsedValue));
|
|
508
|
-
}
|
|
509
|
-
else if (parsedValue.startsWith(VAR_FUNCTION_TOKEN)) {
|
|
510
|
-
indexes.var.push(i);
|
|
511
|
-
types.push(VAR_TOKEN);
|
|
512
|
-
values.push(parsedValue);
|
|
513
|
-
}
|
|
514
|
-
else {
|
|
515
|
-
indexes.number.push(i);
|
|
516
|
-
types.push(NUMBER_TOKEN);
|
|
517
|
-
values.push(parseFloat(parsedValue));
|
|
518
|
-
}
|
|
519
|
-
++i;
|
|
520
|
-
return SPLIT_TOKEN;
|
|
521
|
-
});
|
|
522
|
-
const split = tokenised.split(SPLIT_TOKEN);
|
|
523
|
-
return { values, split, indexes, types };
|
|
524
|
-
}
|
|
525
|
-
function parseComplexValue(v) {
|
|
526
|
-
return analyseComplexValue(v).values;
|
|
527
|
-
}
|
|
528
|
-
function createTransformer(source) {
|
|
529
|
-
const { split, types } = analyseComplexValue(source);
|
|
530
|
-
const numSections = split.length;
|
|
531
|
-
return (v) => {
|
|
532
|
-
let output = "";
|
|
533
|
-
for (let i = 0; i < numSections; i++) {
|
|
534
|
-
output += split[i];
|
|
535
|
-
if (v[i] !== undefined) {
|
|
536
|
-
const type = types[i];
|
|
537
|
-
if (type === NUMBER_TOKEN) {
|
|
538
|
-
output += sanitize(v[i]);
|
|
539
|
-
}
|
|
540
|
-
else if (type === COLOR_TOKEN) {
|
|
541
|
-
output += color.transform(v[i]);
|
|
542
|
-
}
|
|
543
|
-
else {
|
|
544
|
-
output += v[i];
|
|
545
|
-
}
|
|
546
|
-
}
|
|
547
|
-
}
|
|
548
|
-
return output;
|
|
549
|
-
};
|
|
550
|
-
}
|
|
551
|
-
const convertNumbersToZero = (v) => typeof v === "number" ? 0 : v;
|
|
552
|
-
function getAnimatableNone$1(v) {
|
|
553
|
-
const parsed = parseComplexValue(v);
|
|
554
|
-
const transformer = createTransformer(v);
|
|
555
|
-
return transformer(parsed.map(convertNumbersToZero));
|
|
556
|
-
}
|
|
557
|
-
const complex = {
|
|
558
|
-
test,
|
|
559
|
-
parse: parseComplexValue,
|
|
560
|
-
createTransformer,
|
|
561
|
-
getAnimatableNone: getAnimatableNone$1,
|
|
562
|
-
};
|
|
563
|
-
|
|
564
|
-
/**
|
|
565
|
-
* Properties that should default to 1 or 100%
|
|
566
|
-
*/
|
|
567
|
-
const maxDefaults = new Set(["brightness", "contrast", "saturate", "opacity"]);
|
|
568
|
-
function applyDefaultFilter(v) {
|
|
569
|
-
const [name, value] = v.slice(0, -1).split("(");
|
|
570
|
-
if (name === "drop-shadow")
|
|
571
|
-
return v;
|
|
572
|
-
const [number] = value.match(floatRegex) || [];
|
|
573
|
-
if (!number)
|
|
574
|
-
return v;
|
|
575
|
-
const unit = value.replace(number, "");
|
|
576
|
-
let defaultValue = maxDefaults.has(name) ? 1 : 0;
|
|
577
|
-
if (number !== value)
|
|
578
|
-
defaultValue *= 100;
|
|
579
|
-
return name + "(" + defaultValue + unit + ")";
|
|
580
|
-
}
|
|
581
|
-
const functionRegex = /\b([a-z-]*)\(.*?\)/gu;
|
|
582
|
-
const filter = {
|
|
583
|
-
...complex,
|
|
584
|
-
getAnimatableNone: (v) => {
|
|
585
|
-
const functions = v.match(functionRegex);
|
|
586
|
-
return functions ? functions.map(applyDefaultFilter).join(" ") : v;
|
|
587
|
-
},
|
|
588
|
-
};
|
|
589
|
-
|
|
590
|
-
const browserNumberValueTypes = {
|
|
591
|
-
// Border props
|
|
592
|
-
borderWidth: px,
|
|
593
|
-
borderTopWidth: px,
|
|
594
|
-
borderRightWidth: px,
|
|
595
|
-
borderBottomWidth: px,
|
|
596
|
-
borderLeftWidth: px,
|
|
597
|
-
borderRadius: px,
|
|
598
|
-
radius: px,
|
|
599
|
-
borderTopLeftRadius: px,
|
|
600
|
-
borderTopRightRadius: px,
|
|
601
|
-
borderBottomRightRadius: px,
|
|
602
|
-
borderBottomLeftRadius: px,
|
|
603
|
-
// Positioning props
|
|
604
|
-
width: px,
|
|
605
|
-
maxWidth: px,
|
|
606
|
-
height: px,
|
|
607
|
-
maxHeight: px,
|
|
608
|
-
top: px,
|
|
609
|
-
right: px,
|
|
610
|
-
bottom: px,
|
|
611
|
-
left: px,
|
|
612
|
-
// Spacing props
|
|
613
|
-
padding: px,
|
|
614
|
-
paddingTop: px,
|
|
615
|
-
paddingRight: px,
|
|
616
|
-
paddingBottom: px,
|
|
617
|
-
paddingLeft: px,
|
|
618
|
-
margin: px,
|
|
619
|
-
marginTop: px,
|
|
620
|
-
marginRight: px,
|
|
621
|
-
marginBottom: px,
|
|
622
|
-
marginLeft: px,
|
|
623
|
-
// Misc
|
|
624
|
-
backgroundPositionX: px,
|
|
625
|
-
backgroundPositionY: px,
|
|
626
|
-
};
|
|
627
|
-
|
|
628
|
-
const transformValueTypes = {
|
|
629
|
-
rotate: degrees,
|
|
630
|
-
rotateX: degrees,
|
|
631
|
-
rotateY: degrees,
|
|
632
|
-
rotateZ: degrees,
|
|
633
|
-
scale,
|
|
634
|
-
scaleX: scale,
|
|
635
|
-
scaleY: scale,
|
|
636
|
-
scaleZ: scale,
|
|
637
|
-
skew: degrees,
|
|
638
|
-
skewX: degrees,
|
|
639
|
-
skewY: degrees,
|
|
640
|
-
distance: px,
|
|
641
|
-
translateX: px,
|
|
642
|
-
translateY: px,
|
|
643
|
-
translateZ: px,
|
|
644
|
-
x: px,
|
|
645
|
-
y: px,
|
|
646
|
-
z: px,
|
|
647
|
-
perspective: px,
|
|
648
|
-
transformPerspective: px,
|
|
649
|
-
opacity: alpha,
|
|
650
|
-
originX: progressPercentage,
|
|
651
|
-
originY: progressPercentage,
|
|
652
|
-
originZ: px,
|
|
653
|
-
};
|
|
654
|
-
|
|
655
|
-
const int = {
|
|
656
|
-
...number,
|
|
657
|
-
transform: Math.round,
|
|
658
|
-
};
|
|
659
|
-
|
|
660
|
-
const numberValueTypes = {
|
|
661
|
-
...browserNumberValueTypes,
|
|
662
|
-
...transformValueTypes,
|
|
663
|
-
zIndex: int,
|
|
664
|
-
size: px,
|
|
665
|
-
// SVG
|
|
666
|
-
fillOpacity: alpha,
|
|
667
|
-
strokeOpacity: alpha,
|
|
668
|
-
numOctaves: int,
|
|
669
|
-
};
|
|
670
|
-
|
|
671
|
-
/**
|
|
672
|
-
* A map of default value types for common values
|
|
673
|
-
*/
|
|
674
|
-
const defaultValueTypes = {
|
|
675
|
-
...numberValueTypes,
|
|
676
|
-
// Color props
|
|
677
|
-
color,
|
|
678
|
-
backgroundColor: color,
|
|
679
|
-
outlineColor: color,
|
|
680
|
-
fill: color,
|
|
681
|
-
stroke: color,
|
|
682
|
-
// Border props
|
|
683
|
-
borderColor: color,
|
|
684
|
-
borderTopColor: color,
|
|
685
|
-
borderRightColor: color,
|
|
686
|
-
borderBottomColor: color,
|
|
687
|
-
borderLeftColor: color,
|
|
688
|
-
filter,
|
|
689
|
-
WebkitFilter: filter,
|
|
690
|
-
};
|
|
691
|
-
/**
|
|
692
|
-
* Gets the default ValueType for the provided value key
|
|
693
|
-
*/
|
|
694
|
-
const getDefaultValueType = (key) => defaultValueTypes[key];
|
|
695
|
-
|
|
696
|
-
function getAnimatableNone(key, value) {
|
|
697
|
-
let defaultValueType = getDefaultValueType(key);
|
|
698
|
-
if (defaultValueType !== filter)
|
|
699
|
-
defaultValueType = complex;
|
|
700
|
-
// If value is not recognised as animatable, ie "none", create an animatable version origin based on the target
|
|
701
|
-
return defaultValueType.getAnimatableNone
|
|
702
|
-
? defaultValueType.getAnimatableNone(value)
|
|
703
|
-
: undefined;
|
|
704
|
-
}
|
|
705
|
-
|
|
706
|
-
/**
|
|
707
|
-
* If we encounter keyframes like "none" or "0" and we also have keyframes like
|
|
708
|
-
* "#fff" or "200px 200px" we want to find a keyframe to serve as a template for
|
|
709
|
-
* the "none" keyframes. In this case "#fff" or "200px 200px" - then these get turned into
|
|
710
|
-
* zero equivalents, i.e. "#fff0" or "0px 0px".
|
|
711
|
-
*/
|
|
712
|
-
const invalidTemplates = new Set(["auto", "none", "0"]);
|
|
713
|
-
function makeNoneKeyframesAnimatable(unresolvedKeyframes, noneKeyframeIndexes, name) {
|
|
714
|
-
let i = 0;
|
|
715
|
-
let animatableTemplate = undefined;
|
|
716
|
-
while (i < unresolvedKeyframes.length && !animatableTemplate) {
|
|
717
|
-
const keyframe = unresolvedKeyframes[i];
|
|
718
|
-
if (typeof keyframe === "string" &&
|
|
719
|
-
!invalidTemplates.has(keyframe) &&
|
|
720
|
-
analyseComplexValue(keyframe).values.length) {
|
|
721
|
-
animatableTemplate = unresolvedKeyframes[i];
|
|
722
|
-
}
|
|
723
|
-
i++;
|
|
724
|
-
}
|
|
725
|
-
if (animatableTemplate && name) {
|
|
726
|
-
for (const noneIndex of noneKeyframeIndexes) {
|
|
727
|
-
unresolvedKeyframes[noneIndex] = getAnimatableNone(name, animatableTemplate);
|
|
728
|
-
}
|
|
729
|
-
}
|
|
730
|
-
}
|
|
731
|
-
|
|
732
|
-
const radToDeg = (rad) => (rad * 180) / Math.PI;
|
|
733
|
-
const rotate = (v) => {
|
|
734
|
-
const angle = radToDeg(Math.atan2(v[1], v[0]));
|
|
735
|
-
return rebaseAngle(angle);
|
|
736
|
-
};
|
|
737
|
-
const matrix2dParsers = {
|
|
738
|
-
x: 4,
|
|
739
|
-
y: 5,
|
|
740
|
-
translateX: 4,
|
|
741
|
-
translateY: 5,
|
|
742
|
-
scaleX: 0,
|
|
743
|
-
scaleY: 3,
|
|
744
|
-
scale: (v) => (Math.abs(v[0]) + Math.abs(v[3])) / 2,
|
|
745
|
-
rotate,
|
|
746
|
-
rotateZ: rotate,
|
|
747
|
-
skewX: (v) => radToDeg(Math.atan(v[1])),
|
|
748
|
-
skewY: (v) => radToDeg(Math.atan(v[2])),
|
|
749
|
-
skew: (v) => (Math.abs(v[1]) + Math.abs(v[2])) / 2,
|
|
750
|
-
};
|
|
751
|
-
const rebaseAngle = (angle) => {
|
|
752
|
-
angle = angle % 360;
|
|
753
|
-
if (angle < 0)
|
|
754
|
-
angle += 360;
|
|
755
|
-
return angle;
|
|
756
|
-
};
|
|
757
|
-
const rotateZ = rotate;
|
|
758
|
-
const scaleX = (v) => Math.sqrt(v[0] * v[0] + v[1] * v[1]);
|
|
759
|
-
const scaleY = (v) => Math.sqrt(v[4] * v[4] + v[5] * v[5]);
|
|
760
|
-
const matrix3dParsers = {
|
|
761
|
-
x: 12,
|
|
762
|
-
y: 13,
|
|
763
|
-
z: 14,
|
|
764
|
-
translateX: 12,
|
|
765
|
-
translateY: 13,
|
|
766
|
-
translateZ: 14,
|
|
767
|
-
scaleX,
|
|
768
|
-
scaleY,
|
|
769
|
-
scale: (v) => (scaleX(v) + scaleY(v)) / 2,
|
|
770
|
-
rotateX: (v) => rebaseAngle(radToDeg(Math.atan2(v[6], v[5]))),
|
|
771
|
-
rotateY: (v) => rebaseAngle(radToDeg(Math.atan2(-v[2], v[0]))),
|
|
772
|
-
rotateZ,
|
|
773
|
-
rotate: rotateZ,
|
|
774
|
-
skewX: (v) => radToDeg(Math.atan(v[4])),
|
|
775
|
-
skewY: (v) => radToDeg(Math.atan(v[1])),
|
|
776
|
-
skew: (v) => (Math.abs(v[1]) + Math.abs(v[4])) / 2,
|
|
777
|
-
};
|
|
778
|
-
function defaultTransformValue(name) {
|
|
779
|
-
return name.includes("scale") ? 1 : 0;
|
|
780
|
-
}
|
|
781
|
-
function parseValueFromTransform(transform, name) {
|
|
782
|
-
if (!transform || transform === "none") {
|
|
783
|
-
return defaultTransformValue(name);
|
|
784
|
-
}
|
|
785
|
-
const matrix3dMatch = transform.match(/^matrix3d\(([-\d.e\s,]+)\)$/u);
|
|
786
|
-
let parsers;
|
|
787
|
-
let match;
|
|
788
|
-
if (matrix3dMatch) {
|
|
789
|
-
parsers = matrix3dParsers;
|
|
790
|
-
match = matrix3dMatch;
|
|
791
|
-
}
|
|
792
|
-
else {
|
|
793
|
-
const matrix2dMatch = transform.match(/^matrix\(([-\d.e\s,]+)\)$/u);
|
|
794
|
-
parsers = matrix2dParsers;
|
|
795
|
-
match = matrix2dMatch;
|
|
796
|
-
}
|
|
797
|
-
if (!match) {
|
|
798
|
-
return defaultTransformValue(name);
|
|
799
|
-
}
|
|
800
|
-
const valueParser = parsers[name];
|
|
801
|
-
const values = match[1].split(",").map(convertTransformToNumber);
|
|
802
|
-
return typeof valueParser === "function"
|
|
803
|
-
? valueParser(values)
|
|
804
|
-
: values[valueParser];
|
|
805
|
-
}
|
|
806
|
-
const readTransformValue = (instance, name) => {
|
|
807
|
-
const { transform = "none" } = getComputedStyle(instance);
|
|
808
|
-
return parseValueFromTransform(transform, name);
|
|
809
|
-
};
|
|
810
|
-
function convertTransformToNumber(value) {
|
|
811
|
-
return parseFloat(value.trim());
|
|
812
|
-
}
|
|
813
|
-
|
|
814
|
-
const isNumOrPxType = (v) => v === number || v === px;
|
|
815
|
-
const transformKeys = new Set(["x", "y", "z"]);
|
|
816
|
-
const nonTranslationalTransformKeys = transformPropOrder.filter((key) => !transformKeys.has(key));
|
|
817
|
-
function removeNonTranslationalTransform(visualElement) {
|
|
818
|
-
const removedTransforms = [];
|
|
819
|
-
nonTranslationalTransformKeys.forEach((key) => {
|
|
820
|
-
const value = visualElement.getValue(key);
|
|
821
|
-
if (value !== undefined) {
|
|
822
|
-
removedTransforms.push([key, value.get()]);
|
|
823
|
-
value.set(key.startsWith("scale") ? 1 : 0);
|
|
824
|
-
}
|
|
825
|
-
});
|
|
826
|
-
return removedTransforms;
|
|
827
|
-
}
|
|
828
|
-
const positionalValues = {
|
|
829
|
-
// Dimensions
|
|
830
|
-
width: ({ x }, { paddingLeft = "0", paddingRight = "0" }) => x.max - x.min - parseFloat(paddingLeft) - parseFloat(paddingRight),
|
|
831
|
-
height: ({ y }, { paddingTop = "0", paddingBottom = "0" }) => y.max - y.min - parseFloat(paddingTop) - parseFloat(paddingBottom),
|
|
832
|
-
top: (_bbox, { top }) => parseFloat(top),
|
|
833
|
-
left: (_bbox, { left }) => parseFloat(left),
|
|
834
|
-
bottom: ({ y }, { top }) => parseFloat(top) + (y.max - y.min),
|
|
835
|
-
right: ({ x }, { left }) => parseFloat(left) + (x.max - x.min),
|
|
836
|
-
// Transform
|
|
837
|
-
x: (_bbox, { transform }) => parseValueFromTransform(transform, "x"),
|
|
838
|
-
y: (_bbox, { transform }) => parseValueFromTransform(transform, "y"),
|
|
839
|
-
};
|
|
840
|
-
// Alias translate longform names
|
|
841
|
-
positionalValues.translateX = positionalValues.x;
|
|
842
|
-
positionalValues.translateY = positionalValues.y;
|
|
843
|
-
|
|
844
|
-
const toResolve = new Set();
|
|
845
|
-
let isScheduled = false;
|
|
846
|
-
let anyNeedsMeasurement = false;
|
|
847
|
-
function measureAllKeyframes() {
|
|
848
|
-
if (anyNeedsMeasurement) {
|
|
849
|
-
const resolversToMeasure = Array.from(toResolve).filter((resolver) => resolver.needsMeasurement);
|
|
850
|
-
const elementsToMeasure = new Set(resolversToMeasure.map((resolver) => resolver.element));
|
|
851
|
-
const transformsToRestore = new Map();
|
|
852
|
-
/**
|
|
853
|
-
* Write pass
|
|
854
|
-
* If we're measuring elements we want to remove bounding box-changing transforms.
|
|
855
|
-
*/
|
|
856
|
-
elementsToMeasure.forEach((element) => {
|
|
857
|
-
const removedTransforms = removeNonTranslationalTransform(element);
|
|
858
|
-
if (!removedTransforms.length)
|
|
859
|
-
return;
|
|
860
|
-
transformsToRestore.set(element, removedTransforms);
|
|
861
|
-
element.render();
|
|
862
|
-
});
|
|
863
|
-
// Read
|
|
864
|
-
resolversToMeasure.forEach((resolver) => resolver.measureInitialState());
|
|
865
|
-
// Write
|
|
866
|
-
elementsToMeasure.forEach((element) => {
|
|
867
|
-
element.render();
|
|
868
|
-
const restore = transformsToRestore.get(element);
|
|
869
|
-
if (restore) {
|
|
870
|
-
restore.forEach(([key, value]) => {
|
|
871
|
-
element.getValue(key)?.set(value);
|
|
872
|
-
});
|
|
873
|
-
}
|
|
874
|
-
});
|
|
875
|
-
// Read
|
|
876
|
-
resolversToMeasure.forEach((resolver) => resolver.measureEndState());
|
|
877
|
-
// Write
|
|
878
|
-
resolversToMeasure.forEach((resolver) => {
|
|
879
|
-
if (resolver.suspendedScrollY !== undefined) {
|
|
880
|
-
window.scrollTo(0, resolver.suspendedScrollY);
|
|
881
|
-
}
|
|
882
|
-
});
|
|
883
|
-
}
|
|
884
|
-
anyNeedsMeasurement = false;
|
|
885
|
-
isScheduled = false;
|
|
886
|
-
toResolve.forEach((resolver) => resolver.complete());
|
|
887
|
-
toResolve.clear();
|
|
888
|
-
}
|
|
889
|
-
function readAllKeyframes() {
|
|
890
|
-
toResolve.forEach((resolver) => {
|
|
891
|
-
resolver.readKeyframes();
|
|
892
|
-
if (resolver.needsMeasurement) {
|
|
893
|
-
anyNeedsMeasurement = true;
|
|
894
|
-
}
|
|
895
|
-
});
|
|
896
|
-
}
|
|
897
|
-
function flushKeyframeResolvers() {
|
|
898
|
-
readAllKeyframes();
|
|
899
|
-
measureAllKeyframes();
|
|
900
|
-
}
|
|
901
|
-
class KeyframeResolver {
|
|
902
|
-
constructor(unresolvedKeyframes, onComplete, name, motionValue, element, isAsync = false) {
|
|
903
|
-
/**
|
|
904
|
-
* Track whether this resolver has completed. Once complete, it never
|
|
905
|
-
* needs to attempt keyframe resolution again.
|
|
906
|
-
*/
|
|
907
|
-
this.isComplete = false;
|
|
908
|
-
/**
|
|
909
|
-
* Track whether this resolver is async. If it is, it'll be added to the
|
|
910
|
-
* resolver queue and flushed in the next frame. Resolvers that aren't going
|
|
911
|
-
* to trigger read/write thrashing don't need to be async.
|
|
912
|
-
*/
|
|
913
|
-
this.isAsync = false;
|
|
914
|
-
/**
|
|
915
|
-
* Track whether this resolver needs to perform a measurement
|
|
916
|
-
* to resolve its keyframes.
|
|
917
|
-
*/
|
|
918
|
-
this.needsMeasurement = false;
|
|
919
|
-
/**
|
|
920
|
-
* Track whether this resolver is currently scheduled to resolve
|
|
921
|
-
* to allow it to be cancelled and resumed externally.
|
|
922
|
-
*/
|
|
923
|
-
this.isScheduled = false;
|
|
924
|
-
this.unresolvedKeyframes = [...unresolvedKeyframes];
|
|
925
|
-
this.onComplete = onComplete;
|
|
926
|
-
this.name = name;
|
|
927
|
-
this.motionValue = motionValue;
|
|
928
|
-
this.element = element;
|
|
929
|
-
this.isAsync = isAsync;
|
|
930
|
-
}
|
|
931
|
-
scheduleResolve() {
|
|
932
|
-
this.isScheduled = true;
|
|
933
|
-
if (this.isAsync) {
|
|
934
|
-
toResolve.add(this);
|
|
935
|
-
if (!isScheduled) {
|
|
936
|
-
isScheduled = true;
|
|
937
|
-
motionDom.frame.read(readAllKeyframes);
|
|
938
|
-
motionDom.frame.resolveKeyframes(measureAllKeyframes);
|
|
939
|
-
}
|
|
940
|
-
}
|
|
941
|
-
else {
|
|
942
|
-
this.readKeyframes();
|
|
943
|
-
this.complete();
|
|
944
|
-
}
|
|
945
|
-
}
|
|
946
|
-
readKeyframes() {
|
|
947
|
-
const { unresolvedKeyframes, name, element, motionValue } = this;
|
|
948
|
-
/**
|
|
949
|
-
* If a keyframe is null, we hydrate it either by reading it from
|
|
950
|
-
* the instance, or propagating from previous keyframes.
|
|
951
|
-
*/
|
|
952
|
-
for (let i = 0; i < unresolvedKeyframes.length; i++) {
|
|
953
|
-
if (unresolvedKeyframes[i] === null) {
|
|
954
|
-
/**
|
|
955
|
-
* If the first keyframe is null, we need to find its value by sampling the element
|
|
956
|
-
*/
|
|
957
|
-
if (i === 0) {
|
|
958
|
-
const currentValue = motionValue?.get();
|
|
959
|
-
const finalKeyframe = unresolvedKeyframes[unresolvedKeyframes.length - 1];
|
|
960
|
-
if (currentValue !== undefined) {
|
|
961
|
-
unresolvedKeyframes[0] = currentValue;
|
|
962
|
-
}
|
|
963
|
-
else if (element && name) {
|
|
964
|
-
const valueAsRead = element.readValue(name, finalKeyframe);
|
|
965
|
-
if (valueAsRead !== undefined && valueAsRead !== null) {
|
|
966
|
-
unresolvedKeyframes[0] = valueAsRead;
|
|
967
|
-
}
|
|
968
|
-
}
|
|
969
|
-
if (unresolvedKeyframes[0] === undefined) {
|
|
970
|
-
unresolvedKeyframes[0] = finalKeyframe;
|
|
971
|
-
}
|
|
972
|
-
if (motionValue && currentValue === undefined) {
|
|
973
|
-
motionValue.set(unresolvedKeyframes[0]);
|
|
974
|
-
}
|
|
975
|
-
}
|
|
976
|
-
else {
|
|
977
|
-
unresolvedKeyframes[i] = unresolvedKeyframes[i - 1];
|
|
978
|
-
}
|
|
979
|
-
}
|
|
980
|
-
}
|
|
981
|
-
}
|
|
982
|
-
setFinalKeyframe() { }
|
|
983
|
-
measureInitialState() { }
|
|
984
|
-
renderEndStyles() { }
|
|
985
|
-
measureEndState() { }
|
|
986
|
-
complete() {
|
|
987
|
-
this.isComplete = true;
|
|
988
|
-
this.onComplete(this.unresolvedKeyframes, this.finalKeyframe);
|
|
989
|
-
toResolve.delete(this);
|
|
990
|
-
}
|
|
991
|
-
cancel() {
|
|
992
|
-
if (!this.isComplete) {
|
|
993
|
-
this.isScheduled = false;
|
|
994
|
-
toResolve.delete(this);
|
|
995
|
-
}
|
|
996
|
-
}
|
|
997
|
-
resume() {
|
|
998
|
-
if (!this.isComplete)
|
|
999
|
-
this.scheduleResolve();
|
|
1000
|
-
}
|
|
1001
|
-
}
|
|
1002
|
-
|
|
1003
|
-
/**
|
|
1004
|
-
* Check if value is a numerical string, ie a string that is purely a number eg "100" or "-100.1"
|
|
1005
|
-
*/
|
|
1006
|
-
const isNumericalString = (v) => /^-?(?:\d+(?:\.\d+)?|\.\d+)$/u.test(v);
|
|
1007
|
-
|
|
1008
|
-
const checkStringStartsWith = (token) => (key) => typeof key === "string" && key.startsWith(token);
|
|
1009
|
-
const isCSSVariableName =
|
|
1010
|
-
/*@__PURE__*/ checkStringStartsWith("--");
|
|
1011
|
-
const startsAsVariableToken =
|
|
1012
|
-
/*@__PURE__*/ checkStringStartsWith("var(--");
|
|
1013
|
-
const isCSSVariableToken = (value) => {
|
|
1014
|
-
const startsWithToken = startsAsVariableToken(value);
|
|
1015
|
-
if (!startsWithToken)
|
|
1016
|
-
return false;
|
|
1017
|
-
// Ensure any comments are stripped from the value as this can harm performance of the regex.
|
|
1018
|
-
return singleCssVariableRegex.test(value.split("/*")[0].trim());
|
|
1019
|
-
};
|
|
1020
|
-
const singleCssVariableRegex = /var\(--(?:[\w-]+\s*|[\w-]+\s*,(?:\s*[^)(\s]|\s*\((?:[^)(]|\([^)(]*\))*\))+\s*)\)$/iu;
|
|
1021
|
-
|
|
1022
|
-
/**
|
|
1023
|
-
* Parse Framer's special CSS variable format into a CSS token and a fallback.
|
|
1024
|
-
*
|
|
1025
|
-
* ```
|
|
1026
|
-
* `var(--foo, #fff)` => [`--foo`, '#fff']
|
|
1027
|
-
* ```
|
|
1028
|
-
*
|
|
1029
|
-
* @param current
|
|
1030
|
-
*/
|
|
1031
|
-
const splitCSSVariableRegex =
|
|
1032
|
-
// eslint-disable-next-line redos-detector/no-unsafe-regex -- false positive, as it can match a lot of words
|
|
1033
|
-
/^var\(--(?:([\w-]+)|([\w-]+), ?([a-zA-Z\d ()%#.,-]+))\)/u;
|
|
1034
|
-
function parseCSSVariable(current) {
|
|
1035
|
-
const match = splitCSSVariableRegex.exec(current);
|
|
1036
|
-
if (!match)
|
|
1037
|
-
return [,];
|
|
1038
|
-
const [, token1, token2, fallback] = match;
|
|
1039
|
-
return [`--${token1 ?? token2}`, fallback];
|
|
1040
|
-
}
|
|
1041
|
-
const maxDepth = 4;
|
|
1042
|
-
function getVariableValue(current, element, depth = 1) {
|
|
1043
|
-
motionUtils.invariant(depth <= maxDepth, `Max CSS variable fallback depth detected in property "${current}". This may indicate a circular fallback dependency.`);
|
|
1044
|
-
const [token, fallback] = parseCSSVariable(current);
|
|
1045
|
-
// No CSS variable detected
|
|
1046
|
-
if (!token)
|
|
1047
|
-
return;
|
|
1048
|
-
// Attempt to read this CSS variable off the element
|
|
1049
|
-
const resolved = window.getComputedStyle(element).getPropertyValue(token);
|
|
1050
|
-
if (resolved) {
|
|
1051
|
-
const trimmed = resolved.trim();
|
|
1052
|
-
return isNumericalString(trimmed) ? parseFloat(trimmed) : trimmed;
|
|
1053
|
-
}
|
|
1054
|
-
return isCSSVariableToken(fallback)
|
|
1055
|
-
? getVariableValue(fallback, element, depth + 1)
|
|
1056
|
-
: fallback;
|
|
1057
|
-
}
|
|
1058
|
-
|
|
1059
|
-
/**
|
|
1060
|
-
* Tests a provided value against a ValueType
|
|
1061
|
-
*/
|
|
1062
|
-
const testValueType = (v) => (type) => type.test(v);
|
|
1063
|
-
|
|
1064
|
-
/**
|
|
1065
|
-
* ValueType for "auto"
|
|
1066
|
-
*/
|
|
1067
|
-
const auto = {
|
|
1068
|
-
test: (v) => v === "auto",
|
|
1069
|
-
parse: (v) => v,
|
|
1070
|
-
};
|
|
1071
|
-
|
|
1072
|
-
/**
|
|
1073
|
-
* A list of value types commonly used for dimensions
|
|
1074
|
-
*/
|
|
1075
|
-
const dimensionValueTypes = [number, px, percent, degrees, vw, vh, auto];
|
|
1076
|
-
/**
|
|
1077
|
-
* Tests a dimensional value against the list of dimension ValueTypes
|
|
1078
|
-
*/
|
|
1079
|
-
const findDimensionValueType = (v) => dimensionValueTypes.find(testValueType(v));
|
|
1080
|
-
|
|
1081
|
-
class DOMKeyframesResolver extends KeyframeResolver {
|
|
1082
|
-
constructor(unresolvedKeyframes, onComplete, name, motionValue, element) {
|
|
1083
|
-
super(unresolvedKeyframes, onComplete, name, motionValue, element, true);
|
|
1084
|
-
}
|
|
1085
|
-
readKeyframes() {
|
|
1086
|
-
const { unresolvedKeyframes, element, name } = this;
|
|
1087
|
-
if (!element || !element.current)
|
|
1088
|
-
return;
|
|
1089
|
-
super.readKeyframes();
|
|
1090
|
-
/**
|
|
1091
|
-
* If any keyframe is a CSS variable, we need to find its value by sampling the element
|
|
1092
|
-
*/
|
|
1093
|
-
for (let i = 0; i < unresolvedKeyframes.length; i++) {
|
|
1094
|
-
let keyframe = unresolvedKeyframes[i];
|
|
1095
|
-
if (typeof keyframe === "string") {
|
|
1096
|
-
keyframe = keyframe.trim();
|
|
1097
|
-
if (isCSSVariableToken(keyframe)) {
|
|
1098
|
-
const resolved = getVariableValue(keyframe, element.current);
|
|
1099
|
-
if (resolved !== undefined) {
|
|
1100
|
-
unresolvedKeyframes[i] = resolved;
|
|
1101
|
-
}
|
|
1102
|
-
if (i === unresolvedKeyframes.length - 1) {
|
|
1103
|
-
this.finalKeyframe = keyframe;
|
|
1104
|
-
}
|
|
1105
|
-
}
|
|
1106
|
-
}
|
|
1107
|
-
}
|
|
1108
|
-
/**
|
|
1109
|
-
* Resolve "none" values. We do this potentially twice - once before and once after measuring keyframes.
|
|
1110
|
-
* This could be seen as inefficient but it's a trade-off to avoid measurements in more situations, which
|
|
1111
|
-
* have a far bigger performance impact.
|
|
1112
|
-
*/
|
|
1113
|
-
this.resolveNoneKeyframes();
|
|
1114
|
-
/**
|
|
1115
|
-
* Check to see if unit type has changed. If so schedule jobs that will
|
|
1116
|
-
* temporarily set styles to the destination keyframes.
|
|
1117
|
-
* Skip if we have more than two keyframes or this isn't a positional value.
|
|
1118
|
-
* TODO: We can throw if there are multiple keyframes and the value type changes.
|
|
1119
|
-
*/
|
|
1120
|
-
if (!positionalKeys.has(name) || unresolvedKeyframes.length !== 2) {
|
|
1121
|
-
return;
|
|
1122
|
-
}
|
|
1123
|
-
const [origin, target] = unresolvedKeyframes;
|
|
1124
|
-
const originType = findDimensionValueType(origin);
|
|
1125
|
-
const targetType = findDimensionValueType(target);
|
|
1126
|
-
/**
|
|
1127
|
-
* Either we don't recognise these value types or we can animate between them.
|
|
1128
|
-
*/
|
|
1129
|
-
if (originType === targetType)
|
|
1130
|
-
return;
|
|
1131
|
-
/**
|
|
1132
|
-
* If both values are numbers or pixels, we can animate between them by
|
|
1133
|
-
* converting them to numbers.
|
|
1134
|
-
*/
|
|
1135
|
-
if (isNumOrPxType(originType) && isNumOrPxType(targetType)) {
|
|
1136
|
-
for (let i = 0; i < unresolvedKeyframes.length; i++) {
|
|
1137
|
-
const value = unresolvedKeyframes[i];
|
|
1138
|
-
if (typeof value === "string") {
|
|
1139
|
-
unresolvedKeyframes[i] = parseFloat(value);
|
|
1140
|
-
}
|
|
1141
|
-
}
|
|
1142
|
-
}
|
|
1143
|
-
else {
|
|
1144
|
-
/**
|
|
1145
|
-
* Else, the only way to resolve this is by measuring the element.
|
|
1146
|
-
*/
|
|
1147
|
-
this.needsMeasurement = true;
|
|
1148
|
-
}
|
|
1149
|
-
}
|
|
1150
|
-
resolveNoneKeyframes() {
|
|
1151
|
-
const { unresolvedKeyframes, name } = this;
|
|
1152
|
-
const noneKeyframeIndexes = [];
|
|
1153
|
-
for (let i = 0; i < unresolvedKeyframes.length; i++) {
|
|
1154
|
-
if (isNone(unresolvedKeyframes[i])) {
|
|
1155
|
-
noneKeyframeIndexes.push(i);
|
|
1156
|
-
}
|
|
1157
|
-
}
|
|
1158
|
-
if (noneKeyframeIndexes.length) {
|
|
1159
|
-
makeNoneKeyframesAnimatable(unresolvedKeyframes, noneKeyframeIndexes, name);
|
|
1160
|
-
}
|
|
1161
|
-
}
|
|
1162
|
-
measureInitialState() {
|
|
1163
|
-
const { element, unresolvedKeyframes, name } = this;
|
|
1164
|
-
if (!element || !element.current)
|
|
1165
|
-
return;
|
|
1166
|
-
if (name === "height") {
|
|
1167
|
-
this.suspendedScrollY = window.pageYOffset;
|
|
1168
|
-
}
|
|
1169
|
-
this.measuredOrigin = positionalValues[name](element.measureViewportBox(), window.getComputedStyle(element.current));
|
|
1170
|
-
unresolvedKeyframes[0] = this.measuredOrigin;
|
|
1171
|
-
// Set final key frame to measure after next render
|
|
1172
|
-
const measureKeyframe = unresolvedKeyframes[unresolvedKeyframes.length - 1];
|
|
1173
|
-
if (measureKeyframe !== undefined) {
|
|
1174
|
-
element.getValue(name, measureKeyframe).jump(measureKeyframe, false);
|
|
1175
|
-
}
|
|
1176
|
-
}
|
|
1177
|
-
measureEndState() {
|
|
1178
|
-
const { element, name, unresolvedKeyframes } = this;
|
|
1179
|
-
if (!element || !element.current)
|
|
1180
|
-
return;
|
|
1181
|
-
const value = element.getValue(name);
|
|
1182
|
-
value && value.jump(this.measuredOrigin, false);
|
|
1183
|
-
const finalKeyframeIndex = unresolvedKeyframes.length - 1;
|
|
1184
|
-
const finalKeyframe = unresolvedKeyframes[finalKeyframeIndex];
|
|
1185
|
-
unresolvedKeyframes[finalKeyframeIndex] = positionalValues[name](element.measureViewportBox(), window.getComputedStyle(element.current));
|
|
1186
|
-
if (finalKeyframe !== null && this.finalKeyframe === undefined) {
|
|
1187
|
-
this.finalKeyframe = finalKeyframe;
|
|
1188
|
-
}
|
|
1189
|
-
// If we removed transform values, reapply them before the next render
|
|
1190
|
-
if (this.removedTransforms?.length) {
|
|
1191
|
-
this.removedTransforms.forEach(([unsetTransformName, unsetTransformValue]) => {
|
|
1192
|
-
element
|
|
1193
|
-
.getValue(unsetTransformName)
|
|
1194
|
-
.set(unsetTransformValue);
|
|
1195
|
-
});
|
|
1196
|
-
}
|
|
1197
|
-
this.resolveNoneKeyframes();
|
|
1198
|
-
}
|
|
1199
|
-
}
|
|
1200
|
-
|
|
1201
|
-
/**
|
|
1202
|
-
* Check if a value is animatable. Examples:
|
|
1203
|
-
*
|
|
1204
|
-
* ✅: 100, "100px", "#fff"
|
|
1205
|
-
* ❌: "block", "url(2.jpg)"
|
|
1206
|
-
* @param value
|
|
1207
|
-
*
|
|
1208
|
-
* @internal
|
|
1209
|
-
*/
|
|
1210
|
-
const isAnimatable = (value, name) => {
|
|
1211
|
-
// If the list of keys tat might be non-animatable grows, replace with Set
|
|
1212
|
-
if (name === "zIndex")
|
|
1213
|
-
return false;
|
|
1214
|
-
// If it's a number or a keyframes array, we can animate it. We might at some point
|
|
1215
|
-
// need to do a deep isAnimatable check of keyframes, or let Popmotion handle this,
|
|
1216
|
-
// but for now lets leave it like this for performance reasons
|
|
1217
|
-
if (typeof value === "number" || Array.isArray(value))
|
|
1218
|
-
return true;
|
|
1219
|
-
if (typeof value === "string" && // It's animatable if we have a string
|
|
1220
|
-
(complex.test(value) || value === "0") && // And it contains numbers and/or colors
|
|
1221
|
-
!value.startsWith("url(") // Unless it starts with "url("
|
|
1222
|
-
) {
|
|
1223
|
-
return true;
|
|
1224
|
-
}
|
|
1225
|
-
return false;
|
|
1226
|
-
};
|
|
1227
|
-
|
|
1228
|
-
function hasKeyframesChanged(keyframes) {
|
|
1229
|
-
const current = keyframes[0];
|
|
1230
|
-
if (keyframes.length === 1)
|
|
1231
|
-
return true;
|
|
1232
|
-
for (let i = 0; i < keyframes.length; i++) {
|
|
1233
|
-
if (keyframes[i] !== current)
|
|
1234
|
-
return true;
|
|
1235
|
-
}
|
|
1236
|
-
}
|
|
1237
|
-
function canAnimate(keyframes, name, type, velocity) {
|
|
1238
|
-
/**
|
|
1239
|
-
* Check if we're able to animate between the start and end keyframes,
|
|
1240
|
-
* and throw a warning if we're attempting to animate between one that's
|
|
1241
|
-
* animatable and another that isn't.
|
|
1242
|
-
*/
|
|
1243
|
-
const originKeyframe = keyframes[0];
|
|
1244
|
-
if (originKeyframe === null)
|
|
1245
|
-
return false;
|
|
1246
|
-
/**
|
|
1247
|
-
* These aren't traditionally animatable but we do support them.
|
|
1248
|
-
* In future we could look into making this more generic or replacing
|
|
1249
|
-
* this function with mix() === mixImmediate
|
|
1250
|
-
*/
|
|
1251
|
-
if (name === "display" || name === "visibility")
|
|
1252
|
-
return true;
|
|
1253
|
-
const targetKeyframe = keyframes[keyframes.length - 1];
|
|
1254
|
-
const isOriginAnimatable = isAnimatable(originKeyframe, name);
|
|
1255
|
-
const isTargetAnimatable = isAnimatable(targetKeyframe, name);
|
|
1256
|
-
motionUtils.warning(isOriginAnimatable === isTargetAnimatable, `You are trying to animate ${name} from "${originKeyframe}" to "${targetKeyframe}". ${originKeyframe} is not an animatable value - to enable this animation set ${originKeyframe} to a value animatable to ${targetKeyframe} via the \`style\` property.`);
|
|
1257
|
-
// Always skip if any of these are true
|
|
1258
|
-
if (!isOriginAnimatable || !isTargetAnimatable) {
|
|
1259
|
-
return false;
|
|
1260
|
-
}
|
|
1261
|
-
return (hasKeyframesChanged(keyframes) ||
|
|
1262
|
-
((type === "spring" || motionDom.isGenerator(type)) && velocity));
|
|
1263
|
-
}
|
|
1264
|
-
|
|
1265
|
-
const isNotNull = (value) => value !== null;
|
|
1266
|
-
function getFinalKeyframe(keyframes, { repeat, repeatType = "loop" }, finalKeyframe) {
|
|
1267
|
-
const resolvedKeyframes = keyframes.filter(isNotNull);
|
|
1268
|
-
const index = repeat && repeatType !== "loop" && repeat % 2 === 1
|
|
1269
|
-
? 0
|
|
1270
|
-
: resolvedKeyframes.length - 1;
|
|
1271
|
-
return !index || finalKeyframe === undefined
|
|
1272
|
-
? resolvedKeyframes[index]
|
|
1273
|
-
: finalKeyframe;
|
|
1274
|
-
}
|
|
1275
|
-
|
|
1276
|
-
/**
|
|
1277
|
-
* Maximum time allowed between an animation being created and it being
|
|
1278
|
-
* resolved for us to use the latter as the start time.
|
|
1279
|
-
*
|
|
1280
|
-
* This is to ensure that while we prefer to "start" an animation as soon
|
|
1281
|
-
* as it's triggered, we also want to avoid a visual jump if there's a big delay
|
|
1282
|
-
* between these two moments.
|
|
1283
|
-
*/
|
|
1284
|
-
const MAX_RESOLVE_DELAY = 40;
|
|
1285
|
-
class BaseAnimation {
|
|
1286
|
-
constructor({ autoplay = true, delay = 0, type = "keyframes", repeat = 0, repeatDelay = 0, repeatType = "loop", ...options }) {
|
|
1287
|
-
// Track whether the animation has been stopped. Stopped animations won't restart.
|
|
1288
|
-
this.isStopped = false;
|
|
1289
|
-
this.hasAttemptedResolve = false;
|
|
1290
|
-
this.createdAt = motionDom.time.now();
|
|
1291
|
-
this.options = {
|
|
1292
|
-
autoplay,
|
|
1293
|
-
delay,
|
|
1294
|
-
type,
|
|
1295
|
-
repeat,
|
|
1296
|
-
repeatDelay,
|
|
1297
|
-
repeatType,
|
|
1298
|
-
...options,
|
|
1299
|
-
};
|
|
1300
|
-
this.updateFinishedPromise();
|
|
1301
|
-
}
|
|
1302
|
-
/**
|
|
1303
|
-
* This method uses the createdAt and resolvedAt to calculate the
|
|
1304
|
-
* animation startTime. *Ideally*, we would use the createdAt time as t=0
|
|
1305
|
-
* as the following frame would then be the first frame of the animation in
|
|
1306
|
-
* progress, which would feel snappier.
|
|
1307
|
-
*
|
|
1308
|
-
* However, if there's a delay (main thread work) between the creation of
|
|
1309
|
-
* the animation and the first commited frame, we prefer to use resolvedAt
|
|
1310
|
-
* to avoid a sudden jump into the animation.
|
|
1311
|
-
*/
|
|
1312
|
-
calcStartTime() {
|
|
1313
|
-
if (!this.resolvedAt)
|
|
1314
|
-
return this.createdAt;
|
|
1315
|
-
return this.resolvedAt - this.createdAt > MAX_RESOLVE_DELAY
|
|
1316
|
-
? this.resolvedAt
|
|
1317
|
-
: this.createdAt;
|
|
1318
|
-
}
|
|
1319
|
-
/**
|
|
1320
|
-
* A getter for resolved data. If keyframes are not yet resolved, accessing
|
|
1321
|
-
* this.resolved will synchronously flush all pending keyframe resolvers.
|
|
1322
|
-
* This is a deoptimisation, but at its worst still batches read/writes.
|
|
1323
|
-
*/
|
|
1324
|
-
get resolved() {
|
|
1325
|
-
if (!this._resolved && !this.hasAttemptedResolve) {
|
|
1326
|
-
flushKeyframeResolvers();
|
|
1327
|
-
}
|
|
1328
|
-
return this._resolved;
|
|
1329
|
-
}
|
|
1330
|
-
/**
|
|
1331
|
-
* A method to be called when the keyframes resolver completes. This method
|
|
1332
|
-
* will check if its possible to run the animation and, if not, skip it.
|
|
1333
|
-
* Otherwise, it will call initPlayback on the implementing class.
|
|
1334
|
-
*/
|
|
1335
|
-
onKeyframesResolved(keyframes, finalKeyframe) {
|
|
1336
|
-
this.resolvedAt = motionDom.time.now();
|
|
1337
|
-
this.hasAttemptedResolve = true;
|
|
1338
|
-
const { name, type, velocity, delay, onComplete, onUpdate, isGenerator, } = this.options;
|
|
1339
|
-
/**
|
|
1340
|
-
* If we can't animate this value with the resolved keyframes
|
|
1341
|
-
* then we should complete it immediately.
|
|
1342
|
-
*/
|
|
1343
|
-
if (!isGenerator && !canAnimate(keyframes, name, type, velocity)) {
|
|
1344
|
-
// Finish immediately
|
|
1345
|
-
if (instantAnimationState.current || !delay) {
|
|
1346
|
-
onUpdate &&
|
|
1347
|
-
onUpdate(getFinalKeyframe(keyframes, this.options, finalKeyframe));
|
|
1348
|
-
onComplete && onComplete();
|
|
1349
|
-
this.resolveFinishedPromise();
|
|
1350
|
-
return;
|
|
1351
|
-
}
|
|
1352
|
-
// Finish after a delay
|
|
1353
|
-
else {
|
|
1354
|
-
this.options.duration = 0;
|
|
1355
|
-
}
|
|
1356
|
-
}
|
|
1357
|
-
const resolvedAnimation = this.initPlayback(keyframes, finalKeyframe);
|
|
1358
|
-
if (resolvedAnimation === false)
|
|
1359
|
-
return;
|
|
1360
|
-
this._resolved = {
|
|
1361
|
-
keyframes,
|
|
1362
|
-
finalKeyframe,
|
|
1363
|
-
...resolvedAnimation,
|
|
1364
|
-
};
|
|
1365
|
-
this.onPostResolved();
|
|
1366
|
-
}
|
|
1367
|
-
onPostResolved() { }
|
|
1368
|
-
/**
|
|
1369
|
-
* Allows the returned animation to be awaited or promise-chained. Currently
|
|
1370
|
-
* resolves when the animation finishes at all but in a future update could/should
|
|
1371
|
-
* reject if its cancels.
|
|
1372
|
-
*/
|
|
1373
|
-
then(resolve, reject) {
|
|
1374
|
-
return this.currentFinishedPromise.then(resolve, reject);
|
|
1375
|
-
}
|
|
1376
|
-
flatten() {
|
|
1377
|
-
if (!this.options.allowFlatten)
|
|
1378
|
-
return;
|
|
1379
|
-
this.options.type = "keyframes";
|
|
1380
|
-
this.options.ease = "linear";
|
|
1381
|
-
}
|
|
1382
|
-
updateFinishedPromise() {
|
|
1383
|
-
this.currentFinishedPromise = new Promise((resolve) => {
|
|
1384
|
-
this.resolveFinishedPromise = resolve;
|
|
1385
|
-
});
|
|
1386
|
-
}
|
|
1387
|
-
}
|
|
1388
|
-
|
|
1389
|
-
// Adapted from https://gist.github.com/mjackson/5311256
|
|
1390
|
-
function hueToRgb(p, q, t) {
|
|
1391
|
-
if (t < 0)
|
|
1392
|
-
t += 1;
|
|
1393
|
-
if (t > 1)
|
|
1394
|
-
t -= 1;
|
|
1395
|
-
if (t < 1 / 6)
|
|
1396
|
-
return p + (q - p) * 6 * t;
|
|
1397
|
-
if (t < 1 / 2)
|
|
1398
|
-
return q;
|
|
1399
|
-
if (t < 2 / 3)
|
|
1400
|
-
return p + (q - p) * (2 / 3 - t) * 6;
|
|
1401
|
-
return p;
|
|
1402
|
-
}
|
|
1403
|
-
function hslaToRgba({ hue, saturation, lightness, alpha }) {
|
|
1404
|
-
hue /= 360;
|
|
1405
|
-
saturation /= 100;
|
|
1406
|
-
lightness /= 100;
|
|
1407
|
-
let red = 0;
|
|
1408
|
-
let green = 0;
|
|
1409
|
-
let blue = 0;
|
|
1410
|
-
if (!saturation) {
|
|
1411
|
-
red = green = blue = lightness;
|
|
1412
|
-
}
|
|
1413
|
-
else {
|
|
1414
|
-
const q = lightness < 0.5
|
|
1415
|
-
? lightness * (1 + saturation)
|
|
1416
|
-
: lightness + saturation - lightness * saturation;
|
|
1417
|
-
const p = 2 * lightness - q;
|
|
1418
|
-
red = hueToRgb(p, q, hue + 1 / 3);
|
|
1419
|
-
green = hueToRgb(p, q, hue);
|
|
1420
|
-
blue = hueToRgb(p, q, hue - 1 / 3);
|
|
1421
|
-
}
|
|
1422
|
-
return {
|
|
1423
|
-
red: Math.round(red * 255),
|
|
1424
|
-
green: Math.round(green * 255),
|
|
1425
|
-
blue: Math.round(blue * 255),
|
|
1426
|
-
alpha,
|
|
1427
|
-
};
|
|
1428
|
-
}
|
|
1429
|
-
|
|
1430
|
-
function mixImmediate(a, b) {
|
|
1431
|
-
return (p) => (p > 0 ? b : a);
|
|
1432
|
-
}
|
|
1433
|
-
|
|
1434
|
-
// Linear color space blending
|
|
1435
|
-
// Explained https://www.youtube.com/watch?v=LKnqECcg6Gw
|
|
1436
|
-
// Demonstrated http://codepen.io/osublake/pen/xGVVaN
|
|
1437
|
-
const mixLinearColor = (from, to, v) => {
|
|
1438
|
-
const fromExpo = from * from;
|
|
1439
|
-
const expo = v * (to * to - fromExpo) + fromExpo;
|
|
1440
|
-
return expo < 0 ? 0 : Math.sqrt(expo);
|
|
1441
|
-
};
|
|
1442
|
-
const colorTypes = [hex, rgba, hsla];
|
|
1443
|
-
const getColorType = (v) => colorTypes.find((type) => type.test(v));
|
|
1444
|
-
function asRGBA(color) {
|
|
1445
|
-
const type = getColorType(color);
|
|
1446
|
-
motionUtils.warning(Boolean(type), `'${color}' is not an animatable color. Use the equivalent color code instead.`);
|
|
1447
|
-
if (!Boolean(type))
|
|
1448
|
-
return false;
|
|
1449
|
-
let model = type.parse(color);
|
|
1450
|
-
if (type === hsla) {
|
|
1451
|
-
// TODO Remove this cast - needed since Motion's stricter typing
|
|
1452
|
-
model = hslaToRgba(model);
|
|
1453
|
-
}
|
|
1454
|
-
return model;
|
|
1455
|
-
}
|
|
1456
|
-
const mixColor = (from, to) => {
|
|
1457
|
-
const fromRGBA = asRGBA(from);
|
|
1458
|
-
const toRGBA = asRGBA(to);
|
|
1459
|
-
if (!fromRGBA || !toRGBA) {
|
|
1460
|
-
return mixImmediate(from, to);
|
|
1461
|
-
}
|
|
1462
|
-
const blended = { ...fromRGBA };
|
|
1463
|
-
return (v) => {
|
|
1464
|
-
blended.red = mixLinearColor(fromRGBA.red, toRGBA.red, v);
|
|
1465
|
-
blended.green = mixLinearColor(fromRGBA.green, toRGBA.green, v);
|
|
1466
|
-
blended.blue = mixLinearColor(fromRGBA.blue, toRGBA.blue, v);
|
|
1467
|
-
blended.alpha = mixNumber$1(fromRGBA.alpha, toRGBA.alpha, v);
|
|
1468
|
-
return rgba.transform(blended);
|
|
1469
|
-
};
|
|
1470
|
-
};
|
|
1471
|
-
|
|
1472
|
-
/**
|
|
1473
|
-
* Pipe
|
|
1474
|
-
* Compose other transformers to run linearily
|
|
1475
|
-
* pipe(min(20), max(40))
|
|
1476
|
-
* @param {...functions} transformers
|
|
1477
|
-
* @return {function}
|
|
1478
|
-
*/
|
|
1479
|
-
const combineFunctions = (a, b) => (v) => b(a(v));
|
|
1480
|
-
const pipe = (...transformers) => transformers.reduce(combineFunctions);
|
|
1481
|
-
|
|
1482
|
-
const invisibleValues = new Set(["none", "hidden"]);
|
|
1483
|
-
/**
|
|
1484
|
-
* Returns a function that, when provided a progress value between 0 and 1,
|
|
1485
|
-
* will return the "none" or "hidden" string only when the progress is that of
|
|
1486
|
-
* the origin or target.
|
|
1487
|
-
*/
|
|
1488
|
-
function mixVisibility(origin, target) {
|
|
1489
|
-
if (invisibleValues.has(origin)) {
|
|
1490
|
-
return (p) => (p <= 0 ? origin : target);
|
|
1491
|
-
}
|
|
1492
|
-
else {
|
|
1493
|
-
return (p) => (p >= 1 ? target : origin);
|
|
1494
|
-
}
|
|
1495
|
-
}
|
|
1496
|
-
|
|
1497
|
-
function mixNumber(a, b) {
|
|
1498
|
-
return (p) => mixNumber$1(a, b, p);
|
|
1499
|
-
}
|
|
1500
|
-
function getMixer(a) {
|
|
1501
|
-
if (typeof a === "number") {
|
|
1502
|
-
return mixNumber;
|
|
1503
|
-
}
|
|
1504
|
-
else if (typeof a === "string") {
|
|
1505
|
-
return isCSSVariableToken(a)
|
|
1506
|
-
? mixImmediate
|
|
1507
|
-
: color.test(a)
|
|
1508
|
-
? mixColor
|
|
1509
|
-
: mixComplex;
|
|
1510
|
-
}
|
|
1511
|
-
else if (Array.isArray(a)) {
|
|
1512
|
-
return mixArray;
|
|
1513
|
-
}
|
|
1514
|
-
else if (typeof a === "object") {
|
|
1515
|
-
return color.test(a) ? mixColor : mixObject;
|
|
1516
|
-
}
|
|
1517
|
-
return mixImmediate;
|
|
1518
|
-
}
|
|
1519
|
-
function mixArray(a, b) {
|
|
1520
|
-
const output = [...a];
|
|
1521
|
-
const numValues = output.length;
|
|
1522
|
-
const blendValue = a.map((v, i) => getMixer(v)(v, b[i]));
|
|
1523
|
-
return (p) => {
|
|
1524
|
-
for (let i = 0; i < numValues; i++) {
|
|
1525
|
-
output[i] = blendValue[i](p);
|
|
1526
|
-
}
|
|
1527
|
-
return output;
|
|
1528
|
-
};
|
|
1529
|
-
}
|
|
1530
|
-
function mixObject(a, b) {
|
|
1531
|
-
const output = { ...a, ...b };
|
|
1532
|
-
const blendValue = {};
|
|
1533
|
-
for (const key in output) {
|
|
1534
|
-
if (a[key] !== undefined && b[key] !== undefined) {
|
|
1535
|
-
blendValue[key] = getMixer(a[key])(a[key], b[key]);
|
|
1536
|
-
}
|
|
1537
|
-
}
|
|
1538
|
-
return (v) => {
|
|
1539
|
-
for (const key in blendValue) {
|
|
1540
|
-
output[key] = blendValue[key](v);
|
|
1541
|
-
}
|
|
1542
|
-
return output;
|
|
1543
|
-
};
|
|
1544
|
-
}
|
|
1545
|
-
function matchOrder(origin, target) {
|
|
1546
|
-
const orderedOrigin = [];
|
|
1547
|
-
const pointers = { color: 0, var: 0, number: 0 };
|
|
1548
|
-
for (let i = 0; i < target.values.length; i++) {
|
|
1549
|
-
const type = target.types[i];
|
|
1550
|
-
const originIndex = origin.indexes[type][pointers[type]];
|
|
1551
|
-
const originValue = origin.values[originIndex] ?? 0;
|
|
1552
|
-
orderedOrigin[i] = originValue;
|
|
1553
|
-
pointers[type]++;
|
|
1554
|
-
}
|
|
1555
|
-
return orderedOrigin;
|
|
1556
|
-
}
|
|
1557
|
-
const mixComplex = (origin, target) => {
|
|
1558
|
-
const template = complex.createTransformer(target);
|
|
1559
|
-
const originStats = analyseComplexValue(origin);
|
|
1560
|
-
const targetStats = analyseComplexValue(target);
|
|
1561
|
-
const canInterpolate = originStats.indexes.var.length === targetStats.indexes.var.length &&
|
|
1562
|
-
originStats.indexes.color.length === targetStats.indexes.color.length &&
|
|
1563
|
-
originStats.indexes.number.length >= targetStats.indexes.number.length;
|
|
1564
|
-
if (canInterpolate) {
|
|
1565
|
-
if ((invisibleValues.has(origin) &&
|
|
1566
|
-
!targetStats.values.length) ||
|
|
1567
|
-
(invisibleValues.has(target) &&
|
|
1568
|
-
!originStats.values.length)) {
|
|
1569
|
-
return mixVisibility(origin, target);
|
|
1570
|
-
}
|
|
1571
|
-
return pipe(mixArray(matchOrder(originStats, targetStats), targetStats.values), template);
|
|
1572
|
-
}
|
|
1573
|
-
else {
|
|
1574
|
-
motionUtils.warning(true, `Complex values '${origin}' and '${target}' too different to mix. Ensure all colors are of the same type, and that each contains the same quantity of number and color values. Falling back to instant transition.`);
|
|
1575
|
-
return mixImmediate(origin, target);
|
|
1576
|
-
}
|
|
1577
|
-
};
|
|
1578
|
-
|
|
1579
|
-
function mix(from, to, p) {
|
|
1580
|
-
if (typeof from === "number" &&
|
|
1581
|
-
typeof to === "number" &&
|
|
1582
|
-
typeof p === "number") {
|
|
1583
|
-
return mixNumber$1(from, to, p);
|
|
1584
|
-
}
|
|
1585
|
-
const mixer = getMixer(from);
|
|
1586
|
-
return mixer(from, to);
|
|
1587
|
-
}
|
|
1588
|
-
|
|
1589
|
-
const velocitySampleDuration = 5; // ms
|
|
1590
|
-
function calcGeneratorVelocity(resolveValue, t, current) {
|
|
1591
|
-
const prevT = Math.max(t - velocitySampleDuration, 0);
|
|
1592
|
-
return motionUtils.velocityPerSecond(current - resolveValue(prevT), t - prevT);
|
|
1593
|
-
}
|
|
1594
|
-
|
|
1595
|
-
const springDefaults = {
|
|
1596
|
-
// Default spring physics
|
|
1597
|
-
stiffness: 100,
|
|
1598
|
-
damping: 10,
|
|
1599
|
-
mass: 1.0,
|
|
1600
|
-
velocity: 0.0,
|
|
1601
|
-
// Default duration/bounce-based options
|
|
1602
|
-
duration: 800, // in ms
|
|
1603
|
-
bounce: 0.3,
|
|
1604
|
-
visualDuration: 0.3, // in seconds
|
|
1605
|
-
// Rest thresholds
|
|
1606
|
-
restSpeed: {
|
|
1607
|
-
granular: 0.01,
|
|
1608
|
-
default: 2,
|
|
1609
|
-
},
|
|
1610
|
-
restDelta: {
|
|
1611
|
-
granular: 0.005,
|
|
1612
|
-
default: 0.5,
|
|
1613
|
-
},
|
|
1614
|
-
// Limits
|
|
1615
|
-
minDuration: 0.01, // in seconds
|
|
1616
|
-
maxDuration: 10.0, // in seconds
|
|
1617
|
-
minDamping: 0.05,
|
|
1618
|
-
maxDamping: 1,
|
|
1619
|
-
};
|
|
1620
|
-
|
|
1621
|
-
const safeMin = 0.001;
|
|
1622
|
-
function findSpring({ duration = springDefaults.duration, bounce = springDefaults.bounce, velocity = springDefaults.velocity, mass = springDefaults.mass, }) {
|
|
1623
|
-
let envelope;
|
|
1624
|
-
let derivative;
|
|
1625
|
-
motionUtils.warning(duration <= motionUtils.secondsToMilliseconds(springDefaults.maxDuration), "Spring duration must be 10 seconds or less");
|
|
1626
|
-
let dampingRatio = 1 - bounce;
|
|
1627
|
-
/**
|
|
1628
|
-
* Restrict dampingRatio and duration to within acceptable ranges.
|
|
1629
|
-
*/
|
|
1630
|
-
dampingRatio = clamp(springDefaults.minDamping, springDefaults.maxDamping, dampingRatio);
|
|
1631
|
-
duration = clamp(springDefaults.minDuration, springDefaults.maxDuration, motionUtils.millisecondsToSeconds(duration));
|
|
1632
|
-
if (dampingRatio < 1) {
|
|
1633
|
-
/**
|
|
1634
|
-
* Underdamped spring
|
|
1635
|
-
*/
|
|
1636
|
-
envelope = (undampedFreq) => {
|
|
1637
|
-
const exponentialDecay = undampedFreq * dampingRatio;
|
|
1638
|
-
const delta = exponentialDecay * duration;
|
|
1639
|
-
const a = exponentialDecay - velocity;
|
|
1640
|
-
const b = calcAngularFreq(undampedFreq, dampingRatio);
|
|
1641
|
-
const c = Math.exp(-delta);
|
|
1642
|
-
return safeMin - (a / b) * c;
|
|
1643
|
-
};
|
|
1644
|
-
derivative = (undampedFreq) => {
|
|
1645
|
-
const exponentialDecay = undampedFreq * dampingRatio;
|
|
1646
|
-
const delta = exponentialDecay * duration;
|
|
1647
|
-
const d = delta * velocity + velocity;
|
|
1648
|
-
const e = Math.pow(dampingRatio, 2) * Math.pow(undampedFreq, 2) * duration;
|
|
1649
|
-
const f = Math.exp(-delta);
|
|
1650
|
-
const g = calcAngularFreq(Math.pow(undampedFreq, 2), dampingRatio);
|
|
1651
|
-
const factor = -envelope(undampedFreq) + safeMin > 0 ? -1 : 1;
|
|
1652
|
-
return (factor * ((d - e) * f)) / g;
|
|
1653
|
-
};
|
|
1654
|
-
}
|
|
1655
|
-
else {
|
|
1656
|
-
/**
|
|
1657
|
-
* Critically-damped spring
|
|
1658
|
-
*/
|
|
1659
|
-
envelope = (undampedFreq) => {
|
|
1660
|
-
const a = Math.exp(-undampedFreq * duration);
|
|
1661
|
-
const b = (undampedFreq - velocity) * duration + 1;
|
|
1662
|
-
return -safeMin + a * b;
|
|
1663
|
-
};
|
|
1664
|
-
derivative = (undampedFreq) => {
|
|
1665
|
-
const a = Math.exp(-undampedFreq * duration);
|
|
1666
|
-
const b = (velocity - undampedFreq) * (duration * duration);
|
|
1667
|
-
return a * b;
|
|
1668
|
-
};
|
|
1669
|
-
}
|
|
1670
|
-
const initialGuess = 5 / duration;
|
|
1671
|
-
const undampedFreq = approximateRoot(envelope, derivative, initialGuess);
|
|
1672
|
-
duration = motionUtils.secondsToMilliseconds(duration);
|
|
1673
|
-
if (isNaN(undampedFreq)) {
|
|
1674
|
-
return {
|
|
1675
|
-
stiffness: springDefaults.stiffness,
|
|
1676
|
-
damping: springDefaults.damping,
|
|
1677
|
-
duration,
|
|
1678
|
-
};
|
|
1679
|
-
}
|
|
1680
|
-
else {
|
|
1681
|
-
const stiffness = Math.pow(undampedFreq, 2) * mass;
|
|
1682
|
-
return {
|
|
1683
|
-
stiffness,
|
|
1684
|
-
damping: dampingRatio * 2 * Math.sqrt(mass * stiffness),
|
|
1685
|
-
duration,
|
|
1686
|
-
};
|
|
1687
|
-
}
|
|
1688
|
-
}
|
|
1689
|
-
const rootIterations = 12;
|
|
1690
|
-
function approximateRoot(envelope, derivative, initialGuess) {
|
|
1691
|
-
let result = initialGuess;
|
|
1692
|
-
for (let i = 1; i < rootIterations; i++) {
|
|
1693
|
-
result = result - envelope(result) / derivative(result);
|
|
1694
|
-
}
|
|
1695
|
-
return result;
|
|
1696
|
-
}
|
|
1697
|
-
function calcAngularFreq(undampedFreq, dampingRatio) {
|
|
1698
|
-
return undampedFreq * Math.sqrt(1 - dampingRatio * dampingRatio);
|
|
1699
|
-
}
|
|
1700
|
-
|
|
1701
|
-
const durationKeys = ["duration", "bounce"];
|
|
1702
|
-
const physicsKeys = ["stiffness", "damping", "mass"];
|
|
1703
|
-
function isSpringType(options, keys) {
|
|
1704
|
-
return keys.some((key) => options[key] !== undefined);
|
|
1705
|
-
}
|
|
1706
|
-
function getSpringOptions(options) {
|
|
1707
|
-
let springOptions = {
|
|
1708
|
-
velocity: springDefaults.velocity,
|
|
1709
|
-
stiffness: springDefaults.stiffness,
|
|
1710
|
-
damping: springDefaults.damping,
|
|
1711
|
-
mass: springDefaults.mass,
|
|
1712
|
-
isResolvedFromDuration: false,
|
|
1713
|
-
...options,
|
|
1714
|
-
};
|
|
1715
|
-
// stiffness/damping/mass overrides duration/bounce
|
|
1716
|
-
if (!isSpringType(options, physicsKeys) &&
|
|
1717
|
-
isSpringType(options, durationKeys)) {
|
|
1718
|
-
if (options.visualDuration) {
|
|
1719
|
-
const visualDuration = options.visualDuration;
|
|
1720
|
-
const root = (2 * Math.PI) / (visualDuration * 1.2);
|
|
1721
|
-
const stiffness = root * root;
|
|
1722
|
-
const damping = 2 *
|
|
1723
|
-
clamp(0.05, 1, 1 - (options.bounce || 0)) *
|
|
1724
|
-
Math.sqrt(stiffness);
|
|
1725
|
-
springOptions = {
|
|
1726
|
-
...springOptions,
|
|
1727
|
-
mass: springDefaults.mass,
|
|
1728
|
-
stiffness,
|
|
1729
|
-
damping,
|
|
1730
|
-
};
|
|
1731
|
-
}
|
|
1732
|
-
else {
|
|
1733
|
-
const derived = findSpring(options);
|
|
1734
|
-
springOptions = {
|
|
1735
|
-
...springOptions,
|
|
1736
|
-
...derived,
|
|
1737
|
-
mass: springDefaults.mass,
|
|
1738
|
-
};
|
|
1739
|
-
springOptions.isResolvedFromDuration = true;
|
|
1740
|
-
}
|
|
1741
|
-
}
|
|
1742
|
-
return springOptions;
|
|
1743
|
-
}
|
|
1744
|
-
function spring(optionsOrVisualDuration = springDefaults.visualDuration, bounce = springDefaults.bounce) {
|
|
1745
|
-
const options = typeof optionsOrVisualDuration !== "object"
|
|
1746
|
-
? {
|
|
1747
|
-
visualDuration: optionsOrVisualDuration,
|
|
1748
|
-
keyframes: [0, 1],
|
|
1749
|
-
bounce,
|
|
1750
|
-
}
|
|
1751
|
-
: optionsOrVisualDuration;
|
|
1752
|
-
let { restSpeed, restDelta } = options;
|
|
1753
|
-
const origin = options.keyframes[0];
|
|
1754
|
-
const target = options.keyframes[options.keyframes.length - 1];
|
|
1755
|
-
/**
|
|
1756
|
-
* This is the Iterator-spec return value. We ensure it's mutable rather than using a generator
|
|
1757
|
-
* to reduce GC during animation.
|
|
1758
|
-
*/
|
|
1759
|
-
const state = { done: false, value: origin };
|
|
1760
|
-
const { stiffness, damping, mass, duration, velocity, isResolvedFromDuration, } = getSpringOptions({
|
|
1761
|
-
...options,
|
|
1762
|
-
velocity: -motionUtils.millisecondsToSeconds(options.velocity || 0),
|
|
1763
|
-
});
|
|
1764
|
-
const initialVelocity = velocity || 0.0;
|
|
1765
|
-
const dampingRatio = damping / (2 * Math.sqrt(stiffness * mass));
|
|
1766
|
-
const initialDelta = target - origin;
|
|
1767
|
-
const undampedAngularFreq = motionUtils.millisecondsToSeconds(Math.sqrt(stiffness / mass));
|
|
1768
|
-
/**
|
|
1769
|
-
* If we're working on a granular scale, use smaller defaults for determining
|
|
1770
|
-
* when the spring is finished.
|
|
1771
|
-
*
|
|
1772
|
-
* These defaults have been selected emprically based on what strikes a good
|
|
1773
|
-
* ratio between feeling good and finishing as soon as changes are imperceptible.
|
|
1774
|
-
*/
|
|
1775
|
-
const isGranularScale = Math.abs(initialDelta) < 5;
|
|
1776
|
-
restSpeed || (restSpeed = isGranularScale
|
|
1777
|
-
? springDefaults.restSpeed.granular
|
|
1778
|
-
: springDefaults.restSpeed.default);
|
|
1779
|
-
restDelta || (restDelta = isGranularScale
|
|
1780
|
-
? springDefaults.restDelta.granular
|
|
1781
|
-
: springDefaults.restDelta.default);
|
|
1782
|
-
let resolveSpring;
|
|
1783
|
-
if (dampingRatio < 1) {
|
|
1784
|
-
const angularFreq = calcAngularFreq(undampedAngularFreq, dampingRatio);
|
|
1785
|
-
// Underdamped spring
|
|
1786
|
-
resolveSpring = (t) => {
|
|
1787
|
-
const envelope = Math.exp(-dampingRatio * undampedAngularFreq * t);
|
|
1788
|
-
return (target -
|
|
1789
|
-
envelope *
|
|
1790
|
-
(((initialVelocity +
|
|
1791
|
-
dampingRatio * undampedAngularFreq * initialDelta) /
|
|
1792
|
-
angularFreq) *
|
|
1793
|
-
Math.sin(angularFreq * t) +
|
|
1794
|
-
initialDelta * Math.cos(angularFreq * t)));
|
|
1795
|
-
};
|
|
1796
|
-
}
|
|
1797
|
-
else if (dampingRatio === 1) {
|
|
1798
|
-
// Critically damped spring
|
|
1799
|
-
resolveSpring = (t) => target -
|
|
1800
|
-
Math.exp(-undampedAngularFreq * t) *
|
|
1801
|
-
(initialDelta +
|
|
1802
|
-
(initialVelocity + undampedAngularFreq * initialDelta) * t);
|
|
1803
|
-
}
|
|
1804
|
-
else {
|
|
1805
|
-
// Overdamped spring
|
|
1806
|
-
const dampedAngularFreq = undampedAngularFreq * Math.sqrt(dampingRatio * dampingRatio - 1);
|
|
1807
|
-
resolveSpring = (t) => {
|
|
1808
|
-
const envelope = Math.exp(-dampingRatio * undampedAngularFreq * t);
|
|
1809
|
-
// When performing sinh or cosh values can hit Infinity so we cap them here
|
|
1810
|
-
const freqForT = Math.min(dampedAngularFreq * t, 300);
|
|
1811
|
-
return (target -
|
|
1812
|
-
(envelope *
|
|
1813
|
-
((initialVelocity +
|
|
1814
|
-
dampingRatio * undampedAngularFreq * initialDelta) *
|
|
1815
|
-
Math.sinh(freqForT) +
|
|
1816
|
-
dampedAngularFreq *
|
|
1817
|
-
initialDelta *
|
|
1818
|
-
Math.cosh(freqForT))) /
|
|
1819
|
-
dampedAngularFreq);
|
|
1820
|
-
};
|
|
1821
|
-
}
|
|
1822
|
-
const generator = {
|
|
1823
|
-
calculatedDuration: isResolvedFromDuration ? duration || null : null,
|
|
1824
|
-
next: (t) => {
|
|
1825
|
-
const current = resolveSpring(t);
|
|
1826
|
-
if (!isResolvedFromDuration) {
|
|
1827
|
-
let currentVelocity = 0.0;
|
|
1828
|
-
/**
|
|
1829
|
-
* We only need to calculate velocity for under-damped springs
|
|
1830
|
-
* as over- and critically-damped springs can't overshoot, so
|
|
1831
|
-
* checking only for displacement is enough.
|
|
1832
|
-
*/
|
|
1833
|
-
if (dampingRatio < 1) {
|
|
1834
|
-
currentVelocity =
|
|
1835
|
-
t === 0
|
|
1836
|
-
? motionUtils.secondsToMilliseconds(initialVelocity)
|
|
1837
|
-
: calcGeneratorVelocity(resolveSpring, t, current);
|
|
1838
|
-
}
|
|
1839
|
-
const isBelowVelocityThreshold = Math.abs(currentVelocity) <= restSpeed;
|
|
1840
|
-
const isBelowDisplacementThreshold = Math.abs(target - current) <= restDelta;
|
|
1841
|
-
state.done =
|
|
1842
|
-
isBelowVelocityThreshold && isBelowDisplacementThreshold;
|
|
1843
|
-
}
|
|
1844
|
-
else {
|
|
1845
|
-
state.done = t >= duration;
|
|
1846
|
-
}
|
|
1847
|
-
state.value = state.done ? target : current;
|
|
1848
|
-
return state;
|
|
1849
|
-
},
|
|
1850
|
-
toString: () => {
|
|
1851
|
-
const calculatedDuration = Math.min(motionDom.calcGeneratorDuration(generator), motionDom.maxGeneratorDuration);
|
|
1852
|
-
const easing = motionDom.generateLinearEasing((progress) => generator.next(calculatedDuration * progress).value, calculatedDuration, 30);
|
|
1853
|
-
return calculatedDuration + "ms " + easing;
|
|
1854
|
-
},
|
|
1855
|
-
toTransition: () => { },
|
|
1856
|
-
};
|
|
1857
|
-
return generator;
|
|
1858
|
-
}
|
|
1859
|
-
spring.applyToOptions = (options) => {
|
|
1860
|
-
const generatorOptions = motionDom.createGeneratorEasing(options, 100, spring);
|
|
1861
|
-
options.ease = motionDom.supportsLinearEasing() ? generatorOptions.ease : "easeOut";
|
|
1862
|
-
options.duration = motionUtils.secondsToMilliseconds(generatorOptions.duration);
|
|
1863
|
-
options.type = "keyframes";
|
|
1864
|
-
return options;
|
|
1865
|
-
};
|
|
1866
|
-
|
|
1867
|
-
function inertia({ keyframes, velocity = 0.0, power = 0.8, timeConstant = 325, bounceDamping = 10, bounceStiffness = 500, modifyTarget, min, max, restDelta = 0.5, restSpeed, }) {
|
|
1868
|
-
const origin = keyframes[0];
|
|
1869
|
-
const state = {
|
|
1870
|
-
done: false,
|
|
1871
|
-
value: origin,
|
|
1872
|
-
};
|
|
1873
|
-
const isOutOfBounds = (v) => (min !== undefined && v < min) || (max !== undefined && v > max);
|
|
1874
|
-
const nearestBoundary = (v) => {
|
|
1875
|
-
if (min === undefined)
|
|
1876
|
-
return max;
|
|
1877
|
-
if (max === undefined)
|
|
1878
|
-
return min;
|
|
1879
|
-
return Math.abs(min - v) < Math.abs(max - v) ? min : max;
|
|
1880
|
-
};
|
|
1881
|
-
let amplitude = power * velocity;
|
|
1882
|
-
const ideal = origin + amplitude;
|
|
1883
|
-
const target = modifyTarget === undefined ? ideal : modifyTarget(ideal);
|
|
1884
|
-
/**
|
|
1885
|
-
* If the target has changed we need to re-calculate the amplitude, otherwise
|
|
1886
|
-
* the animation will start from the wrong position.
|
|
1887
|
-
*/
|
|
1888
|
-
if (target !== ideal)
|
|
1889
|
-
amplitude = target - origin;
|
|
1890
|
-
const calcDelta = (t) => -amplitude * Math.exp(-t / timeConstant);
|
|
1891
|
-
const calcLatest = (t) => target + calcDelta(t);
|
|
1892
|
-
const applyFriction = (t) => {
|
|
1893
|
-
const delta = calcDelta(t);
|
|
1894
|
-
const latest = calcLatest(t);
|
|
1895
|
-
state.done = Math.abs(delta) <= restDelta;
|
|
1896
|
-
state.value = state.done ? target : latest;
|
|
1897
|
-
};
|
|
1898
|
-
/**
|
|
1899
|
-
* Ideally this would resolve for t in a stateless way, we could
|
|
1900
|
-
* do that by always precalculating the animation but as we know
|
|
1901
|
-
* this will be done anyway we can assume that spring will
|
|
1902
|
-
* be discovered during that.
|
|
1903
|
-
*/
|
|
1904
|
-
let timeReachedBoundary;
|
|
1905
|
-
let spring$1;
|
|
1906
|
-
const checkCatchBoundary = (t) => {
|
|
1907
|
-
if (!isOutOfBounds(state.value))
|
|
1908
|
-
return;
|
|
1909
|
-
timeReachedBoundary = t;
|
|
1910
|
-
spring$1 = spring({
|
|
1911
|
-
keyframes: [state.value, nearestBoundary(state.value)],
|
|
1912
|
-
velocity: calcGeneratorVelocity(calcLatest, t, state.value), // TODO: This should be passing * 1000
|
|
1913
|
-
damping: bounceDamping,
|
|
1914
|
-
stiffness: bounceStiffness,
|
|
1915
|
-
restDelta,
|
|
1916
|
-
restSpeed,
|
|
1917
|
-
});
|
|
1918
|
-
};
|
|
1919
|
-
checkCatchBoundary(0);
|
|
1920
|
-
return {
|
|
1921
|
-
calculatedDuration: null,
|
|
1922
|
-
next: (t) => {
|
|
1923
|
-
/**
|
|
1924
|
-
* We need to resolve the friction to figure out if we need a
|
|
1925
|
-
* spring but we don't want to do this twice per frame. So here
|
|
1926
|
-
* we flag if we updated for this frame and later if we did
|
|
1927
|
-
* we can skip doing it again.
|
|
1928
|
-
*/
|
|
1929
|
-
let hasUpdatedFrame = false;
|
|
1930
|
-
if (!spring$1 && timeReachedBoundary === undefined) {
|
|
1931
|
-
hasUpdatedFrame = true;
|
|
1932
|
-
applyFriction(t);
|
|
1933
|
-
checkCatchBoundary(t);
|
|
1934
|
-
}
|
|
1935
|
-
/**
|
|
1936
|
-
* If we have a spring and the provided t is beyond the moment the friction
|
|
1937
|
-
* animation crossed the min/max boundary, use the spring.
|
|
1938
|
-
*/
|
|
1939
|
-
if (timeReachedBoundary !== undefined && t >= timeReachedBoundary) {
|
|
1940
|
-
return spring$1.next(t - timeReachedBoundary);
|
|
1941
|
-
}
|
|
1942
|
-
else {
|
|
1943
|
-
!hasUpdatedFrame && applyFriction(t);
|
|
1944
|
-
return state;
|
|
1945
|
-
}
|
|
1946
|
-
},
|
|
1947
|
-
};
|
|
1948
|
-
}
|
|
1949
|
-
|
|
1950
|
-
const easeIn = /*@__PURE__*/ cubicBezier(0.42, 0, 1, 1);
|
|
1951
|
-
const easeOut = /*@__PURE__*/ cubicBezier(0, 0, 0.58, 1);
|
|
1952
|
-
const easeInOut = /*@__PURE__*/ cubicBezier(0.42, 0, 0.58, 1);
|
|
1953
|
-
|
|
1954
|
-
const isEasingArray = (ease) => {
|
|
1955
|
-
return Array.isArray(ease) && typeof ease[0] !== "number";
|
|
1956
|
-
};
|
|
1957
|
-
|
|
1958
|
-
const easingLookup = {
|
|
1959
|
-
linear: motionUtils.noop,
|
|
1960
|
-
easeIn,
|
|
1961
|
-
easeInOut,
|
|
1962
|
-
easeOut,
|
|
1963
|
-
circIn,
|
|
1964
|
-
circInOut,
|
|
1965
|
-
circOut,
|
|
1966
|
-
backIn,
|
|
1967
|
-
backInOut,
|
|
1968
|
-
backOut,
|
|
1969
|
-
anticipate,
|
|
1970
|
-
};
|
|
1971
|
-
const easingDefinitionToFunction = (definition) => {
|
|
1972
|
-
if (motionDom.isBezierDefinition(definition)) {
|
|
1973
|
-
// If cubic bezier definition, create bezier curve
|
|
1974
|
-
motionUtils.invariant(definition.length === 4, `Cubic bezier arrays must contain four numerical values.`);
|
|
1975
|
-
const [x1, y1, x2, y2] = definition;
|
|
1976
|
-
return cubicBezier(x1, y1, x2, y2);
|
|
1977
|
-
}
|
|
1978
|
-
else if (typeof definition === "string") {
|
|
1979
|
-
// Else lookup from table
|
|
1980
|
-
motionUtils.invariant(easingLookup[definition] !== undefined, `Invalid easing type '${definition}'`);
|
|
1981
|
-
return easingLookup[definition];
|
|
1982
|
-
}
|
|
1983
|
-
return definition;
|
|
1984
|
-
};
|
|
1985
|
-
|
|
1986
|
-
function createMixers(output, ease, customMixer) {
|
|
1987
|
-
const mixers = [];
|
|
1988
|
-
const mixerFactory = customMixer || mix;
|
|
1989
|
-
const numMixers = output.length - 1;
|
|
1990
|
-
for (let i = 0; i < numMixers; i++) {
|
|
1991
|
-
let mixer = mixerFactory(output[i], output[i + 1]);
|
|
1992
|
-
if (ease) {
|
|
1993
|
-
const easingFunction = Array.isArray(ease) ? ease[i] || motionUtils.noop : ease;
|
|
1994
|
-
mixer = pipe(easingFunction, mixer);
|
|
1995
|
-
}
|
|
1996
|
-
mixers.push(mixer);
|
|
1997
|
-
}
|
|
1998
|
-
return mixers;
|
|
1999
|
-
}
|
|
2000
|
-
/**
|
|
2001
|
-
* Create a function that maps from a numerical input array to a generic output array.
|
|
2002
|
-
*
|
|
2003
|
-
* Accepts:
|
|
2004
|
-
* - Numbers
|
|
2005
|
-
* - Colors (hex, hsl, hsla, rgb, rgba)
|
|
2006
|
-
* - Complex (combinations of one or more numbers or strings)
|
|
2007
|
-
*
|
|
2008
|
-
* ```jsx
|
|
2009
|
-
* const mixColor = interpolate([0, 1], ['#fff', '#000'])
|
|
2010
|
-
*
|
|
2011
|
-
* mixColor(0.5) // 'rgba(128, 128, 128, 1)'
|
|
2012
|
-
* ```
|
|
2013
|
-
*
|
|
2014
|
-
* TODO Revist this approach once we've moved to data models for values,
|
|
2015
|
-
* probably not needed to pregenerate mixer functions.
|
|
2016
|
-
*
|
|
2017
|
-
* @public
|
|
2018
|
-
*/
|
|
2019
|
-
function interpolate(input, output, { clamp: isClamp = true, ease, mixer } = {}) {
|
|
2020
|
-
const inputLength = input.length;
|
|
2021
|
-
motionUtils.invariant(inputLength === output.length, "Both input and output ranges must be the same length");
|
|
2022
|
-
/**
|
|
2023
|
-
* If we're only provided a single input, we can just make a function
|
|
2024
|
-
* that returns the output.
|
|
2025
|
-
*/
|
|
2026
|
-
if (inputLength === 1)
|
|
2027
|
-
return () => output[0];
|
|
2028
|
-
if (inputLength === 2 && output[0] === output[1])
|
|
2029
|
-
return () => output[1];
|
|
2030
|
-
const isZeroDeltaRange = input[0] === input[1];
|
|
2031
|
-
// If input runs highest -> lowest, reverse both arrays
|
|
2032
|
-
if (input[0] > input[inputLength - 1]) {
|
|
2033
|
-
input = [...input].reverse();
|
|
2034
|
-
output = [...output].reverse();
|
|
2035
|
-
}
|
|
2036
|
-
const mixers = createMixers(output, ease, mixer);
|
|
2037
|
-
const numMixers = mixers.length;
|
|
2038
|
-
const interpolator = (v) => {
|
|
2039
|
-
if (isZeroDeltaRange && v < input[0])
|
|
2040
|
-
return output[0];
|
|
2041
|
-
let i = 0;
|
|
2042
|
-
if (numMixers > 1) {
|
|
2043
|
-
for (; i < input.length - 2; i++) {
|
|
2044
|
-
if (v < input[i + 1])
|
|
2045
|
-
break;
|
|
2046
|
-
}
|
|
2047
|
-
}
|
|
2048
|
-
const progressInRange = motionUtils.progress(input[i], input[i + 1], v);
|
|
2049
|
-
return mixers[i](progressInRange);
|
|
2050
|
-
};
|
|
2051
|
-
return isClamp
|
|
2052
|
-
? (v) => interpolator(clamp(input[0], input[inputLength - 1], v))
|
|
2053
|
-
: interpolator;
|
|
2054
|
-
}
|
|
2055
|
-
|
|
2056
|
-
function fillOffset(offset, remaining) {
|
|
2057
|
-
const min = offset[offset.length - 1];
|
|
2058
|
-
for (let i = 1; i <= remaining; i++) {
|
|
2059
|
-
const offsetProgress = motionUtils.progress(0, remaining, i);
|
|
2060
|
-
offset.push(mixNumber$1(min, 1, offsetProgress));
|
|
2061
|
-
}
|
|
2062
|
-
}
|
|
2063
|
-
|
|
2064
|
-
function defaultOffset(arr) {
|
|
2065
|
-
const offset = [0];
|
|
2066
|
-
fillOffset(offset, arr.length - 1);
|
|
2067
|
-
return offset;
|
|
136
|
+
}
|
|
2068
137
|
}
|
|
2069
|
-
|
|
2070
|
-
|
|
2071
|
-
|
|
138
|
+
function calcBoxDelta(delta, source, target, origin) {
|
|
139
|
+
calcAxisDelta(delta.x, source.x, target.x, origin ? origin.originX : undefined);
|
|
140
|
+
calcAxisDelta(delta.y, source.y, target.y, origin ? origin.originY : undefined);
|
|
2072
141
|
}
|
|
2073
|
-
|
|
2074
|
-
|
|
2075
|
-
|
|
142
|
+
function calcRelativeAxis(target, relative, parent) {
|
|
143
|
+
target.min = parent.min + relative.min;
|
|
144
|
+
target.max = target.min + calcLength(relative);
|
|
2076
145
|
}
|
|
2077
|
-
function
|
|
2078
|
-
|
|
2079
|
-
|
|
2080
|
-
* into actual functions.
|
|
2081
|
-
*/
|
|
2082
|
-
const easingFunctions = isEasingArray(ease)
|
|
2083
|
-
? ease.map(easingDefinitionToFunction)
|
|
2084
|
-
: easingDefinitionToFunction(ease);
|
|
2085
|
-
/**
|
|
2086
|
-
* This is the Iterator-spec return value. We ensure it's mutable rather than using a generator
|
|
2087
|
-
* to reduce GC during animation.
|
|
2088
|
-
*/
|
|
2089
|
-
const state = {
|
|
2090
|
-
done: false,
|
|
2091
|
-
value: keyframeValues[0],
|
|
2092
|
-
};
|
|
2093
|
-
/**
|
|
2094
|
-
* Create a times array based on the provided 0-1 offsets
|
|
2095
|
-
*/
|
|
2096
|
-
const absoluteTimes = convertOffsetToTimes(
|
|
2097
|
-
// Only use the provided offsets if they're the correct length
|
|
2098
|
-
// TODO Maybe we should warn here if there's a length mismatch
|
|
2099
|
-
times && times.length === keyframeValues.length
|
|
2100
|
-
? times
|
|
2101
|
-
: defaultOffset(keyframeValues), duration);
|
|
2102
|
-
const mapTimeToKeyframe = interpolate(absoluteTimes, keyframeValues, {
|
|
2103
|
-
ease: Array.isArray(easingFunctions)
|
|
2104
|
-
? easingFunctions
|
|
2105
|
-
: defaultEasing(keyframeValues, easingFunctions),
|
|
2106
|
-
});
|
|
2107
|
-
return {
|
|
2108
|
-
calculatedDuration: duration,
|
|
2109
|
-
next: (t) => {
|
|
2110
|
-
state.value = mapTimeToKeyframe(t);
|
|
2111
|
-
state.done = t >= duration;
|
|
2112
|
-
return state;
|
|
2113
|
-
},
|
|
2114
|
-
};
|
|
146
|
+
function calcRelativeBox(target, relative, parent) {
|
|
147
|
+
calcRelativeAxis(target.x, relative.x, parent.x);
|
|
148
|
+
calcRelativeAxis(target.y, relative.y, parent.y);
|
|
2115
149
|
}
|
|
2116
|
-
|
|
2117
|
-
|
|
2118
|
-
|
|
2119
|
-
return {
|
|
2120
|
-
start: () => motionDom.frame.update(passTimestamp, true),
|
|
2121
|
-
stop: () => motionDom.cancelFrame(passTimestamp),
|
|
2122
|
-
/**
|
|
2123
|
-
* If we're processing this frame we can use the
|
|
2124
|
-
* framelocked timestamp to keep things in sync.
|
|
2125
|
-
*/
|
|
2126
|
-
now: () => (motionDom.frameData.isProcessing ? motionDom.frameData.timestamp : motionDom.time.now()),
|
|
2127
|
-
};
|
|
2128
|
-
};
|
|
2129
|
-
|
|
2130
|
-
const generators = {
|
|
2131
|
-
decay: inertia,
|
|
2132
|
-
inertia,
|
|
2133
|
-
tween: keyframes,
|
|
2134
|
-
keyframes: keyframes,
|
|
2135
|
-
spring,
|
|
2136
|
-
};
|
|
2137
|
-
const percentToProgress = (percent) => percent / 100;
|
|
2138
|
-
/**
|
|
2139
|
-
* Animation that runs on the main thread. Designed to be WAAPI-spec in the subset of
|
|
2140
|
-
* features we expose publically. Mostly the compatibility is to ensure visual identity
|
|
2141
|
-
* between both WAAPI and main thread animations.
|
|
2142
|
-
*/
|
|
2143
|
-
class MainThreadAnimation extends BaseAnimation {
|
|
2144
|
-
constructor(options) {
|
|
2145
|
-
super(options);
|
|
2146
|
-
/**
|
|
2147
|
-
* The time at which the animation was paused.
|
|
2148
|
-
*/
|
|
2149
|
-
this.holdTime = null;
|
|
2150
|
-
/**
|
|
2151
|
-
* The time at which the animation was cancelled.
|
|
2152
|
-
*/
|
|
2153
|
-
this.cancelTime = null;
|
|
2154
|
-
/**
|
|
2155
|
-
* The current time of the animation.
|
|
2156
|
-
*/
|
|
2157
|
-
this.currentTime = 0;
|
|
2158
|
-
/**
|
|
2159
|
-
* Playback speed as a factor. 0 would be stopped, -1 reverse and 2 double speed.
|
|
2160
|
-
*/
|
|
2161
|
-
this.playbackSpeed = 1;
|
|
2162
|
-
/**
|
|
2163
|
-
* The state of the animation to apply when the animation is resolved. This
|
|
2164
|
-
* allows calls to the public API to control the animation before it is resolved,
|
|
2165
|
-
* without us having to resolve it first.
|
|
2166
|
-
*/
|
|
2167
|
-
this.pendingPlayState = "running";
|
|
2168
|
-
/**
|
|
2169
|
-
* The time at which the animation was started.
|
|
2170
|
-
*/
|
|
2171
|
-
this.startTime = null;
|
|
2172
|
-
this.state = "idle";
|
|
2173
|
-
/**
|
|
2174
|
-
* This method is bound to the instance to fix a pattern where
|
|
2175
|
-
* animation.stop is returned as a reference from a useEffect.
|
|
2176
|
-
*/
|
|
2177
|
-
this.stop = () => {
|
|
2178
|
-
this.resolver.cancel();
|
|
2179
|
-
this.isStopped = true;
|
|
2180
|
-
if (this.state === "idle")
|
|
2181
|
-
return;
|
|
2182
|
-
this.teardown();
|
|
2183
|
-
const { onStop } = this.options;
|
|
2184
|
-
onStop && onStop();
|
|
2185
|
-
};
|
|
2186
|
-
const { name, motionValue, element, keyframes } = this.options;
|
|
2187
|
-
const KeyframeResolver$1 = element?.KeyframeResolver || KeyframeResolver;
|
|
2188
|
-
const onResolved = (resolvedKeyframes, finalKeyframe) => this.onKeyframesResolved(resolvedKeyframes, finalKeyframe);
|
|
2189
|
-
this.resolver = new KeyframeResolver$1(keyframes, onResolved, name, motionValue, element);
|
|
2190
|
-
this.resolver.scheduleResolve();
|
|
2191
|
-
}
|
|
2192
|
-
flatten() {
|
|
2193
|
-
super.flatten();
|
|
2194
|
-
// If we've already resolved the animation, re-initialise it
|
|
2195
|
-
if (this._resolved) {
|
|
2196
|
-
Object.assign(this._resolved, this.initPlayback(this._resolved.keyframes));
|
|
2197
|
-
}
|
|
2198
|
-
}
|
|
2199
|
-
initPlayback(keyframes$1) {
|
|
2200
|
-
const { type = "keyframes", repeat = 0, repeatDelay = 0, repeatType, velocity = 0, } = this.options;
|
|
2201
|
-
const generatorFactory = motionDom.isGenerator(type)
|
|
2202
|
-
? type
|
|
2203
|
-
: generators[type] || keyframes;
|
|
2204
|
-
/**
|
|
2205
|
-
* If our generator doesn't support mixing numbers, we need to replace keyframes with
|
|
2206
|
-
* [0, 100] and then make a function that maps that to the actual keyframes.
|
|
2207
|
-
*
|
|
2208
|
-
* 100 is chosen instead of 1 as it works nicer with spring animations.
|
|
2209
|
-
*/
|
|
2210
|
-
let mapPercentToKeyframes;
|
|
2211
|
-
let mirroredGenerator;
|
|
2212
|
-
if (process.env.NODE_ENV !== "production" &&
|
|
2213
|
-
generatorFactory !== keyframes) {
|
|
2214
|
-
motionUtils.invariant(keyframes$1.length <= 2, `Only two keyframes currently supported with spring and inertia animations. Trying to animate ${keyframes$1}`);
|
|
2215
|
-
}
|
|
2216
|
-
if (generatorFactory !== keyframes &&
|
|
2217
|
-
typeof keyframes$1[0] !== "number") {
|
|
2218
|
-
mapPercentToKeyframes = pipe(percentToProgress, mix(keyframes$1[0], keyframes$1[1]));
|
|
2219
|
-
keyframes$1 = [0, 100];
|
|
2220
|
-
}
|
|
2221
|
-
const generator = generatorFactory({ ...this.options, keyframes: keyframes$1 });
|
|
2222
|
-
/**
|
|
2223
|
-
* If we have a mirror repeat type we need to create a second generator that outputs the
|
|
2224
|
-
* mirrored (not reversed) animation and later ping pong between the two generators.
|
|
2225
|
-
*/
|
|
2226
|
-
if (repeatType === "mirror") {
|
|
2227
|
-
mirroredGenerator = generatorFactory({
|
|
2228
|
-
...this.options,
|
|
2229
|
-
keyframes: [...keyframes$1].reverse(),
|
|
2230
|
-
velocity: -velocity,
|
|
2231
|
-
});
|
|
2232
|
-
}
|
|
2233
|
-
/**
|
|
2234
|
-
* If duration is undefined and we have repeat options,
|
|
2235
|
-
* we need to calculate a duration from the generator.
|
|
2236
|
-
*
|
|
2237
|
-
* We set it to the generator itself to cache the duration.
|
|
2238
|
-
* Any timeline resolver will need to have already precalculated
|
|
2239
|
-
* the duration by this step.
|
|
2240
|
-
*/
|
|
2241
|
-
if (generator.calculatedDuration === null) {
|
|
2242
|
-
generator.calculatedDuration = motionDom.calcGeneratorDuration(generator);
|
|
2243
|
-
}
|
|
2244
|
-
const { calculatedDuration } = generator;
|
|
2245
|
-
const resolvedDuration = calculatedDuration + repeatDelay;
|
|
2246
|
-
const totalDuration = resolvedDuration * (repeat + 1) - repeatDelay;
|
|
2247
|
-
return {
|
|
2248
|
-
generator,
|
|
2249
|
-
mirroredGenerator,
|
|
2250
|
-
mapPercentToKeyframes,
|
|
2251
|
-
calculatedDuration,
|
|
2252
|
-
resolvedDuration,
|
|
2253
|
-
totalDuration,
|
|
2254
|
-
};
|
|
2255
|
-
}
|
|
2256
|
-
onPostResolved() {
|
|
2257
|
-
const { autoplay = true } = this.options;
|
|
2258
|
-
motionDom.activeAnimations.mainThread++;
|
|
2259
|
-
this.play();
|
|
2260
|
-
if (this.pendingPlayState === "paused" || !autoplay) {
|
|
2261
|
-
this.pause();
|
|
2262
|
-
}
|
|
2263
|
-
else {
|
|
2264
|
-
this.state = this.pendingPlayState;
|
|
2265
|
-
}
|
|
2266
|
-
}
|
|
2267
|
-
tick(timestamp, sample = false) {
|
|
2268
|
-
const { resolved } = this;
|
|
2269
|
-
// If the animations has failed to resolve, return the final keyframe.
|
|
2270
|
-
if (!resolved) {
|
|
2271
|
-
const { keyframes } = this.options;
|
|
2272
|
-
return { done: true, value: keyframes[keyframes.length - 1] };
|
|
2273
|
-
}
|
|
2274
|
-
const { finalKeyframe, generator, mirroredGenerator, mapPercentToKeyframes, keyframes, calculatedDuration, totalDuration, resolvedDuration, } = resolved;
|
|
2275
|
-
if (this.startTime === null)
|
|
2276
|
-
return generator.next(0);
|
|
2277
|
-
const { delay, repeat, repeatType, repeatDelay, onUpdate } = this.options;
|
|
2278
|
-
/**
|
|
2279
|
-
* requestAnimationFrame timestamps can come through as lower than
|
|
2280
|
-
* the startTime as set by performance.now(). Here we prevent this,
|
|
2281
|
-
* though in the future it could be possible to make setting startTime
|
|
2282
|
-
* a pending operation that gets resolved here.
|
|
2283
|
-
*/
|
|
2284
|
-
if (this.speed > 0) {
|
|
2285
|
-
this.startTime = Math.min(this.startTime, timestamp);
|
|
2286
|
-
}
|
|
2287
|
-
else if (this.speed < 0) {
|
|
2288
|
-
this.startTime = Math.min(timestamp - totalDuration / this.speed, this.startTime);
|
|
2289
|
-
}
|
|
2290
|
-
// Update currentTime
|
|
2291
|
-
if (sample) {
|
|
2292
|
-
this.currentTime = timestamp;
|
|
2293
|
-
}
|
|
2294
|
-
else if (this.holdTime !== null) {
|
|
2295
|
-
this.currentTime = this.holdTime;
|
|
2296
|
-
}
|
|
2297
|
-
else {
|
|
2298
|
-
// Rounding the time because floating point arithmetic is not always accurate, e.g. 3000.367 - 1000.367 =
|
|
2299
|
-
// 2000.0000000000002. This is a problem when we are comparing the currentTime with the duration, for
|
|
2300
|
-
// example.
|
|
2301
|
-
this.currentTime =
|
|
2302
|
-
Math.round(timestamp - this.startTime) * this.speed;
|
|
2303
|
-
}
|
|
2304
|
-
// Rebase on delay
|
|
2305
|
-
const timeWithoutDelay = this.currentTime - delay * (this.speed >= 0 ? 1 : -1);
|
|
2306
|
-
const isInDelayPhase = this.speed >= 0
|
|
2307
|
-
? timeWithoutDelay < 0
|
|
2308
|
-
: timeWithoutDelay > totalDuration;
|
|
2309
|
-
this.currentTime = Math.max(timeWithoutDelay, 0);
|
|
2310
|
-
// If this animation has finished, set the current time to the total duration.
|
|
2311
|
-
if (this.state === "finished" && this.holdTime === null) {
|
|
2312
|
-
this.currentTime = totalDuration;
|
|
2313
|
-
}
|
|
2314
|
-
let elapsed = this.currentTime;
|
|
2315
|
-
let frameGenerator = generator;
|
|
2316
|
-
if (repeat) {
|
|
2317
|
-
/**
|
|
2318
|
-
* Get the current progress (0-1) of the animation. If t is >
|
|
2319
|
-
* than duration we'll get values like 2.5 (midway through the
|
|
2320
|
-
* third iteration)
|
|
2321
|
-
*/
|
|
2322
|
-
const progress = Math.min(this.currentTime, totalDuration) / resolvedDuration;
|
|
2323
|
-
/**
|
|
2324
|
-
* Get the current iteration (0 indexed). For instance the floor of
|
|
2325
|
-
* 2.5 is 2.
|
|
2326
|
-
*/
|
|
2327
|
-
let currentIteration = Math.floor(progress);
|
|
2328
|
-
/**
|
|
2329
|
-
* Get the current progress of the iteration by taking the remainder
|
|
2330
|
-
* so 2.5 is 0.5 through iteration 2
|
|
2331
|
-
*/
|
|
2332
|
-
let iterationProgress = progress % 1.0;
|
|
2333
|
-
/**
|
|
2334
|
-
* If iteration progress is 1 we count that as the end
|
|
2335
|
-
* of the previous iteration.
|
|
2336
|
-
*/
|
|
2337
|
-
if (!iterationProgress && progress >= 1) {
|
|
2338
|
-
iterationProgress = 1;
|
|
2339
|
-
}
|
|
2340
|
-
iterationProgress === 1 && currentIteration--;
|
|
2341
|
-
currentIteration = Math.min(currentIteration, repeat + 1);
|
|
2342
|
-
/**
|
|
2343
|
-
* Reverse progress if we're not running in "normal" direction
|
|
2344
|
-
*/
|
|
2345
|
-
const isOddIteration = Boolean(currentIteration % 2);
|
|
2346
|
-
if (isOddIteration) {
|
|
2347
|
-
if (repeatType === "reverse") {
|
|
2348
|
-
iterationProgress = 1 - iterationProgress;
|
|
2349
|
-
if (repeatDelay) {
|
|
2350
|
-
iterationProgress -= repeatDelay / resolvedDuration;
|
|
2351
|
-
}
|
|
2352
|
-
}
|
|
2353
|
-
else if (repeatType === "mirror") {
|
|
2354
|
-
frameGenerator = mirroredGenerator;
|
|
2355
|
-
}
|
|
2356
|
-
}
|
|
2357
|
-
elapsed = clamp(0, 1, iterationProgress) * resolvedDuration;
|
|
2358
|
-
}
|
|
2359
|
-
/**
|
|
2360
|
-
* If we're in negative time, set state as the initial keyframe.
|
|
2361
|
-
* This prevents delay: x, duration: 0 animations from finishing
|
|
2362
|
-
* instantly.
|
|
2363
|
-
*/
|
|
2364
|
-
const state = isInDelayPhase
|
|
2365
|
-
? { done: false, value: keyframes[0] }
|
|
2366
|
-
: frameGenerator.next(elapsed);
|
|
2367
|
-
if (mapPercentToKeyframes) {
|
|
2368
|
-
state.value = mapPercentToKeyframes(state.value);
|
|
2369
|
-
}
|
|
2370
|
-
let { done } = state;
|
|
2371
|
-
if (!isInDelayPhase && calculatedDuration !== null) {
|
|
2372
|
-
done =
|
|
2373
|
-
this.speed >= 0
|
|
2374
|
-
? this.currentTime >= totalDuration
|
|
2375
|
-
: this.currentTime <= 0;
|
|
2376
|
-
}
|
|
2377
|
-
const isAnimationFinished = this.holdTime === null &&
|
|
2378
|
-
(this.state === "finished" || (this.state === "running" && done));
|
|
2379
|
-
if (isAnimationFinished && finalKeyframe !== undefined) {
|
|
2380
|
-
state.value = getFinalKeyframe(keyframes, this.options, finalKeyframe);
|
|
2381
|
-
}
|
|
2382
|
-
if (onUpdate) {
|
|
2383
|
-
onUpdate(state.value);
|
|
2384
|
-
}
|
|
2385
|
-
if (isAnimationFinished) {
|
|
2386
|
-
this.finish();
|
|
2387
|
-
}
|
|
2388
|
-
return state;
|
|
2389
|
-
}
|
|
2390
|
-
get duration() {
|
|
2391
|
-
const { resolved } = this;
|
|
2392
|
-
return resolved ? motionUtils.millisecondsToSeconds(resolved.calculatedDuration) : 0;
|
|
2393
|
-
}
|
|
2394
|
-
get time() {
|
|
2395
|
-
return motionUtils.millisecondsToSeconds(this.currentTime);
|
|
2396
|
-
}
|
|
2397
|
-
set time(newTime) {
|
|
2398
|
-
newTime = motionUtils.secondsToMilliseconds(newTime);
|
|
2399
|
-
this.currentTime = newTime;
|
|
2400
|
-
if (this.holdTime !== null || this.speed === 0) {
|
|
2401
|
-
this.holdTime = newTime;
|
|
2402
|
-
}
|
|
2403
|
-
else if (this.driver) {
|
|
2404
|
-
this.startTime = this.driver.now() - newTime / this.speed;
|
|
2405
|
-
}
|
|
2406
|
-
}
|
|
2407
|
-
get speed() {
|
|
2408
|
-
return this.playbackSpeed;
|
|
2409
|
-
}
|
|
2410
|
-
set speed(newSpeed) {
|
|
2411
|
-
const hasChanged = this.playbackSpeed !== newSpeed;
|
|
2412
|
-
this.playbackSpeed = newSpeed;
|
|
2413
|
-
if (hasChanged) {
|
|
2414
|
-
this.time = motionUtils.millisecondsToSeconds(this.currentTime);
|
|
2415
|
-
}
|
|
2416
|
-
}
|
|
2417
|
-
play() {
|
|
2418
|
-
if (!this.resolver.isScheduled) {
|
|
2419
|
-
this.resolver.resume();
|
|
2420
|
-
}
|
|
2421
|
-
if (!this._resolved) {
|
|
2422
|
-
this.pendingPlayState = "running";
|
|
2423
|
-
return;
|
|
2424
|
-
}
|
|
2425
|
-
if (this.isStopped)
|
|
2426
|
-
return;
|
|
2427
|
-
const { driver = frameloopDriver, onPlay, startTime } = this.options;
|
|
2428
|
-
if (!this.driver) {
|
|
2429
|
-
this.driver = driver((timestamp) => this.tick(timestamp));
|
|
2430
|
-
}
|
|
2431
|
-
onPlay && onPlay();
|
|
2432
|
-
const now = this.driver.now();
|
|
2433
|
-
if (this.holdTime !== null) {
|
|
2434
|
-
this.startTime = now - this.holdTime;
|
|
2435
|
-
}
|
|
2436
|
-
else if (!this.startTime) {
|
|
2437
|
-
this.startTime = startTime ?? this.calcStartTime();
|
|
2438
|
-
}
|
|
2439
|
-
else if (this.state === "finished") {
|
|
2440
|
-
this.startTime = now;
|
|
2441
|
-
}
|
|
2442
|
-
if (this.state === "finished") {
|
|
2443
|
-
this.updateFinishedPromise();
|
|
2444
|
-
}
|
|
2445
|
-
this.cancelTime = this.startTime;
|
|
2446
|
-
this.holdTime = null;
|
|
2447
|
-
/**
|
|
2448
|
-
* Set playState to running only after we've used it in
|
|
2449
|
-
* the previous logic.
|
|
2450
|
-
*/
|
|
2451
|
-
this.state = "running";
|
|
2452
|
-
this.driver.start();
|
|
2453
|
-
}
|
|
2454
|
-
pause() {
|
|
2455
|
-
if (!this._resolved) {
|
|
2456
|
-
this.pendingPlayState = "paused";
|
|
2457
|
-
return;
|
|
2458
|
-
}
|
|
2459
|
-
this.state = "paused";
|
|
2460
|
-
this.holdTime = this.currentTime ?? 0;
|
|
2461
|
-
}
|
|
2462
|
-
complete() {
|
|
2463
|
-
if (this.state !== "running") {
|
|
2464
|
-
this.play();
|
|
2465
|
-
}
|
|
2466
|
-
this.pendingPlayState = this.state = "finished";
|
|
2467
|
-
this.holdTime = null;
|
|
2468
|
-
}
|
|
2469
|
-
finish() {
|
|
2470
|
-
this.teardown();
|
|
2471
|
-
this.state = "finished";
|
|
2472
|
-
const { onComplete } = this.options;
|
|
2473
|
-
onComplete && onComplete();
|
|
2474
|
-
}
|
|
2475
|
-
cancel() {
|
|
2476
|
-
if (this.cancelTime !== null) {
|
|
2477
|
-
this.tick(this.cancelTime);
|
|
2478
|
-
}
|
|
2479
|
-
this.teardown();
|
|
2480
|
-
this.updateFinishedPromise();
|
|
2481
|
-
}
|
|
2482
|
-
teardown() {
|
|
2483
|
-
this.state = "idle";
|
|
2484
|
-
this.stopDriver();
|
|
2485
|
-
this.resolveFinishedPromise();
|
|
2486
|
-
this.updateFinishedPromise();
|
|
2487
|
-
this.startTime = this.cancelTime = null;
|
|
2488
|
-
this.resolver.cancel();
|
|
2489
|
-
motionDom.activeAnimations.mainThread--;
|
|
2490
|
-
}
|
|
2491
|
-
stopDriver() {
|
|
2492
|
-
if (!this.driver)
|
|
2493
|
-
return;
|
|
2494
|
-
this.driver.stop();
|
|
2495
|
-
this.driver = undefined;
|
|
2496
|
-
}
|
|
2497
|
-
sample(time) {
|
|
2498
|
-
this.startTime = 0;
|
|
2499
|
-
return this.tick(time, true);
|
|
2500
|
-
}
|
|
2501
|
-
get finished() {
|
|
2502
|
-
return this.currentFinishedPromise;
|
|
2503
|
-
}
|
|
150
|
+
function calcRelativeAxisPosition(target, layout, parent) {
|
|
151
|
+
target.min = layout.min - parent.min;
|
|
152
|
+
target.max = target.min + calcLength(layout);
|
|
2504
153
|
}
|
|
2505
|
-
|
|
2506
|
-
|
|
2507
|
-
|
|
154
|
+
function calcRelativePosition(target, layout, parent) {
|
|
155
|
+
calcRelativeAxisPosition(target.x, layout.x, parent.x);
|
|
156
|
+
calcRelativeAxisPosition(target.y, layout.y, parent.y);
|
|
2508
157
|
}
|
|
2509
158
|
|
|
2510
|
-
|
|
2511
|
-
* A list of values that can be hardware-accelerated.
|
|
2512
|
-
*/
|
|
2513
|
-
const acceleratedValues = new Set([
|
|
2514
|
-
"opacity",
|
|
2515
|
-
"clipPath",
|
|
2516
|
-
"filter",
|
|
2517
|
-
"transform",
|
|
2518
|
-
// TODO: Can be accelerated but currently disabled until https://issues.chromium.org/issues/41491098 is resolved
|
|
2519
|
-
// or until we implement support for linear() easing.
|
|
2520
|
-
// "background-color"
|
|
2521
|
-
]);
|
|
2522
|
-
|
|
2523
|
-
const supportsWaapi = /*@__PURE__*/ motionUtils.memo(() => Object.hasOwnProperty.call(Element.prototype, "animate"));
|
|
159
|
+
const isMotionValue = (value) => Boolean(value && value.getVelocity);
|
|
2524
160
|
|
|
2525
|
-
|
|
2526
|
-
|
|
2527
|
-
|
|
2528
|
-
|
|
2529
|
-
|
|
2530
|
-
|
|
2531
|
-
|
|
2532
|
-
|
|
2533
|
-
|
|
2534
|
-
*/
|
|
2535
|
-
const maxDuration = 20000;
|
|
2536
|
-
/**
|
|
2537
|
-
* Check if an animation can run natively via WAAPI or requires pregenerated keyframes.
|
|
2538
|
-
* WAAPI doesn't support spring or function easings so we run these as JS animation before
|
|
2539
|
-
* handing off.
|
|
2540
|
-
*/
|
|
2541
|
-
function requiresPregeneratedKeyframes(options) {
|
|
2542
|
-
return (motionDom.isGenerator(options.type) ||
|
|
2543
|
-
options.type === "spring" ||
|
|
2544
|
-
!motionDom.isWaapiSupportedEasing(options.ease));
|
|
2545
|
-
}
|
|
2546
|
-
function pregenerateKeyframes(keyframes, options) {
|
|
2547
|
-
/**
|
|
2548
|
-
* Create a main-thread animation to pregenerate keyframes.
|
|
2549
|
-
* We sample this at regular intervals to generate keyframes that we then
|
|
2550
|
-
* linearly interpolate between.
|
|
2551
|
-
*/
|
|
2552
|
-
const sampleAnimation = new MainThreadAnimation({
|
|
2553
|
-
...options,
|
|
2554
|
-
keyframes,
|
|
2555
|
-
repeat: 0,
|
|
2556
|
-
delay: 0,
|
|
2557
|
-
isGenerator: true,
|
|
2558
|
-
});
|
|
2559
|
-
let state = { done: false, value: keyframes[0] };
|
|
2560
|
-
const pregeneratedKeyframes = [];
|
|
2561
|
-
/**
|
|
2562
|
-
* Bail after 20 seconds of pre-generated keyframes as it's likely
|
|
2563
|
-
* we're heading for an infinite loop.
|
|
2564
|
-
*/
|
|
2565
|
-
let t = 0;
|
|
2566
|
-
while (!state.done && t < maxDuration) {
|
|
2567
|
-
state = sampleAnimation.sample(t);
|
|
2568
|
-
pregeneratedKeyframes.push(state.value);
|
|
2569
|
-
t += sampleDelta;
|
|
2570
|
-
}
|
|
2571
|
-
return {
|
|
2572
|
-
times: undefined,
|
|
2573
|
-
keyframes: pregeneratedKeyframes,
|
|
2574
|
-
duration: t - sampleDelta,
|
|
2575
|
-
ease: "linear",
|
|
2576
|
-
};
|
|
2577
|
-
}
|
|
2578
|
-
const unsupportedEasingFunctions = {
|
|
2579
|
-
anticipate,
|
|
2580
|
-
backInOut,
|
|
2581
|
-
circInOut,
|
|
2582
|
-
};
|
|
2583
|
-
function isUnsupportedEase(key) {
|
|
2584
|
-
return key in unsupportedEasingFunctions;
|
|
2585
|
-
}
|
|
2586
|
-
class AcceleratedAnimation extends BaseAnimation {
|
|
2587
|
-
constructor(options) {
|
|
2588
|
-
super(options);
|
|
2589
|
-
const { name, motionValue, element, keyframes } = this.options;
|
|
2590
|
-
this.resolver = new DOMKeyframesResolver(keyframes, (resolvedKeyframes, finalKeyframe) => this.onKeyframesResolved(resolvedKeyframes, finalKeyframe), name, motionValue, element);
|
|
2591
|
-
this.resolver.scheduleResolve();
|
|
2592
|
-
}
|
|
2593
|
-
initPlayback(keyframes, finalKeyframe) {
|
|
2594
|
-
let { duration = 300, times, ease, type, motionValue, name, startTime, } = this.options;
|
|
2595
|
-
/**
|
|
2596
|
-
* If element has since been unmounted, return false to indicate
|
|
2597
|
-
* the animation failed to initialised.
|
|
2598
|
-
*/
|
|
2599
|
-
if (!motionValue.owner || !motionValue.owner.current) {
|
|
2600
|
-
return false;
|
|
2601
|
-
}
|
|
2602
|
-
/**
|
|
2603
|
-
* If the user has provided an easing function name that isn't supported
|
|
2604
|
-
* by WAAPI (like "anticipate"), we need to provide the corressponding
|
|
2605
|
-
* function. This will later get converted to a linear() easing function.
|
|
2606
|
-
*/
|
|
2607
|
-
if (typeof ease === "string" &&
|
|
2608
|
-
motionDom.supportsLinearEasing() &&
|
|
2609
|
-
isUnsupportedEase(ease)) {
|
|
2610
|
-
ease = unsupportedEasingFunctions[ease];
|
|
2611
|
-
}
|
|
2612
|
-
/**
|
|
2613
|
-
* If this animation needs pre-generated keyframes then generate.
|
|
2614
|
-
*/
|
|
2615
|
-
if (requiresPregeneratedKeyframes(this.options)) {
|
|
2616
|
-
const { onComplete, onUpdate, motionValue, element, ...options } = this.options;
|
|
2617
|
-
const pregeneratedAnimation = pregenerateKeyframes(keyframes, options);
|
|
2618
|
-
keyframes = pregeneratedAnimation.keyframes;
|
|
2619
|
-
// If this is a very short animation, ensure we have
|
|
2620
|
-
// at least two keyframes to animate between as older browsers
|
|
2621
|
-
// can't animate between a single keyframe.
|
|
2622
|
-
if (keyframes.length === 1) {
|
|
2623
|
-
keyframes[1] = keyframes[0];
|
|
2624
|
-
}
|
|
2625
|
-
duration = pregeneratedAnimation.duration;
|
|
2626
|
-
times = pregeneratedAnimation.times;
|
|
2627
|
-
ease = pregeneratedAnimation.ease;
|
|
2628
|
-
type = "keyframes";
|
|
2629
|
-
}
|
|
2630
|
-
const animation = motionDom.startWaapiAnimation(motionValue.owner.current, name, keyframes, { ...this.options, duration, times, ease });
|
|
2631
|
-
// Override the browser calculated startTime with one synchronised to other JS
|
|
2632
|
-
// and WAAPI animations starting this event loop.
|
|
2633
|
-
animation.startTime = startTime ?? this.calcStartTime();
|
|
2634
|
-
if (this.pendingTimeline) {
|
|
2635
|
-
motionDom.attachTimeline(animation, this.pendingTimeline);
|
|
2636
|
-
this.pendingTimeline = undefined;
|
|
2637
|
-
}
|
|
2638
|
-
else {
|
|
2639
|
-
/**
|
|
2640
|
-
* Prefer the `onfinish` prop as it's more widely supported than
|
|
2641
|
-
* the `finished` promise.
|
|
2642
|
-
*
|
|
2643
|
-
* Here, we synchronously set the provided MotionValue to the end
|
|
2644
|
-
* keyframe. If we didn't, when the WAAPI animation is finished it would
|
|
2645
|
-
* be removed from the element which would then revert to its old styles.
|
|
2646
|
-
*/
|
|
2647
|
-
animation.onfinish = () => {
|
|
2648
|
-
const { onComplete } = this.options;
|
|
2649
|
-
motionValue.set(getFinalKeyframe(keyframes, this.options, finalKeyframe));
|
|
2650
|
-
onComplete && onComplete();
|
|
2651
|
-
this.cancel();
|
|
2652
|
-
this.resolveFinishedPromise();
|
|
2653
|
-
};
|
|
2654
|
-
}
|
|
2655
|
-
return {
|
|
2656
|
-
animation,
|
|
2657
|
-
duration,
|
|
2658
|
-
times,
|
|
2659
|
-
type,
|
|
2660
|
-
ease,
|
|
2661
|
-
keyframes: keyframes,
|
|
2662
|
-
};
|
|
2663
|
-
}
|
|
2664
|
-
get duration() {
|
|
2665
|
-
const { resolved } = this;
|
|
2666
|
-
if (!resolved)
|
|
2667
|
-
return 0;
|
|
2668
|
-
const { duration } = resolved;
|
|
2669
|
-
return motionUtils.millisecondsToSeconds(duration);
|
|
2670
|
-
}
|
|
2671
|
-
get time() {
|
|
2672
|
-
const { resolved } = this;
|
|
2673
|
-
if (!resolved)
|
|
2674
|
-
return 0;
|
|
2675
|
-
const { animation } = resolved;
|
|
2676
|
-
return motionUtils.millisecondsToSeconds(animation.currentTime || 0);
|
|
2677
|
-
}
|
|
2678
|
-
set time(newTime) {
|
|
2679
|
-
const { resolved } = this;
|
|
2680
|
-
if (!resolved)
|
|
2681
|
-
return;
|
|
2682
|
-
const { animation } = resolved;
|
|
2683
|
-
animation.currentTime = motionUtils.secondsToMilliseconds(newTime);
|
|
2684
|
-
}
|
|
2685
|
-
get speed() {
|
|
2686
|
-
const { resolved } = this;
|
|
2687
|
-
if (!resolved)
|
|
2688
|
-
return 1;
|
|
2689
|
-
const { animation } = resolved;
|
|
2690
|
-
return animation.playbackRate;
|
|
2691
|
-
}
|
|
2692
|
-
get finished() {
|
|
2693
|
-
return this.resolved.animation.finished;
|
|
2694
|
-
}
|
|
2695
|
-
set speed(newSpeed) {
|
|
2696
|
-
const { resolved } = this;
|
|
2697
|
-
if (!resolved)
|
|
2698
|
-
return;
|
|
2699
|
-
const { animation } = resolved;
|
|
2700
|
-
animation.playbackRate = newSpeed;
|
|
2701
|
-
}
|
|
2702
|
-
get state() {
|
|
2703
|
-
const { resolved } = this;
|
|
2704
|
-
if (!resolved)
|
|
2705
|
-
return "idle";
|
|
2706
|
-
const { animation } = resolved;
|
|
2707
|
-
return animation.playState;
|
|
2708
|
-
}
|
|
2709
|
-
get startTime() {
|
|
2710
|
-
const { resolved } = this;
|
|
2711
|
-
if (!resolved)
|
|
2712
|
-
return null;
|
|
2713
|
-
const { animation } = resolved;
|
|
2714
|
-
// Coerce to number as TypeScript incorrectly types this
|
|
2715
|
-
// as CSSNumberish
|
|
2716
|
-
return animation.startTime;
|
|
2717
|
-
}
|
|
2718
|
-
/**
|
|
2719
|
-
* Replace the default DocumentTimeline with another AnimationTimeline.
|
|
2720
|
-
* Currently used for scroll animations.
|
|
2721
|
-
*/
|
|
2722
|
-
attachTimeline(timeline) {
|
|
2723
|
-
if (!this._resolved) {
|
|
2724
|
-
this.pendingTimeline = timeline;
|
|
2725
|
-
}
|
|
2726
|
-
else {
|
|
2727
|
-
const { resolved } = this;
|
|
2728
|
-
if (!resolved)
|
|
2729
|
-
return motionUtils.noop;
|
|
2730
|
-
const { animation } = resolved;
|
|
2731
|
-
motionDom.attachTimeline(animation, timeline);
|
|
2732
|
-
}
|
|
2733
|
-
return motionUtils.noop;
|
|
2734
|
-
}
|
|
2735
|
-
play() {
|
|
2736
|
-
if (this.isStopped)
|
|
2737
|
-
return;
|
|
2738
|
-
const { resolved } = this;
|
|
2739
|
-
if (!resolved)
|
|
2740
|
-
return;
|
|
2741
|
-
const { animation } = resolved;
|
|
2742
|
-
if (animation.playState === "finished") {
|
|
2743
|
-
this.updateFinishedPromise();
|
|
2744
|
-
}
|
|
2745
|
-
animation.play();
|
|
2746
|
-
}
|
|
2747
|
-
pause() {
|
|
2748
|
-
const { resolved } = this;
|
|
2749
|
-
if (!resolved)
|
|
2750
|
-
return;
|
|
2751
|
-
const { animation } = resolved;
|
|
2752
|
-
animation.pause();
|
|
2753
|
-
}
|
|
2754
|
-
stop() {
|
|
2755
|
-
this.resolver.cancel();
|
|
2756
|
-
this.isStopped = true;
|
|
2757
|
-
if (this.state === "idle")
|
|
2758
|
-
return;
|
|
2759
|
-
this.resolveFinishedPromise();
|
|
2760
|
-
this.updateFinishedPromise();
|
|
2761
|
-
const { resolved } = this;
|
|
2762
|
-
if (!resolved)
|
|
2763
|
-
return;
|
|
2764
|
-
const { animation, keyframes, duration, type, ease, times } = resolved;
|
|
2765
|
-
if (animation.playState === "idle" ||
|
|
2766
|
-
animation.playState === "finished") {
|
|
2767
|
-
return;
|
|
2768
|
-
}
|
|
2769
|
-
/**
|
|
2770
|
-
* WAAPI doesn't natively have any interruption capabilities.
|
|
2771
|
-
*
|
|
2772
|
-
* Rather than read commited styles back out of the DOM, we can
|
|
2773
|
-
* create a renderless JS animation and sample it twice to calculate
|
|
2774
|
-
* its current value, "previous" value, and therefore allow
|
|
2775
|
-
* Motion to calculate velocity for any subsequent animation.
|
|
2776
|
-
*/
|
|
2777
|
-
if (this.time) {
|
|
2778
|
-
const { motionValue, onUpdate, onComplete, element, ...options } = this.options;
|
|
2779
|
-
const sampleAnimation = new MainThreadAnimation({
|
|
2780
|
-
...options,
|
|
2781
|
-
keyframes,
|
|
2782
|
-
duration,
|
|
2783
|
-
type,
|
|
2784
|
-
ease,
|
|
2785
|
-
times,
|
|
2786
|
-
isGenerator: true,
|
|
2787
|
-
});
|
|
2788
|
-
const sampleTime = motionUtils.secondsToMilliseconds(this.time);
|
|
2789
|
-
motionValue.setWithVelocity(sampleAnimation.sample(sampleTime - sampleDelta).value, sampleAnimation.sample(sampleTime).value, sampleDelta);
|
|
2790
|
-
}
|
|
2791
|
-
const { onStop } = this.options;
|
|
2792
|
-
onStop && onStop();
|
|
2793
|
-
this.cancel();
|
|
2794
|
-
}
|
|
2795
|
-
complete() {
|
|
2796
|
-
const { resolved } = this;
|
|
2797
|
-
if (!resolved)
|
|
2798
|
-
return;
|
|
2799
|
-
resolved.animation.finish();
|
|
2800
|
-
}
|
|
2801
|
-
cancel() {
|
|
2802
|
-
const { resolved } = this;
|
|
2803
|
-
if (!resolved)
|
|
2804
|
-
return;
|
|
2805
|
-
resolved.animation.cancel();
|
|
2806
|
-
}
|
|
2807
|
-
static supports(options) {
|
|
2808
|
-
const { motionValue, name, repeatDelay, repeatType, damping, type } = options;
|
|
2809
|
-
if (!motionValue ||
|
|
2810
|
-
!motionValue.owner ||
|
|
2811
|
-
!(motionValue.owner.current instanceof HTMLElement)) {
|
|
2812
|
-
return false;
|
|
2813
|
-
}
|
|
2814
|
-
const { onUpdate, transformTemplate } = motionValue.owner.getProps();
|
|
2815
|
-
return (supportsWaapi() &&
|
|
2816
|
-
name &&
|
|
2817
|
-
acceleratedValues.has(name) &&
|
|
2818
|
-
(name !== "transform" || !transformTemplate) &&
|
|
2819
|
-
/**
|
|
2820
|
-
* If we're outputting values to onUpdate then we can't use WAAPI as there's
|
|
2821
|
-
* no way to read the value from WAAPI every frame.
|
|
2822
|
-
*/
|
|
2823
|
-
!onUpdate &&
|
|
2824
|
-
!repeatDelay &&
|
|
2825
|
-
repeatType !== "mirror" &&
|
|
2826
|
-
damping !== 0 &&
|
|
2827
|
-
type !== "inertia");
|
|
2828
|
-
}
|
|
161
|
+
const isNotNull = (value) => value !== null;
|
|
162
|
+
function getFinalKeyframe(keyframes, { repeat, repeatType = "loop" }, finalKeyframe) {
|
|
163
|
+
const resolvedKeyframes = keyframes.filter(isNotNull);
|
|
164
|
+
const index = repeat && repeatType !== "loop" && repeat % 2 === 1
|
|
165
|
+
? 0
|
|
166
|
+
: resolvedKeyframes.length - 1;
|
|
167
|
+
return !index || finalKeyframe === undefined
|
|
168
|
+
? resolvedKeyframes[index]
|
|
169
|
+
: finalKeyframe;
|
|
2829
170
|
}
|
|
2830
171
|
|
|
2831
172
|
const underDampedSpring = {
|
|
@@ -2857,7 +198,7 @@ const getDefaultTransition = (valueKey, { keyframes }) => {
|
|
|
2857
198
|
if (keyframes.length > 2) {
|
|
2858
199
|
return keyframesTransition;
|
|
2859
200
|
}
|
|
2860
|
-
else if (transformProps.has(valueKey)) {
|
|
201
|
+
else if (motionDom.transformProps.has(valueKey)) {
|
|
2861
202
|
return valueKey.startsWith("scale")
|
|
2862
203
|
? criticallyDampedSpring(keyframes[1])
|
|
2863
204
|
: underDampedSpring;
|
|
@@ -2888,7 +229,7 @@ const animateMotionValue = (name, value, target, transition = {}, element, isHan
|
|
|
2888
229
|
*/
|
|
2889
230
|
let { elapsed = 0 } = transition;
|
|
2890
231
|
elapsed = elapsed - motionUtils.secondsToMilliseconds(delay);
|
|
2891
|
-
|
|
232
|
+
const options = {
|
|
2892
233
|
keyframes: Array.isArray(target) ? target : [null, target],
|
|
2893
234
|
ease: "easeOut",
|
|
2894
235
|
velocity: value.getVelocity(),
|
|
@@ -2911,22 +252,18 @@ const animateMotionValue = (name, value, target, transition = {}, element, isHan
|
|
|
2911
252
|
* unique transition settings for this value.
|
|
2912
253
|
*/
|
|
2913
254
|
if (!isTransitionDefined(valueTransition)) {
|
|
2914
|
-
options
|
|
2915
|
-
...options,
|
|
2916
|
-
...getDefaultTransition(name, options),
|
|
2917
|
-
};
|
|
255
|
+
Object.assign(options, getDefaultTransition(name, options));
|
|
2918
256
|
}
|
|
2919
257
|
/**
|
|
2920
258
|
* Both WAAPI and our internal animation functions use durations
|
|
2921
259
|
* as defined by milliseconds, while our external API defines them
|
|
2922
260
|
* as seconds.
|
|
2923
261
|
*/
|
|
2924
|
-
|
|
2925
|
-
|
|
2926
|
-
|
|
2927
|
-
|
|
2928
|
-
|
|
2929
|
-
}
|
|
262
|
+
options.duration && (options.duration = motionUtils.secondsToMilliseconds(options.duration));
|
|
263
|
+
options.repeatDelay && (options.repeatDelay = motionUtils.secondsToMilliseconds(options.repeatDelay));
|
|
264
|
+
/**
|
|
265
|
+
* Support deprecated way to set initial value. Prefer keyframe syntax.
|
|
266
|
+
*/
|
|
2930
267
|
if (options.from !== undefined) {
|
|
2931
268
|
options.keyframes[0] = options.from;
|
|
2932
269
|
}
|
|
@@ -2938,7 +275,7 @@ const animateMotionValue = (name, value, target, transition = {}, element, isHan
|
|
|
2938
275
|
shouldSkip = true;
|
|
2939
276
|
}
|
|
2940
277
|
}
|
|
2941
|
-
if (
|
|
278
|
+
if (motionUtils.MotionGlobalConfig.instantAnimations ||
|
|
2942
279
|
motionUtils.MotionGlobalConfig.skipAnimations) {
|
|
2943
280
|
shouldSkip = true;
|
|
2944
281
|
options.duration = 0;
|
|
@@ -2961,22 +298,10 @@ const animateMotionValue = (name, value, target, transition = {}, element, isHan
|
|
|
2961
298
|
options.onUpdate(finalKeyframe);
|
|
2962
299
|
options.onComplete();
|
|
2963
300
|
});
|
|
2964
|
-
|
|
2965
|
-
// than returning undefined
|
|
2966
|
-
return new motionDom.GroupAnimationWithThen([]);
|
|
301
|
+
return;
|
|
2967
302
|
}
|
|
2968
303
|
}
|
|
2969
|
-
|
|
2970
|
-
* Animate via WAAPI if possible. If this is a handoff animation, the optimised animation will be running via
|
|
2971
|
-
* WAAPI. Therefore, this animation must be JS to ensure it runs "under" the
|
|
2972
|
-
* optimised animation.
|
|
2973
|
-
*/
|
|
2974
|
-
if (!isHandoff && AcceleratedAnimation.supports(options)) {
|
|
2975
|
-
return new AcceleratedAnimation(options);
|
|
2976
|
-
}
|
|
2977
|
-
else {
|
|
2978
|
-
return new MainThreadAnimation(options);
|
|
2979
|
-
}
|
|
304
|
+
return new motionDom.AsyncMotionValueAnimation(options);
|
|
2980
305
|
};
|
|
2981
306
|
|
|
2982
307
|
function animateSingleValue(value, keyframes, options) {
|
|
@@ -3035,7 +360,7 @@ function delay(callback, timeout) {
|
|
|
3035
360
|
callback(elapsed - timeout);
|
|
3036
361
|
}
|
|
3037
362
|
};
|
|
3038
|
-
motionDom.frame.
|
|
363
|
+
motionDom.frame.setup(checkElapsed, true);
|
|
3039
364
|
return () => motionDom.cancelFrame(checkElapsed);
|
|
3040
365
|
}
|
|
3041
366
|
|
|
@@ -3066,14 +391,14 @@ function resolveMotionValue(value) {
|
|
|
3066
391
|
const borders = ["TopLeft", "TopRight", "BottomLeft", "BottomRight"];
|
|
3067
392
|
const numBorders = borders.length;
|
|
3068
393
|
const asNumber = (value) => typeof value === "string" ? parseFloat(value) : value;
|
|
3069
|
-
const isPx = (value) => typeof value === "number" || px.test(value);
|
|
394
|
+
const isPx = (value) => typeof value === "number" || motionDom.px.test(value);
|
|
3070
395
|
function mixValues(target, follow, lead, progress, shouldCrossfadeOpacity, isOnlyMember) {
|
|
3071
396
|
if (shouldCrossfadeOpacity) {
|
|
3072
|
-
target.opacity = mixNumber
|
|
3073
|
-
target.opacityExit = mixNumber
|
|
397
|
+
target.opacity = motionDom.mixNumber(0, lead.opacity ?? 1, easeCrossfadeIn(progress));
|
|
398
|
+
target.opacityExit = motionDom.mixNumber(follow.opacity ?? 1, 0, easeCrossfadeOut(progress));
|
|
3074
399
|
}
|
|
3075
400
|
else if (isOnlyMember) {
|
|
3076
|
-
target.opacity = mixNumber
|
|
401
|
+
target.opacity = motionDom.mixNumber(follow.opacity ?? 1, lead.opacity ?? 1, progress);
|
|
3077
402
|
}
|
|
3078
403
|
/**
|
|
3079
404
|
* Mix border radius
|
|
@@ -3090,8 +415,8 @@ function mixValues(target, follow, lead, progress, shouldCrossfadeOpacity, isOnl
|
|
|
3090
415
|
leadRadius === 0 ||
|
|
3091
416
|
isPx(followRadius) === isPx(leadRadius);
|
|
3092
417
|
if (canMix) {
|
|
3093
|
-
target[borderLabel] = Math.max(mixNumber
|
|
3094
|
-
if (percent.test(leadRadius) || percent.test(followRadius)) {
|
|
418
|
+
target[borderLabel] = Math.max(motionDom.mixNumber(asNumber(followRadius), asNumber(leadRadius), progress), 0);
|
|
419
|
+
if (motionDom.percent.test(leadRadius) || motionDom.percent.test(followRadius)) {
|
|
3095
420
|
target[borderLabel] += "%";
|
|
3096
421
|
}
|
|
3097
422
|
}
|
|
@@ -3103,7 +428,7 @@ function mixValues(target, follow, lead, progress, shouldCrossfadeOpacity, isOnl
|
|
|
3103
428
|
* Mix rotation
|
|
3104
429
|
*/
|
|
3105
430
|
if (follow.rotate || lead.rotate) {
|
|
3106
|
-
target.rotate = mixNumber
|
|
431
|
+
target.rotate = motionDom.mixNumber(follow.rotate || 0, lead.rotate || 0, progress);
|
|
3107
432
|
}
|
|
3108
433
|
}
|
|
3109
434
|
function getRadius(values, radiusName) {
|
|
@@ -3134,7 +459,7 @@ function getRadius(values, radiusName) {
|
|
|
3134
459
|
// latestLeadValues.backgroundColor as string
|
|
3135
460
|
// )(p)
|
|
3136
461
|
// }
|
|
3137
|
-
const easeCrossfadeIn = /*@__PURE__*/ compress(0, 0.5, circOut);
|
|
462
|
+
const easeCrossfadeIn = /*@__PURE__*/ compress(0, 0.5, motionUtils.circOut);
|
|
3138
463
|
const easeCrossfadeOut = /*@__PURE__*/ compress(0.5, 0.95, motionUtils.noop);
|
|
3139
464
|
function compress(min, max, easing) {
|
|
3140
465
|
return (p) => {
|
|
@@ -3305,7 +630,7 @@ function translateAxis(axis, distance) {
|
|
|
3305
630
|
* and applyAxisDelta
|
|
3306
631
|
*/
|
|
3307
632
|
function transformAxis(axis, axisTranslate, axisScale, boxScale, axisOrigin = 0.5) {
|
|
3308
|
-
const originPoint = mixNumber
|
|
633
|
+
const originPoint = motionDom.mixNumber(axis.min, axis.max, axisOrigin);
|
|
3309
634
|
// Apply the axis delta to the final axis
|
|
3310
635
|
applyAxisDelta(axis, axisTranslate, axisScale, originPoint, boxScale);
|
|
3311
636
|
}
|
|
@@ -3332,14 +657,14 @@ function removePointDelta(point, translate, scale, originPoint, boxScale) {
|
|
|
3332
657
|
* Remove a delta from an axis. This is essentially the steps of applyAxisDelta in reverse
|
|
3333
658
|
*/
|
|
3334
659
|
function removeAxisDelta(axis, translate = 0, scale = 1, origin = 0.5, boxScale, originAxis = axis, sourceAxis = axis) {
|
|
3335
|
-
if (percent.test(translate)) {
|
|
660
|
+
if (motionDom.percent.test(translate)) {
|
|
3336
661
|
translate = parseFloat(translate);
|
|
3337
|
-
const relativeProgress = mixNumber
|
|
662
|
+
const relativeProgress = motionDom.mixNumber(sourceAxis.min, sourceAxis.max, translate / 100);
|
|
3338
663
|
translate = relativeProgress - sourceAxis.min;
|
|
3339
664
|
}
|
|
3340
665
|
if (typeof translate !== "number")
|
|
3341
666
|
return;
|
|
3342
|
-
let originPoint = mixNumber
|
|
667
|
+
let originPoint = motionDom.mixNumber(originAxis.min, originAxis.max, origin);
|
|
3343
668
|
if (axis === originAxis)
|
|
3344
669
|
originPoint -= translate;
|
|
3345
670
|
axis.min = removePointDelta(axis.min, translate, scale, originPoint, boxScale);
|
|
@@ -3523,7 +848,7 @@ const scaleCorrectors = {};
|
|
|
3523
848
|
function addScaleCorrector(correctors) {
|
|
3524
849
|
for (const key in correctors) {
|
|
3525
850
|
scaleCorrectors[key] = correctors[key];
|
|
3526
|
-
if (isCSSVariableName(key)) {
|
|
851
|
+
if (motionDom.isCSSVariableName(key)) {
|
|
3527
852
|
scaleCorrectors[key].isCSSVariable = true;
|
|
3528
853
|
}
|
|
3529
854
|
}
|
|
@@ -4029,7 +1354,7 @@ function createProjectionNode$1({ attachResizeListener, defaultParent, measureSc
|
|
|
4029
1354
|
* to leave a flash of incorrectly styled content.
|
|
4030
1355
|
*/
|
|
4031
1356
|
const now = motionDom.time.now();
|
|
4032
|
-
motionDom.frameData.delta = clamp(0, 1000 / 60, now - motionDom.frameData.timestamp);
|
|
1357
|
+
motionDom.frameData.delta = motionUtils.clamp(0, 1000 / 60, now - motionDom.frameData.timestamp);
|
|
4033
1358
|
motionDom.frameData.timestamp = now;
|
|
4034
1359
|
motionDom.frameData.isProcessing = true;
|
|
4035
1360
|
motionDom.frameSteps.update.process(motionDom.frameData);
|
|
@@ -4571,9 +1896,7 @@ function createProjectionNode$1({ attachResizeListener, defaultParent, measureSc
|
|
|
4571
1896
|
}
|
|
4572
1897
|
setAnimationOrigin(delta, hasOnlyRelativeTargetChanged = false) {
|
|
4573
1898
|
const snapshot = this.snapshot;
|
|
4574
|
-
const snapshotLatestValues = snapshot
|
|
4575
|
-
? snapshot.latestValues
|
|
4576
|
-
: {};
|
|
1899
|
+
const snapshotLatestValues = snapshot ? snapshot.latestValues : {};
|
|
4577
1900
|
const mixedValues = { ...this.latestValues };
|
|
4578
1901
|
const targetDelta = createDelta();
|
|
4579
1902
|
if (!this.relativeParent ||
|
|
@@ -5117,14 +2440,14 @@ function removeLeadSnapshots(stack) {
|
|
|
5117
2440
|
stack.removeLeadSnapshot();
|
|
5118
2441
|
}
|
|
5119
2442
|
function mixAxisDelta(output, delta, p) {
|
|
5120
|
-
output.translate = mixNumber
|
|
5121
|
-
output.scale = mixNumber
|
|
2443
|
+
output.translate = motionDom.mixNumber(delta.translate, 0, p);
|
|
2444
|
+
output.scale = motionDom.mixNumber(delta.scale, 1, p);
|
|
5122
2445
|
output.origin = delta.origin;
|
|
5123
2446
|
output.originPoint = delta.originPoint;
|
|
5124
2447
|
}
|
|
5125
2448
|
function mixAxis(output, from, to, p) {
|
|
5126
|
-
output.min = mixNumber
|
|
5127
|
-
output.max = mixNumber
|
|
2449
|
+
output.min = motionDom.mixNumber(from.min, to.min, p);
|
|
2450
|
+
output.max = motionDom.mixNumber(from.max, to.max, p);
|
|
5128
2451
|
}
|
|
5129
2452
|
function mixBox(output, from, to, p) {
|
|
5130
2453
|
mixAxis(output.x, from.x, to.x, p);
|
|
@@ -5224,7 +2547,7 @@ const correctBorderRadius = {
|
|
|
5224
2547
|
* going to be stretched appropriately. Otherwise, if it's a pixel, convert it to a number.
|
|
5225
2548
|
*/
|
|
5226
2549
|
if (typeof latest === "string") {
|
|
5227
|
-
if (px.test(latest)) {
|
|
2550
|
+
if (motionDom.px.test(latest)) {
|
|
5228
2551
|
latest = parseFloat(latest);
|
|
5229
2552
|
}
|
|
5230
2553
|
else {
|
|
@@ -5244,11 +2567,11 @@ const correctBorderRadius = {
|
|
|
5244
2567
|
const correctBoxShadow = {
|
|
5245
2568
|
correct: (latest, { treeScale, projectionDelta }) => {
|
|
5246
2569
|
const original = latest;
|
|
5247
|
-
const shadow = complex.parse(latest);
|
|
2570
|
+
const shadow = motionDom.complex.parse(latest);
|
|
5248
2571
|
// TODO: Doesn't support multiple shadows
|
|
5249
2572
|
if (shadow.length > 5)
|
|
5250
2573
|
return original;
|
|
5251
|
-
const template = complex.createTransformer(latest);
|
|
2574
|
+
const template = motionDom.complex.createTransformer(latest);
|
|
5252
2575
|
const offset = typeof shadow[0] !== "number" ? 1 : 0;
|
|
5253
2576
|
// Calculate the overall context scale
|
|
5254
2577
|
const xScale = projectionDelta.x.scale * treeScale.x;
|
|
@@ -5261,7 +2584,7 @@ const correctBoxShadow = {
|
|
|
5261
2584
|
* We could potentially improve the outcome of this by incorporating the ratio between
|
|
5262
2585
|
* the two scales.
|
|
5263
2586
|
*/
|
|
5264
|
-
const averageScale = mixNumber
|
|
2587
|
+
const averageScale = motionDom.mixNumber(xScale, yScale, 0.5);
|
|
5265
2588
|
// Blur
|
|
5266
2589
|
if (typeof shadow[2 + offset] === "number")
|
|
5267
2590
|
shadow[2 + offset] /= averageScale;
|
|
@@ -5363,15 +2686,6 @@ function initPrefersReducedMotion() {
|
|
|
5363
2686
|
}
|
|
5364
2687
|
}
|
|
5365
2688
|
|
|
5366
|
-
/**
|
|
5367
|
-
* A list of all ValueTypes
|
|
5368
|
-
*/
|
|
5369
|
-
const valueTypes = [...dimensionValueTypes, color, complex];
|
|
5370
|
-
/**
|
|
5371
|
-
* Tests a value against the list of ValueTypes
|
|
5372
|
-
*/
|
|
5373
|
-
const findValueType = (v) => valueTypes.find(testValueType(v));
|
|
5374
|
-
|
|
5375
2689
|
const visualElementStore = new WeakMap();
|
|
5376
2690
|
|
|
5377
2691
|
function isAnimationControls(v) {
|
|
@@ -5421,7 +2735,7 @@ function updateMotionValuesFromProps(element, next, prev) {
|
|
|
5421
2735
|
* and warn against mismatches.
|
|
5422
2736
|
*/
|
|
5423
2737
|
if (process.env.NODE_ENV === "development") {
|
|
5424
|
-
motionUtils.warnOnce(nextValue.version === "12.7.
|
|
2738
|
+
motionUtils.warnOnce(nextValue.version === "12.7.5-alpha.0", `Attempting to mix Motion versions ${nextValue.version} with 12.7.5-alpha.0 may not work as expected.`);
|
|
5425
2739
|
}
|
|
5426
2740
|
}
|
|
5427
2741
|
else if (isMotionValue(prevValue)) {
|
|
@@ -5548,7 +2862,7 @@ class VisualElement {
|
|
|
5548
2862
|
* value might be provided externally by the component via props.
|
|
5549
2863
|
*/
|
|
5550
2864
|
this.values = new Map();
|
|
5551
|
-
this.KeyframeResolver = KeyframeResolver;
|
|
2865
|
+
this.KeyframeResolver = motionDom.KeyframeResolver;
|
|
5552
2866
|
/**
|
|
5553
2867
|
* Cleanup functions for active features (hover/tap/exit etc)
|
|
5554
2868
|
*/
|
|
@@ -5676,7 +2990,7 @@ class VisualElement {
|
|
|
5676
2990
|
if (this.valueSubscriptions.has(key)) {
|
|
5677
2991
|
this.valueSubscriptions.get(key)();
|
|
5678
2992
|
}
|
|
5679
|
-
const valueIsTransform = transformProps.has(key);
|
|
2993
|
+
const valueIsTransform = motionDom.transformProps.has(key);
|
|
5680
2994
|
if (valueIsTransform && this.onBindTransform) {
|
|
5681
2995
|
this.onBindTransform();
|
|
5682
2996
|
}
|
|
@@ -5886,12 +3200,12 @@ class VisualElement {
|
|
|
5886
3200
|
this.readValueFromInstance(this.current, key, this.options);
|
|
5887
3201
|
if (value !== undefined && value !== null) {
|
|
5888
3202
|
if (typeof value === "string" &&
|
|
5889
|
-
(isNumericalString(value) || isZeroValueString(value))) {
|
|
3203
|
+
(motionUtils.isNumericalString(value) || motionUtils.isZeroValueString(value))) {
|
|
5890
3204
|
// If this is a number read as a string, ie "0" or "200", convert it to a number
|
|
5891
3205
|
value = parseFloat(value);
|
|
5892
3206
|
}
|
|
5893
|
-
else if (!findValueType(value) && complex.test(target)) {
|
|
5894
|
-
value = getAnimatableNone(key, target);
|
|
3207
|
+
else if (!motionDom.findValueType(value) && motionDom.complex.test(target)) {
|
|
3208
|
+
value = motionDom.getAnimatableNone(key, target);
|
|
5895
3209
|
}
|
|
5896
3210
|
this.setBaseTarget(key, isMotionValue(value) ? value.get() : value);
|
|
5897
3211
|
}
|
|
@@ -5955,7 +3269,7 @@ class VisualElement {
|
|
|
5955
3269
|
class DOMVisualElement extends VisualElement {
|
|
5956
3270
|
constructor() {
|
|
5957
3271
|
super(...arguments);
|
|
5958
|
-
this.KeyframeResolver = DOMKeyframesResolver;
|
|
3272
|
+
this.KeyframeResolver = motionDom.DOMKeyframesResolver;
|
|
5959
3273
|
}
|
|
5960
3274
|
sortInstanceNodePosition(a, b) {
|
|
5961
3275
|
/**
|
|
@@ -5990,22 +3304,13 @@ class DOMVisualElement extends VisualElement {
|
|
|
5990
3304
|
}
|
|
5991
3305
|
}
|
|
5992
3306
|
|
|
5993
|
-
/**
|
|
5994
|
-
* Provided a value and a ValueType, returns the value as that value type.
|
|
5995
|
-
*/
|
|
5996
|
-
const getValueAsType = (value, type) => {
|
|
5997
|
-
return type && typeof value === "number"
|
|
5998
|
-
? type.transform(value)
|
|
5999
|
-
: value;
|
|
6000
|
-
};
|
|
6001
|
-
|
|
6002
3307
|
const translateAlias = {
|
|
6003
3308
|
x: "translateX",
|
|
6004
3309
|
y: "translateY",
|
|
6005
3310
|
z: "translateZ",
|
|
6006
3311
|
transformPerspective: "perspective",
|
|
6007
3312
|
};
|
|
6008
|
-
const numTransforms = transformPropOrder.length;
|
|
3313
|
+
const numTransforms = motionDom.transformPropOrder.length;
|
|
6009
3314
|
/**
|
|
6010
3315
|
* Build a CSS transform style from individual x/y/scale etc properties.
|
|
6011
3316
|
*
|
|
@@ -6021,7 +3326,7 @@ function buildTransform(latestValues, transform, transformTemplate) {
|
|
|
6021
3326
|
* are present to the transform string.
|
|
6022
3327
|
*/
|
|
6023
3328
|
for (let i = 0; i < numTransforms; i++) {
|
|
6024
|
-
const key = transformPropOrder[i];
|
|
3329
|
+
const key = motionDom.transformPropOrder[i];
|
|
6025
3330
|
const value = latestValues[key];
|
|
6026
3331
|
if (value === undefined)
|
|
6027
3332
|
continue;
|
|
@@ -6033,7 +3338,7 @@ function buildTransform(latestValues, transform, transformTemplate) {
|
|
|
6033
3338
|
valueIsDefault = parseFloat(value) === 0;
|
|
6034
3339
|
}
|
|
6035
3340
|
if (!valueIsDefault || transformTemplate) {
|
|
6036
|
-
const valueAsType = getValueAsType(value, numberValueTypes[key]);
|
|
3341
|
+
const valueAsType = motionDom.getValueAsType(value, motionDom.numberValueTypes[key]);
|
|
6037
3342
|
if (!valueIsDefault) {
|
|
6038
3343
|
transformIsDefault = false;
|
|
6039
3344
|
const transformName = translateAlias[key] || key;
|
|
@@ -6069,18 +3374,18 @@ function buildHTMLStyles(state, latestValues, transformTemplate) {
|
|
|
6069
3374
|
*/
|
|
6070
3375
|
for (const key in latestValues) {
|
|
6071
3376
|
const value = latestValues[key];
|
|
6072
|
-
if (transformProps.has(key)) {
|
|
3377
|
+
if (motionDom.transformProps.has(key)) {
|
|
6073
3378
|
// If this is a transform, flag to enable further transform processing
|
|
6074
3379
|
hasTransform = true;
|
|
6075
3380
|
continue;
|
|
6076
3381
|
}
|
|
6077
|
-
else if (isCSSVariableName(key)) {
|
|
3382
|
+
else if (motionDom.isCSSVariableName(key)) {
|
|
6078
3383
|
vars[key] = value;
|
|
6079
3384
|
continue;
|
|
6080
3385
|
}
|
|
6081
3386
|
else {
|
|
6082
3387
|
// Convert the value to its default value type, ie 0 -> "0px"
|
|
6083
|
-
const valueAsType = getValueAsType(value, numberValueTypes[key]);
|
|
3388
|
+
const valueAsType = motionDom.getValueAsType(value, motionDom.numberValueTypes[key]);
|
|
6084
3389
|
if (key.startsWith("origin")) {
|
|
6085
3390
|
// If this is a transform origin, flag and enable further transform-origin processing
|
|
6086
3391
|
hasTransformOrigin = true;
|
|
@@ -6123,7 +3428,7 @@ function renderHTML(element, { style, vars }, styleProp, projection) {
|
|
|
6123
3428
|
}
|
|
6124
3429
|
|
|
6125
3430
|
function isForcedMotionValue(key, { layout, layoutId }) {
|
|
6126
|
-
return (transformProps.has(key) ||
|
|
3431
|
+
return (motionDom.transformProps.has(key) ||
|
|
6127
3432
|
key.startsWith("origin") ||
|
|
6128
3433
|
((layout || layoutId !== undefined) &&
|
|
6129
3434
|
(!!scaleCorrectors[key] || key === "opacity")));
|
|
@@ -6144,7 +3449,7 @@ function scrapeMotionValuesFromProps$1(props, prevProps, visualElement) {
|
|
|
6144
3449
|
return newValues;
|
|
6145
3450
|
}
|
|
6146
3451
|
|
|
6147
|
-
function getComputedStyle
|
|
3452
|
+
function getComputedStyle(element) {
|
|
6148
3453
|
return window.getComputedStyle(element);
|
|
6149
3454
|
}
|
|
6150
3455
|
class HTMLVisualElement extends DOMVisualElement {
|
|
@@ -6154,12 +3459,12 @@ class HTMLVisualElement extends DOMVisualElement {
|
|
|
6154
3459
|
this.renderInstance = renderHTML;
|
|
6155
3460
|
}
|
|
6156
3461
|
readValueFromInstance(instance, key) {
|
|
6157
|
-
if (transformProps.has(key)) {
|
|
6158
|
-
return readTransformValue(instance, key);
|
|
3462
|
+
if (motionDom.transformProps.has(key)) {
|
|
3463
|
+
return motionDom.readTransformValue(instance, key);
|
|
6159
3464
|
}
|
|
6160
3465
|
else {
|
|
6161
|
-
const computedStyle = getComputedStyle
|
|
6162
|
-
const value = (isCSSVariableName(key)
|
|
3466
|
+
const computedStyle = getComputedStyle(instance);
|
|
3467
|
+
const value = (motionDom.isCSSVariableName(key)
|
|
6163
3468
|
? computedStyle.getPropertyValue(key)
|
|
6164
3469
|
: computedStyle[key]) || 0;
|
|
6165
3470
|
return typeof value === "string" ? value.trim() : value;
|
|
@@ -6393,7 +3698,7 @@ function animateTarget(visualElement, targetAndTransition, { delay = 0, transiti
|
|
|
6393
3698
|
}
|
|
6394
3699
|
}
|
|
6395
3700
|
addValueToWillChange(visualElement, key);
|
|
6396
|
-
value.start(animateMotionValue(key, value, valueTarget, visualElement.shouldReduceMotion && positionalKeys.has(key)
|
|
3701
|
+
value.start(animateMotionValue(key, value, valueTarget, visualElement.shouldReduceMotion && motionDom.positionalKeys.has(key)
|
|
6397
3702
|
? { type: false }
|
|
6398
3703
|
: valueTransition, visualElement, isHandoff));
|
|
6399
3704
|
const animation = value.animation;
|
|
@@ -7064,7 +4369,7 @@ class PanSession {
|
|
|
7064
4369
|
const { onSessionStart } = handlers;
|
|
7065
4370
|
onSessionStart &&
|
|
7066
4371
|
onSessionStart(event, getPanInfo(initialInfo, this.history));
|
|
7067
|
-
this.removeListeners = pipe(addPointerEvent(this.contextWindow, "pointermove", this.handlePointerMove), addPointerEvent(this.contextWindow, "pointerup", this.handlePointerUp), addPointerEvent(this.contextWindow, "pointercancel", this.handlePointerUp));
|
|
4372
|
+
this.removeListeners = motionUtils.pipe(addPointerEvent(this.contextWindow, "pointermove", this.handlePointerMove), addPointerEvent(this.contextWindow, "pointerup", this.handlePointerUp), addPointerEvent(this.contextWindow, "pointercancel", this.handlePointerUp));
|
|
7068
4373
|
}
|
|
7069
4374
|
updateHandlers(handlers) {
|
|
7070
4375
|
this.handlers = handlers;
|
|
@@ -7138,13 +4443,13 @@ function applyConstraints(point, { min, max }, elastic) {
|
|
|
7138
4443
|
if (min !== undefined && point < min) {
|
|
7139
4444
|
// If we have a min point defined, and this is outside of that, constrain
|
|
7140
4445
|
point = elastic
|
|
7141
|
-
? mixNumber
|
|
4446
|
+
? motionDom.mixNumber(min, point, elastic.min)
|
|
7142
4447
|
: Math.max(point, min);
|
|
7143
4448
|
}
|
|
7144
4449
|
else if (max !== undefined && point > max) {
|
|
7145
4450
|
// If we have a max point defined, and this is outside of that, constrain
|
|
7146
4451
|
point = elastic
|
|
7147
|
-
? mixNumber
|
|
4452
|
+
? motionDom.mixNumber(max, point, elastic.max)
|
|
7148
4453
|
: Math.min(point, max);
|
|
7149
4454
|
}
|
|
7150
4455
|
return point;
|
|
@@ -7209,7 +4514,7 @@ function calcOrigin$1(source, target) {
|
|
|
7209
4514
|
else if (sourceLength > targetLength) {
|
|
7210
4515
|
origin = motionUtils.progress(source.min, source.max - targetLength, target.min);
|
|
7211
4516
|
}
|
|
7212
|
-
return clamp(0, 1, origin);
|
|
4517
|
+
return motionUtils.clamp(0, 1, origin);
|
|
7213
4518
|
}
|
|
7214
4519
|
/**
|
|
7215
4520
|
* Rebase the calculated viewport constraints relative to the layout.min point.
|
|
@@ -7316,7 +4621,7 @@ class VisualElementDragControls {
|
|
|
7316
4621
|
/**
|
|
7317
4622
|
* If the MotionValue is a percentage value convert to px
|
|
7318
4623
|
*/
|
|
7319
|
-
if (percent.test(current)) {
|
|
4624
|
+
if (motionDom.percent.test(current)) {
|
|
7320
4625
|
const { projection } = this.visualElement;
|
|
7321
4626
|
if (projection && projection.layout) {
|
|
7322
4627
|
const measuredAxis = projection.layout.layoutBox[axis];
|
|
@@ -7563,7 +4868,7 @@ class VisualElementDragControls {
|
|
|
7563
4868
|
const axisValue = this.getAxisMotionValue(axis);
|
|
7564
4869
|
if (projection && projection.layout) {
|
|
7565
4870
|
const { min, max } = projection.layout.layoutBox[axis];
|
|
7566
|
-
axisValue.set(point[axis] - mixNumber
|
|
4871
|
+
axisValue.set(point[axis] - motionDom.mixNumber(min, max, 0.5));
|
|
7567
4872
|
}
|
|
7568
4873
|
});
|
|
7569
4874
|
}
|
|
@@ -7618,7 +4923,7 @@ class VisualElementDragControls {
|
|
|
7618
4923
|
*/
|
|
7619
4924
|
const axisValue = this.getAxisMotionValue(axis);
|
|
7620
4925
|
const { min, max } = this.constraints[axis];
|
|
7621
|
-
axisValue.set(mixNumber
|
|
4926
|
+
axisValue.set(motionDom.mixNumber(min, max, boxProgress[axis]));
|
|
7622
4927
|
});
|
|
7623
4928
|
}
|
|
7624
4929
|
addListeners() {
|
|
@@ -7966,7 +5271,7 @@ class FocusGesture extends Feature {
|
|
|
7966
5271
|
this.isActive = false;
|
|
7967
5272
|
}
|
|
7968
5273
|
mount() {
|
|
7969
|
-
this.unmount = pipe(addDomEvent(this.node.current, "focus", () => this.onFocus()), addDomEvent(this.node.current, "blur", () => this.onBlur()));
|
|
5274
|
+
this.unmount = motionUtils.pipe(addDomEvent(this.node.current, "focus", () => this.onFocus()), addDomEvent(this.node.current, "blur", () => this.onBlur()));
|
|
7970
5275
|
}
|
|
7971
5276
|
unmount() { }
|
|
7972
5277
|
}
|
|
@@ -8538,17 +5843,17 @@ function buildSVGPath(attrs, length, spacing = 1, offset = 0, useDashCase = true
|
|
|
8538
5843
|
// when defining props on a React component.
|
|
8539
5844
|
const keys = useDashCase ? dashKeys : camelKeys;
|
|
8540
5845
|
// Build the dash offset
|
|
8541
|
-
attrs[keys.offset] = px.transform(-offset);
|
|
5846
|
+
attrs[keys.offset] = motionDom.px.transform(-offset);
|
|
8542
5847
|
// Build the dash array
|
|
8543
|
-
const pathLength = px.transform(length);
|
|
8544
|
-
const pathSpacing = px.transform(spacing);
|
|
5848
|
+
const pathLength = motionDom.px.transform(length);
|
|
5849
|
+
const pathSpacing = motionDom.px.transform(spacing);
|
|
8545
5850
|
attrs[keys.array] = `${pathLength} ${pathSpacing}`;
|
|
8546
5851
|
}
|
|
8547
5852
|
|
|
8548
5853
|
function calcOrigin(origin, offset, size) {
|
|
8549
5854
|
return typeof origin === "string"
|
|
8550
5855
|
? origin
|
|
8551
|
-
: px.transform(offset + size * origin);
|
|
5856
|
+
: motionDom.px.transform(offset + size * origin);
|
|
8552
5857
|
}
|
|
8553
5858
|
/**
|
|
8554
5859
|
* The SVG transform origin defaults are different to CSS and is less intuitive,
|
|
@@ -8800,7 +6105,7 @@ function scrapeMotionValuesFromProps(props, prevProps, visualElement) {
|
|
|
8800
6105
|
for (const key in props) {
|
|
8801
6106
|
if (isMotionValue(props[key]) ||
|
|
8802
6107
|
isMotionValue(prevProps[key])) {
|
|
8803
|
-
const targetKey = transformPropOrder.indexOf(key) !== -1
|
|
6108
|
+
const targetKey = motionDom.transformPropOrder.indexOf(key) !== -1
|
|
8804
6109
|
? "attr" + key.charAt(0).toUpperCase() + key.substring(1)
|
|
8805
6110
|
: key;
|
|
8806
6111
|
newValues[targetKey] = props[key];
|
|
@@ -8820,7 +6125,7 @@ const svgMotionConfig = {
|
|
|
8820
6125
|
let hasTransform = !!props.drag;
|
|
8821
6126
|
if (!hasTransform) {
|
|
8822
6127
|
for (const key in latestValues) {
|
|
8823
|
-
if (transformProps.has(key)) {
|
|
6128
|
+
if (motionDom.transformProps.has(key)) {
|
|
8824
6129
|
hasTransform = true;
|
|
8825
6130
|
break;
|
|
8826
6131
|
}
|
|
@@ -8887,8 +6192,8 @@ class SVGVisualElement extends DOMVisualElement {
|
|
|
8887
6192
|
return props[key];
|
|
8888
6193
|
}
|
|
8889
6194
|
readValueFromInstance(instance, key) {
|
|
8890
|
-
if (transformProps.has(key)) {
|
|
8891
|
-
const defaultType = getDefaultValueType(key);
|
|
6195
|
+
if (motionDom.transformProps.has(key)) {
|
|
6196
|
+
const defaultType = motionDom.getDefaultValueType(key);
|
|
8892
6197
|
return defaultType ? defaultType.default || 0 : 0;
|
|
8893
6198
|
}
|
|
8894
6199
|
key = !camelCaseAttributes.has(key) ? camelToDash(key) : key;
|
|
@@ -8929,7 +6234,6 @@ const createMotionComponent = /*@__PURE__*/ createMotionComponentFactory({
|
|
|
8929
6234
|
...layout,
|
|
8930
6235
|
}, createDomVisualElement);
|
|
8931
6236
|
|
|
8932
|
-
exports.AcceleratedAnimation = AcceleratedAnimation;
|
|
8933
6237
|
exports.FlatTree = FlatTree;
|
|
8934
6238
|
exports.HTMLVisualElement = HTMLVisualElement;
|
|
8935
6239
|
exports.LayoutGroupContext = LayoutGroupContext;
|
|
@@ -8940,79 +6244,46 @@ exports.PresenceContext = PresenceContext;
|
|
|
8940
6244
|
exports.SVGVisualElement = SVGVisualElement;
|
|
8941
6245
|
exports.SwitchLayoutGroupContext = SwitchLayoutGroupContext;
|
|
8942
6246
|
exports.VisualElement = VisualElement;
|
|
8943
|
-
exports.acceleratedValues = acceleratedValues;
|
|
8944
6247
|
exports.addDomEvent = addDomEvent;
|
|
8945
6248
|
exports.addPointerEvent = addPointerEvent;
|
|
8946
6249
|
exports.addPointerInfo = addPointerInfo;
|
|
8947
6250
|
exports.addScaleCorrector = addScaleCorrector;
|
|
8948
6251
|
exports.animateSingleValue = animateSingleValue;
|
|
8949
6252
|
exports.animateTarget = animateTarget;
|
|
8950
|
-
exports.animateValue = animateValue;
|
|
8951
6253
|
exports.animateVisualElement = animateVisualElement;
|
|
8952
6254
|
exports.animations = animations;
|
|
8953
|
-
exports.anticipate = anticipate;
|
|
8954
|
-
exports.backIn = backIn;
|
|
8955
|
-
exports.backInOut = backInOut;
|
|
8956
|
-
exports.backOut = backOut;
|
|
8957
6255
|
exports.buildTransform = buildTransform;
|
|
8958
6256
|
exports.calcLength = calcLength;
|
|
8959
6257
|
exports.camelToDash = camelToDash;
|
|
8960
|
-
exports.circIn = circIn;
|
|
8961
|
-
exports.circInOut = circInOut;
|
|
8962
|
-
exports.circOut = circOut;
|
|
8963
|
-
exports.clamp = clamp;
|
|
8964
|
-
exports.color = color;
|
|
8965
|
-
exports.complex = complex;
|
|
8966
6258
|
exports.createBox = createBox;
|
|
8967
6259
|
exports.createDomVisualElement = createDomVisualElement;
|
|
8968
6260
|
exports.createMotionComponent = createMotionComponent;
|
|
8969
6261
|
exports.createMotionComponentFactory = createMotionComponentFactory;
|
|
8970
6262
|
exports.createRendererMotionComponent = createRendererMotionComponent;
|
|
8971
|
-
exports.cubicBezier = cubicBezier;
|
|
8972
|
-
exports.defaultOffset = defaultOffset;
|
|
8973
6263
|
exports.delay = delay;
|
|
8974
6264
|
exports.distance = distance;
|
|
8975
6265
|
exports.distance2D = distance2D;
|
|
8976
6266
|
exports.drag = drag;
|
|
8977
|
-
exports.easeIn = easeIn;
|
|
8978
|
-
exports.easeInOut = easeInOut;
|
|
8979
|
-
exports.easeOut = easeOut;
|
|
8980
|
-
exports.easingDefinitionToFunction = easingDefinitionToFunction;
|
|
8981
|
-
exports.fillOffset = fillOffset;
|
|
8982
6267
|
exports.filterProps = filterProps;
|
|
8983
|
-
exports.findSpring = findSpring;
|
|
8984
6268
|
exports.gestureAnimations = gestureAnimations;
|
|
8985
6269
|
exports.getOptimisedAppearId = getOptimisedAppearId;
|
|
8986
6270
|
exports.hasReducedMotionListener = hasReducedMotionListener;
|
|
8987
|
-
exports.inertia = inertia;
|
|
8988
6271
|
exports.initPrefersReducedMotion = initPrefersReducedMotion;
|
|
8989
|
-
exports.instantAnimationState = instantAnimationState;
|
|
8990
|
-
exports.interpolate = interpolate;
|
|
8991
6272
|
exports.isBrowser = isBrowser;
|
|
8992
|
-
exports.isEasingArray = isEasingArray;
|
|
8993
6273
|
exports.isMotionValue = isMotionValue;
|
|
8994
6274
|
exports.isSVGElement = isSVGElement;
|
|
8995
6275
|
exports.isValidMotionProp = isValidMotionProp;
|
|
8996
|
-
exports.keyframes = keyframes;
|
|
8997
6276
|
exports.layout = layout;
|
|
8998
6277
|
exports.loadExternalIsValidProp = loadExternalIsValidProp;
|
|
8999
6278
|
exports.loadFeatures = loadFeatures;
|
|
9000
6279
|
exports.makeUseVisualState = makeUseVisualState;
|
|
9001
|
-
exports.mirrorEasing = mirrorEasing;
|
|
9002
|
-
exports.mix = mix;
|
|
9003
|
-
exports.mixNumber = mixNumber$1;
|
|
9004
6280
|
exports.motionComponentSymbol = motionComponentSymbol;
|
|
9005
6281
|
exports.optimizedAppearDataAttribute = optimizedAppearDataAttribute;
|
|
9006
6282
|
exports.optimizedAppearDataId = optimizedAppearDataId;
|
|
9007
|
-
exports.pipe = pipe;
|
|
9008
6283
|
exports.prefersReducedMotion = prefersReducedMotion;
|
|
9009
|
-
exports.px = px;
|
|
9010
6284
|
exports.resolveMotionValue = resolveMotionValue;
|
|
9011
|
-
exports.reverseEasing = reverseEasing;
|
|
9012
6285
|
exports.rootProjectionNode = rootProjectionNode;
|
|
9013
6286
|
exports.setTarget = setTarget;
|
|
9014
|
-
exports.spring = spring;
|
|
9015
|
-
exports.transformProps = transformProps;
|
|
9016
6287
|
exports.useConstant = useConstant;
|
|
9017
6288
|
exports.useIsPresent = useIsPresent;
|
|
9018
6289
|
exports.useIsomorphicLayoutEffect = useIsomorphicLayoutEffect;
|