@betterstore/react 0.2.42 → 0.2.44
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/CHANGELOG.md +12 -0
- package/dist/components/checkout-embed/steps/payment/form.d.ts +3 -2
- package/dist/components/payment-element/index.d.ts +2 -1
- package/dist/index.cjs.js +1645 -1730
- package/dist/index.mjs +1645 -1730
- package/package.json +2 -2
package/dist/index.cjs.js
CHANGED
|
@@ -3274,7 +3274,7 @@ const CheckoutEmbed$2 = {
|
|
|
3274
3274
|
day: "den",
|
|
3275
3275
|
days: "dny",
|
|
3276
3276
|
estimatedDeliveryDate: "Odhadované datum dodání:",
|
|
3277
|
-
|
|
3277
|
+
address: "Adresa:",
|
|
3278
3278
|
shipping: "Doprava:",
|
|
3279
3279
|
title: "Přeprava",
|
|
3280
3280
|
description: {
|
|
@@ -3341,7 +3341,7 @@ const CheckoutEmbed$1 = {
|
|
|
3341
3341
|
Shipping: {
|
|
3342
3342
|
title: "Shipping",
|
|
3343
3343
|
change: "Change",
|
|
3344
|
-
|
|
3344
|
+
address: "Address:",
|
|
3345
3345
|
contact: "Contact:",
|
|
3346
3346
|
button: "Continue to checkout",
|
|
3347
3347
|
back: "Back to information",
|
|
@@ -3943,8 +3943,10 @@ const AnimatePresence = ({ children, custom, initial = true, onExitComplete, pre
|
|
|
3943
3943
|
};
|
|
3944
3944
|
|
|
3945
3945
|
const stepsOrder = [
|
|
3946
|
+
"setup", // Compute
|
|
3946
3947
|
"read", // Read
|
|
3947
3948
|
"resolveKeyframes", // Write/Read/Write/Read
|
|
3949
|
+
"preUpdate", // Compute
|
|
3948
3950
|
"update", // Compute
|
|
3949
3951
|
"preRender", // Compute
|
|
3950
3952
|
"render", // Write
|
|
@@ -4043,9 +4045,7 @@ function createRenderStep(runNextFrame, stepName) {
|
|
|
4043
4045
|
return step;
|
|
4044
4046
|
}
|
|
4045
4047
|
|
|
4046
|
-
const MotionGlobalConfig = {
|
|
4047
|
-
useManualTiming: false,
|
|
4048
|
-
};
|
|
4048
|
+
const MotionGlobalConfig = {};
|
|
4049
4049
|
|
|
4050
4050
|
const maxElapsed = 40;
|
|
4051
4051
|
function createRenderBatcher(scheduleNextBatch, allowKeepAlive) {
|
|
@@ -4061,11 +4061,13 @@ function createRenderBatcher(scheduleNextBatch, allowKeepAlive) {
|
|
|
4061
4061
|
acc[key] = createRenderStep(flagRunNextFrame, allowKeepAlive ? key : undefined);
|
|
4062
4062
|
return acc;
|
|
4063
4063
|
}, {});
|
|
4064
|
-
const { read, resolveKeyframes, update, preRender, render, postRender } = steps;
|
|
4064
|
+
const { setup, read, resolveKeyframes, preUpdate, update, preRender, render, postRender, } = steps;
|
|
4065
4065
|
const processBatch = () => {
|
|
4066
|
-
const timestamp =
|
|
4066
|
+
const timestamp = MotionGlobalConfig.useManualTiming
|
|
4067
|
+
? state.timestamp
|
|
4068
|
+
: performance.now();
|
|
4067
4069
|
runNextFrame = false;
|
|
4068
|
-
{
|
|
4070
|
+
if (!MotionGlobalConfig.useManualTiming) {
|
|
4069
4071
|
state.delta = useDefaultElapsed
|
|
4070
4072
|
? 1000 / 60
|
|
4071
4073
|
: Math.max(Math.min(timestamp - state.timestamp, maxElapsed), 1);
|
|
@@ -4073,8 +4075,10 @@ function createRenderBatcher(scheduleNextBatch, allowKeepAlive) {
|
|
|
4073
4075
|
state.timestamp = timestamp;
|
|
4074
4076
|
state.isProcessing = true;
|
|
4075
4077
|
// Unrolled render loop for better per-frame performance
|
|
4078
|
+
setup.process(state);
|
|
4076
4079
|
read.process(state);
|
|
4077
4080
|
resolveKeyframes.process(state);
|
|
4081
|
+
preUpdate.process(state);
|
|
4078
4082
|
update.process(state);
|
|
4079
4083
|
preRender.process(state);
|
|
4080
4084
|
render.process(state);
|
|
@@ -4689,7 +4693,7 @@ const transformPropOrder = [
|
|
|
4689
4693
|
/**
|
|
4690
4694
|
* A quick lookup for transform props.
|
|
4691
4695
|
*/
|
|
4692
|
-
const transformProps = new Set(transformPropOrder);
|
|
4696
|
+
const transformProps = /*@__PURE__*/ (() => new Set(transformPropOrder))();
|
|
4693
4697
|
|
|
4694
4698
|
function isForcedMotionValue(key, { layout, layoutId }) {
|
|
4695
4699
|
return (transformProps.has(key) ||
|
|
@@ -4731,6 +4735,12 @@ const scale = {
|
|
|
4731
4735
|
default: 1,
|
|
4732
4736
|
};
|
|
4733
4737
|
|
|
4738
|
+
const int = {
|
|
4739
|
+
...number,
|
|
4740
|
+
transform: Math.round,
|
|
4741
|
+
};
|
|
4742
|
+
|
|
4743
|
+
/*#__NO_SIDE_EFFECTS__*/
|
|
4734
4744
|
const createUnitType = (unit) => ({
|
|
4735
4745
|
test: (v) => typeof v === "string" && v.endsWith(unit) && v.split(" ").length === 1,
|
|
4736
4746
|
parse: parseFloat,
|
|
@@ -4741,13 +4751,40 @@ const percent = /*@__PURE__*/ createUnitType("%");
|
|
|
4741
4751
|
const px = /*@__PURE__*/ createUnitType("px");
|
|
4742
4752
|
const vh = /*@__PURE__*/ createUnitType("vh");
|
|
4743
4753
|
const vw = /*@__PURE__*/ createUnitType("vw");
|
|
4744
|
-
const progressPercentage = {
|
|
4754
|
+
const progressPercentage = /*@__PURE__*/ (() => ({
|
|
4745
4755
|
...percent,
|
|
4746
4756
|
parse: (v) => percent.parse(v) / 100,
|
|
4747
4757
|
transform: (v) => percent.transform(v * 100),
|
|
4758
|
+
}))();
|
|
4759
|
+
|
|
4760
|
+
const transformValueTypes = {
|
|
4761
|
+
rotate: degrees,
|
|
4762
|
+
rotateX: degrees,
|
|
4763
|
+
rotateY: degrees,
|
|
4764
|
+
rotateZ: degrees,
|
|
4765
|
+
scale,
|
|
4766
|
+
scaleX: scale,
|
|
4767
|
+
scaleY: scale,
|
|
4768
|
+
scaleZ: scale,
|
|
4769
|
+
skew: degrees,
|
|
4770
|
+
skewX: degrees,
|
|
4771
|
+
skewY: degrees,
|
|
4772
|
+
distance: px,
|
|
4773
|
+
translateX: px,
|
|
4774
|
+
translateY: px,
|
|
4775
|
+
translateZ: px,
|
|
4776
|
+
x: px,
|
|
4777
|
+
y: px,
|
|
4778
|
+
z: px,
|
|
4779
|
+
perspective: px,
|
|
4780
|
+
transformPerspective: px,
|
|
4781
|
+
opacity: alpha,
|
|
4782
|
+
originX: progressPercentage,
|
|
4783
|
+
originY: progressPercentage,
|
|
4784
|
+
originZ: px,
|
|
4748
4785
|
};
|
|
4749
4786
|
|
|
4750
|
-
const
|
|
4787
|
+
const numberValueTypes = {
|
|
4751
4788
|
// Border props
|
|
4752
4789
|
borderWidth: px,
|
|
4753
4790
|
borderTopWidth: px,
|
|
@@ -4783,45 +4820,8 @@ const browserNumberValueTypes = {
|
|
|
4783
4820
|
// Misc
|
|
4784
4821
|
backgroundPositionX: px,
|
|
4785
4822
|
backgroundPositionY: px,
|
|
4786
|
-
};
|
|
4787
|
-
|
|
4788
|
-
const transformValueTypes = {
|
|
4789
|
-
rotate: degrees,
|
|
4790
|
-
rotateX: degrees,
|
|
4791
|
-
rotateY: degrees,
|
|
4792
|
-
rotateZ: degrees,
|
|
4793
|
-
scale,
|
|
4794
|
-
scaleX: scale,
|
|
4795
|
-
scaleY: scale,
|
|
4796
|
-
scaleZ: scale,
|
|
4797
|
-
skew: degrees,
|
|
4798
|
-
skewX: degrees,
|
|
4799
|
-
skewY: degrees,
|
|
4800
|
-
distance: px,
|
|
4801
|
-
translateX: px,
|
|
4802
|
-
translateY: px,
|
|
4803
|
-
translateZ: px,
|
|
4804
|
-
x: px,
|
|
4805
|
-
y: px,
|
|
4806
|
-
z: px,
|
|
4807
|
-
perspective: px,
|
|
4808
|
-
transformPerspective: px,
|
|
4809
|
-
opacity: alpha,
|
|
4810
|
-
originX: progressPercentage,
|
|
4811
|
-
originY: progressPercentage,
|
|
4812
|
-
originZ: px,
|
|
4813
|
-
};
|
|
4814
|
-
|
|
4815
|
-
const int = {
|
|
4816
|
-
...number,
|
|
4817
|
-
transform: Math.round,
|
|
4818
|
-
};
|
|
4819
|
-
|
|
4820
|
-
const numberValueTypes = {
|
|
4821
|
-
...browserNumberValueTypes,
|
|
4822
4823
|
...transformValueTypes,
|
|
4823
4824
|
zIndex: int,
|
|
4824
|
-
size: px,
|
|
4825
4825
|
// SVG
|
|
4826
4826
|
fillOpacity: alpha,
|
|
4827
4827
|
strokeOpacity: alpha,
|
|
@@ -5234,28 +5234,13 @@ function resolveVariantFromProps(props, definition, custom, visualElement) {
|
|
|
5234
5234
|
return definition;
|
|
5235
5235
|
}
|
|
5236
5236
|
|
|
5237
|
-
const isKeyframesTarget = (v) => {
|
|
5238
|
-
return Array.isArray(v);
|
|
5239
|
-
};
|
|
5240
|
-
|
|
5241
|
-
const isCustomValue = (v) => {
|
|
5242
|
-
return Boolean(v && typeof v === "object" && v.mix && v.toValue);
|
|
5243
|
-
};
|
|
5244
|
-
const resolveFinalValueInKeyframes = (v) => {
|
|
5245
|
-
// TODO maybe throw if v.length - 1 is placeholder token?
|
|
5246
|
-
return isKeyframesTarget(v) ? v[v.length - 1] || 0 : v;
|
|
5247
|
-
};
|
|
5248
|
-
|
|
5249
5237
|
/**
|
|
5250
5238
|
* If the provided value is a MotionValue, this returns the actual value, otherwise just the value itself
|
|
5251
5239
|
*
|
|
5252
5240
|
* TODO: Remove and move to library
|
|
5253
5241
|
*/
|
|
5254
5242
|
function resolveMotionValue(value) {
|
|
5255
|
-
|
|
5256
|
-
return isCustomValue(unwrappedValue)
|
|
5257
|
-
? unwrappedValue.toValue()
|
|
5258
|
-
: unwrappedValue;
|
|
5243
|
+
return isMotionValue(value) ? value.get() : value;
|
|
5259
5244
|
}
|
|
5260
5245
|
|
|
5261
5246
|
function makeState({ scrapeMotionValuesFromProps, createRenderState, onUpdate, }, props, context, presenceContext) {
|
|
@@ -5501,15 +5486,9 @@ function resolveVariant(visualElement, definition, custom) {
|
|
|
5501
5486
|
return resolveVariantFromProps(props, definition, custom !== undefined ? custom : props.custom, visualElement);
|
|
5502
5487
|
}
|
|
5503
5488
|
|
|
5504
|
-
const
|
|
5505
|
-
|
|
5506
|
-
|
|
5507
|
-
"top",
|
|
5508
|
-
"left",
|
|
5509
|
-
"right",
|
|
5510
|
-
"bottom",
|
|
5511
|
-
...transformPropOrder,
|
|
5512
|
-
]);
|
|
5489
|
+
const isKeyframesTarget = (v) => {
|
|
5490
|
+
return Array.isArray(v);
|
|
5491
|
+
};
|
|
5513
5492
|
|
|
5514
5493
|
let now;
|
|
5515
5494
|
function clearTime() {
|
|
@@ -5620,7 +5599,7 @@ class MotionValue {
|
|
|
5620
5599
|
* This will be replaced by the build step with the latest version number.
|
|
5621
5600
|
* When MotionValues are provided to motion components, warn if versions are mixed.
|
|
5622
5601
|
*/
|
|
5623
|
-
this.version = "12.
|
|
5602
|
+
this.version = "12.8.0";
|
|
5624
5603
|
/**
|
|
5625
5604
|
* Tracks whether this value can output a velocity. Currently this is only true
|
|
5626
5605
|
* if the value is numerical, but we might be able to widen the scope here and support
|
|
@@ -5764,6 +5743,8 @@ class MotionValue {
|
|
|
5764
5743
|
* @public
|
|
5765
5744
|
*/
|
|
5766
5745
|
set(v, render = true) {
|
|
5746
|
+
if (v === "none")
|
|
5747
|
+
console.trace();
|
|
5767
5748
|
if (!render || !this.passiveEffect) {
|
|
5768
5749
|
this.updateAndNotify(v, render);
|
|
5769
5750
|
}
|
|
@@ -5884,6 +5865,7 @@ class MotionValue {
|
|
|
5884
5865
|
* @public
|
|
5885
5866
|
*/
|
|
5886
5867
|
destroy() {
|
|
5868
|
+
this.events.destroy?.notify();
|
|
5887
5869
|
this.clearListeners();
|
|
5888
5870
|
this.stop();
|
|
5889
5871
|
if (this.stopPassiveEffect) {
|
|
@@ -5907,6 +5889,10 @@ function setMotionValue(visualElement, key, value) {
|
|
|
5907
5889
|
visualElement.addValue(key, motionValue(value));
|
|
5908
5890
|
}
|
|
5909
5891
|
}
|
|
5892
|
+
function resolveFinalValueInKeyframes(v) {
|
|
5893
|
+
// TODO maybe throw if v.length - 1 is placeholder token?
|
|
5894
|
+
return isKeyframesTarget(v) ? v[v.length - 1] || 0 : v;
|
|
5895
|
+
}
|
|
5910
5896
|
function setTarget(visualElement, definition) {
|
|
5911
5897
|
const resolved = resolveVariant(visualElement, definition);
|
|
5912
5898
|
let { transitionEnd = {}, transition = {}, ...target } = resolved || {};
|
|
@@ -5941,89 +5927,79 @@ function getOptimisedAppearId(visualElement) {
|
|
|
5941
5927
|
return visualElement.props[optimizedAppearDataAttribute];
|
|
5942
5928
|
}
|
|
5943
5929
|
|
|
5944
|
-
|
|
5945
|
-
|
|
5946
|
-
|
|
5947
|
-
|
|
5948
|
-
|
|
5949
|
-
|
|
5950
|
-
|
|
5951
|
-
|
|
5952
|
-
usually improved times, depending on the curve.
|
|
5953
|
-
I also removed the lookup table, as for the added bundle size and loop we're
|
|
5954
|
-
only cutting ~4 or so subdivision iterations. I bumped the max iterations up
|
|
5955
|
-
to 12 to compensate and this still tended to be faster for no perceivable
|
|
5956
|
-
loss in accuracy.
|
|
5957
|
-
Usage
|
|
5958
|
-
const easeOut = cubicBezier(.17,.67,.83,.67);
|
|
5959
|
-
const x = easeOut(0.5); // returns 0.627...
|
|
5960
|
-
*/
|
|
5961
|
-
// Returns x(t) given t, x1, and x2, or y(t) given t, y1, and y2.
|
|
5962
|
-
const calcBezier = (t, a1, a2) => (((1.0 - 3.0 * a2 + 3.0 * a1) * t + (3.0 * a2 - 6.0 * a1)) * t + 3.0 * a1) *
|
|
5963
|
-
t;
|
|
5964
|
-
const subdivisionPrecision = 0.0000001;
|
|
5965
|
-
const subdivisionMaxIterations = 12;
|
|
5966
|
-
function binarySubdivide(x, lowerBound, upperBound, mX1, mX2) {
|
|
5967
|
-
let currentX;
|
|
5968
|
-
let currentT;
|
|
5969
|
-
let i = 0;
|
|
5970
|
-
do {
|
|
5971
|
-
currentT = lowerBound + (upperBound - lowerBound) / 2.0;
|
|
5972
|
-
currentX = calcBezier(currentT, mX1, mX2) - x;
|
|
5973
|
-
if (currentX > 0.0) {
|
|
5974
|
-
upperBound = currentT;
|
|
5975
|
-
}
|
|
5976
|
-
else {
|
|
5977
|
-
lowerBound = currentT;
|
|
5978
|
-
}
|
|
5979
|
-
} while (Math.abs(currentX) > subdivisionPrecision &&
|
|
5980
|
-
++i < subdivisionMaxIterations);
|
|
5981
|
-
return currentT;
|
|
5982
|
-
}
|
|
5983
|
-
function cubicBezier(mX1, mY1, mX2, mY2) {
|
|
5984
|
-
// If this is a linear gradient, return linear easing
|
|
5985
|
-
if (mX1 === mY1 && mX2 === mY2)
|
|
5986
|
-
return noop;
|
|
5987
|
-
const getTForX = (aX) => binarySubdivide(aX, 0, 1, mX1, mX2);
|
|
5988
|
-
// If animation is at start/end, return t without easing
|
|
5989
|
-
return (t) => t === 0 || t === 1 ? t : calcBezier(getTForX(t), mY1, mY2);
|
|
5930
|
+
const isNotNull$1 = (value) => value !== null;
|
|
5931
|
+
function getFinalKeyframe$1(keyframes, { repeat, repeatType = "loop" }, finalKeyframe) {
|
|
5932
|
+
const resolvedKeyframes = keyframes.filter(isNotNull$1);
|
|
5933
|
+
const index = repeat && repeatType !== "loop" && repeat % 2 === 1
|
|
5934
|
+
? 0
|
|
5935
|
+
: resolvedKeyframes.length - 1;
|
|
5936
|
+
return resolvedKeyframes[index]
|
|
5937
|
+
;
|
|
5990
5938
|
}
|
|
5991
5939
|
|
|
5992
|
-
|
|
5993
|
-
|
|
5994
|
-
|
|
5995
|
-
|
|
5996
|
-
|
|
5997
|
-
|
|
5998
|
-
const
|
|
5999
|
-
|
|
6000
|
-
|
|
6001
|
-
|
|
6002
|
-
|
|
6003
|
-
|
|
6004
|
-
const
|
|
6005
|
-
|
|
6006
|
-
|
|
6007
|
-
|
|
6008
|
-
const circInOut = mirrorEasing(circIn);
|
|
6009
|
-
|
|
5940
|
+
const underDampedSpring = {
|
|
5941
|
+
type: "spring",
|
|
5942
|
+
stiffness: 500,
|
|
5943
|
+
damping: 25,
|
|
5944
|
+
restSpeed: 10,
|
|
5945
|
+
};
|
|
5946
|
+
const criticallyDampedSpring = (target) => ({
|
|
5947
|
+
type: "spring",
|
|
5948
|
+
stiffness: 550,
|
|
5949
|
+
damping: target === 0 ? 2 * Math.sqrt(550) : 30,
|
|
5950
|
+
restSpeed: 10,
|
|
5951
|
+
});
|
|
5952
|
+
const keyframesTransition = {
|
|
5953
|
+
type: "keyframes",
|
|
5954
|
+
duration: 0.8,
|
|
5955
|
+
};
|
|
6010
5956
|
/**
|
|
6011
|
-
*
|
|
5957
|
+
* Default easing curve is a slightly shallower version of
|
|
5958
|
+
* the default browser easing curve.
|
|
6012
5959
|
*/
|
|
6013
|
-
const
|
|
6014
|
-
|
|
6015
|
-
|
|
6016
|
-
|
|
6017
|
-
|
|
6018
|
-
|
|
6019
|
-
|
|
6020
|
-
return
|
|
5960
|
+
const ease = {
|
|
5961
|
+
type: "keyframes",
|
|
5962
|
+
ease: [0.25, 0.1, 0.35, 1],
|
|
5963
|
+
duration: 0.3,
|
|
5964
|
+
};
|
|
5965
|
+
const getDefaultTransition = (valueKey, { keyframes }) => {
|
|
5966
|
+
if (keyframes.length > 2) {
|
|
5967
|
+
return keyframesTransition;
|
|
6021
5968
|
}
|
|
6022
|
-
else {
|
|
6023
|
-
return
|
|
5969
|
+
else if (transformProps.has(valueKey)) {
|
|
5970
|
+
return valueKey.startsWith("scale")
|
|
5971
|
+
? criticallyDampedSpring(keyframes[1])
|
|
5972
|
+
: underDampedSpring;
|
|
6024
5973
|
}
|
|
5974
|
+
return ease;
|
|
5975
|
+
};
|
|
5976
|
+
|
|
5977
|
+
/**
|
|
5978
|
+
* Decide whether a transition is defined on a given Transition.
|
|
5979
|
+
* This filters out orchestration options and returns true
|
|
5980
|
+
* if any options are left.
|
|
5981
|
+
*/
|
|
5982
|
+
function isTransitionDefined({ when, delay: _delay, delayChildren, staggerChildren, staggerDirection, repeat, repeatType, repeatDelay, from, elapsed, ...transition }) {
|
|
5983
|
+
return !!Object.keys(transition).length;
|
|
5984
|
+
}
|
|
5985
|
+
|
|
5986
|
+
function getValueTransition(transition, key) {
|
|
5987
|
+
return (transition?.[key] ??
|
|
5988
|
+
transition?.["default"] ??
|
|
5989
|
+
transition);
|
|
6025
5990
|
}
|
|
6026
5991
|
|
|
5992
|
+
/**
|
|
5993
|
+
* Converts seconds to milliseconds
|
|
5994
|
+
*
|
|
5995
|
+
* @param seconds - Time in seconds.
|
|
5996
|
+
* @return milliseconds - Converted time in milliseconds.
|
|
5997
|
+
*/
|
|
5998
|
+
/*#__NO_SIDE_EFFECTS__*/
|
|
5999
|
+
const secondsToMilliseconds = (seconds) => seconds * 1000;
|
|
6000
|
+
/*#__NO_SIDE_EFFECTS__*/
|
|
6001
|
+
const millisecondsToSeconds = (milliseconds) => milliseconds / 1000;
|
|
6002
|
+
|
|
6027
6003
|
// If this number is a decimal, make it just five decimal places
|
|
6028
6004
|
// to avoid exponents
|
|
6029
6005
|
const sanitize = (v) => Math.round(v * 100000) / 100000;
|
|
@@ -6240,765 +6216,6 @@ const complex = {
|
|
|
6240
6216
|
getAnimatableNone: getAnimatableNone$1,
|
|
6241
6217
|
};
|
|
6242
6218
|
|
|
6243
|
-
/**
|
|
6244
|
-
* Properties that should default to 1 or 100%
|
|
6245
|
-
*/
|
|
6246
|
-
const maxDefaults = new Set(["brightness", "contrast", "saturate", "opacity"]);
|
|
6247
|
-
function applyDefaultFilter(v) {
|
|
6248
|
-
const [name, value] = v.slice(0, -1).split("(");
|
|
6249
|
-
if (name === "drop-shadow")
|
|
6250
|
-
return v;
|
|
6251
|
-
const [number] = value.match(floatRegex) || [];
|
|
6252
|
-
if (!number)
|
|
6253
|
-
return v;
|
|
6254
|
-
const unit = value.replace(number, "");
|
|
6255
|
-
let defaultValue = maxDefaults.has(name) ? 1 : 0;
|
|
6256
|
-
if (number !== value)
|
|
6257
|
-
defaultValue *= 100;
|
|
6258
|
-
return name + "(" + defaultValue + unit + ")";
|
|
6259
|
-
}
|
|
6260
|
-
const functionRegex = /\b([a-z-]*)\(.*?\)/gu;
|
|
6261
|
-
const filter = {
|
|
6262
|
-
...complex,
|
|
6263
|
-
getAnimatableNone: (v) => {
|
|
6264
|
-
const functions = v.match(functionRegex);
|
|
6265
|
-
return functions ? functions.map(applyDefaultFilter).join(" ") : v;
|
|
6266
|
-
},
|
|
6267
|
-
};
|
|
6268
|
-
|
|
6269
|
-
/**
|
|
6270
|
-
* A map of default value types for common values
|
|
6271
|
-
*/
|
|
6272
|
-
const defaultValueTypes = {
|
|
6273
|
-
...numberValueTypes,
|
|
6274
|
-
// Color props
|
|
6275
|
-
color,
|
|
6276
|
-
backgroundColor: color,
|
|
6277
|
-
outlineColor: color,
|
|
6278
|
-
fill: color,
|
|
6279
|
-
stroke: color,
|
|
6280
|
-
// Border props
|
|
6281
|
-
borderColor: color,
|
|
6282
|
-
borderTopColor: color,
|
|
6283
|
-
borderRightColor: color,
|
|
6284
|
-
borderBottomColor: color,
|
|
6285
|
-
borderLeftColor: color,
|
|
6286
|
-
filter,
|
|
6287
|
-
WebkitFilter: filter,
|
|
6288
|
-
};
|
|
6289
|
-
/**
|
|
6290
|
-
* Gets the default ValueType for the provided value key
|
|
6291
|
-
*/
|
|
6292
|
-
const getDefaultValueType = (key) => defaultValueTypes[key];
|
|
6293
|
-
|
|
6294
|
-
function getAnimatableNone(key, value) {
|
|
6295
|
-
let defaultValueType = getDefaultValueType(key);
|
|
6296
|
-
if (defaultValueType !== filter)
|
|
6297
|
-
defaultValueType = complex;
|
|
6298
|
-
// If value is not recognised as animatable, ie "none", create an animatable version origin based on the target
|
|
6299
|
-
return defaultValueType.getAnimatableNone
|
|
6300
|
-
? defaultValueType.getAnimatableNone(value)
|
|
6301
|
-
: undefined;
|
|
6302
|
-
}
|
|
6303
|
-
|
|
6304
|
-
/**
|
|
6305
|
-
* If we encounter keyframes like "none" or "0" and we also have keyframes like
|
|
6306
|
-
* "#fff" or "200px 200px" we want to find a keyframe to serve as a template for
|
|
6307
|
-
* the "none" keyframes. In this case "#fff" or "200px 200px" - then these get turned into
|
|
6308
|
-
* zero equivalents, i.e. "#fff0" or "0px 0px".
|
|
6309
|
-
*/
|
|
6310
|
-
const invalidTemplates = new Set(["auto", "none", "0"]);
|
|
6311
|
-
function makeNoneKeyframesAnimatable(unresolvedKeyframes, noneKeyframeIndexes, name) {
|
|
6312
|
-
let i = 0;
|
|
6313
|
-
let animatableTemplate = undefined;
|
|
6314
|
-
while (i < unresolvedKeyframes.length && !animatableTemplate) {
|
|
6315
|
-
const keyframe = unresolvedKeyframes[i];
|
|
6316
|
-
if (typeof keyframe === "string" &&
|
|
6317
|
-
!invalidTemplates.has(keyframe) &&
|
|
6318
|
-
analyseComplexValue(keyframe).values.length) {
|
|
6319
|
-
animatableTemplate = unresolvedKeyframes[i];
|
|
6320
|
-
}
|
|
6321
|
-
i++;
|
|
6322
|
-
}
|
|
6323
|
-
if (animatableTemplate && name) {
|
|
6324
|
-
for (const noneIndex of noneKeyframeIndexes) {
|
|
6325
|
-
unresolvedKeyframes[noneIndex] = getAnimatableNone(name, animatableTemplate);
|
|
6326
|
-
}
|
|
6327
|
-
}
|
|
6328
|
-
}
|
|
6329
|
-
|
|
6330
|
-
const radToDeg = (rad) => (rad * 180) / Math.PI;
|
|
6331
|
-
const rotate = (v) => {
|
|
6332
|
-
const angle = radToDeg(Math.atan2(v[1], v[0]));
|
|
6333
|
-
return rebaseAngle(angle);
|
|
6334
|
-
};
|
|
6335
|
-
const matrix2dParsers = {
|
|
6336
|
-
x: 4,
|
|
6337
|
-
y: 5,
|
|
6338
|
-
translateX: 4,
|
|
6339
|
-
translateY: 5,
|
|
6340
|
-
scaleX: 0,
|
|
6341
|
-
scaleY: 3,
|
|
6342
|
-
scale: (v) => (Math.abs(v[0]) + Math.abs(v[3])) / 2,
|
|
6343
|
-
rotate,
|
|
6344
|
-
rotateZ: rotate,
|
|
6345
|
-
skewX: (v) => radToDeg(Math.atan(v[1])),
|
|
6346
|
-
skewY: (v) => radToDeg(Math.atan(v[2])),
|
|
6347
|
-
skew: (v) => (Math.abs(v[1]) + Math.abs(v[2])) / 2,
|
|
6348
|
-
};
|
|
6349
|
-
const rebaseAngle = (angle) => {
|
|
6350
|
-
angle = angle % 360;
|
|
6351
|
-
if (angle < 0)
|
|
6352
|
-
angle += 360;
|
|
6353
|
-
return angle;
|
|
6354
|
-
};
|
|
6355
|
-
const rotateZ = rotate;
|
|
6356
|
-
const scaleX = (v) => Math.sqrt(v[0] * v[0] + v[1] * v[1]);
|
|
6357
|
-
const scaleY = (v) => Math.sqrt(v[4] * v[4] + v[5] * v[5]);
|
|
6358
|
-
const matrix3dParsers = {
|
|
6359
|
-
x: 12,
|
|
6360
|
-
y: 13,
|
|
6361
|
-
z: 14,
|
|
6362
|
-
translateX: 12,
|
|
6363
|
-
translateY: 13,
|
|
6364
|
-
translateZ: 14,
|
|
6365
|
-
scaleX,
|
|
6366
|
-
scaleY,
|
|
6367
|
-
scale: (v) => (scaleX(v) + scaleY(v)) / 2,
|
|
6368
|
-
rotateX: (v) => rebaseAngle(radToDeg(Math.atan2(v[6], v[5]))),
|
|
6369
|
-
rotateY: (v) => rebaseAngle(radToDeg(Math.atan2(-v[2], v[0]))),
|
|
6370
|
-
rotateZ,
|
|
6371
|
-
rotate: rotateZ,
|
|
6372
|
-
skewX: (v) => radToDeg(Math.atan(v[4])),
|
|
6373
|
-
skewY: (v) => radToDeg(Math.atan(v[1])),
|
|
6374
|
-
skew: (v) => (Math.abs(v[1]) + Math.abs(v[4])) / 2,
|
|
6375
|
-
};
|
|
6376
|
-
function defaultTransformValue(name) {
|
|
6377
|
-
return name.includes("scale") ? 1 : 0;
|
|
6378
|
-
}
|
|
6379
|
-
function parseValueFromTransform(transform, name) {
|
|
6380
|
-
if (!transform || transform === "none") {
|
|
6381
|
-
return defaultTransformValue(name);
|
|
6382
|
-
}
|
|
6383
|
-
const matrix3dMatch = transform.match(/^matrix3d\(([-\d.e\s,]+)\)$/u);
|
|
6384
|
-
let parsers;
|
|
6385
|
-
let match;
|
|
6386
|
-
if (matrix3dMatch) {
|
|
6387
|
-
parsers = matrix3dParsers;
|
|
6388
|
-
match = matrix3dMatch;
|
|
6389
|
-
}
|
|
6390
|
-
else {
|
|
6391
|
-
const matrix2dMatch = transform.match(/^matrix\(([-\d.e\s,]+)\)$/u);
|
|
6392
|
-
parsers = matrix2dParsers;
|
|
6393
|
-
match = matrix2dMatch;
|
|
6394
|
-
}
|
|
6395
|
-
if (!match) {
|
|
6396
|
-
return defaultTransformValue(name);
|
|
6397
|
-
}
|
|
6398
|
-
const valueParser = parsers[name];
|
|
6399
|
-
const values = match[1].split(",").map(convertTransformToNumber);
|
|
6400
|
-
return typeof valueParser === "function"
|
|
6401
|
-
? valueParser(values)
|
|
6402
|
-
: values[valueParser];
|
|
6403
|
-
}
|
|
6404
|
-
const readTransformValue = (instance, name) => {
|
|
6405
|
-
const { transform = "none" } = getComputedStyle(instance);
|
|
6406
|
-
return parseValueFromTransform(transform, name);
|
|
6407
|
-
};
|
|
6408
|
-
function convertTransformToNumber(value) {
|
|
6409
|
-
return parseFloat(value.trim());
|
|
6410
|
-
}
|
|
6411
|
-
|
|
6412
|
-
const isNumOrPxType = (v) => v === number || v === px;
|
|
6413
|
-
const transformKeys = new Set(["x", "y", "z"]);
|
|
6414
|
-
const nonTranslationalTransformKeys = transformPropOrder.filter((key) => !transformKeys.has(key));
|
|
6415
|
-
function removeNonTranslationalTransform(visualElement) {
|
|
6416
|
-
const removedTransforms = [];
|
|
6417
|
-
nonTranslationalTransformKeys.forEach((key) => {
|
|
6418
|
-
const value = visualElement.getValue(key);
|
|
6419
|
-
if (value !== undefined) {
|
|
6420
|
-
removedTransforms.push([key, value.get()]);
|
|
6421
|
-
value.set(key.startsWith("scale") ? 1 : 0);
|
|
6422
|
-
}
|
|
6423
|
-
});
|
|
6424
|
-
return removedTransforms;
|
|
6425
|
-
}
|
|
6426
|
-
const positionalValues = {
|
|
6427
|
-
// Dimensions
|
|
6428
|
-
width: ({ x }, { paddingLeft = "0", paddingRight = "0" }) => x.max - x.min - parseFloat(paddingLeft) - parseFloat(paddingRight),
|
|
6429
|
-
height: ({ y }, { paddingTop = "0", paddingBottom = "0" }) => y.max - y.min - parseFloat(paddingTop) - parseFloat(paddingBottom),
|
|
6430
|
-
top: (_bbox, { top }) => parseFloat(top),
|
|
6431
|
-
left: (_bbox, { left }) => parseFloat(left),
|
|
6432
|
-
bottom: ({ y }, { top }) => parseFloat(top) + (y.max - y.min),
|
|
6433
|
-
right: ({ x }, { left }) => parseFloat(left) + (x.max - x.min),
|
|
6434
|
-
// Transform
|
|
6435
|
-
x: (_bbox, { transform }) => parseValueFromTransform(transform, "x"),
|
|
6436
|
-
y: (_bbox, { transform }) => parseValueFromTransform(transform, "y"),
|
|
6437
|
-
};
|
|
6438
|
-
// Alias translate longform names
|
|
6439
|
-
positionalValues.translateX = positionalValues.x;
|
|
6440
|
-
positionalValues.translateY = positionalValues.y;
|
|
6441
|
-
|
|
6442
|
-
const toResolve = new Set();
|
|
6443
|
-
let isScheduled = false;
|
|
6444
|
-
let anyNeedsMeasurement = false;
|
|
6445
|
-
function measureAllKeyframes() {
|
|
6446
|
-
if (anyNeedsMeasurement) {
|
|
6447
|
-
const resolversToMeasure = Array.from(toResolve).filter((resolver) => resolver.needsMeasurement);
|
|
6448
|
-
const elementsToMeasure = new Set(resolversToMeasure.map((resolver) => resolver.element));
|
|
6449
|
-
const transformsToRestore = new Map();
|
|
6450
|
-
/**
|
|
6451
|
-
* Write pass
|
|
6452
|
-
* If we're measuring elements we want to remove bounding box-changing transforms.
|
|
6453
|
-
*/
|
|
6454
|
-
elementsToMeasure.forEach((element) => {
|
|
6455
|
-
const removedTransforms = removeNonTranslationalTransform(element);
|
|
6456
|
-
if (!removedTransforms.length)
|
|
6457
|
-
return;
|
|
6458
|
-
transformsToRestore.set(element, removedTransforms);
|
|
6459
|
-
element.render();
|
|
6460
|
-
});
|
|
6461
|
-
// Read
|
|
6462
|
-
resolversToMeasure.forEach((resolver) => resolver.measureInitialState());
|
|
6463
|
-
// Write
|
|
6464
|
-
elementsToMeasure.forEach((element) => {
|
|
6465
|
-
element.render();
|
|
6466
|
-
const restore = transformsToRestore.get(element);
|
|
6467
|
-
if (restore) {
|
|
6468
|
-
restore.forEach(([key, value]) => {
|
|
6469
|
-
element.getValue(key)?.set(value);
|
|
6470
|
-
});
|
|
6471
|
-
}
|
|
6472
|
-
});
|
|
6473
|
-
// Read
|
|
6474
|
-
resolversToMeasure.forEach((resolver) => resolver.measureEndState());
|
|
6475
|
-
// Write
|
|
6476
|
-
resolversToMeasure.forEach((resolver) => {
|
|
6477
|
-
if (resolver.suspendedScrollY !== undefined) {
|
|
6478
|
-
window.scrollTo(0, resolver.suspendedScrollY);
|
|
6479
|
-
}
|
|
6480
|
-
});
|
|
6481
|
-
}
|
|
6482
|
-
anyNeedsMeasurement = false;
|
|
6483
|
-
isScheduled = false;
|
|
6484
|
-
toResolve.forEach((resolver) => resolver.complete());
|
|
6485
|
-
toResolve.clear();
|
|
6486
|
-
}
|
|
6487
|
-
function readAllKeyframes() {
|
|
6488
|
-
toResolve.forEach((resolver) => {
|
|
6489
|
-
resolver.readKeyframes();
|
|
6490
|
-
if (resolver.needsMeasurement) {
|
|
6491
|
-
anyNeedsMeasurement = true;
|
|
6492
|
-
}
|
|
6493
|
-
});
|
|
6494
|
-
}
|
|
6495
|
-
function flushKeyframeResolvers() {
|
|
6496
|
-
readAllKeyframes();
|
|
6497
|
-
measureAllKeyframes();
|
|
6498
|
-
}
|
|
6499
|
-
class KeyframeResolver {
|
|
6500
|
-
constructor(unresolvedKeyframes, onComplete, name, motionValue, element, isAsync = false) {
|
|
6501
|
-
/**
|
|
6502
|
-
* Track whether this resolver has completed. Once complete, it never
|
|
6503
|
-
* needs to attempt keyframe resolution again.
|
|
6504
|
-
*/
|
|
6505
|
-
this.isComplete = false;
|
|
6506
|
-
/**
|
|
6507
|
-
* Track whether this resolver is async. If it is, it'll be added to the
|
|
6508
|
-
* resolver queue and flushed in the next frame. Resolvers that aren't going
|
|
6509
|
-
* to trigger read/write thrashing don't need to be async.
|
|
6510
|
-
*/
|
|
6511
|
-
this.isAsync = false;
|
|
6512
|
-
/**
|
|
6513
|
-
* Track whether this resolver needs to perform a measurement
|
|
6514
|
-
* to resolve its keyframes.
|
|
6515
|
-
*/
|
|
6516
|
-
this.needsMeasurement = false;
|
|
6517
|
-
/**
|
|
6518
|
-
* Track whether this resolver is currently scheduled to resolve
|
|
6519
|
-
* to allow it to be cancelled and resumed externally.
|
|
6520
|
-
*/
|
|
6521
|
-
this.isScheduled = false;
|
|
6522
|
-
this.unresolvedKeyframes = [...unresolvedKeyframes];
|
|
6523
|
-
this.onComplete = onComplete;
|
|
6524
|
-
this.name = name;
|
|
6525
|
-
this.motionValue = motionValue;
|
|
6526
|
-
this.element = element;
|
|
6527
|
-
this.isAsync = isAsync;
|
|
6528
|
-
}
|
|
6529
|
-
scheduleResolve() {
|
|
6530
|
-
this.isScheduled = true;
|
|
6531
|
-
if (this.isAsync) {
|
|
6532
|
-
toResolve.add(this);
|
|
6533
|
-
if (!isScheduled) {
|
|
6534
|
-
isScheduled = true;
|
|
6535
|
-
frame.read(readAllKeyframes);
|
|
6536
|
-
frame.resolveKeyframes(measureAllKeyframes);
|
|
6537
|
-
}
|
|
6538
|
-
}
|
|
6539
|
-
else {
|
|
6540
|
-
this.readKeyframes();
|
|
6541
|
-
this.complete();
|
|
6542
|
-
}
|
|
6543
|
-
}
|
|
6544
|
-
readKeyframes() {
|
|
6545
|
-
const { unresolvedKeyframes, name, element, motionValue } = this;
|
|
6546
|
-
/**
|
|
6547
|
-
* If a keyframe is null, we hydrate it either by reading it from
|
|
6548
|
-
* the instance, or propagating from previous keyframes.
|
|
6549
|
-
*/
|
|
6550
|
-
for (let i = 0; i < unresolvedKeyframes.length; i++) {
|
|
6551
|
-
if (unresolvedKeyframes[i] === null) {
|
|
6552
|
-
/**
|
|
6553
|
-
* If the first keyframe is null, we need to find its value by sampling the element
|
|
6554
|
-
*/
|
|
6555
|
-
if (i === 0) {
|
|
6556
|
-
const currentValue = motionValue?.get();
|
|
6557
|
-
const finalKeyframe = unresolvedKeyframes[unresolvedKeyframes.length - 1];
|
|
6558
|
-
if (currentValue !== undefined) {
|
|
6559
|
-
unresolvedKeyframes[0] = currentValue;
|
|
6560
|
-
}
|
|
6561
|
-
else if (element && name) {
|
|
6562
|
-
const valueAsRead = element.readValue(name, finalKeyframe);
|
|
6563
|
-
if (valueAsRead !== undefined && valueAsRead !== null) {
|
|
6564
|
-
unresolvedKeyframes[0] = valueAsRead;
|
|
6565
|
-
}
|
|
6566
|
-
}
|
|
6567
|
-
if (unresolvedKeyframes[0] === undefined) {
|
|
6568
|
-
unresolvedKeyframes[0] = finalKeyframe;
|
|
6569
|
-
}
|
|
6570
|
-
if (motionValue && currentValue === undefined) {
|
|
6571
|
-
motionValue.set(unresolvedKeyframes[0]);
|
|
6572
|
-
}
|
|
6573
|
-
}
|
|
6574
|
-
else {
|
|
6575
|
-
unresolvedKeyframes[i] = unresolvedKeyframes[i - 1];
|
|
6576
|
-
}
|
|
6577
|
-
}
|
|
6578
|
-
}
|
|
6579
|
-
}
|
|
6580
|
-
setFinalKeyframe() { }
|
|
6581
|
-
measureInitialState() { }
|
|
6582
|
-
renderEndStyles() { }
|
|
6583
|
-
measureEndState() { }
|
|
6584
|
-
complete() {
|
|
6585
|
-
this.isComplete = true;
|
|
6586
|
-
this.onComplete(this.unresolvedKeyframes, this.finalKeyframe);
|
|
6587
|
-
toResolve.delete(this);
|
|
6588
|
-
}
|
|
6589
|
-
cancel() {
|
|
6590
|
-
if (!this.isComplete) {
|
|
6591
|
-
this.isScheduled = false;
|
|
6592
|
-
toResolve.delete(this);
|
|
6593
|
-
}
|
|
6594
|
-
}
|
|
6595
|
-
resume() {
|
|
6596
|
-
if (!this.isComplete)
|
|
6597
|
-
this.scheduleResolve();
|
|
6598
|
-
}
|
|
6599
|
-
}
|
|
6600
|
-
|
|
6601
|
-
/**
|
|
6602
|
-
* Check if value is a numerical string, ie a string that is purely a number eg "100" or "-100.1"
|
|
6603
|
-
*/
|
|
6604
|
-
const isNumericalString = (v) => /^-?(?:\d+(?:\.\d+)?|\.\d+)$/u.test(v);
|
|
6605
|
-
|
|
6606
|
-
/**
|
|
6607
|
-
* Parse Framer's special CSS variable format into a CSS token and a fallback.
|
|
6608
|
-
*
|
|
6609
|
-
* ```
|
|
6610
|
-
* `var(--foo, #fff)` => [`--foo`, '#fff']
|
|
6611
|
-
* ```
|
|
6612
|
-
*
|
|
6613
|
-
* @param current
|
|
6614
|
-
*/
|
|
6615
|
-
const splitCSSVariableRegex =
|
|
6616
|
-
// eslint-disable-next-line redos-detector/no-unsafe-regex -- false positive, as it can match a lot of words
|
|
6617
|
-
/^var\(--(?:([\w-]+)|([\w-]+), ?([a-zA-Z\d ()%#.,-]+))\)/u;
|
|
6618
|
-
function parseCSSVariable(current) {
|
|
6619
|
-
const match = splitCSSVariableRegex.exec(current);
|
|
6620
|
-
if (!match)
|
|
6621
|
-
return [,];
|
|
6622
|
-
const [, token1, token2, fallback] = match;
|
|
6623
|
-
return [`--${token1 ?? token2}`, fallback];
|
|
6624
|
-
}
|
|
6625
|
-
const maxDepth = 4;
|
|
6626
|
-
function getVariableValue(current, element, depth = 1) {
|
|
6627
|
-
invariant(depth <= maxDepth, `Max CSS variable fallback depth detected in property "${current}". This may indicate a circular fallback dependency.`);
|
|
6628
|
-
const [token, fallback] = parseCSSVariable(current);
|
|
6629
|
-
// No CSS variable detected
|
|
6630
|
-
if (!token)
|
|
6631
|
-
return;
|
|
6632
|
-
// Attempt to read this CSS variable off the element
|
|
6633
|
-
const resolved = window.getComputedStyle(element).getPropertyValue(token);
|
|
6634
|
-
if (resolved) {
|
|
6635
|
-
const trimmed = resolved.trim();
|
|
6636
|
-
return isNumericalString(trimmed) ? parseFloat(trimmed) : trimmed;
|
|
6637
|
-
}
|
|
6638
|
-
return isCSSVariableToken(fallback)
|
|
6639
|
-
? getVariableValue(fallback, element, depth + 1)
|
|
6640
|
-
: fallback;
|
|
6641
|
-
}
|
|
6642
|
-
|
|
6643
|
-
/**
|
|
6644
|
-
* Tests a provided value against a ValueType
|
|
6645
|
-
*/
|
|
6646
|
-
const testValueType = (v) => (type) => type.test(v);
|
|
6647
|
-
|
|
6648
|
-
/**
|
|
6649
|
-
* ValueType for "auto"
|
|
6650
|
-
*/
|
|
6651
|
-
const auto = {
|
|
6652
|
-
test: (v) => v === "auto",
|
|
6653
|
-
parse: (v) => v,
|
|
6654
|
-
};
|
|
6655
|
-
|
|
6656
|
-
/**
|
|
6657
|
-
* A list of value types commonly used for dimensions
|
|
6658
|
-
*/
|
|
6659
|
-
const dimensionValueTypes = [number, px, percent, degrees, vw, vh, auto];
|
|
6660
|
-
/**
|
|
6661
|
-
* Tests a dimensional value against the list of dimension ValueTypes
|
|
6662
|
-
*/
|
|
6663
|
-
const findDimensionValueType = (v) => dimensionValueTypes.find(testValueType(v));
|
|
6664
|
-
|
|
6665
|
-
class DOMKeyframesResolver extends KeyframeResolver {
|
|
6666
|
-
constructor(unresolvedKeyframes, onComplete, name, motionValue, element) {
|
|
6667
|
-
super(unresolvedKeyframes, onComplete, name, motionValue, element, true);
|
|
6668
|
-
}
|
|
6669
|
-
readKeyframes() {
|
|
6670
|
-
const { unresolvedKeyframes, element, name } = this;
|
|
6671
|
-
if (!element || !element.current)
|
|
6672
|
-
return;
|
|
6673
|
-
super.readKeyframes();
|
|
6674
|
-
/**
|
|
6675
|
-
* If any keyframe is a CSS variable, we need to find its value by sampling the element
|
|
6676
|
-
*/
|
|
6677
|
-
for (let i = 0; i < unresolvedKeyframes.length; i++) {
|
|
6678
|
-
let keyframe = unresolvedKeyframes[i];
|
|
6679
|
-
if (typeof keyframe === "string") {
|
|
6680
|
-
keyframe = keyframe.trim();
|
|
6681
|
-
if (isCSSVariableToken(keyframe)) {
|
|
6682
|
-
const resolved = getVariableValue(keyframe, element.current);
|
|
6683
|
-
if (resolved !== undefined) {
|
|
6684
|
-
unresolvedKeyframes[i] = resolved;
|
|
6685
|
-
}
|
|
6686
|
-
if (i === unresolvedKeyframes.length - 1) {
|
|
6687
|
-
this.finalKeyframe = keyframe;
|
|
6688
|
-
}
|
|
6689
|
-
}
|
|
6690
|
-
}
|
|
6691
|
-
}
|
|
6692
|
-
/**
|
|
6693
|
-
* Resolve "none" values. We do this potentially twice - once before and once after measuring keyframes.
|
|
6694
|
-
* This could be seen as inefficient but it's a trade-off to avoid measurements in more situations, which
|
|
6695
|
-
* have a far bigger performance impact.
|
|
6696
|
-
*/
|
|
6697
|
-
this.resolveNoneKeyframes();
|
|
6698
|
-
/**
|
|
6699
|
-
* Check to see if unit type has changed. If so schedule jobs that will
|
|
6700
|
-
* temporarily set styles to the destination keyframes.
|
|
6701
|
-
* Skip if we have more than two keyframes or this isn't a positional value.
|
|
6702
|
-
* TODO: We can throw if there are multiple keyframes and the value type changes.
|
|
6703
|
-
*/
|
|
6704
|
-
if (!positionalKeys.has(name) || unresolvedKeyframes.length !== 2) {
|
|
6705
|
-
return;
|
|
6706
|
-
}
|
|
6707
|
-
const [origin, target] = unresolvedKeyframes;
|
|
6708
|
-
const originType = findDimensionValueType(origin);
|
|
6709
|
-
const targetType = findDimensionValueType(target);
|
|
6710
|
-
/**
|
|
6711
|
-
* Either we don't recognise these value types or we can animate between them.
|
|
6712
|
-
*/
|
|
6713
|
-
if (originType === targetType)
|
|
6714
|
-
return;
|
|
6715
|
-
/**
|
|
6716
|
-
* If both values are numbers or pixels, we can animate between them by
|
|
6717
|
-
* converting them to numbers.
|
|
6718
|
-
*/
|
|
6719
|
-
if (isNumOrPxType(originType) && isNumOrPxType(targetType)) {
|
|
6720
|
-
for (let i = 0; i < unresolvedKeyframes.length; i++) {
|
|
6721
|
-
const value = unresolvedKeyframes[i];
|
|
6722
|
-
if (typeof value === "string") {
|
|
6723
|
-
unresolvedKeyframes[i] = parseFloat(value);
|
|
6724
|
-
}
|
|
6725
|
-
}
|
|
6726
|
-
}
|
|
6727
|
-
else {
|
|
6728
|
-
/**
|
|
6729
|
-
* Else, the only way to resolve this is by measuring the element.
|
|
6730
|
-
*/
|
|
6731
|
-
this.needsMeasurement = true;
|
|
6732
|
-
}
|
|
6733
|
-
}
|
|
6734
|
-
resolveNoneKeyframes() {
|
|
6735
|
-
const { unresolvedKeyframes, name } = this;
|
|
6736
|
-
const noneKeyframeIndexes = [];
|
|
6737
|
-
for (let i = 0; i < unresolvedKeyframes.length; i++) {
|
|
6738
|
-
if (isNone(unresolvedKeyframes[i])) {
|
|
6739
|
-
noneKeyframeIndexes.push(i);
|
|
6740
|
-
}
|
|
6741
|
-
}
|
|
6742
|
-
if (noneKeyframeIndexes.length) {
|
|
6743
|
-
makeNoneKeyframesAnimatable(unresolvedKeyframes, noneKeyframeIndexes, name);
|
|
6744
|
-
}
|
|
6745
|
-
}
|
|
6746
|
-
measureInitialState() {
|
|
6747
|
-
const { element, unresolvedKeyframes, name } = this;
|
|
6748
|
-
if (!element || !element.current)
|
|
6749
|
-
return;
|
|
6750
|
-
if (name === "height") {
|
|
6751
|
-
this.suspendedScrollY = window.pageYOffset;
|
|
6752
|
-
}
|
|
6753
|
-
this.measuredOrigin = positionalValues[name](element.measureViewportBox(), window.getComputedStyle(element.current));
|
|
6754
|
-
unresolvedKeyframes[0] = this.measuredOrigin;
|
|
6755
|
-
// Set final key frame to measure after next render
|
|
6756
|
-
const measureKeyframe = unresolvedKeyframes[unresolvedKeyframes.length - 1];
|
|
6757
|
-
if (measureKeyframe !== undefined) {
|
|
6758
|
-
element.getValue(name, measureKeyframe).jump(measureKeyframe, false);
|
|
6759
|
-
}
|
|
6760
|
-
}
|
|
6761
|
-
measureEndState() {
|
|
6762
|
-
const { element, name, unresolvedKeyframes } = this;
|
|
6763
|
-
if (!element || !element.current)
|
|
6764
|
-
return;
|
|
6765
|
-
const value = element.getValue(name);
|
|
6766
|
-
value && value.jump(this.measuredOrigin, false);
|
|
6767
|
-
const finalKeyframeIndex = unresolvedKeyframes.length - 1;
|
|
6768
|
-
const finalKeyframe = unresolvedKeyframes[finalKeyframeIndex];
|
|
6769
|
-
unresolvedKeyframes[finalKeyframeIndex] = positionalValues[name](element.measureViewportBox(), window.getComputedStyle(element.current));
|
|
6770
|
-
if (finalKeyframe !== null && this.finalKeyframe === undefined) {
|
|
6771
|
-
this.finalKeyframe = finalKeyframe;
|
|
6772
|
-
}
|
|
6773
|
-
// If we removed transform values, reapply them before the next render
|
|
6774
|
-
if (this.removedTransforms?.length) {
|
|
6775
|
-
this.removedTransforms.forEach(([unsetTransformName, unsetTransformValue]) => {
|
|
6776
|
-
element
|
|
6777
|
-
.getValue(unsetTransformName)
|
|
6778
|
-
.set(unsetTransformValue);
|
|
6779
|
-
});
|
|
6780
|
-
}
|
|
6781
|
-
this.resolveNoneKeyframes();
|
|
6782
|
-
}
|
|
6783
|
-
}
|
|
6784
|
-
|
|
6785
|
-
/**
|
|
6786
|
-
* Check if a value is animatable. Examples:
|
|
6787
|
-
*
|
|
6788
|
-
* ✅: 100, "100px", "#fff"
|
|
6789
|
-
* ❌: "block", "url(2.jpg)"
|
|
6790
|
-
* @param value
|
|
6791
|
-
*
|
|
6792
|
-
* @internal
|
|
6793
|
-
*/
|
|
6794
|
-
const isAnimatable = (value, name) => {
|
|
6795
|
-
// If the list of keys tat might be non-animatable grows, replace with Set
|
|
6796
|
-
if (name === "zIndex")
|
|
6797
|
-
return false;
|
|
6798
|
-
// If it's a number or a keyframes array, we can animate it. We might at some point
|
|
6799
|
-
// need to do a deep isAnimatable check of keyframes, or let Popmotion handle this,
|
|
6800
|
-
// but for now lets leave it like this for performance reasons
|
|
6801
|
-
if (typeof value === "number" || Array.isArray(value))
|
|
6802
|
-
return true;
|
|
6803
|
-
if (typeof value === "string" && // It's animatable if we have a string
|
|
6804
|
-
(complex.test(value) || value === "0") && // And it contains numbers and/or colors
|
|
6805
|
-
!value.startsWith("url(") // Unless it starts with "url("
|
|
6806
|
-
) {
|
|
6807
|
-
return true;
|
|
6808
|
-
}
|
|
6809
|
-
return false;
|
|
6810
|
-
};
|
|
6811
|
-
|
|
6812
|
-
function isGenerator(type) {
|
|
6813
|
-
return typeof type === "function" && "applyToOptions" in type;
|
|
6814
|
-
}
|
|
6815
|
-
|
|
6816
|
-
function hasKeyframesChanged(keyframes) {
|
|
6817
|
-
const current = keyframes[0];
|
|
6818
|
-
if (keyframes.length === 1)
|
|
6819
|
-
return true;
|
|
6820
|
-
for (let i = 0; i < keyframes.length; i++) {
|
|
6821
|
-
if (keyframes[i] !== current)
|
|
6822
|
-
return true;
|
|
6823
|
-
}
|
|
6824
|
-
}
|
|
6825
|
-
function canAnimate(keyframes, name, type, velocity) {
|
|
6826
|
-
/**
|
|
6827
|
-
* Check if we're able to animate between the start and end keyframes,
|
|
6828
|
-
* and throw a warning if we're attempting to animate between one that's
|
|
6829
|
-
* animatable and another that isn't.
|
|
6830
|
-
*/
|
|
6831
|
-
const originKeyframe = keyframes[0];
|
|
6832
|
-
if (originKeyframe === null)
|
|
6833
|
-
return false;
|
|
6834
|
-
/**
|
|
6835
|
-
* These aren't traditionally animatable but we do support them.
|
|
6836
|
-
* In future we could look into making this more generic or replacing
|
|
6837
|
-
* this function with mix() === mixImmediate
|
|
6838
|
-
*/
|
|
6839
|
-
if (name === "display" || name === "visibility")
|
|
6840
|
-
return true;
|
|
6841
|
-
const targetKeyframe = keyframes[keyframes.length - 1];
|
|
6842
|
-
const isOriginAnimatable = isAnimatable(originKeyframe, name);
|
|
6843
|
-
const isTargetAnimatable = isAnimatable(targetKeyframe, name);
|
|
6844
|
-
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.`);
|
|
6845
|
-
// Always skip if any of these are true
|
|
6846
|
-
if (!isOriginAnimatable || !isTargetAnimatable) {
|
|
6847
|
-
return false;
|
|
6848
|
-
}
|
|
6849
|
-
return (hasKeyframesChanged(keyframes) ||
|
|
6850
|
-
((type === "spring" || isGenerator(type)) && velocity));
|
|
6851
|
-
}
|
|
6852
|
-
|
|
6853
|
-
const isNotNull = (value) => value !== null;
|
|
6854
|
-
function getFinalKeyframe(keyframes, { repeat, repeatType = "loop" }, finalKeyframe) {
|
|
6855
|
-
const resolvedKeyframes = keyframes.filter(isNotNull);
|
|
6856
|
-
const index = repeat && repeatType !== "loop" && repeat % 2 === 1
|
|
6857
|
-
? 0
|
|
6858
|
-
: resolvedKeyframes.length - 1;
|
|
6859
|
-
return !index || finalKeyframe === undefined
|
|
6860
|
-
? resolvedKeyframes[index]
|
|
6861
|
-
: finalKeyframe;
|
|
6862
|
-
}
|
|
6863
|
-
|
|
6864
|
-
/**
|
|
6865
|
-
* Maximum time allowed between an animation being created and it being
|
|
6866
|
-
* resolved for us to use the latter as the start time.
|
|
6867
|
-
*
|
|
6868
|
-
* This is to ensure that while we prefer to "start" an animation as soon
|
|
6869
|
-
* as it's triggered, we also want to avoid a visual jump if there's a big delay
|
|
6870
|
-
* between these two moments.
|
|
6871
|
-
*/
|
|
6872
|
-
const MAX_RESOLVE_DELAY = 40;
|
|
6873
|
-
class BaseAnimation {
|
|
6874
|
-
constructor({ autoplay = true, delay = 0, type = "keyframes", repeat = 0, repeatDelay = 0, repeatType = "loop", ...options }) {
|
|
6875
|
-
// Track whether the animation has been stopped. Stopped animations won't restart.
|
|
6876
|
-
this.isStopped = false;
|
|
6877
|
-
this.hasAttemptedResolve = false;
|
|
6878
|
-
this.createdAt = time.now();
|
|
6879
|
-
this.options = {
|
|
6880
|
-
autoplay,
|
|
6881
|
-
delay,
|
|
6882
|
-
type,
|
|
6883
|
-
repeat,
|
|
6884
|
-
repeatDelay,
|
|
6885
|
-
repeatType,
|
|
6886
|
-
...options,
|
|
6887
|
-
};
|
|
6888
|
-
this.updateFinishedPromise();
|
|
6889
|
-
}
|
|
6890
|
-
/**
|
|
6891
|
-
* This method uses the createdAt and resolvedAt to calculate the
|
|
6892
|
-
* animation startTime. *Ideally*, we would use the createdAt time as t=0
|
|
6893
|
-
* as the following frame would then be the first frame of the animation in
|
|
6894
|
-
* progress, which would feel snappier.
|
|
6895
|
-
*
|
|
6896
|
-
* However, if there's a delay (main thread work) between the creation of
|
|
6897
|
-
* the animation and the first commited frame, we prefer to use resolvedAt
|
|
6898
|
-
* to avoid a sudden jump into the animation.
|
|
6899
|
-
*/
|
|
6900
|
-
calcStartTime() {
|
|
6901
|
-
if (!this.resolvedAt)
|
|
6902
|
-
return this.createdAt;
|
|
6903
|
-
return this.resolvedAt - this.createdAt > MAX_RESOLVE_DELAY
|
|
6904
|
-
? this.resolvedAt
|
|
6905
|
-
: this.createdAt;
|
|
6906
|
-
}
|
|
6907
|
-
/**
|
|
6908
|
-
* A getter for resolved data. If keyframes are not yet resolved, accessing
|
|
6909
|
-
* this.resolved will synchronously flush all pending keyframe resolvers.
|
|
6910
|
-
* This is a deoptimisation, but at its worst still batches read/writes.
|
|
6911
|
-
*/
|
|
6912
|
-
get resolved() {
|
|
6913
|
-
if (!this._resolved && !this.hasAttemptedResolve) {
|
|
6914
|
-
flushKeyframeResolvers();
|
|
6915
|
-
}
|
|
6916
|
-
return this._resolved;
|
|
6917
|
-
}
|
|
6918
|
-
/**
|
|
6919
|
-
* A method to be called when the keyframes resolver completes. This method
|
|
6920
|
-
* will check if its possible to run the animation and, if not, skip it.
|
|
6921
|
-
* Otherwise, it will call initPlayback on the implementing class.
|
|
6922
|
-
*/
|
|
6923
|
-
onKeyframesResolved(keyframes, finalKeyframe) {
|
|
6924
|
-
this.resolvedAt = time.now();
|
|
6925
|
-
this.hasAttemptedResolve = true;
|
|
6926
|
-
const { name, type, velocity, delay, onComplete, onUpdate, isGenerator, } = this.options;
|
|
6927
|
-
/**
|
|
6928
|
-
* If we can't animate this value with the resolved keyframes
|
|
6929
|
-
* then we should complete it immediately.
|
|
6930
|
-
*/
|
|
6931
|
-
if (!isGenerator && !canAnimate(keyframes, name, type, velocity)) {
|
|
6932
|
-
// Finish immediately
|
|
6933
|
-
if (!delay) {
|
|
6934
|
-
onUpdate &&
|
|
6935
|
-
onUpdate(getFinalKeyframe(keyframes, this.options, finalKeyframe));
|
|
6936
|
-
onComplete && onComplete();
|
|
6937
|
-
this.resolveFinishedPromise();
|
|
6938
|
-
return;
|
|
6939
|
-
}
|
|
6940
|
-
// Finish after a delay
|
|
6941
|
-
else {
|
|
6942
|
-
this.options.duration = 0;
|
|
6943
|
-
}
|
|
6944
|
-
}
|
|
6945
|
-
const resolvedAnimation = this.initPlayback(keyframes, finalKeyframe);
|
|
6946
|
-
if (resolvedAnimation === false)
|
|
6947
|
-
return;
|
|
6948
|
-
this._resolved = {
|
|
6949
|
-
keyframes,
|
|
6950
|
-
finalKeyframe,
|
|
6951
|
-
...resolvedAnimation,
|
|
6952
|
-
};
|
|
6953
|
-
this.onPostResolved();
|
|
6954
|
-
}
|
|
6955
|
-
onPostResolved() { }
|
|
6956
|
-
/**
|
|
6957
|
-
* Allows the returned animation to be awaited or promise-chained. Currently
|
|
6958
|
-
* resolves when the animation finishes at all but in a future update could/should
|
|
6959
|
-
* reject if its cancels.
|
|
6960
|
-
*/
|
|
6961
|
-
then(resolve, reject) {
|
|
6962
|
-
return this.currentFinishedPromise.then(resolve, reject);
|
|
6963
|
-
}
|
|
6964
|
-
flatten() {
|
|
6965
|
-
if (!this.options.allowFlatten)
|
|
6966
|
-
return;
|
|
6967
|
-
this.options.type = "keyframes";
|
|
6968
|
-
this.options.ease = "linear";
|
|
6969
|
-
}
|
|
6970
|
-
updateFinishedPromise() {
|
|
6971
|
-
this.currentFinishedPromise = new Promise((resolve) => {
|
|
6972
|
-
this.resolveFinishedPromise = resolve;
|
|
6973
|
-
});
|
|
6974
|
-
}
|
|
6975
|
-
}
|
|
6976
|
-
|
|
6977
|
-
/*
|
|
6978
|
-
Value in range from progress
|
|
6979
|
-
|
|
6980
|
-
Given a lower limit and an upper limit, we return the value within
|
|
6981
|
-
that range as expressed by progress (usually a number from 0 to 1)
|
|
6982
|
-
|
|
6983
|
-
So progress = 0.5 would change
|
|
6984
|
-
|
|
6985
|
-
from -------- to
|
|
6986
|
-
|
|
6987
|
-
to
|
|
6988
|
-
|
|
6989
|
-
from ---- to
|
|
6990
|
-
|
|
6991
|
-
E.g. from = 10, to = 20, progress = 0.5 => 15
|
|
6992
|
-
|
|
6993
|
-
@param [number]: Lower limit of range
|
|
6994
|
-
@param [number]: Upper limit of range
|
|
6995
|
-
@param [number]: The progress between lower and upper limits expressed 0-1
|
|
6996
|
-
@return [number]: Value as calculated from progress within range (not limited within range)
|
|
6997
|
-
*/
|
|
6998
|
-
const mixNumber$1 = (from, to, progress) => {
|
|
6999
|
-
return from + (to - from) * progress;
|
|
7000
|
-
};
|
|
7001
|
-
|
|
7002
6219
|
// Adapted from https://gist.github.com/mjackson/5311256
|
|
7003
6220
|
function hueToRgb(p, q, t) {
|
|
7004
6221
|
if (t < 0)
|
|
@@ -7044,6 +6261,31 @@ function mixImmediate(a, b) {
|
|
|
7044
6261
|
return (p) => (p > 0 ? b : a);
|
|
7045
6262
|
}
|
|
7046
6263
|
|
|
6264
|
+
/*
|
|
6265
|
+
Value in range from progress
|
|
6266
|
+
|
|
6267
|
+
Given a lower limit and an upper limit, we return the value within
|
|
6268
|
+
that range as expressed by progress (usually a number from 0 to 1)
|
|
6269
|
+
|
|
6270
|
+
So progress = 0.5 would change
|
|
6271
|
+
|
|
6272
|
+
from -------- to
|
|
6273
|
+
|
|
6274
|
+
to
|
|
6275
|
+
|
|
6276
|
+
from ---- to
|
|
6277
|
+
|
|
6278
|
+
E.g. from = 10, to = 20, progress = 0.5 => 15
|
|
6279
|
+
|
|
6280
|
+
@param [number]: Lower limit of range
|
|
6281
|
+
@param [number]: Upper limit of range
|
|
6282
|
+
@param [number]: The progress between lower and upper limits expressed 0-1
|
|
6283
|
+
@return [number]: Value as calculated from progress within range (not limited within range)
|
|
6284
|
+
*/
|
|
6285
|
+
const mixNumber$1 = (from, to, progress) => {
|
|
6286
|
+
return from + (to - from) * progress;
|
|
6287
|
+
};
|
|
6288
|
+
|
|
7047
6289
|
// Linear color space blending
|
|
7048
6290
|
// Explained https://www.youtube.com/watch?v=LKnqECcg6Gw
|
|
7049
6291
|
// Demonstrated http://codepen.io/osublake/pen/xGVVaN
|
|
@@ -7082,16 +6324,6 @@ const mixColor = (from, to) => {
|
|
|
7082
6324
|
};
|
|
7083
6325
|
};
|
|
7084
6326
|
|
|
7085
|
-
/**
|
|
7086
|
-
* Pipe
|
|
7087
|
-
* Compose other transformers to run linearily
|
|
7088
|
-
* pipe(min(20), max(40))
|
|
7089
|
-
* @param {...functions} transformers
|
|
7090
|
-
* @return {function}
|
|
7091
|
-
*/
|
|
7092
|
-
const combineFunctions = (a, b) => (v) => b(a(v));
|
|
7093
|
-
const pipe = (...transformers) => transformers.reduce(combineFunctions);
|
|
7094
|
-
|
|
7095
6327
|
const invisibleValues = new Set(["none", "hidden"]);
|
|
7096
6328
|
/**
|
|
7097
6329
|
* Returns a function that, when provided a progress value between 0 and 1,
|
|
@@ -7107,6 +6339,16 @@ function mixVisibility(origin, target) {
|
|
|
7107
6339
|
}
|
|
7108
6340
|
}
|
|
7109
6341
|
|
|
6342
|
+
/**
|
|
6343
|
+
* Pipe
|
|
6344
|
+
* Compose other transformers to run linearily
|
|
6345
|
+
* pipe(min(20), max(40))
|
|
6346
|
+
* @param {...functions} transformers
|
|
6347
|
+
* @return {function}
|
|
6348
|
+
*/
|
|
6349
|
+
const combineFunctions = (a, b) => (v) => b(a(v));
|
|
6350
|
+
const pipe = (...transformers) => transformers.reduce(combineFunctions);
|
|
6351
|
+
|
|
7110
6352
|
function mixNumber(a, b) {
|
|
7111
6353
|
return (p) => mixNumber$1(a, b, p);
|
|
7112
6354
|
}
|
|
@@ -7189,14 +6431,69 @@ const mixComplex = (origin, target) => {
|
|
|
7189
6431
|
}
|
|
7190
6432
|
};
|
|
7191
6433
|
|
|
7192
|
-
function mix(from, to, p) {
|
|
7193
|
-
if (typeof from === "number" &&
|
|
7194
|
-
typeof to === "number" &&
|
|
7195
|
-
typeof p === "number") {
|
|
7196
|
-
return mixNumber$1(from, to, p);
|
|
6434
|
+
function mix(from, to, p) {
|
|
6435
|
+
if (typeof from === "number" &&
|
|
6436
|
+
typeof to === "number" &&
|
|
6437
|
+
typeof p === "number") {
|
|
6438
|
+
return mixNumber$1(from, to, p);
|
|
6439
|
+
}
|
|
6440
|
+
const mixer = getMixer(from);
|
|
6441
|
+
return mixer(from, to);
|
|
6442
|
+
}
|
|
6443
|
+
|
|
6444
|
+
const frameloopDriver = (update) => {
|
|
6445
|
+
const passTimestamp = ({ timestamp }) => update(timestamp);
|
|
6446
|
+
return {
|
|
6447
|
+
start: () => frame.update(passTimestamp, true),
|
|
6448
|
+
stop: () => cancelFrame(passTimestamp),
|
|
6449
|
+
/**
|
|
6450
|
+
* If we're processing this frame we can use the
|
|
6451
|
+
* framelocked timestamp to keep things in sync.
|
|
6452
|
+
*/
|
|
6453
|
+
now: () => (frameData.isProcessing ? frameData.timestamp : time.now()),
|
|
6454
|
+
};
|
|
6455
|
+
};
|
|
6456
|
+
|
|
6457
|
+
const generateLinearEasing = (easing, duration, // as milliseconds
|
|
6458
|
+
resolution = 10 // as milliseconds
|
|
6459
|
+
) => {
|
|
6460
|
+
let points = "";
|
|
6461
|
+
const numPoints = Math.max(Math.round(duration / resolution), 2);
|
|
6462
|
+
for (let i = 0; i < numPoints; i++) {
|
|
6463
|
+
points += easing(i / (numPoints - 1)) + ", ";
|
|
6464
|
+
}
|
|
6465
|
+
return `linear(${points.substring(0, points.length - 2)})`;
|
|
6466
|
+
};
|
|
6467
|
+
|
|
6468
|
+
/**
|
|
6469
|
+
* Implement a practical max duration for keyframe generation
|
|
6470
|
+
* to prevent infinite loops
|
|
6471
|
+
*/
|
|
6472
|
+
const maxGeneratorDuration = 20000;
|
|
6473
|
+
function calcGeneratorDuration(generator) {
|
|
6474
|
+
let duration = 0;
|
|
6475
|
+
const timeStep = 50;
|
|
6476
|
+
let state = generator.next(duration);
|
|
6477
|
+
while (!state.done && duration < maxGeneratorDuration) {
|
|
6478
|
+
duration += timeStep;
|
|
6479
|
+
state = generator.next(duration);
|
|
7197
6480
|
}
|
|
7198
|
-
|
|
7199
|
-
|
|
6481
|
+
return duration >= maxGeneratorDuration ? Infinity : duration;
|
|
6482
|
+
}
|
|
6483
|
+
|
|
6484
|
+
/**
|
|
6485
|
+
* Create a progress => progress easing function from a generator.
|
|
6486
|
+
*/
|
|
6487
|
+
function createGeneratorEasing(options, scale = 100, createGenerator) {
|
|
6488
|
+
const generator = createGenerator({ ...options, keyframes: [0, scale] });
|
|
6489
|
+
const duration = Math.min(calcGeneratorDuration(generator), maxGeneratorDuration);
|
|
6490
|
+
return {
|
|
6491
|
+
type: "keyframes",
|
|
6492
|
+
ease: (progress) => {
|
|
6493
|
+
return generator.next(duration * progress).value / scale;
|
|
6494
|
+
},
|
|
6495
|
+
duration: millisecondsToSeconds(duration),
|
|
6496
|
+
};
|
|
7200
6497
|
}
|
|
7201
6498
|
|
|
7202
6499
|
const velocitySampleDuration = 5; // ms
|
|
@@ -7231,17 +6528,6 @@ const springDefaults = {
|
|
|
7231
6528
|
maxDamping: 1,
|
|
7232
6529
|
};
|
|
7233
6530
|
|
|
7234
|
-
/**
|
|
7235
|
-
* Converts seconds to milliseconds
|
|
7236
|
-
*
|
|
7237
|
-
* @param seconds - Time in seconds.
|
|
7238
|
-
* @return milliseconds - Converted time in milliseconds.
|
|
7239
|
-
*/
|
|
7240
|
-
/*#__NO_SIDE_EFFECTS__*/
|
|
7241
|
-
const secondsToMilliseconds = (seconds) => seconds * 1000;
|
|
7242
|
-
/*#__NO_SIDE_EFFECTS__*/
|
|
7243
|
-
const millisecondsToSeconds = (milliseconds) => milliseconds / 1000;
|
|
7244
|
-
|
|
7245
6531
|
const safeMin = 0.001;
|
|
7246
6532
|
function findSpring({ duration = springDefaults.duration, bounce = springDefaults.bounce, velocity = springDefaults.velocity, mass = springDefaults.mass, }) {
|
|
7247
6533
|
let envelope;
|
|
@@ -7322,81 +6608,6 @@ function calcAngularFreq(undampedFreq, dampingRatio) {
|
|
|
7322
6608
|
return undampedFreq * Math.sqrt(1 - dampingRatio * dampingRatio);
|
|
7323
6609
|
}
|
|
7324
6610
|
|
|
7325
|
-
/**
|
|
7326
|
-
* Implement a practical max duration for keyframe generation
|
|
7327
|
-
* to prevent infinite loops
|
|
7328
|
-
*/
|
|
7329
|
-
const maxGeneratorDuration = 20000;
|
|
7330
|
-
function calcGeneratorDuration(generator) {
|
|
7331
|
-
let duration = 0;
|
|
7332
|
-
const timeStep = 50;
|
|
7333
|
-
let state = generator.next(duration);
|
|
7334
|
-
while (!state.done && duration < maxGeneratorDuration) {
|
|
7335
|
-
duration += timeStep;
|
|
7336
|
-
state = generator.next(duration);
|
|
7337
|
-
}
|
|
7338
|
-
return duration >= maxGeneratorDuration ? Infinity : duration;
|
|
7339
|
-
}
|
|
7340
|
-
|
|
7341
|
-
/**
|
|
7342
|
-
* Create a progress => progress easing function from a generator.
|
|
7343
|
-
*/
|
|
7344
|
-
function createGeneratorEasing(options, scale = 100, createGenerator) {
|
|
7345
|
-
const generator = createGenerator({ ...options, keyframes: [0, scale] });
|
|
7346
|
-
const duration = Math.min(calcGeneratorDuration(generator), maxGeneratorDuration);
|
|
7347
|
-
return {
|
|
7348
|
-
type: "keyframes",
|
|
7349
|
-
ease: (progress) => {
|
|
7350
|
-
return generator.next(duration * progress).value / scale;
|
|
7351
|
-
},
|
|
7352
|
-
duration: millisecondsToSeconds(duration),
|
|
7353
|
-
};
|
|
7354
|
-
}
|
|
7355
|
-
|
|
7356
|
-
/**
|
|
7357
|
-
* Add the ability for test suites to manually set support flags
|
|
7358
|
-
* to better test more environments.
|
|
7359
|
-
*/
|
|
7360
|
-
const supportsFlags = {};
|
|
7361
|
-
|
|
7362
|
-
/*#__NO_SIDE_EFFECTS__*/
|
|
7363
|
-
function memo(callback) {
|
|
7364
|
-
let result;
|
|
7365
|
-
return () => {
|
|
7366
|
-
if (result === undefined)
|
|
7367
|
-
result = callback();
|
|
7368
|
-
return result;
|
|
7369
|
-
};
|
|
7370
|
-
}
|
|
7371
|
-
|
|
7372
|
-
function memoSupports(callback, supportsFlag) {
|
|
7373
|
-
const memoized = memo(callback);
|
|
7374
|
-
return () => supportsFlags[supportsFlag] ?? memoized();
|
|
7375
|
-
}
|
|
7376
|
-
|
|
7377
|
-
const supportsLinearEasing = /*@__PURE__*/ memoSupports(() => {
|
|
7378
|
-
try {
|
|
7379
|
-
document
|
|
7380
|
-
.createElement("div")
|
|
7381
|
-
.animate({ opacity: 0 }, { easing: "linear(0, 1)" });
|
|
7382
|
-
}
|
|
7383
|
-
catch (e) {
|
|
7384
|
-
return false;
|
|
7385
|
-
}
|
|
7386
|
-
return true;
|
|
7387
|
-
}, "linearEasing");
|
|
7388
|
-
|
|
7389
|
-
const generateLinearEasing = (easing, duration, // as milliseconds
|
|
7390
|
-
resolution = 10 // as milliseconds
|
|
7391
|
-
) => {
|
|
7392
|
-
let points = "";
|
|
7393
|
-
const numPoints = Math.max(Math.round(duration / resolution), 2);
|
|
7394
|
-
for (let i = 0; i < numPoints; i++) {
|
|
7395
|
-
points += easing(i / (numPoints - 1)) + ", ";
|
|
7396
|
-
}
|
|
7397
|
-
return `linear(${points.substring(0, points.length - 2)})`;
|
|
7398
|
-
};
|
|
7399
|
-
|
|
7400
6611
|
const durationKeys = ["duration", "bounce"];
|
|
7401
6612
|
const physicsKeys = ["stiffness", "damping", "mass"];
|
|
7402
6613
|
function isSpringType(options, keys) {
|
|
@@ -7523,7 +6734,7 @@ function spring(optionsOrVisualDuration = springDefaults.visualDuration, bounce
|
|
|
7523
6734
|
next: (t) => {
|
|
7524
6735
|
const current = resolveSpring(t);
|
|
7525
6736
|
if (!isResolvedFromDuration) {
|
|
7526
|
-
let currentVelocity = 0.0;
|
|
6737
|
+
let currentVelocity = t === 0 ? initialVelocity : 0.0;
|
|
7527
6738
|
/**
|
|
7528
6739
|
* We only need to calculate velocity for under-damped springs
|
|
7529
6740
|
* as over- and critically-damped springs can't overshoot, so
|
|
@@ -7557,7 +6768,7 @@ function spring(optionsOrVisualDuration = springDefaults.visualDuration, bounce
|
|
|
7557
6768
|
}
|
|
7558
6769
|
spring.applyToOptions = (options) => {
|
|
7559
6770
|
const generatorOptions = createGeneratorEasing(options, 100, spring);
|
|
7560
|
-
options.ease =
|
|
6771
|
+
options.ease = generatorOptions.ease;
|
|
7561
6772
|
options.duration = secondsToMilliseconds(generatorOptions.duration);
|
|
7562
6773
|
options.type = "keyframes";
|
|
7563
6774
|
return options;
|
|
@@ -7646,44 +6857,6 @@ function inertia({ keyframes, velocity = 0.0, power = 0.8, timeConstant = 325, b
|
|
|
7646
6857
|
};
|
|
7647
6858
|
}
|
|
7648
6859
|
|
|
7649
|
-
const easeIn = /*@__PURE__*/ cubicBezier(0.42, 0, 1, 1);
|
|
7650
|
-
const easeOut = /*@__PURE__*/ cubicBezier(0, 0, 0.58, 1);
|
|
7651
|
-
const easeInOut = /*@__PURE__*/ cubicBezier(0.42, 0, 0.58, 1);
|
|
7652
|
-
|
|
7653
|
-
const isEasingArray = (ease) => {
|
|
7654
|
-
return Array.isArray(ease) && typeof ease[0] !== "number";
|
|
7655
|
-
};
|
|
7656
|
-
|
|
7657
|
-
const isBezierDefinition = (easing) => Array.isArray(easing) && typeof easing[0] === "number";
|
|
7658
|
-
|
|
7659
|
-
const easingLookup = {
|
|
7660
|
-
linear: noop,
|
|
7661
|
-
easeIn,
|
|
7662
|
-
easeInOut,
|
|
7663
|
-
easeOut,
|
|
7664
|
-
circIn,
|
|
7665
|
-
circInOut,
|
|
7666
|
-
circOut,
|
|
7667
|
-
backIn,
|
|
7668
|
-
backInOut,
|
|
7669
|
-
backOut,
|
|
7670
|
-
anticipate,
|
|
7671
|
-
};
|
|
7672
|
-
const easingDefinitionToFunction = (definition) => {
|
|
7673
|
-
if (isBezierDefinition(definition)) {
|
|
7674
|
-
// If cubic bezier definition, create bezier curve
|
|
7675
|
-
invariant(definition.length === 4, `Cubic bezier arrays must contain four numerical values.`);
|
|
7676
|
-
const [x1, y1, x2, y2] = definition;
|
|
7677
|
-
return cubicBezier(x1, y1, x2, y2);
|
|
7678
|
-
}
|
|
7679
|
-
else if (typeof definition === "string") {
|
|
7680
|
-
// Else lookup from table
|
|
7681
|
-
invariant(easingLookup[definition] !== undefined, `Invalid easing type '${definition}'`);
|
|
7682
|
-
return easingLookup[definition];
|
|
7683
|
-
}
|
|
7684
|
-
return definition;
|
|
7685
|
-
};
|
|
7686
|
-
|
|
7687
6860
|
/*
|
|
7688
6861
|
Progress within given range
|
|
7689
6862
|
|
|
@@ -7704,7 +6877,7 @@ const progress = (from, to, value) => {
|
|
|
7704
6877
|
|
|
7705
6878
|
function createMixers(output, ease, customMixer) {
|
|
7706
6879
|
const mixers = [];
|
|
7707
|
-
const mixerFactory = customMixer || mix;
|
|
6880
|
+
const mixerFactory = customMixer || MotionGlobalConfig.mix || mix;
|
|
7708
6881
|
const numMixers = output.length - 1;
|
|
7709
6882
|
for (let i = 0; i < numMixers; i++) {
|
|
7710
6883
|
let mixer = mixerFactory(output[i], output[i + 1]);
|
|
@@ -7786,9 +6959,116 @@ function defaultOffset(arr) {
|
|
|
7786
6959
|
return offset;
|
|
7787
6960
|
}
|
|
7788
6961
|
|
|
7789
|
-
function convertOffsetToTimes(offset, duration) {
|
|
7790
|
-
return offset.map((o) => o * duration);
|
|
7791
|
-
}
|
|
6962
|
+
function convertOffsetToTimes(offset, duration) {
|
|
6963
|
+
return offset.map((o) => o * duration);
|
|
6964
|
+
}
|
|
6965
|
+
|
|
6966
|
+
/*
|
|
6967
|
+
Bezier function generator
|
|
6968
|
+
This has been modified from Gaëtan Renaudeau's BezierEasing
|
|
6969
|
+
https://github.com/gre/bezier-easing/blob/master/src/index.js
|
|
6970
|
+
https://github.com/gre/bezier-easing/blob/master/LICENSE
|
|
6971
|
+
|
|
6972
|
+
I've removed the newtonRaphsonIterate algo because in benchmarking it
|
|
6973
|
+
wasn't noticiably faster than binarySubdivision, indeed removing it
|
|
6974
|
+
usually improved times, depending on the curve.
|
|
6975
|
+
I also removed the lookup table, as for the added bundle size and loop we're
|
|
6976
|
+
only cutting ~4 or so subdivision iterations. I bumped the max iterations up
|
|
6977
|
+
to 12 to compensate and this still tended to be faster for no perceivable
|
|
6978
|
+
loss in accuracy.
|
|
6979
|
+
Usage
|
|
6980
|
+
const easeOut = cubicBezier(.17,.67,.83,.67);
|
|
6981
|
+
const x = easeOut(0.5); // returns 0.627...
|
|
6982
|
+
*/
|
|
6983
|
+
// Returns x(t) given t, x1, and x2, or y(t) given t, y1, and y2.
|
|
6984
|
+
const calcBezier = (t, a1, a2) => (((1.0 - 3.0 * a2 + 3.0 * a1) * t + (3.0 * a2 - 6.0 * a1)) * t + 3.0 * a1) *
|
|
6985
|
+
t;
|
|
6986
|
+
const subdivisionPrecision = 0.0000001;
|
|
6987
|
+
const subdivisionMaxIterations = 12;
|
|
6988
|
+
function binarySubdivide(x, lowerBound, upperBound, mX1, mX2) {
|
|
6989
|
+
let currentX;
|
|
6990
|
+
let currentT;
|
|
6991
|
+
let i = 0;
|
|
6992
|
+
do {
|
|
6993
|
+
currentT = lowerBound + (upperBound - lowerBound) / 2.0;
|
|
6994
|
+
currentX = calcBezier(currentT, mX1, mX2) - x;
|
|
6995
|
+
if (currentX > 0.0) {
|
|
6996
|
+
upperBound = currentT;
|
|
6997
|
+
}
|
|
6998
|
+
else {
|
|
6999
|
+
lowerBound = currentT;
|
|
7000
|
+
}
|
|
7001
|
+
} while (Math.abs(currentX) > subdivisionPrecision &&
|
|
7002
|
+
++i < subdivisionMaxIterations);
|
|
7003
|
+
return currentT;
|
|
7004
|
+
}
|
|
7005
|
+
function cubicBezier(mX1, mY1, mX2, mY2) {
|
|
7006
|
+
// If this is a linear gradient, return linear easing
|
|
7007
|
+
if (mX1 === mY1 && mX2 === mY2)
|
|
7008
|
+
return noop;
|
|
7009
|
+
const getTForX = (aX) => binarySubdivide(aX, 0, 1, mX1, mX2);
|
|
7010
|
+
// If animation is at start/end, return t without easing
|
|
7011
|
+
return (t) => t === 0 || t === 1 ? t : calcBezier(getTForX(t), mY1, mY2);
|
|
7012
|
+
}
|
|
7013
|
+
|
|
7014
|
+
const easeIn = /*@__PURE__*/ cubicBezier(0.42, 0, 1, 1);
|
|
7015
|
+
const easeOut = /*@__PURE__*/ cubicBezier(0, 0, 0.58, 1);
|
|
7016
|
+
const easeInOut = /*@__PURE__*/ cubicBezier(0.42, 0, 0.58, 1);
|
|
7017
|
+
|
|
7018
|
+
const isEasingArray = (ease) => {
|
|
7019
|
+
return Array.isArray(ease) && typeof ease[0] !== "number";
|
|
7020
|
+
};
|
|
7021
|
+
|
|
7022
|
+
// Accepts an easing function and returns a new one that outputs mirrored values for
|
|
7023
|
+
// the second half of the animation. Turns easeIn into easeInOut.
|
|
7024
|
+
const mirrorEasing = (easing) => (p) => p <= 0.5 ? easing(2 * p) / 2 : (2 - easing(2 * (1 - p))) / 2;
|
|
7025
|
+
|
|
7026
|
+
// Accepts an easing function and returns a new one that outputs reversed values.
|
|
7027
|
+
// Turns easeIn into easeOut.
|
|
7028
|
+
const reverseEasing = (easing) => (p) => 1 - easing(1 - p);
|
|
7029
|
+
|
|
7030
|
+
const backOut = /*@__PURE__*/ cubicBezier(0.33, 1.53, 0.69, 0.99);
|
|
7031
|
+
const backIn = /*@__PURE__*/ reverseEasing(backOut);
|
|
7032
|
+
const backInOut = /*@__PURE__*/ mirrorEasing(backIn);
|
|
7033
|
+
|
|
7034
|
+
const anticipate = (p) => (p *= 2) < 1 ? 0.5 * backIn(p) : 0.5 * (2 - Math.pow(2, -10 * (p - 1)));
|
|
7035
|
+
|
|
7036
|
+
const circIn = (p) => 1 - Math.sin(Math.acos(p));
|
|
7037
|
+
const circOut = reverseEasing(circIn);
|
|
7038
|
+
const circInOut = mirrorEasing(circIn);
|
|
7039
|
+
|
|
7040
|
+
const isBezierDefinition = (easing) => Array.isArray(easing) && typeof easing[0] === "number";
|
|
7041
|
+
|
|
7042
|
+
const easingLookup = {
|
|
7043
|
+
linear: noop,
|
|
7044
|
+
easeIn,
|
|
7045
|
+
easeInOut,
|
|
7046
|
+
easeOut,
|
|
7047
|
+
circIn,
|
|
7048
|
+
circInOut,
|
|
7049
|
+
circOut,
|
|
7050
|
+
backIn,
|
|
7051
|
+
backInOut,
|
|
7052
|
+
backOut,
|
|
7053
|
+
anticipate,
|
|
7054
|
+
};
|
|
7055
|
+
const isValidEasing = (easing) => {
|
|
7056
|
+
return typeof easing === "string";
|
|
7057
|
+
};
|
|
7058
|
+
const easingDefinitionToFunction = (definition) => {
|
|
7059
|
+
if (isBezierDefinition(definition)) {
|
|
7060
|
+
// If cubic bezier definition, create bezier curve
|
|
7061
|
+
invariant(definition.length === 4, `Cubic bezier arrays must contain four numerical values.`);
|
|
7062
|
+
const [x1, y1, x2, y2] = definition;
|
|
7063
|
+
return cubicBezier(x1, y1, x2, y2);
|
|
7064
|
+
}
|
|
7065
|
+
else if (isValidEasing(definition)) {
|
|
7066
|
+
// Else lookup from table
|
|
7067
|
+
invariant(easingLookup[definition] !== undefined, `Invalid easing type '${definition}'`);
|
|
7068
|
+
return easingLookup[definition];
|
|
7069
|
+
}
|
|
7070
|
+
return definition;
|
|
7071
|
+
};
|
|
7792
7072
|
|
|
7793
7073
|
function defaultEasing(values, easing) {
|
|
7794
7074
|
return values.map(() => easing || easeInOut).splice(0, values.length - 1);
|
|
@@ -7833,68 +7113,84 @@ function keyframes({ duration = 300, keyframes: keyframeValues, times, ease = "e
|
|
|
7833
7113
|
};
|
|
7834
7114
|
}
|
|
7835
7115
|
|
|
7836
|
-
const
|
|
7837
|
-
|
|
7838
|
-
|
|
7839
|
-
|
|
7840
|
-
|
|
7841
|
-
|
|
7842
|
-
|
|
7843
|
-
|
|
7844
|
-
|
|
7845
|
-
now: () => (frameData.isProcessing ? frameData.timestamp : time.now()),
|
|
7846
|
-
};
|
|
7847
|
-
};
|
|
7116
|
+
const isNotNull = (value) => value !== null;
|
|
7117
|
+
function getFinalKeyframe(keyframes, { repeat, repeatType = "loop" }, finalKeyframe, speed = 1) {
|
|
7118
|
+
const resolvedKeyframes = keyframes.filter(isNotNull);
|
|
7119
|
+
const useFirstKeyframe = speed < 0 || (repeat && repeatType !== "loop" && repeat % 2 === 1);
|
|
7120
|
+
const index = useFirstKeyframe ? 0 : resolvedKeyframes.length - 1;
|
|
7121
|
+
return !index || finalKeyframe === undefined
|
|
7122
|
+
? resolvedKeyframes[index]
|
|
7123
|
+
: finalKeyframe;
|
|
7124
|
+
}
|
|
7848
7125
|
|
|
7849
|
-
const
|
|
7126
|
+
const transitionTypeMap = {
|
|
7850
7127
|
decay: inertia,
|
|
7851
7128
|
inertia,
|
|
7852
7129
|
tween: keyframes,
|
|
7853
7130
|
keyframes: keyframes,
|
|
7854
7131
|
spring,
|
|
7855
7132
|
};
|
|
7133
|
+
function replaceTransitionType(transition) {
|
|
7134
|
+
if (typeof transition.type === "string") {
|
|
7135
|
+
transition.type = transitionTypeMap[transition.type];
|
|
7136
|
+
}
|
|
7137
|
+
}
|
|
7138
|
+
|
|
7139
|
+
class WithPromise {
|
|
7140
|
+
constructor() {
|
|
7141
|
+
this.count = 0;
|
|
7142
|
+
this.updateFinished();
|
|
7143
|
+
}
|
|
7144
|
+
get finished() {
|
|
7145
|
+
return this._finished;
|
|
7146
|
+
}
|
|
7147
|
+
updateFinished() {
|
|
7148
|
+
this.count++;
|
|
7149
|
+
this._finished = new Promise((resolve) => {
|
|
7150
|
+
this.resolve = resolve;
|
|
7151
|
+
});
|
|
7152
|
+
}
|
|
7153
|
+
notifyFinished() {
|
|
7154
|
+
this.resolve();
|
|
7155
|
+
}
|
|
7156
|
+
/**
|
|
7157
|
+
* Allows the animation to be awaited.
|
|
7158
|
+
*
|
|
7159
|
+
* @deprecated Use `finished` instead.
|
|
7160
|
+
*/
|
|
7161
|
+
then(onResolve, onReject) {
|
|
7162
|
+
return this.finished.then(onResolve, onReject);
|
|
7163
|
+
}
|
|
7164
|
+
}
|
|
7165
|
+
|
|
7856
7166
|
const percentToProgress = (percent) => percent / 100;
|
|
7857
|
-
|
|
7858
|
-
* Animation that runs on the main thread. Designed to be WAAPI-spec in the subset of
|
|
7859
|
-
* features we expose publically. Mostly the compatibility is to ensure visual identity
|
|
7860
|
-
* between both WAAPI and main thread animations.
|
|
7861
|
-
*/
|
|
7862
|
-
class MainThreadAnimation extends BaseAnimation {
|
|
7167
|
+
class JSAnimation extends WithPromise {
|
|
7863
7168
|
constructor(options) {
|
|
7864
|
-
super(
|
|
7865
|
-
|
|
7866
|
-
|
|
7867
|
-
|
|
7868
|
-
this.holdTime = null;
|
|
7869
|
-
/**
|
|
7870
|
-
* The time at which the animation was cancelled.
|
|
7871
|
-
*/
|
|
7872
|
-
this.cancelTime = null;
|
|
7169
|
+
super();
|
|
7170
|
+
this.state = "idle";
|
|
7171
|
+
this.startTime = null;
|
|
7172
|
+
this.isStopped = false;
|
|
7873
7173
|
/**
|
|
7874
7174
|
* The current time of the animation.
|
|
7875
7175
|
*/
|
|
7876
7176
|
this.currentTime = 0;
|
|
7877
7177
|
/**
|
|
7878
|
-
*
|
|
7879
|
-
*/
|
|
7880
|
-
this.playbackSpeed = 1;
|
|
7881
|
-
/**
|
|
7882
|
-
* The state of the animation to apply when the animation is resolved. This
|
|
7883
|
-
* allows calls to the public API to control the animation before it is resolved,
|
|
7884
|
-
* without us having to resolve it first.
|
|
7178
|
+
* The time at which the animation was paused.
|
|
7885
7179
|
*/
|
|
7886
|
-
this.
|
|
7180
|
+
this.holdTime = null;
|
|
7887
7181
|
/**
|
|
7888
|
-
*
|
|
7182
|
+
* Playback speed as a factor. 0 would be stopped, -1 reverse and 2 double speed.
|
|
7889
7183
|
*/
|
|
7890
|
-
this.
|
|
7891
|
-
this.state = "idle";
|
|
7184
|
+
this.playbackSpeed = 1;
|
|
7892
7185
|
/**
|
|
7893
7186
|
* This method is bound to the instance to fix a pattern where
|
|
7894
7187
|
* animation.stop is returned as a reference from a useEffect.
|
|
7895
7188
|
*/
|
|
7896
7189
|
this.stop = () => {
|
|
7897
|
-
this.
|
|
7190
|
+
const { motionValue } = this.options;
|
|
7191
|
+
if (motionValue && motionValue.updatedAt !== time.now()) {
|
|
7192
|
+
this.tick(time.now());
|
|
7193
|
+
}
|
|
7898
7194
|
this.isStopped = true;
|
|
7899
7195
|
if (this.state === "idle")
|
|
7900
7196
|
return;
|
|
@@ -7902,49 +7198,35 @@ class MainThreadAnimation extends BaseAnimation {
|
|
|
7902
7198
|
const { onStop } = this.options;
|
|
7903
7199
|
onStop && onStop();
|
|
7904
7200
|
};
|
|
7905
|
-
|
|
7906
|
-
|
|
7907
|
-
|
|
7908
|
-
|
|
7909
|
-
|
|
7910
|
-
}
|
|
7911
|
-
|
|
7912
|
-
|
|
7913
|
-
|
|
7914
|
-
|
|
7915
|
-
|
|
7916
|
-
|
|
7917
|
-
}
|
|
7918
|
-
initPlayback(keyframes$1) {
|
|
7919
|
-
const { type = "keyframes", repeat = 0, repeatDelay = 0, repeatType, velocity = 0, } = this.options;
|
|
7920
|
-
const generatorFactory = isGenerator(type)
|
|
7921
|
-
? type
|
|
7922
|
-
: generators[type] || keyframes;
|
|
7923
|
-
/**
|
|
7924
|
-
* If our generator doesn't support mixing numbers, we need to replace keyframes with
|
|
7925
|
-
* [0, 100] and then make a function that maps that to the actual keyframes.
|
|
7926
|
-
*
|
|
7927
|
-
* 100 is chosen instead of 1 as it works nicer with spring animations.
|
|
7928
|
-
*/
|
|
7929
|
-
let mapPercentToKeyframes;
|
|
7930
|
-
let mirroredGenerator;
|
|
7201
|
+
this.options = options;
|
|
7202
|
+
this.initAnimation();
|
|
7203
|
+
this.play();
|
|
7204
|
+
if (options.autoplay === false)
|
|
7205
|
+
this.pause();
|
|
7206
|
+
}
|
|
7207
|
+
initAnimation() {
|
|
7208
|
+
const { options } = this;
|
|
7209
|
+
replaceTransitionType(options);
|
|
7210
|
+
const { type = keyframes, repeat = 0, repeatDelay = 0, repeatType, velocity = 0, } = options;
|
|
7211
|
+
let { keyframes: keyframes$1 } = options;
|
|
7212
|
+
const generatorFactory = type || keyframes;
|
|
7931
7213
|
if (process.env.NODE_ENV !== "production" &&
|
|
7932
7214
|
generatorFactory !== keyframes) {
|
|
7933
7215
|
invariant(keyframes$1.length <= 2, `Only two keyframes currently supported with spring and inertia animations. Trying to animate ${keyframes$1}`);
|
|
7934
7216
|
}
|
|
7935
7217
|
if (generatorFactory !== keyframes &&
|
|
7936
7218
|
typeof keyframes$1[0] !== "number") {
|
|
7937
|
-
|
|
7219
|
+
this.mixKeyframes = pipe(percentToProgress, mix(keyframes$1[0], keyframes$1[1]));
|
|
7938
7220
|
keyframes$1 = [0, 100];
|
|
7939
7221
|
}
|
|
7940
|
-
const generator = generatorFactory({ ...
|
|
7222
|
+
const generator = generatorFactory({ ...options, keyframes: keyframes$1 });
|
|
7941
7223
|
/**
|
|
7942
7224
|
* If we have a mirror repeat type we need to create a second generator that outputs the
|
|
7943
7225
|
* mirrored (not reversed) animation and later ping pong between the two generators.
|
|
7944
7226
|
*/
|
|
7945
7227
|
if (repeatType === "mirror") {
|
|
7946
|
-
mirroredGenerator = generatorFactory({
|
|
7947
|
-
...
|
|
7228
|
+
this.mirroredGenerator = generatorFactory({
|
|
7229
|
+
...options,
|
|
7948
7230
|
keyframes: [...keyframes$1].reverse(),
|
|
7949
7231
|
velocity: -velocity,
|
|
7950
7232
|
});
|
|
@@ -7961,38 +7243,29 @@ class MainThreadAnimation extends BaseAnimation {
|
|
|
7961
7243
|
generator.calculatedDuration = calcGeneratorDuration(generator);
|
|
7962
7244
|
}
|
|
7963
7245
|
const { calculatedDuration } = generator;
|
|
7964
|
-
|
|
7965
|
-
|
|
7966
|
-
|
|
7967
|
-
|
|
7968
|
-
mirroredGenerator,
|
|
7969
|
-
mapPercentToKeyframes,
|
|
7970
|
-
calculatedDuration,
|
|
7971
|
-
resolvedDuration,
|
|
7972
|
-
totalDuration,
|
|
7973
|
-
};
|
|
7246
|
+
this.calculatedDuration = calculatedDuration;
|
|
7247
|
+
this.resolvedDuration = calculatedDuration + repeatDelay;
|
|
7248
|
+
this.totalDuration = this.resolvedDuration * (repeat + 1) - repeatDelay;
|
|
7249
|
+
this.generator = generator;
|
|
7974
7250
|
}
|
|
7975
|
-
|
|
7976
|
-
const
|
|
7977
|
-
|
|
7978
|
-
if (this.
|
|
7979
|
-
this.
|
|
7251
|
+
updateTime(timestamp) {
|
|
7252
|
+
const animationTime = Math.round(timestamp - this.startTime) * this.playbackSpeed;
|
|
7253
|
+
// Update currentTime
|
|
7254
|
+
if (this.holdTime !== null) {
|
|
7255
|
+
this.currentTime = this.holdTime;
|
|
7980
7256
|
}
|
|
7981
7257
|
else {
|
|
7982
|
-
|
|
7258
|
+
// Rounding the time because floating point arithmetic is not always accurate, e.g. 3000.367 - 1000.367 =
|
|
7259
|
+
// 2000.0000000000002. This is a problem when we are comparing the currentTime with the duration, for
|
|
7260
|
+
// example.
|
|
7261
|
+
this.currentTime = animationTime;
|
|
7983
7262
|
}
|
|
7984
7263
|
}
|
|
7985
7264
|
tick(timestamp, sample = false) {
|
|
7986
|
-
const {
|
|
7987
|
-
// If the animations has failed to resolve, return the final keyframe.
|
|
7988
|
-
if (!resolved) {
|
|
7989
|
-
const { keyframes } = this.options;
|
|
7990
|
-
return { done: true, value: keyframes[keyframes.length - 1] };
|
|
7991
|
-
}
|
|
7992
|
-
const { finalKeyframe, generator, mirroredGenerator, mapPercentToKeyframes, keyframes, calculatedDuration, totalDuration, resolvedDuration, } = resolved;
|
|
7265
|
+
const { generator, totalDuration, mixKeyframes, mirroredGenerator, resolvedDuration, calculatedDuration, } = this;
|
|
7993
7266
|
if (this.startTime === null)
|
|
7994
7267
|
return generator.next(0);
|
|
7995
|
-
const { delay, repeat, repeatType, repeatDelay, onUpdate } = this.options;
|
|
7268
|
+
const { delay = 0, keyframes, repeat, repeatType, repeatDelay, type, onUpdate, finalKeyframe, } = this.options;
|
|
7996
7269
|
/**
|
|
7997
7270
|
* requestAnimationFrame timestamps can come through as lower than
|
|
7998
7271
|
* the startTime as set by performance.now(). Here we prevent this,
|
|
@@ -8005,23 +7278,15 @@ class MainThreadAnimation extends BaseAnimation {
|
|
|
8005
7278
|
else if (this.speed < 0) {
|
|
8006
7279
|
this.startTime = Math.min(timestamp - totalDuration / this.speed, this.startTime);
|
|
8007
7280
|
}
|
|
8008
|
-
// Update currentTime
|
|
8009
7281
|
if (sample) {
|
|
8010
7282
|
this.currentTime = timestamp;
|
|
8011
7283
|
}
|
|
8012
|
-
else if (this.holdTime !== null) {
|
|
8013
|
-
this.currentTime = this.holdTime;
|
|
8014
|
-
}
|
|
8015
7284
|
else {
|
|
8016
|
-
|
|
8017
|
-
// 2000.0000000000002. This is a problem when we are comparing the currentTime with the duration, for
|
|
8018
|
-
// example.
|
|
8019
|
-
this.currentTime =
|
|
8020
|
-
Math.round(timestamp - this.startTime) * this.speed;
|
|
7285
|
+
this.updateTime(timestamp);
|
|
8021
7286
|
}
|
|
8022
7287
|
// Rebase on delay
|
|
8023
|
-
const timeWithoutDelay = this.currentTime - delay * (this.
|
|
8024
|
-
const isInDelayPhase = this.
|
|
7288
|
+
const timeWithoutDelay = this.currentTime - delay * (this.playbackSpeed >= 0 ? 1 : -1);
|
|
7289
|
+
const isInDelayPhase = this.playbackSpeed >= 0
|
|
8025
7290
|
? timeWithoutDelay < 0
|
|
8026
7291
|
: timeWithoutDelay > totalDuration;
|
|
8027
7292
|
this.currentTime = Math.max(timeWithoutDelay, 0);
|
|
@@ -8082,20 +7347,21 @@ class MainThreadAnimation extends BaseAnimation {
|
|
|
8082
7347
|
const state = isInDelayPhase
|
|
8083
7348
|
? { done: false, value: keyframes[0] }
|
|
8084
7349
|
: frameGenerator.next(elapsed);
|
|
8085
|
-
if (
|
|
8086
|
-
state.value =
|
|
7350
|
+
if (mixKeyframes) {
|
|
7351
|
+
state.value = mixKeyframes(state.value);
|
|
8087
7352
|
}
|
|
8088
7353
|
let { done } = state;
|
|
8089
7354
|
if (!isInDelayPhase && calculatedDuration !== null) {
|
|
8090
7355
|
done =
|
|
8091
|
-
this.
|
|
7356
|
+
this.playbackSpeed >= 0
|
|
8092
7357
|
? this.currentTime >= totalDuration
|
|
8093
7358
|
: this.currentTime <= 0;
|
|
8094
7359
|
}
|
|
8095
7360
|
const isAnimationFinished = this.holdTime === null &&
|
|
8096
7361
|
(this.state === "finished" || (this.state === "running" && done));
|
|
8097
|
-
|
|
8098
|
-
|
|
7362
|
+
// TODO: The exception for inertia could be cleaner here
|
|
7363
|
+
if (isAnimationFinished && type !== inertia) {
|
|
7364
|
+
state.value = getFinalKeyframe(keyframes, this.options, finalKeyframe, this.speed);
|
|
8099
7365
|
}
|
|
8100
7366
|
if (onUpdate) {
|
|
8101
7367
|
onUpdate(state.value);
|
|
@@ -8105,135 +7371,434 @@ class MainThreadAnimation extends BaseAnimation {
|
|
|
8105
7371
|
}
|
|
8106
7372
|
return state;
|
|
8107
7373
|
}
|
|
7374
|
+
/**
|
|
7375
|
+
* Allows the returned animation to be awaited or promise-chained. Currently
|
|
7376
|
+
* resolves when the animation finishes at all but in a future update could/should
|
|
7377
|
+
* reject if its cancels.
|
|
7378
|
+
*/
|
|
7379
|
+
then(resolve, reject) {
|
|
7380
|
+
return this.finished.then(resolve, reject);
|
|
7381
|
+
}
|
|
8108
7382
|
get duration() {
|
|
8109
|
-
|
|
8110
|
-
|
|
7383
|
+
return millisecondsToSeconds(this.calculatedDuration);
|
|
7384
|
+
}
|
|
7385
|
+
get time() {
|
|
7386
|
+
return millisecondsToSeconds(this.currentTime);
|
|
7387
|
+
}
|
|
7388
|
+
set time(newTime) {
|
|
7389
|
+
newTime = secondsToMilliseconds(newTime);
|
|
7390
|
+
this.currentTime = newTime;
|
|
7391
|
+
if (this.startTime === null ||
|
|
7392
|
+
this.holdTime !== null ||
|
|
7393
|
+
this.playbackSpeed === 0) {
|
|
7394
|
+
this.holdTime = newTime;
|
|
7395
|
+
}
|
|
7396
|
+
else if (this.driver) {
|
|
7397
|
+
this.startTime = this.driver.now() - newTime / this.playbackSpeed;
|
|
7398
|
+
}
|
|
7399
|
+
}
|
|
7400
|
+
get speed() {
|
|
7401
|
+
return this.playbackSpeed;
|
|
7402
|
+
}
|
|
7403
|
+
set speed(newSpeed) {
|
|
7404
|
+
this.updateTime(time.now());
|
|
7405
|
+
const hasChanged = this.playbackSpeed !== newSpeed;
|
|
7406
|
+
this.playbackSpeed = newSpeed;
|
|
7407
|
+
if (hasChanged) {
|
|
7408
|
+
this.time = millisecondsToSeconds(this.currentTime);
|
|
7409
|
+
}
|
|
7410
|
+
}
|
|
7411
|
+
play() {
|
|
7412
|
+
if (this.isStopped)
|
|
7413
|
+
return;
|
|
7414
|
+
const { driver = frameloopDriver, onPlay, startTime } = this.options;
|
|
7415
|
+
if (!this.driver) {
|
|
7416
|
+
this.driver = driver((timestamp) => this.tick(timestamp));
|
|
7417
|
+
}
|
|
7418
|
+
onPlay && onPlay();
|
|
7419
|
+
const now = this.driver.now();
|
|
7420
|
+
if (this.holdTime !== null) {
|
|
7421
|
+
this.startTime = now - this.holdTime;
|
|
7422
|
+
}
|
|
7423
|
+
else if (this.state === "finished") {
|
|
7424
|
+
this.updateFinished();
|
|
7425
|
+
this.startTime = now;
|
|
7426
|
+
}
|
|
7427
|
+
else if (!this.startTime) {
|
|
7428
|
+
this.startTime = startTime ?? now;
|
|
7429
|
+
}
|
|
7430
|
+
if (this.state === "finished" && this.speed < 0) {
|
|
7431
|
+
this.startTime += this.calculatedDuration;
|
|
7432
|
+
}
|
|
7433
|
+
this.holdTime = null;
|
|
7434
|
+
/**
|
|
7435
|
+
* Set playState to running only after we've used it in
|
|
7436
|
+
* the previous logic.
|
|
7437
|
+
*/
|
|
7438
|
+
this.state = "running";
|
|
7439
|
+
this.driver.start();
|
|
7440
|
+
}
|
|
7441
|
+
pause() {
|
|
7442
|
+
this.state = "paused";
|
|
7443
|
+
this.updateTime(time.now());
|
|
7444
|
+
this.holdTime = this.currentTime;
|
|
7445
|
+
}
|
|
7446
|
+
complete() {
|
|
7447
|
+
if (this.state !== "running") {
|
|
7448
|
+
this.play();
|
|
7449
|
+
}
|
|
7450
|
+
this.state = "finished";
|
|
7451
|
+
this.holdTime = null;
|
|
7452
|
+
}
|
|
7453
|
+
finish() {
|
|
7454
|
+
this.notifyFinished();
|
|
7455
|
+
this.teardown();
|
|
7456
|
+
this.state = "finished";
|
|
7457
|
+
const { onComplete } = this.options;
|
|
7458
|
+
onComplete && onComplete();
|
|
7459
|
+
}
|
|
7460
|
+
cancel() {
|
|
7461
|
+
this.holdTime = null;
|
|
7462
|
+
this.startTime = 0;
|
|
7463
|
+
this.tick(0);
|
|
7464
|
+
this.teardown();
|
|
7465
|
+
}
|
|
7466
|
+
teardown() {
|
|
7467
|
+
this.state = "idle";
|
|
7468
|
+
this.stopDriver();
|
|
7469
|
+
this.startTime = this.holdTime = null;
|
|
7470
|
+
}
|
|
7471
|
+
stopDriver() {
|
|
7472
|
+
if (!this.driver)
|
|
7473
|
+
return;
|
|
7474
|
+
this.driver.stop();
|
|
7475
|
+
this.driver = undefined;
|
|
7476
|
+
}
|
|
7477
|
+
sample(sampleTime) {
|
|
7478
|
+
this.startTime = 0;
|
|
7479
|
+
return this.tick(sampleTime, true);
|
|
7480
|
+
}
|
|
7481
|
+
attachTimeline(timeline) {
|
|
7482
|
+
if (this.options.allowFlatten) {
|
|
7483
|
+
this.options.type = "keyframes";
|
|
7484
|
+
this.options.ease = "linear";
|
|
7485
|
+
this.initAnimation();
|
|
7486
|
+
}
|
|
7487
|
+
return timeline.observe(this);
|
|
7488
|
+
}
|
|
7489
|
+
}
|
|
7490
|
+
|
|
7491
|
+
function fillWildcards(keyframes) {
|
|
7492
|
+
for (let i = 1; i < keyframes.length; i++) {
|
|
7493
|
+
keyframes[i] ?? (keyframes[i] = keyframes[i - 1]);
|
|
7494
|
+
}
|
|
7495
|
+
}
|
|
7496
|
+
|
|
7497
|
+
const radToDeg = (rad) => (rad * 180) / Math.PI;
|
|
7498
|
+
const rotate = (v) => {
|
|
7499
|
+
const angle = radToDeg(Math.atan2(v[1], v[0]));
|
|
7500
|
+
return rebaseAngle(angle);
|
|
7501
|
+
};
|
|
7502
|
+
const matrix2dParsers = {
|
|
7503
|
+
x: 4,
|
|
7504
|
+
y: 5,
|
|
7505
|
+
translateX: 4,
|
|
7506
|
+
translateY: 5,
|
|
7507
|
+
scaleX: 0,
|
|
7508
|
+
scaleY: 3,
|
|
7509
|
+
scale: (v) => (Math.abs(v[0]) + Math.abs(v[3])) / 2,
|
|
7510
|
+
rotate,
|
|
7511
|
+
rotateZ: rotate,
|
|
7512
|
+
skewX: (v) => radToDeg(Math.atan(v[1])),
|
|
7513
|
+
skewY: (v) => radToDeg(Math.atan(v[2])),
|
|
7514
|
+
skew: (v) => (Math.abs(v[1]) + Math.abs(v[2])) / 2,
|
|
7515
|
+
};
|
|
7516
|
+
const rebaseAngle = (angle) => {
|
|
7517
|
+
angle = angle % 360;
|
|
7518
|
+
if (angle < 0)
|
|
7519
|
+
angle += 360;
|
|
7520
|
+
return angle;
|
|
7521
|
+
};
|
|
7522
|
+
const rotateZ = rotate;
|
|
7523
|
+
const scaleX = (v) => Math.sqrt(v[0] * v[0] + v[1] * v[1]);
|
|
7524
|
+
const scaleY = (v) => Math.sqrt(v[4] * v[4] + v[5] * v[5]);
|
|
7525
|
+
const matrix3dParsers = {
|
|
7526
|
+
x: 12,
|
|
7527
|
+
y: 13,
|
|
7528
|
+
z: 14,
|
|
7529
|
+
translateX: 12,
|
|
7530
|
+
translateY: 13,
|
|
7531
|
+
translateZ: 14,
|
|
7532
|
+
scaleX,
|
|
7533
|
+
scaleY,
|
|
7534
|
+
scale: (v) => (scaleX(v) + scaleY(v)) / 2,
|
|
7535
|
+
rotateX: (v) => rebaseAngle(radToDeg(Math.atan2(v[6], v[5]))),
|
|
7536
|
+
rotateY: (v) => rebaseAngle(radToDeg(Math.atan2(-v[2], v[0]))),
|
|
7537
|
+
rotateZ,
|
|
7538
|
+
rotate: rotateZ,
|
|
7539
|
+
skewX: (v) => radToDeg(Math.atan(v[4])),
|
|
7540
|
+
skewY: (v) => radToDeg(Math.atan(v[1])),
|
|
7541
|
+
skew: (v) => (Math.abs(v[1]) + Math.abs(v[4])) / 2,
|
|
7542
|
+
};
|
|
7543
|
+
function defaultTransformValue(name) {
|
|
7544
|
+
return name.includes("scale") ? 1 : 0;
|
|
7545
|
+
}
|
|
7546
|
+
function parseValueFromTransform(transform, name) {
|
|
7547
|
+
if (!transform || transform === "none") {
|
|
7548
|
+
return defaultTransformValue(name);
|
|
8111
7549
|
}
|
|
8112
|
-
|
|
8113
|
-
|
|
7550
|
+
const matrix3dMatch = transform.match(/^matrix3d\(([-\d.e\s,]+)\)$/u);
|
|
7551
|
+
let parsers;
|
|
7552
|
+
let match;
|
|
7553
|
+
if (matrix3dMatch) {
|
|
7554
|
+
parsers = matrix3dParsers;
|
|
7555
|
+
match = matrix3dMatch;
|
|
8114
7556
|
}
|
|
8115
|
-
|
|
8116
|
-
|
|
8117
|
-
|
|
8118
|
-
|
|
8119
|
-
this.holdTime = newTime;
|
|
8120
|
-
}
|
|
8121
|
-
else if (this.driver) {
|
|
8122
|
-
this.startTime = this.driver.now() - newTime / this.speed;
|
|
8123
|
-
}
|
|
7557
|
+
else {
|
|
7558
|
+
const matrix2dMatch = transform.match(/^matrix\(([-\d.e\s,]+)\)$/u);
|
|
7559
|
+
parsers = matrix2dParsers;
|
|
7560
|
+
match = matrix2dMatch;
|
|
8124
7561
|
}
|
|
8125
|
-
|
|
8126
|
-
return
|
|
7562
|
+
if (!match) {
|
|
7563
|
+
return defaultTransformValue(name);
|
|
8127
7564
|
}
|
|
8128
|
-
|
|
8129
|
-
|
|
8130
|
-
|
|
8131
|
-
|
|
8132
|
-
|
|
7565
|
+
const valueParser = parsers[name];
|
|
7566
|
+
const values = match[1].split(",").map(convertTransformToNumber);
|
|
7567
|
+
return typeof valueParser === "function"
|
|
7568
|
+
? valueParser(values)
|
|
7569
|
+
: values[valueParser];
|
|
7570
|
+
}
|
|
7571
|
+
const readTransformValue = (instance, name) => {
|
|
7572
|
+
const { transform = "none" } = getComputedStyle(instance);
|
|
7573
|
+
return parseValueFromTransform(transform, name);
|
|
7574
|
+
};
|
|
7575
|
+
function convertTransformToNumber(value) {
|
|
7576
|
+
return parseFloat(value.trim());
|
|
7577
|
+
}
|
|
7578
|
+
|
|
7579
|
+
const isNumOrPxType = (v) => v === number || v === px;
|
|
7580
|
+
const transformKeys = new Set(["x", "y", "z"]);
|
|
7581
|
+
const nonTranslationalTransformKeys = transformPropOrder.filter((key) => !transformKeys.has(key));
|
|
7582
|
+
function removeNonTranslationalTransform(visualElement) {
|
|
7583
|
+
const removedTransforms = [];
|
|
7584
|
+
nonTranslationalTransformKeys.forEach((key) => {
|
|
7585
|
+
const value = visualElement.getValue(key);
|
|
7586
|
+
if (value !== undefined) {
|
|
7587
|
+
removedTransforms.push([key, value.get()]);
|
|
7588
|
+
value.set(key.startsWith("scale") ? 1 : 0);
|
|
8133
7589
|
}
|
|
7590
|
+
});
|
|
7591
|
+
return removedTransforms;
|
|
7592
|
+
}
|
|
7593
|
+
const positionalValues = {
|
|
7594
|
+
// Dimensions
|
|
7595
|
+
width: ({ x }, { paddingLeft = "0", paddingRight = "0" }) => x.max - x.min - parseFloat(paddingLeft) - parseFloat(paddingRight),
|
|
7596
|
+
height: ({ y }, { paddingTop = "0", paddingBottom = "0" }) => y.max - y.min - parseFloat(paddingTop) - parseFloat(paddingBottom),
|
|
7597
|
+
top: (_bbox, { top }) => parseFloat(top),
|
|
7598
|
+
left: (_bbox, { left }) => parseFloat(left),
|
|
7599
|
+
bottom: ({ y }, { top }) => parseFloat(top) + (y.max - y.min),
|
|
7600
|
+
right: ({ x }, { left }) => parseFloat(left) + (x.max - x.min),
|
|
7601
|
+
// Transform
|
|
7602
|
+
x: (_bbox, { transform }) => parseValueFromTransform(transform, "x"),
|
|
7603
|
+
y: (_bbox, { transform }) => parseValueFromTransform(transform, "y"),
|
|
7604
|
+
};
|
|
7605
|
+
// Alias translate longform names
|
|
7606
|
+
positionalValues.translateX = positionalValues.x;
|
|
7607
|
+
positionalValues.translateY = positionalValues.y;
|
|
7608
|
+
|
|
7609
|
+
const toResolve = new Set();
|
|
7610
|
+
let isScheduled = false;
|
|
7611
|
+
let anyNeedsMeasurement = false;
|
|
7612
|
+
let isForced = false;
|
|
7613
|
+
function measureAllKeyframes() {
|
|
7614
|
+
if (anyNeedsMeasurement) {
|
|
7615
|
+
const resolversToMeasure = Array.from(toResolve).filter((resolver) => resolver.needsMeasurement);
|
|
7616
|
+
const elementsToMeasure = new Set(resolversToMeasure.map((resolver) => resolver.element));
|
|
7617
|
+
const transformsToRestore = new Map();
|
|
7618
|
+
/**
|
|
7619
|
+
* Write pass
|
|
7620
|
+
* If we're measuring elements we want to remove bounding box-changing transforms.
|
|
7621
|
+
*/
|
|
7622
|
+
elementsToMeasure.forEach((element) => {
|
|
7623
|
+
const removedTransforms = removeNonTranslationalTransform(element);
|
|
7624
|
+
if (!removedTransforms.length)
|
|
7625
|
+
return;
|
|
7626
|
+
transformsToRestore.set(element, removedTransforms);
|
|
7627
|
+
element.render();
|
|
7628
|
+
});
|
|
7629
|
+
// Read
|
|
7630
|
+
resolversToMeasure.forEach((resolver) => resolver.measureInitialState());
|
|
7631
|
+
// Write
|
|
7632
|
+
elementsToMeasure.forEach((element) => {
|
|
7633
|
+
element.render();
|
|
7634
|
+
const restore = transformsToRestore.get(element);
|
|
7635
|
+
if (restore) {
|
|
7636
|
+
restore.forEach(([key, value]) => {
|
|
7637
|
+
element.getValue(key)?.set(value);
|
|
7638
|
+
});
|
|
7639
|
+
}
|
|
7640
|
+
});
|
|
7641
|
+
// Read
|
|
7642
|
+
resolversToMeasure.forEach((resolver) => resolver.measureEndState());
|
|
7643
|
+
// Write
|
|
7644
|
+
resolversToMeasure.forEach((resolver) => {
|
|
7645
|
+
if (resolver.suspendedScrollY !== undefined) {
|
|
7646
|
+
window.scrollTo(0, resolver.suspendedScrollY);
|
|
7647
|
+
}
|
|
7648
|
+
});
|
|
8134
7649
|
}
|
|
8135
|
-
|
|
8136
|
-
|
|
8137
|
-
|
|
8138
|
-
|
|
8139
|
-
|
|
8140
|
-
|
|
8141
|
-
|
|
8142
|
-
|
|
8143
|
-
if (
|
|
8144
|
-
|
|
8145
|
-
const { driver = frameloopDriver, onPlay, startTime } = this.options;
|
|
8146
|
-
if (!this.driver) {
|
|
8147
|
-
this.driver = driver((timestamp) => this.tick(timestamp));
|
|
8148
|
-
}
|
|
8149
|
-
onPlay && onPlay();
|
|
8150
|
-
const now = this.driver.now();
|
|
8151
|
-
if (this.holdTime !== null) {
|
|
8152
|
-
this.startTime = now - this.holdTime;
|
|
8153
|
-
}
|
|
8154
|
-
else if (!this.startTime) {
|
|
8155
|
-
this.startTime = startTime ?? this.calcStartTime();
|
|
8156
|
-
}
|
|
8157
|
-
else if (this.state === "finished") {
|
|
8158
|
-
this.startTime = now;
|
|
8159
|
-
}
|
|
8160
|
-
if (this.state === "finished") {
|
|
8161
|
-
this.updateFinishedPromise();
|
|
7650
|
+
anyNeedsMeasurement = false;
|
|
7651
|
+
isScheduled = false;
|
|
7652
|
+
toResolve.forEach((resolver) => resolver.complete(isForced));
|
|
7653
|
+
toResolve.clear();
|
|
7654
|
+
}
|
|
7655
|
+
function readAllKeyframes() {
|
|
7656
|
+
toResolve.forEach((resolver) => {
|
|
7657
|
+
resolver.readKeyframes();
|
|
7658
|
+
if (resolver.needsMeasurement) {
|
|
7659
|
+
anyNeedsMeasurement = true;
|
|
8162
7660
|
}
|
|
8163
|
-
|
|
8164
|
-
|
|
7661
|
+
});
|
|
7662
|
+
}
|
|
7663
|
+
function flushKeyframeResolvers() {
|
|
7664
|
+
isForced = true;
|
|
7665
|
+
readAllKeyframes();
|
|
7666
|
+
measureAllKeyframes();
|
|
7667
|
+
isForced = false;
|
|
7668
|
+
}
|
|
7669
|
+
class KeyframeResolver {
|
|
7670
|
+
constructor(unresolvedKeyframes, onComplete, name, motionValue, element, isAsync = false) {
|
|
8165
7671
|
/**
|
|
8166
|
-
*
|
|
8167
|
-
*
|
|
7672
|
+
* Track whether this resolver has completed. Once complete, it never
|
|
7673
|
+
* needs to attempt keyframe resolution again.
|
|
8168
7674
|
*/
|
|
8169
|
-
this.
|
|
8170
|
-
|
|
7675
|
+
this.isComplete = false;
|
|
7676
|
+
/**
|
|
7677
|
+
* Track whether this resolver is async. If it is, it'll be added to the
|
|
7678
|
+
* resolver queue and flushed in the next frame. Resolvers that aren't going
|
|
7679
|
+
* to trigger read/write thrashing don't need to be async.
|
|
7680
|
+
*/
|
|
7681
|
+
this.isAsync = false;
|
|
7682
|
+
/**
|
|
7683
|
+
* Track whether this resolver needs to perform a measurement
|
|
7684
|
+
* to resolve its keyframes.
|
|
7685
|
+
*/
|
|
7686
|
+
this.needsMeasurement = false;
|
|
7687
|
+
/**
|
|
7688
|
+
* Track whether this resolver is currently scheduled to resolve
|
|
7689
|
+
* to allow it to be cancelled and resumed externally.
|
|
7690
|
+
*/
|
|
7691
|
+
this.isScheduled = false;
|
|
7692
|
+
this.unresolvedKeyframes = [...unresolvedKeyframes];
|
|
7693
|
+
this.onComplete = onComplete;
|
|
7694
|
+
this.name = name;
|
|
7695
|
+
this.motionValue = motionValue;
|
|
7696
|
+
this.element = element;
|
|
7697
|
+
this.isAsync = isAsync;
|
|
8171
7698
|
}
|
|
8172
|
-
|
|
8173
|
-
|
|
8174
|
-
|
|
8175
|
-
|
|
7699
|
+
scheduleResolve() {
|
|
7700
|
+
this.isScheduled = true;
|
|
7701
|
+
if (this.isAsync) {
|
|
7702
|
+
toResolve.add(this);
|
|
7703
|
+
if (!isScheduled) {
|
|
7704
|
+
isScheduled = true;
|
|
7705
|
+
frame.read(readAllKeyframes);
|
|
7706
|
+
frame.resolveKeyframes(measureAllKeyframes);
|
|
7707
|
+
}
|
|
7708
|
+
}
|
|
7709
|
+
else {
|
|
7710
|
+
this.readKeyframes();
|
|
7711
|
+
this.complete();
|
|
8176
7712
|
}
|
|
8177
|
-
this.state = "paused";
|
|
8178
|
-
this.holdTime = this.currentTime ?? 0;
|
|
8179
7713
|
}
|
|
8180
|
-
|
|
8181
|
-
|
|
8182
|
-
|
|
7714
|
+
readKeyframes() {
|
|
7715
|
+
const { unresolvedKeyframes, name, element, motionValue } = this;
|
|
7716
|
+
// If initial keyframe is null we need to read it from the DOM
|
|
7717
|
+
if (unresolvedKeyframes[0] === null) {
|
|
7718
|
+
const currentValue = motionValue?.get();
|
|
7719
|
+
// TODO: This doesn't work if the final keyframe is a wildcard
|
|
7720
|
+
const finalKeyframe = unresolvedKeyframes[unresolvedKeyframes.length - 1];
|
|
7721
|
+
if (currentValue !== undefined) {
|
|
7722
|
+
unresolvedKeyframes[0] = currentValue;
|
|
7723
|
+
}
|
|
7724
|
+
else if (element && name) {
|
|
7725
|
+
const valueAsRead = element.readValue(name, finalKeyframe);
|
|
7726
|
+
if (valueAsRead !== undefined && valueAsRead !== null) {
|
|
7727
|
+
unresolvedKeyframes[0] = valueAsRead;
|
|
7728
|
+
}
|
|
7729
|
+
}
|
|
7730
|
+
if (unresolvedKeyframes[0] === undefined) {
|
|
7731
|
+
unresolvedKeyframes[0] = finalKeyframe;
|
|
7732
|
+
}
|
|
7733
|
+
if (motionValue && currentValue === undefined) {
|
|
7734
|
+
motionValue.set(unresolvedKeyframes[0]);
|
|
7735
|
+
}
|
|
8183
7736
|
}
|
|
8184
|
-
|
|
8185
|
-
this.holdTime = null;
|
|
7737
|
+
fillWildcards(unresolvedKeyframes);
|
|
8186
7738
|
}
|
|
8187
|
-
|
|
8188
|
-
|
|
8189
|
-
|
|
8190
|
-
|
|
8191
|
-
|
|
7739
|
+
setFinalKeyframe() { }
|
|
7740
|
+
measureInitialState() { }
|
|
7741
|
+
renderEndStyles() { }
|
|
7742
|
+
measureEndState() { }
|
|
7743
|
+
complete(isForced = false) {
|
|
7744
|
+
this.isComplete = true;
|
|
7745
|
+
this.onComplete(this.unresolvedKeyframes, this.finalKeyframe, isForced);
|
|
7746
|
+
toResolve.delete(this);
|
|
8192
7747
|
}
|
|
8193
7748
|
cancel() {
|
|
8194
|
-
if (this.
|
|
8195
|
-
this.
|
|
7749
|
+
if (!this.isComplete) {
|
|
7750
|
+
this.isScheduled = false;
|
|
7751
|
+
toResolve.delete(this);
|
|
8196
7752
|
}
|
|
8197
|
-
this.teardown();
|
|
8198
|
-
this.updateFinishedPromise();
|
|
8199
|
-
}
|
|
8200
|
-
teardown() {
|
|
8201
|
-
this.state = "idle";
|
|
8202
|
-
this.stopDriver();
|
|
8203
|
-
this.resolveFinishedPromise();
|
|
8204
|
-
this.updateFinishedPromise();
|
|
8205
|
-
this.startTime = this.cancelTime = null;
|
|
8206
|
-
this.resolver.cancel();
|
|
8207
|
-
}
|
|
8208
|
-
stopDriver() {
|
|
8209
|
-
if (!this.driver)
|
|
8210
|
-
return;
|
|
8211
|
-
this.driver.stop();
|
|
8212
|
-
this.driver = undefined;
|
|
8213
|
-
}
|
|
8214
|
-
sample(time) {
|
|
8215
|
-
this.startTime = 0;
|
|
8216
|
-
return this.tick(time, true);
|
|
8217
7753
|
}
|
|
8218
|
-
|
|
8219
|
-
|
|
7754
|
+
resume() {
|
|
7755
|
+
if (!this.isComplete)
|
|
7756
|
+
this.scheduleResolve();
|
|
8220
7757
|
}
|
|
8221
7758
|
}
|
|
8222
7759
|
|
|
7760
|
+
const isCSSVar = (name) => name.startsWith("--");
|
|
7761
|
+
|
|
7762
|
+
function setStyle(element, name, value) {
|
|
7763
|
+
isCSSVar(name)
|
|
7764
|
+
? element.style.setProperty(name, value)
|
|
7765
|
+
: (element.style[name] = value);
|
|
7766
|
+
}
|
|
7767
|
+
|
|
7768
|
+
/*#__NO_SIDE_EFFECTS__*/
|
|
7769
|
+
function memo(callback) {
|
|
7770
|
+
let result;
|
|
7771
|
+
return () => {
|
|
7772
|
+
if (result === undefined)
|
|
7773
|
+
result = callback();
|
|
7774
|
+
return result;
|
|
7775
|
+
};
|
|
7776
|
+
}
|
|
7777
|
+
|
|
7778
|
+
const supportsScrollTimeline = /* @__PURE__ */ memo(() => window.ScrollTimeline !== undefined);
|
|
7779
|
+
|
|
8223
7780
|
/**
|
|
8224
|
-
*
|
|
7781
|
+
* Add the ability for test suites to manually set support flags
|
|
7782
|
+
* to better test more environments.
|
|
8225
7783
|
*/
|
|
8226
|
-
const
|
|
8227
|
-
"opacity",
|
|
8228
|
-
"clipPath",
|
|
8229
|
-
"filter",
|
|
8230
|
-
"transform",
|
|
8231
|
-
// TODO: Can be accelerated but currently disabled until https://issues.chromium.org/issues/41491098 is resolved
|
|
8232
|
-
// or until we implement support for linear() easing.
|
|
8233
|
-
// "background-color"
|
|
8234
|
-
]);
|
|
7784
|
+
const supportsFlags = {};
|
|
8235
7785
|
|
|
8236
|
-
|
|
7786
|
+
function memoSupports(callback, supportsFlag) {
|
|
7787
|
+
const memoized = memo(callback);
|
|
7788
|
+
return () => supportsFlags[supportsFlag] ?? memoized();
|
|
7789
|
+
}
|
|
7790
|
+
|
|
7791
|
+
const supportsLinearEasing = /*@__PURE__*/ memoSupports(() => {
|
|
7792
|
+
try {
|
|
7793
|
+
document
|
|
7794
|
+
.createElement("div")
|
|
7795
|
+
.animate({ opacity: 0 }, { easing: "linear(0, 1)" });
|
|
7796
|
+
}
|
|
7797
|
+
catch (e) {
|
|
7798
|
+
return false;
|
|
7799
|
+
}
|
|
7800
|
+
return true;
|
|
7801
|
+
}, "linearEasing");
|
|
8237
7802
|
|
|
8238
7803
|
const cubicBezierAsString = ([a, b, c, d]) => `cubic-bezier(${a}, ${b}, ${c}, ${d})`;
|
|
8239
7804
|
|
|
@@ -8253,8 +7818,10 @@ function mapEasingToNativeEasing(easing, duration) {
|
|
|
8253
7818
|
if (!easing) {
|
|
8254
7819
|
return undefined;
|
|
8255
7820
|
}
|
|
8256
|
-
else if (typeof easing === "function"
|
|
8257
|
-
return
|
|
7821
|
+
else if (typeof easing === "function") {
|
|
7822
|
+
return supportsLinearEasing()
|
|
7823
|
+
? generateLinearEasing(easing, duration)
|
|
7824
|
+
: "ease-out";
|
|
8258
7825
|
}
|
|
8259
7826
|
else if (isBezierDefinition(easing)) {
|
|
8260
7827
|
return cubicBezierAsString(easing);
|
|
@@ -8268,7 +7835,7 @@ function mapEasingToNativeEasing(easing, duration) {
|
|
|
8268
7835
|
}
|
|
8269
7836
|
}
|
|
8270
7837
|
|
|
8271
|
-
function startWaapiAnimation(element, valueName, keyframes, { delay = 0, duration = 300, repeat = 0, repeatType = "loop", ease = "
|
|
7838
|
+
function startWaapiAnimation(element, valueName, keyframes, { delay = 0, duration = 300, repeat = 0, repeatType = "loop", ease = "easeOut", times, } = {}, pseudoElement = undefined) {
|
|
8272
7839
|
const keyframeOptions = {
|
|
8273
7840
|
[valueName]: keyframes,
|
|
8274
7841
|
};
|
|
@@ -8280,85 +7847,181 @@ function startWaapiAnimation(element, valueName, keyframes, { delay = 0, duratio
|
|
|
8280
7847
|
*/
|
|
8281
7848
|
if (Array.isArray(easing))
|
|
8282
7849
|
keyframeOptions.easing = easing;
|
|
8283
|
-
const
|
|
7850
|
+
const options = {
|
|
8284
7851
|
delay,
|
|
8285
7852
|
duration,
|
|
8286
7853
|
easing: !Array.isArray(easing) ? easing : "linear",
|
|
8287
7854
|
fill: "both",
|
|
8288
7855
|
iterations: repeat + 1,
|
|
8289
7856
|
direction: repeatType === "reverse" ? "alternate" : "normal",
|
|
8290
|
-
|
|
8291
|
-
|
|
7857
|
+
};
|
|
7858
|
+
if (pseudoElement)
|
|
7859
|
+
options.pseudoElement = pseudoElement;
|
|
7860
|
+
const animation = element.animate(keyframeOptions, options);
|
|
8292
7861
|
return animation;
|
|
8293
7862
|
}
|
|
8294
7863
|
|
|
8295
|
-
function
|
|
8296
|
-
|
|
8297
|
-
animation.onfinish = null;
|
|
7864
|
+
function isGenerator(type) {
|
|
7865
|
+
return typeof type === "function" && "applyToOptions" in type;
|
|
8298
7866
|
}
|
|
8299
7867
|
|
|
8300
|
-
function
|
|
8301
|
-
|
|
8302
|
-
|
|
8303
|
-
|
|
8304
|
-
|
|
8305
|
-
|
|
8306
|
-
(
|
|
7868
|
+
function applyGeneratorOptions({ type, ...options }) {
|
|
7869
|
+
if (isGenerator(type) && supportsLinearEasing()) {
|
|
7870
|
+
return type.applyToOptions(options);
|
|
7871
|
+
}
|
|
7872
|
+
else {
|
|
7873
|
+
options.duration ?? (options.duration = 300);
|
|
7874
|
+
options.ease ?? (options.ease = "easeOut");
|
|
7875
|
+
}
|
|
7876
|
+
return options;
|
|
8307
7877
|
}
|
|
8308
7878
|
|
|
8309
7879
|
/**
|
|
8310
|
-
*
|
|
8311
|
-
* results (more than one keyframe per frame at 60fps) and
|
|
8312
|
-
* keyframe quantity.
|
|
8313
|
-
*/
|
|
8314
|
-
const sampleDelta = 10; //ms
|
|
8315
|
-
/**
|
|
8316
|
-
* Implement a practical max duration for keyframe generation
|
|
8317
|
-
* to prevent infinite loops
|
|
8318
|
-
*/
|
|
8319
|
-
const maxDuration = 20000;
|
|
8320
|
-
/**
|
|
8321
|
-
* Check if an animation can run natively via WAAPI or requires pregenerated keyframes.
|
|
8322
|
-
* WAAPI doesn't support spring or function easings so we run these as JS animation before
|
|
8323
|
-
* handing off.
|
|
7880
|
+
* NativeAnimation implements AnimationPlaybackControls for the browser's Web Animations API.
|
|
8324
7881
|
*/
|
|
8325
|
-
|
|
8326
|
-
|
|
8327
|
-
|
|
8328
|
-
|
|
8329
|
-
|
|
8330
|
-
|
|
7882
|
+
class NativeAnimation extends WithPromise {
|
|
7883
|
+
constructor(options) {
|
|
7884
|
+
super();
|
|
7885
|
+
this.finishedTime = null;
|
|
7886
|
+
this.isStopped = false;
|
|
7887
|
+
if (!options)
|
|
7888
|
+
return;
|
|
7889
|
+
const { element, name, keyframes, pseudoElement, allowFlatten = false, finalKeyframe, } = options;
|
|
7890
|
+
this.isPseudoElement = Boolean(pseudoElement);
|
|
7891
|
+
this.allowFlatten = allowFlatten;
|
|
7892
|
+
this.options = options;
|
|
7893
|
+
invariant(typeof options.type !== "string", `animateMini doesn't support "type" as a string. Did you mean to import { spring } from "motion"?`);
|
|
7894
|
+
const transition = applyGeneratorOptions(options);
|
|
7895
|
+
this.animation = startWaapiAnimation(element, name, keyframes, transition, pseudoElement);
|
|
7896
|
+
if (transition.autoplay === false) {
|
|
7897
|
+
this.animation.pause();
|
|
7898
|
+
}
|
|
7899
|
+
this.animation.onfinish = () => {
|
|
7900
|
+
this.finishedTime = this.time;
|
|
7901
|
+
if (!pseudoElement) {
|
|
7902
|
+
const keyframe = getFinalKeyframe(keyframes, this.options, finalKeyframe, this.speed);
|
|
7903
|
+
if (this.updateMotionValue) {
|
|
7904
|
+
this.updateMotionValue(keyframe);
|
|
7905
|
+
}
|
|
7906
|
+
else {
|
|
7907
|
+
/**
|
|
7908
|
+
* If we can, we want to commit the final style as set by the user,
|
|
7909
|
+
* rather than the computed keyframe value supplied by the animation.
|
|
7910
|
+
*/
|
|
7911
|
+
setStyle(element, name, keyframe);
|
|
7912
|
+
}
|
|
7913
|
+
this.animation.cancel();
|
|
7914
|
+
}
|
|
7915
|
+
this.notifyFinished();
|
|
7916
|
+
};
|
|
7917
|
+
}
|
|
7918
|
+
play() {
|
|
7919
|
+
if (this.isStopped)
|
|
7920
|
+
return;
|
|
7921
|
+
this.animation.play();
|
|
7922
|
+
if (this.state === "finished") {
|
|
7923
|
+
this.updateFinished();
|
|
7924
|
+
}
|
|
7925
|
+
}
|
|
7926
|
+
pause() {
|
|
7927
|
+
this.animation.pause();
|
|
7928
|
+
}
|
|
7929
|
+
complete() {
|
|
7930
|
+
this.animation.finish?.();
|
|
7931
|
+
}
|
|
7932
|
+
cancel() {
|
|
7933
|
+
try {
|
|
7934
|
+
this.animation.cancel();
|
|
7935
|
+
}
|
|
7936
|
+
catch (e) { }
|
|
7937
|
+
}
|
|
7938
|
+
stop() {
|
|
7939
|
+
if (this.isStopped)
|
|
7940
|
+
return;
|
|
7941
|
+
this.isStopped = true;
|
|
7942
|
+
const { state } = this;
|
|
7943
|
+
if (state === "idle" || state === "finished") {
|
|
7944
|
+
return;
|
|
7945
|
+
}
|
|
7946
|
+
if (this.updateMotionValue) {
|
|
7947
|
+
this.updateMotionValue();
|
|
7948
|
+
}
|
|
7949
|
+
else {
|
|
7950
|
+
this.commitStyles();
|
|
7951
|
+
}
|
|
7952
|
+
if (!this.isPseudoElement)
|
|
7953
|
+
this.cancel();
|
|
7954
|
+
}
|
|
8331
7955
|
/**
|
|
8332
|
-
*
|
|
8333
|
-
*
|
|
8334
|
-
*
|
|
7956
|
+
* WAAPI doesn't natively have any interruption capabilities.
|
|
7957
|
+
*
|
|
7958
|
+
* In this method, we commit styles back to the DOM before cancelling
|
|
7959
|
+
* the animation.
|
|
7960
|
+
*
|
|
7961
|
+
* This is designed to be overridden by NativeAnimationExtended, which
|
|
7962
|
+
* will create a renderless JS animation and sample it twice to calculate
|
|
7963
|
+
* its current value, "previous" value, and therefore allow
|
|
7964
|
+
* Motion to also correctly calculate velocity for any subsequent animation
|
|
7965
|
+
* while deferring the commit until the next animation frame.
|
|
8335
7966
|
*/
|
|
8336
|
-
|
|
8337
|
-
|
|
8338
|
-
|
|
8339
|
-
|
|
8340
|
-
|
|
8341
|
-
|
|
8342
|
-
|
|
8343
|
-
|
|
8344
|
-
|
|
7967
|
+
commitStyles() {
|
|
7968
|
+
if (!this.isPseudoElement) {
|
|
7969
|
+
this.animation.commitStyles?.();
|
|
7970
|
+
}
|
|
7971
|
+
}
|
|
7972
|
+
get duration() {
|
|
7973
|
+
const duration = this.animation.effect?.getComputedTiming?.().duration || 0;
|
|
7974
|
+
return millisecondsToSeconds(Number(duration));
|
|
7975
|
+
}
|
|
7976
|
+
get time() {
|
|
7977
|
+
return millisecondsToSeconds(Number(this.animation.currentTime) || 0);
|
|
7978
|
+
}
|
|
7979
|
+
set time(newTime) {
|
|
7980
|
+
this.finishedTime = null;
|
|
7981
|
+
this.animation.currentTime = secondsToMilliseconds(newTime);
|
|
7982
|
+
}
|
|
8345
7983
|
/**
|
|
8346
|
-
*
|
|
8347
|
-
*
|
|
7984
|
+
* The playback speed of the animation.
|
|
7985
|
+
* 1 = normal speed, 2 = double speed, 0.5 = half speed.
|
|
8348
7986
|
*/
|
|
8349
|
-
|
|
8350
|
-
|
|
8351
|
-
|
|
8352
|
-
|
|
8353
|
-
|
|
7987
|
+
get speed() {
|
|
7988
|
+
return this.animation.playbackRate;
|
|
7989
|
+
}
|
|
7990
|
+
set speed(newSpeed) {
|
|
7991
|
+
// Allow backwards playback after finishing
|
|
7992
|
+
if (newSpeed < 0)
|
|
7993
|
+
this.finishedTime = null;
|
|
7994
|
+
this.animation.playbackRate = newSpeed;
|
|
7995
|
+
}
|
|
7996
|
+
get state() {
|
|
7997
|
+
return this.finishedTime !== null
|
|
7998
|
+
? "finished"
|
|
7999
|
+
: this.animation.playState;
|
|
8000
|
+
}
|
|
8001
|
+
get startTime() {
|
|
8002
|
+
return Number(this.animation.startTime);
|
|
8003
|
+
}
|
|
8004
|
+
set startTime(newStartTime) {
|
|
8005
|
+
this.animation.startTime = newStartTime;
|
|
8006
|
+
}
|
|
8007
|
+
/**
|
|
8008
|
+
* Attaches a timeline to the animation, for instance the `ScrollTimeline`.
|
|
8009
|
+
*/
|
|
8010
|
+
attachTimeline({ timeline, observe }) {
|
|
8011
|
+
if (this.allowFlatten) {
|
|
8012
|
+
this.animation.effect?.updateTiming({ easing: "linear" });
|
|
8013
|
+
}
|
|
8014
|
+
this.animation.onfinish = null;
|
|
8015
|
+
if (timeline && supportsScrollTimeline()) {
|
|
8016
|
+
this.animation.timeline = timeline;
|
|
8017
|
+
return noop;
|
|
8018
|
+
}
|
|
8019
|
+
else {
|
|
8020
|
+
return observe(this);
|
|
8021
|
+
}
|
|
8354
8022
|
}
|
|
8355
|
-
return {
|
|
8356
|
-
times: undefined,
|
|
8357
|
-
keyframes: pregeneratedKeyframes,
|
|
8358
|
-
duration: t - sampleDelta,
|
|
8359
|
-
ease: "linear",
|
|
8360
|
-
};
|
|
8361
8023
|
}
|
|
8024
|
+
|
|
8362
8025
|
const unsupportedEasingFunctions = {
|
|
8363
8026
|
anticipate,
|
|
8364
8027
|
backInOut,
|
|
@@ -8367,386 +8030,334 @@ const unsupportedEasingFunctions = {
|
|
|
8367
8030
|
function isUnsupportedEase(key) {
|
|
8368
8031
|
return key in unsupportedEasingFunctions;
|
|
8369
8032
|
}
|
|
8370
|
-
|
|
8033
|
+
function replaceStringEasing(transition) {
|
|
8034
|
+
if (typeof transition.ease === "string" &&
|
|
8035
|
+
isUnsupportedEase(transition.ease)) {
|
|
8036
|
+
transition.ease = unsupportedEasingFunctions[transition.ease];
|
|
8037
|
+
}
|
|
8038
|
+
}
|
|
8039
|
+
|
|
8040
|
+
/**
|
|
8041
|
+
* 10ms is chosen here as it strikes a balance between smooth
|
|
8042
|
+
* results (more than one keyframe per frame at 60fps) and
|
|
8043
|
+
* keyframe quantity.
|
|
8044
|
+
*/
|
|
8045
|
+
const sampleDelta = 10; //ms
|
|
8046
|
+
class NativeAnimationExtended extends NativeAnimation {
|
|
8371
8047
|
constructor(options) {
|
|
8048
|
+
/**
|
|
8049
|
+
* The base NativeAnimation function only supports a subset
|
|
8050
|
+
* of Motion easings, and WAAPI also only supports some
|
|
8051
|
+
* easing functions via string/cubic-bezier definitions.
|
|
8052
|
+
*
|
|
8053
|
+
* This function replaces those unsupported easing functions
|
|
8054
|
+
* with a JS easing function. This will later get compiled
|
|
8055
|
+
* to a linear() easing function.
|
|
8056
|
+
*/
|
|
8057
|
+
replaceStringEasing(options);
|
|
8058
|
+
/**
|
|
8059
|
+
* Ensure we replace the transition type with a generator function
|
|
8060
|
+
* before passing to WAAPI.
|
|
8061
|
+
*
|
|
8062
|
+
* TODO: Does this have a better home? It could be shared with
|
|
8063
|
+
* JSAnimation.
|
|
8064
|
+
*/
|
|
8065
|
+
replaceTransitionType(options);
|
|
8372
8066
|
super(options);
|
|
8373
|
-
|
|
8374
|
-
|
|
8375
|
-
|
|
8067
|
+
if (options.startTime) {
|
|
8068
|
+
this.startTime = options.startTime;
|
|
8069
|
+
}
|
|
8070
|
+
this.options = options;
|
|
8071
|
+
}
|
|
8072
|
+
/**
|
|
8073
|
+
* WAAPI doesn't natively have any interruption capabilities.
|
|
8074
|
+
*
|
|
8075
|
+
* Rather than read commited styles back out of the DOM, we can
|
|
8076
|
+
* create a renderless JS animation and sample it twice to calculate
|
|
8077
|
+
* its current value, "previous" value, and therefore allow
|
|
8078
|
+
* Motion to calculate velocity for any subsequent animation.
|
|
8079
|
+
*/
|
|
8080
|
+
updateMotionValue(value) {
|
|
8081
|
+
const { motionValue, onUpdate, onComplete, element, ...options } = this.options;
|
|
8082
|
+
if (!motionValue)
|
|
8083
|
+
return;
|
|
8084
|
+
if (value !== undefined) {
|
|
8085
|
+
motionValue.set(value);
|
|
8086
|
+
return;
|
|
8087
|
+
}
|
|
8088
|
+
const sampleAnimation = new JSAnimation({
|
|
8089
|
+
...options,
|
|
8090
|
+
autoplay: false,
|
|
8091
|
+
});
|
|
8092
|
+
const sampleTime = secondsToMilliseconds(this.finishedTime ?? this.time);
|
|
8093
|
+
motionValue.setWithVelocity(sampleAnimation.sample(sampleTime - sampleDelta).value, sampleAnimation.sample(sampleTime).value, sampleDelta);
|
|
8094
|
+
sampleAnimation.stop();
|
|
8095
|
+
}
|
|
8096
|
+
}
|
|
8097
|
+
|
|
8098
|
+
/**
|
|
8099
|
+
* Check if a value is animatable. Examples:
|
|
8100
|
+
*
|
|
8101
|
+
* ✅: 100, "100px", "#fff"
|
|
8102
|
+
* ❌: "block", "url(2.jpg)"
|
|
8103
|
+
* @param value
|
|
8104
|
+
*
|
|
8105
|
+
* @internal
|
|
8106
|
+
*/
|
|
8107
|
+
const isAnimatable = (value, name) => {
|
|
8108
|
+
// If the list of keys tat might be non-animatable grows, replace with Set
|
|
8109
|
+
if (name === "zIndex")
|
|
8110
|
+
return false;
|
|
8111
|
+
// If it's a number or a keyframes array, we can animate it. We might at some point
|
|
8112
|
+
// need to do a deep isAnimatable check of keyframes, or let Popmotion handle this,
|
|
8113
|
+
// but for now lets leave it like this for performance reasons
|
|
8114
|
+
if (typeof value === "number" || Array.isArray(value))
|
|
8115
|
+
return true;
|
|
8116
|
+
if (typeof value === "string" && // It's animatable if we have a string
|
|
8117
|
+
(complex.test(value) || value === "0") && // And it contains numbers and/or colors
|
|
8118
|
+
!value.startsWith("url(") // Unless it starts with "url("
|
|
8119
|
+
) {
|
|
8120
|
+
return true;
|
|
8121
|
+
}
|
|
8122
|
+
return false;
|
|
8123
|
+
};
|
|
8124
|
+
|
|
8125
|
+
function hasKeyframesChanged(keyframes) {
|
|
8126
|
+
const current = keyframes[0];
|
|
8127
|
+
if (keyframes.length === 1)
|
|
8128
|
+
return true;
|
|
8129
|
+
for (let i = 0; i < keyframes.length; i++) {
|
|
8130
|
+
if (keyframes[i] !== current)
|
|
8131
|
+
return true;
|
|
8132
|
+
}
|
|
8133
|
+
}
|
|
8134
|
+
function canAnimate(keyframes, name, type, velocity) {
|
|
8135
|
+
/**
|
|
8136
|
+
* Check if we're able to animate between the start and end keyframes,
|
|
8137
|
+
* and throw a warning if we're attempting to animate between one that's
|
|
8138
|
+
* animatable and another that isn't.
|
|
8139
|
+
*/
|
|
8140
|
+
const originKeyframe = keyframes[0];
|
|
8141
|
+
if (originKeyframe === null)
|
|
8142
|
+
return false;
|
|
8143
|
+
/**
|
|
8144
|
+
* These aren't traditionally animatable but we do support them.
|
|
8145
|
+
* In future we could look into making this more generic or replacing
|
|
8146
|
+
* this function with mix() === mixImmediate
|
|
8147
|
+
*/
|
|
8148
|
+
if (name === "display" || name === "visibility")
|
|
8149
|
+
return true;
|
|
8150
|
+
const targetKeyframe = keyframes[keyframes.length - 1];
|
|
8151
|
+
const isOriginAnimatable = isAnimatable(originKeyframe, name);
|
|
8152
|
+
const isTargetAnimatable = isAnimatable(targetKeyframe, name);
|
|
8153
|
+
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.`);
|
|
8154
|
+
// Always skip if any of these are true
|
|
8155
|
+
if (!isOriginAnimatable || !isTargetAnimatable) {
|
|
8156
|
+
return false;
|
|
8157
|
+
}
|
|
8158
|
+
return (hasKeyframesChanged(keyframes) ||
|
|
8159
|
+
((type === "spring" || isGenerator(type)) && velocity));
|
|
8160
|
+
}
|
|
8161
|
+
|
|
8162
|
+
/**
|
|
8163
|
+
* A list of values that can be hardware-accelerated.
|
|
8164
|
+
*/
|
|
8165
|
+
const acceleratedValues = new Set([
|
|
8166
|
+
"opacity",
|
|
8167
|
+
"clipPath",
|
|
8168
|
+
"filter",
|
|
8169
|
+
"transform",
|
|
8170
|
+
// TODO: Can be accelerated but currently disabled until https://issues.chromium.org/issues/41491098 is resolved
|
|
8171
|
+
// or until we implement support for linear() easing.
|
|
8172
|
+
// "background-color"
|
|
8173
|
+
]);
|
|
8174
|
+
const supportsWaapi = /*@__PURE__*/ memo(() => Object.hasOwnProperty.call(Element.prototype, "animate"));
|
|
8175
|
+
function supportsBrowserAnimation(options) {
|
|
8176
|
+
const { motionValue, name, repeatDelay, repeatType, damping, type } = options;
|
|
8177
|
+
if (!motionValue ||
|
|
8178
|
+
!motionValue.owner ||
|
|
8179
|
+
!(motionValue.owner.current instanceof HTMLElement)) {
|
|
8180
|
+
return false;
|
|
8181
|
+
}
|
|
8182
|
+
const { onUpdate, transformTemplate } = motionValue.owner.getProps();
|
|
8183
|
+
return (supportsWaapi() &&
|
|
8184
|
+
name &&
|
|
8185
|
+
acceleratedValues.has(name) &&
|
|
8186
|
+
(name !== "transform" || !transformTemplate) &&
|
|
8187
|
+
/**
|
|
8188
|
+
* If we're outputting values to onUpdate then we can't use WAAPI as there's
|
|
8189
|
+
* no way to read the value from WAAPI every frame.
|
|
8190
|
+
*/
|
|
8191
|
+
!onUpdate &&
|
|
8192
|
+
!repeatDelay &&
|
|
8193
|
+
repeatType !== "mirror" &&
|
|
8194
|
+
damping !== 0 &&
|
|
8195
|
+
type !== "inertia");
|
|
8196
|
+
}
|
|
8197
|
+
|
|
8198
|
+
/**
|
|
8199
|
+
* Maximum time allowed between an animation being created and it being
|
|
8200
|
+
* resolved for us to use the latter as the start time.
|
|
8201
|
+
*
|
|
8202
|
+
* This is to ensure that while we prefer to "start" an animation as soon
|
|
8203
|
+
* as it's triggered, we also want to avoid a visual jump if there's a big delay
|
|
8204
|
+
* between these two moments.
|
|
8205
|
+
*/
|
|
8206
|
+
const MAX_RESOLVE_DELAY = 40;
|
|
8207
|
+
class AsyncMotionValueAnimation extends WithPromise {
|
|
8208
|
+
constructor({ autoplay = true, delay = 0, type = "keyframes", repeat = 0, repeatDelay = 0, repeatType = "loop", keyframes, name, motionValue, element, ...options }) {
|
|
8209
|
+
super();
|
|
8210
|
+
/**
|
|
8211
|
+
* Bound to support return animation.stop pattern
|
|
8212
|
+
*/
|
|
8213
|
+
this.stop = () => {
|
|
8214
|
+
if (this._animation) {
|
|
8215
|
+
this._animation.stop();
|
|
8216
|
+
this.stopTimeline?.();
|
|
8217
|
+
}
|
|
8218
|
+
else {
|
|
8219
|
+
this.keyframeResolver?.cancel();
|
|
8220
|
+
}
|
|
8221
|
+
};
|
|
8222
|
+
this.createdAt = time.now();
|
|
8223
|
+
const optionsWithDefaults = {
|
|
8224
|
+
autoplay,
|
|
8225
|
+
delay,
|
|
8226
|
+
type,
|
|
8227
|
+
repeat,
|
|
8228
|
+
repeatDelay,
|
|
8229
|
+
repeatType,
|
|
8230
|
+
name,
|
|
8231
|
+
motionValue,
|
|
8232
|
+
element,
|
|
8233
|
+
...options,
|
|
8234
|
+
};
|
|
8235
|
+
const KeyframeResolver$1 = element?.KeyframeResolver || KeyframeResolver;
|
|
8236
|
+
this.keyframeResolver = new KeyframeResolver$1(keyframes, (resolvedKeyframes, finalKeyframe, forced) => this.onKeyframesResolved(resolvedKeyframes, finalKeyframe, optionsWithDefaults, !forced), name, motionValue, element);
|
|
8237
|
+
this.keyframeResolver?.scheduleResolve();
|
|
8376
8238
|
}
|
|
8377
|
-
|
|
8378
|
-
|
|
8239
|
+
onKeyframesResolved(keyframes, finalKeyframe, options, sync) {
|
|
8240
|
+
this.keyframeResolver = undefined;
|
|
8241
|
+
const { name, type, velocity, delay, isHandoff, onUpdate, onComplete } = options;
|
|
8242
|
+
this.resolvedAt = time.now();
|
|
8379
8243
|
/**
|
|
8380
|
-
* If
|
|
8381
|
-
*
|
|
8244
|
+
* If we can't animate this value with the resolved keyframes
|
|
8245
|
+
* then we should complete it immediately.
|
|
8382
8246
|
*/
|
|
8383
|
-
if (!
|
|
8384
|
-
|
|
8247
|
+
if (!canAnimate(keyframes, name, type, velocity)) {
|
|
8248
|
+
if (MotionGlobalConfig.instantAnimations || !delay) {
|
|
8249
|
+
onUpdate?.(getFinalKeyframe(keyframes, options, finalKeyframe));
|
|
8250
|
+
}
|
|
8251
|
+
keyframes[0] = keyframes[keyframes.length - 1];
|
|
8252
|
+
options.duration = 0;
|
|
8253
|
+
options.repeat = 0;
|
|
8385
8254
|
}
|
|
8386
8255
|
/**
|
|
8387
|
-
*
|
|
8388
|
-
*
|
|
8389
|
-
*
|
|
8256
|
+
* Resolve startTime for the animation.
|
|
8257
|
+
*
|
|
8258
|
+
* This method uses the createdAt and resolvedAt to calculate the
|
|
8259
|
+
* animation startTime. *Ideally*, we would use the createdAt time as t=0
|
|
8260
|
+
* as the following frame would then be the first frame of the animation in
|
|
8261
|
+
* progress, which would feel snappier.
|
|
8262
|
+
*
|
|
8263
|
+
* However, if there's a delay (main thread work) between the creation of
|
|
8264
|
+
* the animation and the first commited frame, we prefer to use resolvedAt
|
|
8265
|
+
* to avoid a sudden jump into the animation.
|
|
8390
8266
|
*/
|
|
8391
|
-
|
|
8392
|
-
|
|
8393
|
-
|
|
8394
|
-
|
|
8395
|
-
|
|
8267
|
+
const startTime = sync
|
|
8268
|
+
? !this.resolvedAt
|
|
8269
|
+
? this.createdAt
|
|
8270
|
+
: this.resolvedAt - this.createdAt > MAX_RESOLVE_DELAY
|
|
8271
|
+
? this.resolvedAt
|
|
8272
|
+
: this.createdAt
|
|
8273
|
+
: undefined;
|
|
8274
|
+
const resolvedOptions = {
|
|
8275
|
+
startTime,
|
|
8276
|
+
finalKeyframe,
|
|
8277
|
+
...options,
|
|
8278
|
+
keyframes,
|
|
8279
|
+
};
|
|
8396
8280
|
/**
|
|
8397
|
-
* If this animation
|
|
8281
|
+
* Animate via WAAPI if possible. If this is a handoff animation, the optimised animation will be running via
|
|
8282
|
+
* WAAPI. Therefore, this animation must be JS to ensure it runs "under" the
|
|
8283
|
+
* optimised animation.
|
|
8398
8284
|
*/
|
|
8399
|
-
|
|
8400
|
-
|
|
8401
|
-
|
|
8402
|
-
|
|
8403
|
-
|
|
8404
|
-
|
|
8405
|
-
|
|
8406
|
-
|
|
8407
|
-
|
|
8408
|
-
|
|
8409
|
-
|
|
8410
|
-
|
|
8411
|
-
ease = pregeneratedAnimation.ease;
|
|
8412
|
-
type = "keyframes";
|
|
8413
|
-
}
|
|
8414
|
-
const animation = startWaapiAnimation(motionValue.owner.current, name, keyframes, { ...this.options, duration, times, ease });
|
|
8415
|
-
// Override the browser calculated startTime with one synchronised to other JS
|
|
8416
|
-
// and WAAPI animations starting this event loop.
|
|
8417
|
-
animation.startTime = startTime ?? this.calcStartTime();
|
|
8285
|
+
const animation = !isHandoff && supportsBrowserAnimation(resolvedOptions)
|
|
8286
|
+
? new NativeAnimationExtended({
|
|
8287
|
+
...resolvedOptions,
|
|
8288
|
+
element: resolvedOptions.motionValue.owner.current,
|
|
8289
|
+
})
|
|
8290
|
+
: new JSAnimation(resolvedOptions);
|
|
8291
|
+
animation.finished
|
|
8292
|
+
.then(() => {
|
|
8293
|
+
onComplete?.();
|
|
8294
|
+
this.notifyFinished();
|
|
8295
|
+
})
|
|
8296
|
+
.catch(noop);
|
|
8418
8297
|
if (this.pendingTimeline) {
|
|
8419
|
-
attachTimeline(
|
|
8298
|
+
this.stopTimeline = animation.attachTimeline(this.pendingTimeline);
|
|
8420
8299
|
this.pendingTimeline = undefined;
|
|
8421
8300
|
}
|
|
8301
|
+
this._animation = animation;
|
|
8302
|
+
}
|
|
8303
|
+
get finished() {
|
|
8304
|
+
if (!this._animation) {
|
|
8305
|
+
return this._finished;
|
|
8306
|
+
}
|
|
8422
8307
|
else {
|
|
8423
|
-
|
|
8424
|
-
* Prefer the `onfinish` prop as it's more widely supported than
|
|
8425
|
-
* the `finished` promise.
|
|
8426
|
-
*
|
|
8427
|
-
* Here, we synchronously set the provided MotionValue to the end
|
|
8428
|
-
* keyframe. If we didn't, when the WAAPI animation is finished it would
|
|
8429
|
-
* be removed from the element which would then revert to its old styles.
|
|
8430
|
-
*/
|
|
8431
|
-
animation.onfinish = () => {
|
|
8432
|
-
const { onComplete } = this.options;
|
|
8433
|
-
motionValue.set(getFinalKeyframe(keyframes, this.options, finalKeyframe));
|
|
8434
|
-
onComplete && onComplete();
|
|
8435
|
-
this.cancel();
|
|
8436
|
-
this.resolveFinishedPromise();
|
|
8437
|
-
};
|
|
8308
|
+
return this.animation.finished;
|
|
8438
8309
|
}
|
|
8439
|
-
|
|
8440
|
-
|
|
8441
|
-
|
|
8442
|
-
|
|
8443
|
-
|
|
8444
|
-
|
|
8445
|
-
|
|
8446
|
-
}
|
|
8310
|
+
}
|
|
8311
|
+
then(onResolve, _onReject) {
|
|
8312
|
+
return this.finished.finally(onResolve).then(() => { });
|
|
8313
|
+
}
|
|
8314
|
+
get animation() {
|
|
8315
|
+
if (!this._animation) {
|
|
8316
|
+
flushKeyframeResolvers();
|
|
8317
|
+
}
|
|
8318
|
+
return this._animation;
|
|
8447
8319
|
}
|
|
8448
8320
|
get duration() {
|
|
8449
|
-
|
|
8450
|
-
if (!resolved)
|
|
8451
|
-
return 0;
|
|
8452
|
-
const { duration } = resolved;
|
|
8453
|
-
return millisecondsToSeconds(duration);
|
|
8321
|
+
return this.animation.duration;
|
|
8454
8322
|
}
|
|
8455
8323
|
get time() {
|
|
8456
|
-
|
|
8457
|
-
if (!resolved)
|
|
8458
|
-
return 0;
|
|
8459
|
-
const { animation } = resolved;
|
|
8460
|
-
return millisecondsToSeconds(animation.currentTime || 0);
|
|
8324
|
+
return this.animation.time;
|
|
8461
8325
|
}
|
|
8462
8326
|
set time(newTime) {
|
|
8463
|
-
|
|
8464
|
-
if (!resolved)
|
|
8465
|
-
return;
|
|
8466
|
-
const { animation } = resolved;
|
|
8467
|
-
animation.currentTime = secondsToMilliseconds(newTime);
|
|
8327
|
+
this.animation.time = newTime;
|
|
8468
8328
|
}
|
|
8469
8329
|
get speed() {
|
|
8470
|
-
|
|
8471
|
-
if (!resolved)
|
|
8472
|
-
return 1;
|
|
8473
|
-
const { animation } = resolved;
|
|
8474
|
-
return animation.playbackRate;
|
|
8330
|
+
return this.animation.speed;
|
|
8475
8331
|
}
|
|
8476
|
-
get
|
|
8477
|
-
return this.
|
|
8332
|
+
get state() {
|
|
8333
|
+
return this.animation.state;
|
|
8478
8334
|
}
|
|
8479
8335
|
set speed(newSpeed) {
|
|
8480
|
-
|
|
8481
|
-
if (!resolved)
|
|
8482
|
-
return;
|
|
8483
|
-
const { animation } = resolved;
|
|
8484
|
-
animation.playbackRate = newSpeed;
|
|
8485
|
-
}
|
|
8486
|
-
get state() {
|
|
8487
|
-
const { resolved } = this;
|
|
8488
|
-
if (!resolved)
|
|
8489
|
-
return "idle";
|
|
8490
|
-
const { animation } = resolved;
|
|
8491
|
-
return animation.playState;
|
|
8336
|
+
this.animation.speed = newSpeed;
|
|
8492
8337
|
}
|
|
8493
8338
|
get startTime() {
|
|
8494
|
-
|
|
8495
|
-
if (!resolved)
|
|
8496
|
-
return null;
|
|
8497
|
-
const { animation } = resolved;
|
|
8498
|
-
// Coerce to number as TypeScript incorrectly types this
|
|
8499
|
-
// as CSSNumberish
|
|
8500
|
-
return animation.startTime;
|
|
8339
|
+
return this.animation.startTime;
|
|
8501
8340
|
}
|
|
8502
|
-
/**
|
|
8503
|
-
* Replace the default DocumentTimeline with another AnimationTimeline.
|
|
8504
|
-
* Currently used for scroll animations.
|
|
8505
|
-
*/
|
|
8506
8341
|
attachTimeline(timeline) {
|
|
8507
|
-
if (
|
|
8508
|
-
this.
|
|
8342
|
+
if (this._animation) {
|
|
8343
|
+
this.stopTimeline = this.animation.attachTimeline(timeline);
|
|
8509
8344
|
}
|
|
8510
8345
|
else {
|
|
8511
|
-
|
|
8512
|
-
if (!resolved)
|
|
8513
|
-
return noop;
|
|
8514
|
-
const { animation } = resolved;
|
|
8515
|
-
attachTimeline(animation, timeline);
|
|
8346
|
+
this.pendingTimeline = timeline;
|
|
8516
8347
|
}
|
|
8517
|
-
return
|
|
8348
|
+
return () => this.stop();
|
|
8518
8349
|
}
|
|
8519
8350
|
play() {
|
|
8520
|
-
|
|
8521
|
-
return;
|
|
8522
|
-
const { resolved } = this;
|
|
8523
|
-
if (!resolved)
|
|
8524
|
-
return;
|
|
8525
|
-
const { animation } = resolved;
|
|
8526
|
-
if (animation.playState === "finished") {
|
|
8527
|
-
this.updateFinishedPromise();
|
|
8528
|
-
}
|
|
8529
|
-
animation.play();
|
|
8351
|
+
this.animation.play();
|
|
8530
8352
|
}
|
|
8531
8353
|
pause() {
|
|
8532
|
-
|
|
8533
|
-
if (!resolved)
|
|
8534
|
-
return;
|
|
8535
|
-
const { animation } = resolved;
|
|
8536
|
-
animation.pause();
|
|
8537
|
-
}
|
|
8538
|
-
stop() {
|
|
8539
|
-
this.resolver.cancel();
|
|
8540
|
-
this.isStopped = true;
|
|
8541
|
-
if (this.state === "idle")
|
|
8542
|
-
return;
|
|
8543
|
-
this.resolveFinishedPromise();
|
|
8544
|
-
this.updateFinishedPromise();
|
|
8545
|
-
const { resolved } = this;
|
|
8546
|
-
if (!resolved)
|
|
8547
|
-
return;
|
|
8548
|
-
const { animation, keyframes, duration, type, ease, times } = resolved;
|
|
8549
|
-
if (animation.playState === "idle" ||
|
|
8550
|
-
animation.playState === "finished") {
|
|
8551
|
-
return;
|
|
8552
|
-
}
|
|
8553
|
-
/**
|
|
8554
|
-
* WAAPI doesn't natively have any interruption capabilities.
|
|
8555
|
-
*
|
|
8556
|
-
* Rather than read commited styles back out of the DOM, we can
|
|
8557
|
-
* create a renderless JS animation and sample it twice to calculate
|
|
8558
|
-
* its current value, "previous" value, and therefore allow
|
|
8559
|
-
* Motion to calculate velocity for any subsequent animation.
|
|
8560
|
-
*/
|
|
8561
|
-
if (this.time) {
|
|
8562
|
-
const { motionValue, onUpdate, onComplete, element, ...options } = this.options;
|
|
8563
|
-
const sampleAnimation = new MainThreadAnimation({
|
|
8564
|
-
...options,
|
|
8565
|
-
keyframes,
|
|
8566
|
-
duration,
|
|
8567
|
-
type,
|
|
8568
|
-
ease,
|
|
8569
|
-
times,
|
|
8570
|
-
isGenerator: true,
|
|
8571
|
-
});
|
|
8572
|
-
const sampleTime = secondsToMilliseconds(this.time);
|
|
8573
|
-
motionValue.setWithVelocity(sampleAnimation.sample(sampleTime - sampleDelta).value, sampleAnimation.sample(sampleTime).value, sampleDelta);
|
|
8574
|
-
}
|
|
8575
|
-
const { onStop } = this.options;
|
|
8576
|
-
onStop && onStop();
|
|
8577
|
-
this.cancel();
|
|
8354
|
+
this.animation.pause();
|
|
8578
8355
|
}
|
|
8579
8356
|
complete() {
|
|
8580
|
-
|
|
8581
|
-
if (!resolved)
|
|
8582
|
-
return;
|
|
8583
|
-
resolved.animation.finish();
|
|
8584
|
-
}
|
|
8585
|
-
cancel() {
|
|
8586
|
-
const { resolved } = this;
|
|
8587
|
-
if (!resolved)
|
|
8588
|
-
return;
|
|
8589
|
-
resolved.animation.cancel();
|
|
8590
|
-
}
|
|
8591
|
-
static supports(options) {
|
|
8592
|
-
const { motionValue, name, repeatDelay, repeatType, damping, type } = options;
|
|
8593
|
-
if (!motionValue ||
|
|
8594
|
-
!motionValue.owner ||
|
|
8595
|
-
!(motionValue.owner.current instanceof HTMLElement)) {
|
|
8596
|
-
return false;
|
|
8597
|
-
}
|
|
8598
|
-
const { onUpdate, transformTemplate } = motionValue.owner.getProps();
|
|
8599
|
-
return (supportsWaapi() &&
|
|
8600
|
-
name &&
|
|
8601
|
-
acceleratedValues.has(name) &&
|
|
8602
|
-
(name !== "transform" || !transformTemplate) &&
|
|
8603
|
-
/**
|
|
8604
|
-
* If we're outputting values to onUpdate then we can't use WAAPI as there's
|
|
8605
|
-
* no way to read the value from WAAPI every frame.
|
|
8606
|
-
*/
|
|
8607
|
-
!onUpdate &&
|
|
8608
|
-
!repeatDelay &&
|
|
8609
|
-
repeatType !== "mirror" &&
|
|
8610
|
-
damping !== 0 &&
|
|
8611
|
-
type !== "inertia");
|
|
8612
|
-
}
|
|
8613
|
-
}
|
|
8614
|
-
|
|
8615
|
-
const underDampedSpring = {
|
|
8616
|
-
type: "spring",
|
|
8617
|
-
stiffness: 500,
|
|
8618
|
-
damping: 25,
|
|
8619
|
-
restSpeed: 10,
|
|
8620
|
-
};
|
|
8621
|
-
const criticallyDampedSpring = (target) => ({
|
|
8622
|
-
type: "spring",
|
|
8623
|
-
stiffness: 550,
|
|
8624
|
-
damping: target === 0 ? 2 * Math.sqrt(550) : 30,
|
|
8625
|
-
restSpeed: 10,
|
|
8626
|
-
});
|
|
8627
|
-
const keyframesTransition = {
|
|
8628
|
-
type: "keyframes",
|
|
8629
|
-
duration: 0.8,
|
|
8630
|
-
};
|
|
8631
|
-
/**
|
|
8632
|
-
* Default easing curve is a slightly shallower version of
|
|
8633
|
-
* the default browser easing curve.
|
|
8634
|
-
*/
|
|
8635
|
-
const ease = {
|
|
8636
|
-
type: "keyframes",
|
|
8637
|
-
ease: [0.25, 0.1, 0.35, 1],
|
|
8638
|
-
duration: 0.3,
|
|
8639
|
-
};
|
|
8640
|
-
const getDefaultTransition = (valueKey, { keyframes }) => {
|
|
8641
|
-
if (keyframes.length > 2) {
|
|
8642
|
-
return keyframesTransition;
|
|
8643
|
-
}
|
|
8644
|
-
else if (transformProps.has(valueKey)) {
|
|
8645
|
-
return valueKey.startsWith("scale")
|
|
8646
|
-
? criticallyDampedSpring(keyframes[1])
|
|
8647
|
-
: underDampedSpring;
|
|
8648
|
-
}
|
|
8649
|
-
return ease;
|
|
8650
|
-
};
|
|
8651
|
-
|
|
8652
|
-
/**
|
|
8653
|
-
* Decide whether a transition is defined on a given Transition.
|
|
8654
|
-
* This filters out orchestration options and returns true
|
|
8655
|
-
* if any options are left.
|
|
8656
|
-
*/
|
|
8657
|
-
function isTransitionDefined({ when, delay: _delay, delayChildren, staggerChildren, staggerDirection, repeat, repeatType, repeatDelay, from, elapsed, ...transition }) {
|
|
8658
|
-
return !!Object.keys(transition).length;
|
|
8659
|
-
}
|
|
8660
|
-
|
|
8661
|
-
function getValueTransition(transition, key) {
|
|
8662
|
-
return (transition?.[key] ??
|
|
8663
|
-
transition?.["default"] ??
|
|
8664
|
-
transition);
|
|
8665
|
-
}
|
|
8666
|
-
|
|
8667
|
-
const supportsScrollTimeline = /* @__PURE__ */ memo(() => window.ScrollTimeline !== undefined);
|
|
8668
|
-
|
|
8669
|
-
class GroupAnimation {
|
|
8670
|
-
constructor(animations) {
|
|
8671
|
-
// Bound to accomodate common `return animation.stop` pattern
|
|
8672
|
-
this.stop = () => this.runAll("stop");
|
|
8673
|
-
this.animations = animations.filter(Boolean);
|
|
8674
|
-
}
|
|
8675
|
-
get finished() {
|
|
8676
|
-
return Promise.all(this.animations.map((animation) => animation.finished));
|
|
8677
|
-
}
|
|
8678
|
-
/**
|
|
8679
|
-
* TODO: Filter out cancelled or stopped animations before returning
|
|
8680
|
-
*/
|
|
8681
|
-
getAll(propName) {
|
|
8682
|
-
return this.animations[0][propName];
|
|
8683
|
-
}
|
|
8684
|
-
setAll(propName, newValue) {
|
|
8685
|
-
for (let i = 0; i < this.animations.length; i++) {
|
|
8686
|
-
this.animations[i][propName] = newValue;
|
|
8687
|
-
}
|
|
8688
|
-
}
|
|
8689
|
-
attachTimeline(timeline, fallback) {
|
|
8690
|
-
const subscriptions = this.animations.map((animation) => {
|
|
8691
|
-
if (supportsScrollTimeline() && animation.attachTimeline) {
|
|
8692
|
-
return animation.attachTimeline(timeline);
|
|
8693
|
-
}
|
|
8694
|
-
else if (typeof fallback === "function") {
|
|
8695
|
-
return fallback(animation);
|
|
8696
|
-
}
|
|
8697
|
-
});
|
|
8698
|
-
return () => {
|
|
8699
|
-
subscriptions.forEach((cancel, i) => {
|
|
8700
|
-
cancel && cancel();
|
|
8701
|
-
this.animations[i].stop();
|
|
8702
|
-
});
|
|
8703
|
-
};
|
|
8704
|
-
}
|
|
8705
|
-
get time() {
|
|
8706
|
-
return this.getAll("time");
|
|
8707
|
-
}
|
|
8708
|
-
set time(time) {
|
|
8709
|
-
this.setAll("time", time);
|
|
8710
|
-
}
|
|
8711
|
-
get speed() {
|
|
8712
|
-
return this.getAll("speed");
|
|
8713
|
-
}
|
|
8714
|
-
set speed(speed) {
|
|
8715
|
-
this.setAll("speed", speed);
|
|
8716
|
-
}
|
|
8717
|
-
get startTime() {
|
|
8718
|
-
return this.getAll("startTime");
|
|
8719
|
-
}
|
|
8720
|
-
get duration() {
|
|
8721
|
-
let max = 0;
|
|
8722
|
-
for (let i = 0; i < this.animations.length; i++) {
|
|
8723
|
-
max = Math.max(max, this.animations[i].duration);
|
|
8724
|
-
}
|
|
8725
|
-
return max;
|
|
8726
|
-
}
|
|
8727
|
-
runAll(methodName) {
|
|
8728
|
-
this.animations.forEach((controls) => controls[methodName]());
|
|
8729
|
-
}
|
|
8730
|
-
flatten() {
|
|
8731
|
-
this.runAll("flatten");
|
|
8732
|
-
}
|
|
8733
|
-
play() {
|
|
8734
|
-
this.runAll("play");
|
|
8735
|
-
}
|
|
8736
|
-
pause() {
|
|
8737
|
-
this.runAll("pause");
|
|
8357
|
+
this.animation.complete();
|
|
8738
8358
|
}
|
|
8739
8359
|
cancel() {
|
|
8740
|
-
this.
|
|
8741
|
-
}
|
|
8742
|
-
complete() {
|
|
8743
|
-
this.runAll("complete");
|
|
8744
|
-
}
|
|
8745
|
-
}
|
|
8746
|
-
|
|
8747
|
-
class GroupAnimationWithThen extends GroupAnimation {
|
|
8748
|
-
then(onResolve, _onReject) {
|
|
8749
|
-
return this.finished.finally(onResolve).then(() => { });
|
|
8360
|
+
this.animation.cancel();
|
|
8750
8361
|
}
|
|
8751
8362
|
}
|
|
8752
8363
|
|
|
@@ -8764,7 +8375,7 @@ const animateMotionValue = (name, value, target, transition = {}, element, isHan
|
|
|
8764
8375
|
*/
|
|
8765
8376
|
let { elapsed = 0 } = transition;
|
|
8766
8377
|
elapsed = elapsed - secondsToMilliseconds(delay);
|
|
8767
|
-
|
|
8378
|
+
const options = {
|
|
8768
8379
|
keyframes: Array.isArray(target) ? target : [null, target],
|
|
8769
8380
|
ease: "easeOut",
|
|
8770
8381
|
velocity: value.getVelocity(),
|
|
@@ -8787,22 +8398,18 @@ const animateMotionValue = (name, value, target, transition = {}, element, isHan
|
|
|
8787
8398
|
* unique transition settings for this value.
|
|
8788
8399
|
*/
|
|
8789
8400
|
if (!isTransitionDefined(valueTransition)) {
|
|
8790
|
-
options
|
|
8791
|
-
...options,
|
|
8792
|
-
...getDefaultTransition(name, options),
|
|
8793
|
-
};
|
|
8401
|
+
Object.assign(options, getDefaultTransition(name, options));
|
|
8794
8402
|
}
|
|
8795
8403
|
/**
|
|
8796
8404
|
* Both WAAPI and our internal animation functions use durations
|
|
8797
8405
|
* as defined by milliseconds, while our external API defines them
|
|
8798
8406
|
* as seconds.
|
|
8799
8407
|
*/
|
|
8800
|
-
|
|
8801
|
-
|
|
8802
|
-
|
|
8803
|
-
|
|
8804
|
-
|
|
8805
|
-
}
|
|
8408
|
+
options.duration && (options.duration = secondsToMilliseconds(options.duration));
|
|
8409
|
+
options.repeatDelay && (options.repeatDelay = secondsToMilliseconds(options.repeatDelay));
|
|
8410
|
+
/**
|
|
8411
|
+
* Support deprecated way to set initial value. Prefer keyframe syntax.
|
|
8412
|
+
*/
|
|
8806
8413
|
if (options.from !== undefined) {
|
|
8807
8414
|
options.keyframes[0] = options.from;
|
|
8808
8415
|
}
|
|
@@ -8814,6 +8421,12 @@ const animateMotionValue = (name, value, target, transition = {}, element, isHan
|
|
|
8814
8421
|
shouldSkip = true;
|
|
8815
8422
|
}
|
|
8816
8423
|
}
|
|
8424
|
+
if (MotionGlobalConfig.instantAnimations ||
|
|
8425
|
+
MotionGlobalConfig.skipAnimations) {
|
|
8426
|
+
shouldSkip = true;
|
|
8427
|
+
options.duration = 0;
|
|
8428
|
+
options.delay = 0;
|
|
8429
|
+
}
|
|
8817
8430
|
/**
|
|
8818
8431
|
* If the transition type or easing has been explicitly set by the user
|
|
8819
8432
|
* then we don't want to allow flattening the animation.
|
|
@@ -8825,30 +8438,28 @@ const animateMotionValue = (name, value, target, transition = {}, element, isHan
|
|
|
8825
8438
|
* this early check prevents the need to create an animation at all.
|
|
8826
8439
|
*/
|
|
8827
8440
|
if (shouldSkip && !isHandoff && value.get() !== undefined) {
|
|
8828
|
-
const finalKeyframe = getFinalKeyframe(options.keyframes, valueTransition);
|
|
8441
|
+
const finalKeyframe = getFinalKeyframe$1(options.keyframes, valueTransition);
|
|
8829
8442
|
if (finalKeyframe !== undefined) {
|
|
8830
8443
|
frame.update(() => {
|
|
8831
8444
|
options.onUpdate(finalKeyframe);
|
|
8832
8445
|
options.onComplete();
|
|
8833
8446
|
});
|
|
8834
|
-
|
|
8835
|
-
// than returning undefined
|
|
8836
|
-
return new GroupAnimationWithThen([]);
|
|
8447
|
+
return;
|
|
8837
8448
|
}
|
|
8838
8449
|
}
|
|
8839
|
-
|
|
8840
|
-
* Animate via WAAPI if possible. If this is a handoff animation, the optimised animation will be running via
|
|
8841
|
-
* WAAPI. Therefore, this animation must be JS to ensure it runs "under" the
|
|
8842
|
-
* optimised animation.
|
|
8843
|
-
*/
|
|
8844
|
-
if (!isHandoff && AcceleratedAnimation.supports(options)) {
|
|
8845
|
-
return new AcceleratedAnimation(options);
|
|
8846
|
-
}
|
|
8847
|
-
else {
|
|
8848
|
-
return new MainThreadAnimation(options);
|
|
8849
|
-
}
|
|
8450
|
+
return new AsyncMotionValueAnimation(options);
|
|
8850
8451
|
};
|
|
8851
8452
|
|
|
8453
|
+
const positionalKeys = new Set([
|
|
8454
|
+
"width",
|
|
8455
|
+
"height",
|
|
8456
|
+
"top",
|
|
8457
|
+
"left",
|
|
8458
|
+
"right",
|
|
8459
|
+
"bottom",
|
|
8460
|
+
...transformPropOrder,
|
|
8461
|
+
]);
|
|
8462
|
+
|
|
8852
8463
|
/**
|
|
8853
8464
|
* Decide whether we should block this animation. Previously, we achieved this
|
|
8854
8465
|
* just by checking whether the key was listed in protectedKeys, but this
|
|
@@ -10832,7 +10443,7 @@ function delay(callback, timeout) {
|
|
|
10832
10443
|
callback(elapsed - timeout);
|
|
10833
10444
|
}
|
|
10834
10445
|
};
|
|
10835
|
-
frame.
|
|
10446
|
+
frame.setup(checkElapsed, true);
|
|
10836
10447
|
return () => cancelFrame(checkElapsed);
|
|
10837
10448
|
}
|
|
10838
10449
|
|
|
@@ -12126,9 +11737,7 @@ function createProjectionNode({ attachResizeListener, defaultParent, measureScro
|
|
|
12126
11737
|
}
|
|
12127
11738
|
setAnimationOrigin(delta, hasOnlyRelativeTargetChanged = false) {
|
|
12128
11739
|
const snapshot = this.snapshot;
|
|
12129
|
-
const snapshotLatestValues = snapshot
|
|
12130
|
-
? snapshot.latestValues
|
|
12131
|
-
: {};
|
|
11740
|
+
const snapshotLatestValues = snapshot ? snapshot.latestValues : {};
|
|
12132
11741
|
const mixedValues = { ...this.latestValues };
|
|
12133
11742
|
const targetDelta = createDelta();
|
|
12134
11743
|
if (!this.relativeParent ||
|
|
@@ -13196,15 +12805,6 @@ function initPrefersReducedMotion() {
|
|
|
13196
12805
|
}
|
|
13197
12806
|
}
|
|
13198
12807
|
|
|
13199
|
-
/**
|
|
13200
|
-
* A list of all ValueTypes
|
|
13201
|
-
*/
|
|
13202
|
-
const valueTypes = [...dimensionValueTypes, color, complex];
|
|
13203
|
-
/**
|
|
13204
|
-
* Tests a value against the list of ValueTypes
|
|
13205
|
-
*/
|
|
13206
|
-
const findValueType = (v) => valueTypes.find(testValueType(v));
|
|
13207
|
-
|
|
13208
12808
|
const visualElementStore = new WeakMap();
|
|
13209
12809
|
|
|
13210
12810
|
function updateMotionValuesFromProps(element, next, prev) {
|
|
@@ -13222,7 +12822,7 @@ function updateMotionValuesFromProps(element, next, prev) {
|
|
|
13222
12822
|
* and warn against mismatches.
|
|
13223
12823
|
*/
|
|
13224
12824
|
if (process.env.NODE_ENV === "development") {
|
|
13225
|
-
warnOnce(nextValue.version === "12.
|
|
12825
|
+
warnOnce(nextValue.version === "12.8.0", `Attempting to mix Motion versions ${nextValue.version} with 12.8.0 may not work as expected.`);
|
|
13226
12826
|
}
|
|
13227
12827
|
}
|
|
13228
12828
|
else if (isMotionValue(prevValue)) {
|
|
@@ -13261,6 +12861,108 @@ function updateMotionValuesFromProps(element, next, prev) {
|
|
|
13261
12861
|
return next;
|
|
13262
12862
|
}
|
|
13263
12863
|
|
|
12864
|
+
/**
|
|
12865
|
+
* Check if value is a numerical string, ie a string that is purely a number eg "100" or "-100.1"
|
|
12866
|
+
*/
|
|
12867
|
+
const isNumericalString = (v) => /^-?(?:\d+(?:\.\d+)?|\.\d+)$/u.test(v);
|
|
12868
|
+
|
|
12869
|
+
/**
|
|
12870
|
+
* Check if the value is a zero value string like "0px" or "0%"
|
|
12871
|
+
*/
|
|
12872
|
+
const isZeroValueString = (v) => /^0[^.\s]+$/u.test(v);
|
|
12873
|
+
|
|
12874
|
+
/**
|
|
12875
|
+
* ValueType for "auto"
|
|
12876
|
+
*/
|
|
12877
|
+
const auto = {
|
|
12878
|
+
test: (v) => v === "auto",
|
|
12879
|
+
parse: (v) => v,
|
|
12880
|
+
};
|
|
12881
|
+
|
|
12882
|
+
/**
|
|
12883
|
+
* Tests a provided value against a ValueType
|
|
12884
|
+
*/
|
|
12885
|
+
const testValueType = (v) => (type) => type.test(v);
|
|
12886
|
+
|
|
12887
|
+
/**
|
|
12888
|
+
* A list of value types commonly used for dimensions
|
|
12889
|
+
*/
|
|
12890
|
+
const dimensionValueTypes = [number, px, percent, degrees, vw, vh, auto];
|
|
12891
|
+
/**
|
|
12892
|
+
* Tests a dimensional value against the list of dimension ValueTypes
|
|
12893
|
+
*/
|
|
12894
|
+
const findDimensionValueType = (v) => dimensionValueTypes.find(testValueType(v));
|
|
12895
|
+
|
|
12896
|
+
/**
|
|
12897
|
+
* A list of all ValueTypes
|
|
12898
|
+
*/
|
|
12899
|
+
const valueTypes = [...dimensionValueTypes, color, complex];
|
|
12900
|
+
/**
|
|
12901
|
+
* Tests a value against the list of ValueTypes
|
|
12902
|
+
*/
|
|
12903
|
+
const findValueType = (v) => valueTypes.find(testValueType(v));
|
|
12904
|
+
|
|
12905
|
+
/**
|
|
12906
|
+
* Properties that should default to 1 or 100%
|
|
12907
|
+
*/
|
|
12908
|
+
const maxDefaults = new Set(["brightness", "contrast", "saturate", "opacity"]);
|
|
12909
|
+
function applyDefaultFilter(v) {
|
|
12910
|
+
const [name, value] = v.slice(0, -1).split("(");
|
|
12911
|
+
if (name === "drop-shadow")
|
|
12912
|
+
return v;
|
|
12913
|
+
const [number] = value.match(floatRegex) || [];
|
|
12914
|
+
if (!number)
|
|
12915
|
+
return v;
|
|
12916
|
+
const unit = value.replace(number, "");
|
|
12917
|
+
let defaultValue = maxDefaults.has(name) ? 1 : 0;
|
|
12918
|
+
if (number !== value)
|
|
12919
|
+
defaultValue *= 100;
|
|
12920
|
+
return name + "(" + defaultValue + unit + ")";
|
|
12921
|
+
}
|
|
12922
|
+
const functionRegex = /\b([a-z-]*)\(.*?\)/gu;
|
|
12923
|
+
const filter = {
|
|
12924
|
+
...complex,
|
|
12925
|
+
getAnimatableNone: (v) => {
|
|
12926
|
+
const functions = v.match(functionRegex);
|
|
12927
|
+
return functions ? functions.map(applyDefaultFilter).join(" ") : v;
|
|
12928
|
+
},
|
|
12929
|
+
};
|
|
12930
|
+
|
|
12931
|
+
/**
|
|
12932
|
+
* A map of default value types for common values
|
|
12933
|
+
*/
|
|
12934
|
+
const defaultValueTypes = {
|
|
12935
|
+
...numberValueTypes,
|
|
12936
|
+
// Color props
|
|
12937
|
+
color,
|
|
12938
|
+
backgroundColor: color,
|
|
12939
|
+
outlineColor: color,
|
|
12940
|
+
fill: color,
|
|
12941
|
+
stroke: color,
|
|
12942
|
+
// Border props
|
|
12943
|
+
borderColor: color,
|
|
12944
|
+
borderTopColor: color,
|
|
12945
|
+
borderRightColor: color,
|
|
12946
|
+
borderBottomColor: color,
|
|
12947
|
+
borderLeftColor: color,
|
|
12948
|
+
filter,
|
|
12949
|
+
WebkitFilter: filter,
|
|
12950
|
+
};
|
|
12951
|
+
/**
|
|
12952
|
+
* Gets the default ValueType for the provided value key
|
|
12953
|
+
*/
|
|
12954
|
+
const getDefaultValueType = (key) => defaultValueTypes[key];
|
|
12955
|
+
|
|
12956
|
+
function getAnimatableNone(key, value) {
|
|
12957
|
+
let defaultValueType = getDefaultValueType(key);
|
|
12958
|
+
if (defaultValueType !== filter)
|
|
12959
|
+
defaultValueType = complex;
|
|
12960
|
+
// If value is not recognised as animatable, ie "none", create an animatable version origin based on the target
|
|
12961
|
+
return defaultValueType.getAnimatableNone
|
|
12962
|
+
? defaultValueType.getAnimatableNone(value)
|
|
12963
|
+
: undefined;
|
|
12964
|
+
}
|
|
12965
|
+
|
|
13264
12966
|
const propEventHandlers = [
|
|
13265
12967
|
"AnimationStart",
|
|
13266
12968
|
"AnimationComplete",
|
|
@@ -13718,6 +13420,202 @@ class VisualElement {
|
|
|
13718
13420
|
}
|
|
13719
13421
|
}
|
|
13720
13422
|
|
|
13423
|
+
/**
|
|
13424
|
+
* Parse Framer's special CSS variable format into a CSS token and a fallback.
|
|
13425
|
+
*
|
|
13426
|
+
* ```
|
|
13427
|
+
* `var(--foo, #fff)` => [`--foo`, '#fff']
|
|
13428
|
+
* ```
|
|
13429
|
+
*
|
|
13430
|
+
* @param current
|
|
13431
|
+
*/
|
|
13432
|
+
const splitCSSVariableRegex =
|
|
13433
|
+
// eslint-disable-next-line redos-detector/no-unsafe-regex -- false positive, as it can match a lot of words
|
|
13434
|
+
/^var\(--(?:([\w-]+)|([\w-]+), ?([a-zA-Z\d ()%#.,-]+))\)/u;
|
|
13435
|
+
function parseCSSVariable(current) {
|
|
13436
|
+
const match = splitCSSVariableRegex.exec(current);
|
|
13437
|
+
if (!match)
|
|
13438
|
+
return [,];
|
|
13439
|
+
const [, token1, token2, fallback] = match;
|
|
13440
|
+
return [`--${token1 ?? token2}`, fallback];
|
|
13441
|
+
}
|
|
13442
|
+
const maxDepth = 4;
|
|
13443
|
+
function getVariableValue(current, element, depth = 1) {
|
|
13444
|
+
invariant(depth <= maxDepth, `Max CSS variable fallback depth detected in property "${current}". This may indicate a circular fallback dependency.`);
|
|
13445
|
+
const [token, fallback] = parseCSSVariable(current);
|
|
13446
|
+
// No CSS variable detected
|
|
13447
|
+
if (!token)
|
|
13448
|
+
return;
|
|
13449
|
+
// Attempt to read this CSS variable off the element
|
|
13450
|
+
const resolved = window.getComputedStyle(element).getPropertyValue(token);
|
|
13451
|
+
if (resolved) {
|
|
13452
|
+
const trimmed = resolved.trim();
|
|
13453
|
+
return isNumericalString(trimmed) ? parseFloat(trimmed) : trimmed;
|
|
13454
|
+
}
|
|
13455
|
+
return isCSSVariableToken(fallback)
|
|
13456
|
+
? getVariableValue(fallback, element, depth + 1)
|
|
13457
|
+
: fallback;
|
|
13458
|
+
}
|
|
13459
|
+
|
|
13460
|
+
function isNone(value) {
|
|
13461
|
+
if (typeof value === "number") {
|
|
13462
|
+
return value === 0;
|
|
13463
|
+
}
|
|
13464
|
+
else if (value !== null) {
|
|
13465
|
+
return value === "none" || value === "0" || isZeroValueString(value);
|
|
13466
|
+
}
|
|
13467
|
+
else {
|
|
13468
|
+
return true;
|
|
13469
|
+
}
|
|
13470
|
+
}
|
|
13471
|
+
|
|
13472
|
+
/**
|
|
13473
|
+
* If we encounter keyframes like "none" or "0" and we also have keyframes like
|
|
13474
|
+
* "#fff" or "200px 200px" we want to find a keyframe to serve as a template for
|
|
13475
|
+
* the "none" keyframes. In this case "#fff" or "200px 200px" - then these get turned into
|
|
13476
|
+
* zero equivalents, i.e. "#fff0" or "0px 0px".
|
|
13477
|
+
*/
|
|
13478
|
+
const invalidTemplates = new Set(["auto", "none", "0"]);
|
|
13479
|
+
function makeNoneKeyframesAnimatable(unresolvedKeyframes, noneKeyframeIndexes, name) {
|
|
13480
|
+
let i = 0;
|
|
13481
|
+
let animatableTemplate = undefined;
|
|
13482
|
+
while (i < unresolvedKeyframes.length && !animatableTemplate) {
|
|
13483
|
+
const keyframe = unresolvedKeyframes[i];
|
|
13484
|
+
if (typeof keyframe === "string" &&
|
|
13485
|
+
!invalidTemplates.has(keyframe) &&
|
|
13486
|
+
analyseComplexValue(keyframe).values.length) {
|
|
13487
|
+
animatableTemplate = unresolvedKeyframes[i];
|
|
13488
|
+
}
|
|
13489
|
+
i++;
|
|
13490
|
+
}
|
|
13491
|
+
if (animatableTemplate && name) {
|
|
13492
|
+
for (const noneIndex of noneKeyframeIndexes) {
|
|
13493
|
+
unresolvedKeyframes[noneIndex] = getAnimatableNone(name, animatableTemplate);
|
|
13494
|
+
}
|
|
13495
|
+
}
|
|
13496
|
+
}
|
|
13497
|
+
|
|
13498
|
+
class DOMKeyframesResolver extends KeyframeResolver {
|
|
13499
|
+
constructor(unresolvedKeyframes, onComplete, name, motionValue, element) {
|
|
13500
|
+
super(unresolvedKeyframes, onComplete, name, motionValue, element, true);
|
|
13501
|
+
}
|
|
13502
|
+
readKeyframes() {
|
|
13503
|
+
const { unresolvedKeyframes, element, name } = this;
|
|
13504
|
+
if (!element || !element.current)
|
|
13505
|
+
return;
|
|
13506
|
+
super.readKeyframes();
|
|
13507
|
+
/**
|
|
13508
|
+
* If any keyframe is a CSS variable, we need to find its value by sampling the element
|
|
13509
|
+
*/
|
|
13510
|
+
for (let i = 0; i < unresolvedKeyframes.length; i++) {
|
|
13511
|
+
let keyframe = unresolvedKeyframes[i];
|
|
13512
|
+
if (typeof keyframe === "string") {
|
|
13513
|
+
keyframe = keyframe.trim();
|
|
13514
|
+
if (isCSSVariableToken(keyframe)) {
|
|
13515
|
+
const resolved = getVariableValue(keyframe, element.current);
|
|
13516
|
+
if (resolved !== undefined) {
|
|
13517
|
+
unresolvedKeyframes[i] = resolved;
|
|
13518
|
+
}
|
|
13519
|
+
if (i === unresolvedKeyframes.length - 1) {
|
|
13520
|
+
this.finalKeyframe = keyframe;
|
|
13521
|
+
}
|
|
13522
|
+
}
|
|
13523
|
+
}
|
|
13524
|
+
}
|
|
13525
|
+
/**
|
|
13526
|
+
* Resolve "none" values. We do this potentially twice - once before and once after measuring keyframes.
|
|
13527
|
+
* This could be seen as inefficient but it's a trade-off to avoid measurements in more situations, which
|
|
13528
|
+
* have a far bigger performance impact.
|
|
13529
|
+
*/
|
|
13530
|
+
this.resolveNoneKeyframes();
|
|
13531
|
+
/**
|
|
13532
|
+
* Check to see if unit type has changed. If so schedule jobs that will
|
|
13533
|
+
* temporarily set styles to the destination keyframes.
|
|
13534
|
+
* Skip if we have more than two keyframes or this isn't a positional value.
|
|
13535
|
+
* TODO: We can throw if there are multiple keyframes and the value type changes.
|
|
13536
|
+
*/
|
|
13537
|
+
if (!positionalKeys.has(name) || unresolvedKeyframes.length !== 2) {
|
|
13538
|
+
return;
|
|
13539
|
+
}
|
|
13540
|
+
const [origin, target] = unresolvedKeyframes;
|
|
13541
|
+
const originType = findDimensionValueType(origin);
|
|
13542
|
+
const targetType = findDimensionValueType(target);
|
|
13543
|
+
/**
|
|
13544
|
+
* Either we don't recognise these value types or we can animate between them.
|
|
13545
|
+
*/
|
|
13546
|
+
if (originType === targetType)
|
|
13547
|
+
return;
|
|
13548
|
+
/**
|
|
13549
|
+
* If both values are numbers or pixels, we can animate between them by
|
|
13550
|
+
* converting them to numbers.
|
|
13551
|
+
*/
|
|
13552
|
+
if (isNumOrPxType(originType) && isNumOrPxType(targetType)) {
|
|
13553
|
+
for (let i = 0; i < unresolvedKeyframes.length; i++) {
|
|
13554
|
+
const value = unresolvedKeyframes[i];
|
|
13555
|
+
if (typeof value === "string") {
|
|
13556
|
+
unresolvedKeyframes[i] = parseFloat(value);
|
|
13557
|
+
}
|
|
13558
|
+
}
|
|
13559
|
+
}
|
|
13560
|
+
else {
|
|
13561
|
+
/**
|
|
13562
|
+
* Else, the only way to resolve this is by measuring the element.
|
|
13563
|
+
*/
|
|
13564
|
+
this.needsMeasurement = true;
|
|
13565
|
+
}
|
|
13566
|
+
}
|
|
13567
|
+
resolveNoneKeyframes() {
|
|
13568
|
+
const { unresolvedKeyframes, name } = this;
|
|
13569
|
+
const noneKeyframeIndexes = [];
|
|
13570
|
+
for (let i = 0; i < unresolvedKeyframes.length; i++) {
|
|
13571
|
+
if (unresolvedKeyframes[i] === null ||
|
|
13572
|
+
isNone(unresolvedKeyframes[i])) {
|
|
13573
|
+
noneKeyframeIndexes.push(i);
|
|
13574
|
+
}
|
|
13575
|
+
}
|
|
13576
|
+
if (noneKeyframeIndexes.length) {
|
|
13577
|
+
makeNoneKeyframesAnimatable(unresolvedKeyframes, noneKeyframeIndexes, name);
|
|
13578
|
+
}
|
|
13579
|
+
}
|
|
13580
|
+
measureInitialState() {
|
|
13581
|
+
const { element, unresolvedKeyframes, name } = this;
|
|
13582
|
+
if (!element || !element.current)
|
|
13583
|
+
return;
|
|
13584
|
+
if (name === "height") {
|
|
13585
|
+
this.suspendedScrollY = window.pageYOffset;
|
|
13586
|
+
}
|
|
13587
|
+
this.measuredOrigin = positionalValues[name](element.measureViewportBox(), window.getComputedStyle(element.current));
|
|
13588
|
+
unresolvedKeyframes[0] = this.measuredOrigin;
|
|
13589
|
+
// Set final key frame to measure after next render
|
|
13590
|
+
const measureKeyframe = unresolvedKeyframes[unresolvedKeyframes.length - 1];
|
|
13591
|
+
if (measureKeyframe !== undefined) {
|
|
13592
|
+
element.getValue(name, measureKeyframe).jump(measureKeyframe, false);
|
|
13593
|
+
}
|
|
13594
|
+
}
|
|
13595
|
+
measureEndState() {
|
|
13596
|
+
const { element, name, unresolvedKeyframes } = this;
|
|
13597
|
+
if (!element || !element.current)
|
|
13598
|
+
return;
|
|
13599
|
+
const value = element.getValue(name);
|
|
13600
|
+
value && value.jump(this.measuredOrigin, false);
|
|
13601
|
+
const finalKeyframeIndex = unresolvedKeyframes.length - 1;
|
|
13602
|
+
const finalKeyframe = unresolvedKeyframes[finalKeyframeIndex];
|
|
13603
|
+
unresolvedKeyframes[finalKeyframeIndex] = positionalValues[name](element.measureViewportBox(), window.getComputedStyle(element.current));
|
|
13604
|
+
if (finalKeyframe !== null && this.finalKeyframe === undefined) {
|
|
13605
|
+
this.finalKeyframe = finalKeyframe;
|
|
13606
|
+
}
|
|
13607
|
+
// If we removed transform values, reapply them before the next render
|
|
13608
|
+
if (this.removedTransforms?.length) {
|
|
13609
|
+
this.removedTransforms.forEach(([unsetTransformName, unsetTransformValue]) => {
|
|
13610
|
+
element
|
|
13611
|
+
.getValue(unsetTransformName)
|
|
13612
|
+
.set(unsetTransformValue);
|
|
13613
|
+
});
|
|
13614
|
+
}
|
|
13615
|
+
this.resolveNoneKeyframes();
|
|
13616
|
+
}
|
|
13617
|
+
}
|
|
13618
|
+
|
|
13721
13619
|
class DOMVisualElement extends VisualElement {
|
|
13722
13620
|
constructor() {
|
|
13723
13621
|
super(...arguments);
|
|
@@ -18547,12 +18445,12 @@ function deepEqual(object1, object2) {
|
|
|
18547
18445
|
}
|
|
18548
18446
|
|
|
18549
18447
|
const useDeepEqualEffect = (effect, deps) => {
|
|
18550
|
-
const ref =
|
|
18448
|
+
const ref = React__namespace.useRef(deps);
|
|
18551
18449
|
if (!deepEqual(deps, ref.current)) {
|
|
18552
18450
|
ref.current = deps;
|
|
18553
18451
|
}
|
|
18554
18452
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
18555
|
-
|
|
18453
|
+
React__namespace.useEffect(effect, ref.current);
|
|
18556
18454
|
};
|
|
18557
18455
|
|
|
18558
18456
|
/**
|
|
@@ -20537,6 +20435,7 @@ function useForm(props = {}) {
|
|
|
20537
20435
|
...data,
|
|
20538
20436
|
isReady: true,
|
|
20539
20437
|
}));
|
|
20438
|
+
control._formState.isReady = true;
|
|
20540
20439
|
return sub;
|
|
20541
20440
|
}, [control]);
|
|
20542
20441
|
React.useEffect(() => control._disableForm(props.disabled), [control, props.disabled]);
|
|
@@ -34144,20 +34043,20 @@ var initStripe = function initStripe(maybeStripe, args, startTime) {
|
|
|
34144
34043
|
return stripe;
|
|
34145
34044
|
}; // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
|
|
34146
34045
|
|
|
34147
|
-
var stripePromise
|
|
34046
|
+
var stripePromise;
|
|
34148
34047
|
var loadCalled = false;
|
|
34149
34048
|
|
|
34150
34049
|
var getStripePromise = function getStripePromise() {
|
|
34151
|
-
if (stripePromise
|
|
34152
|
-
return stripePromise
|
|
34050
|
+
if (stripePromise) {
|
|
34051
|
+
return stripePromise;
|
|
34153
34052
|
}
|
|
34154
34053
|
|
|
34155
|
-
stripePromise
|
|
34054
|
+
stripePromise = loadScript(null)["catch"](function (error) {
|
|
34156
34055
|
// clear cache on error
|
|
34157
|
-
stripePromise
|
|
34056
|
+
stripePromise = null;
|
|
34158
34057
|
return Promise.reject(error);
|
|
34159
34058
|
});
|
|
34160
|
-
return stripePromise
|
|
34059
|
+
return stripePromise;
|
|
34161
34060
|
}; // Execute our own script injection after a tick to give users time to do their
|
|
34162
34061
|
// own script injection.
|
|
34163
34062
|
|
|
@@ -34227,9 +34126,8 @@ const CheckoutForm$1 = ({ onSuccess, onError, children, setSubmitting, }) => {
|
|
|
34227
34126
|
};
|
|
34228
34127
|
var CheckoutForm$2 = React.memo(CheckoutForm$1);
|
|
34229
34128
|
|
|
34230
|
-
|
|
34231
|
-
const stripePromise = loadStripe(
|
|
34232
|
-
function PaymentElement({ paymentSecret, checkoutAppearance, locale, fonts, onSuccess, onError, children, setSubmitting, }) {
|
|
34129
|
+
function PaymentElement({ paymentSecret, publicKey, checkoutAppearance, locale, fonts, onSuccess, onError, children, setSubmitting, }) {
|
|
34130
|
+
const stripePromise = loadStripe(publicKey !== null && publicKey !== void 0 ? publicKey : "");
|
|
34233
34131
|
const options = {
|
|
34234
34132
|
locale: locale !== null && locale !== void 0 ? locale : "en",
|
|
34235
34133
|
appearance: checkoutAppearance,
|
|
@@ -34241,7 +34139,7 @@ function PaymentElement({ paymentSecret, checkoutAppearance, locale, fonts, onSu
|
|
|
34241
34139
|
}
|
|
34242
34140
|
var PaymentElement$1 = React.memo(PaymentElement);
|
|
34243
34141
|
|
|
34244
|
-
function PaymentForm({ paymentSecret, onSuccess, onError, onBack, onDoubleBack, contactEmail, shippingAddress,
|
|
34142
|
+
function PaymentForm({ paymentSecret, onSuccess, onError, onBack, onDoubleBack, contactEmail, shippingAddress, shippingName, shippingPrice, checkoutAppearance, fonts, locale, publicKey, }) {
|
|
34245
34143
|
const [isSubmitting, setIsSubmitting] = React.useState(false);
|
|
34246
34144
|
const { t } = useTranslation();
|
|
34247
34145
|
return (React.createElement("div", { className: "space-y-6" },
|
|
@@ -34257,7 +34155,7 @@ function PaymentForm({ paymentSecret, onSuccess, onError, onBack, onDoubleBack,
|
|
|
34257
34155
|
React.createElement(Button, { variant: "link", size: "link", onClick: onDoubleBack }, t("CheckoutEmbed.Shipping.change"))),
|
|
34258
34156
|
React.createElement("div", { className: "flex items-center justify-between text-sm" },
|
|
34259
34157
|
React.createElement("p", null,
|
|
34260
|
-
React.createElement("span", { className: "font-medium" }, t("CheckoutEmbed.Shipping.
|
|
34158
|
+
React.createElement("span", { className: "font-medium" }, t("CheckoutEmbed.Shipping.address")),
|
|
34261
34159
|
" ",
|
|
34262
34160
|
React.createElement("span", { className: "text-muted-foreground" }, shippingAddress)),
|
|
34263
34161
|
React.createElement(Button, { variant: "link", size: "link", onClick: onDoubleBack }, t("CheckoutEmbed.Shipping.change"))),
|
|
@@ -34266,11 +34164,11 @@ function PaymentForm({ paymentSecret, onSuccess, onError, onBack, onDoubleBack,
|
|
|
34266
34164
|
React.createElement("span", { className: "font-medium" }, t("CheckoutEmbed.Shipping.shipping")),
|
|
34267
34165
|
" ",
|
|
34268
34166
|
React.createElement("span", { className: "text-muted-foreground" },
|
|
34269
|
-
|
|
34167
|
+
shippingName,
|
|
34270
34168
|
" \u00B7 ",
|
|
34271
34169
|
shippingPrice)),
|
|
34272
34170
|
React.createElement(Button, { variant: "link", size: "link", onClick: onBack }, t("CheckoutEmbed.Shipping.change")))),
|
|
34273
|
-
React.createElement("div", { className: "mt-8" }, paymentSecret && (React.createElement(PaymentElement$1, { fonts: fonts, checkoutAppearance: convertCheckoutAppearanceToStripeAppearance(checkoutAppearance, fonts), locale: locale, paymentSecret: paymentSecret, onSuccess: onSuccess, onError: onError, setSubmitting: setIsSubmitting },
|
|
34171
|
+
React.createElement("div", { className: "mt-8" }, paymentSecret && (React.createElement(PaymentElement$1, { fonts: fonts, checkoutAppearance: convertCheckoutAppearanceToStripeAppearance(checkoutAppearance, fonts), locale: locale, paymentSecret: paymentSecret, onSuccess: onSuccess, onError: onError, setSubmitting: setIsSubmitting, publicKey: publicKey },
|
|
34274
34172
|
React.createElement("div", { className: "flex justify-between items-center pt-8" },
|
|
34275
34173
|
React.createElement(Button, { type: "button", variant: "ghost", onClick: onBack },
|
|
34276
34174
|
React.createElement(ChevronLeft, null),
|
|
@@ -34354,7 +34252,7 @@ function ShippingMethodForm({ shippingRates, initialData, onSubmit, onBack, cont
|
|
|
34354
34252
|
React.createElement(Button, { variant: "link", size: "link", onClick: onBack }, t("CheckoutEmbed.Shipping.change"))),
|
|
34355
34253
|
React.createElement("div", { className: "flex items-center justify-between text-sm" },
|
|
34356
34254
|
React.createElement("p", null,
|
|
34357
|
-
React.createElement("span", { className: "font-medium" }, t("CheckoutEmbed.Shipping.
|
|
34255
|
+
React.createElement("span", { className: "font-medium" }, t("CheckoutEmbed.Shipping.address")),
|
|
34358
34256
|
" ",
|
|
34359
34257
|
React.createElement("span", { className: "text-muted-foreground" }, shippingAddress)),
|
|
34360
34258
|
React.createElement(Button, { variant: "link", size: "link", onClick: onBack }, t("CheckoutEmbed.Shipping.change")))),
|
|
@@ -34449,6 +34347,7 @@ const motionSettings = {
|
|
|
34449
34347
|
function CheckoutForm({ storeClient, checkoutId, onSuccess, onError, cancelUrl, clientSecret, customer, currency, checkoutAppearance, fonts, locale, setShippingCost, exchangeRate, }) {
|
|
34450
34348
|
const { formData, setFormData, step, setStep } = useFormStore();
|
|
34451
34349
|
const [paymentSecret, setPaymentSecret] = React.useState(null);
|
|
34350
|
+
const [publicKey, setPublicKey] = React.useState(null);
|
|
34452
34351
|
const [shippingRates, setShippingRates] = React.useState([]);
|
|
34453
34352
|
const validateStep = React.useCallback(() => {
|
|
34454
34353
|
if (step === "customer")
|
|
@@ -34472,6 +34371,7 @@ function CheckoutForm({ storeClient, checkoutId, onSuccess, onError, cancelUrl,
|
|
|
34472
34371
|
React.useEffect(() => {
|
|
34473
34372
|
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x;
|
|
34474
34373
|
if (customer && !((_a = formData.customer) === null || _a === void 0 ? void 0 : _a.email)) {
|
|
34374
|
+
const step = customer.id ? "shipping" : "customer";
|
|
34475
34375
|
setFormData(Object.assign(Object.assign({}, formData), { customerId: customer.id, customer: {
|
|
34476
34376
|
firstName: (_d = (_c = (_b = customer.address) === null || _b === void 0 ? void 0 : _b.name) === null || _c === void 0 ? void 0 : _c.split(" ")[0]) !== null && _d !== void 0 ? _d : "",
|
|
34477
34377
|
lastName: (_g = (_f = (_e = customer.address) === null || _e === void 0 ? void 0 : _e.name) === null || _f === void 0 ? void 0 : _f.split(" ")[1]) !== null && _g !== void 0 ? _g : "",
|
|
@@ -34486,6 +34386,7 @@ function CheckoutForm({ storeClient, checkoutId, onSuccess, onError, cancelUrl,
|
|
|
34486
34386
|
countryCode: (_x = (_w = customer.address) === null || _w === void 0 ? void 0 : _w.countryCode) !== null && _x !== void 0 ? _x : "",
|
|
34487
34387
|
},
|
|
34488
34388
|
} }));
|
|
34389
|
+
setStep(step);
|
|
34489
34390
|
}
|
|
34490
34391
|
}, [customer]);
|
|
34491
34392
|
React.useEffect(() => {
|
|
@@ -34546,8 +34447,9 @@ function CheckoutForm({ storeClient, checkoutId, onSuccess, onError, cancelUrl,
|
|
|
34546
34447
|
pickupPointId: data.pickupPointId,
|
|
34547
34448
|
},
|
|
34548
34449
|
});
|
|
34549
|
-
const paymentSecret = yield storeClient.generateCheckoutsPaymentSecret(clientSecret, checkoutId);
|
|
34450
|
+
const { paymentSecret, publicKey } = yield storeClient.generateCheckoutsPaymentSecret(clientSecret, checkoutId);
|
|
34550
34451
|
setPaymentSecret(paymentSecret);
|
|
34452
|
+
setPublicKey(publicKey);
|
|
34551
34453
|
setShippingCost(data.price);
|
|
34552
34454
|
setStep("payment");
|
|
34553
34455
|
});
|
|
@@ -34566,8 +34468,9 @@ function CheckoutForm({ storeClient, checkoutId, onSuccess, onError, cancelUrl,
|
|
|
34566
34468
|
};
|
|
34567
34469
|
React.useEffect(() => {
|
|
34568
34470
|
const asyncFunc = () => __awaiter(this, void 0, void 0, function* () {
|
|
34569
|
-
const paymentSecret = yield storeClient.generateCheckoutsPaymentSecret(clientSecret, checkoutId);
|
|
34471
|
+
const { paymentSecret, publicKey } = yield storeClient.generateCheckoutsPaymentSecret(clientSecret, checkoutId);
|
|
34570
34472
|
setPaymentSecret(paymentSecret);
|
|
34473
|
+
setPublicKey(publicKey);
|
|
34571
34474
|
});
|
|
34572
34475
|
if (!paymentSecret && step === "payment") {
|
|
34573
34476
|
asyncFunc();
|
|
@@ -34580,7 +34483,7 @@ function CheckoutForm({ storeClient, checkoutId, onSuccess, onError, cancelUrl,
|
|
|
34580
34483
|
step === "shipping" && formData.customer && (React.createElement(motion.div, Object.assign({ key: "shipping" }, motionSettings, { className: "absolute w-full" }),
|
|
34581
34484
|
React.createElement(ShippingMethodForm, { setFormData: setFormData, formData: formData, shippingRates: shippingRates, initialData: formData.shipping, onSubmit: handleShippingSubmit, onBack: handleBack, contactEmail: formData.customer.email, shippingAddress: formatAddress(formData.customer.address), currency: currency, exchangeRate: exchangeRate, locale: locale, countryCode: formData.customer.address.countryCode }))),
|
|
34582
34485
|
step === "payment" && formData.customer && formData.shipping && (React.createElement(motion.div, Object.assign({ key: "payment" }, motionSettings, { className: "absolute w-full" }),
|
|
34583
|
-
React.createElement(PaymentForm, { locale: locale, fonts: fonts, checkoutAppearance: checkoutAppearance, paymentSecret: paymentSecret, onSuccess: onSuccess, onError: onError, onBack: handleBack, onDoubleBack: handleDoubleBack, contactEmail: formData.customer.email, shippingAddress: formatAddress(formData.customer.address),
|
|
34486
|
+
React.createElement(PaymentForm, { locale: locale, fonts: fonts, checkoutAppearance: checkoutAppearance, paymentSecret: paymentSecret, onSuccess: onSuccess, onError: onError, onBack: handleBack, onDoubleBack: handleDoubleBack, contactEmail: formData.customer.email, shippingAddress: formatAddress(formData.customer.address), shippingName: formData.shipping.name, shippingPrice: storeHelpers.formatPrice(formData.shipping.price, currency, exchangeRate), publicKey: publicKey }))))));
|
|
34584
34487
|
}
|
|
34585
34488
|
|
|
34586
34489
|
function CheckoutFormLoading() {
|
|
@@ -34609,7 +34512,13 @@ function CheckoutSummary({ lineItems, shipping, tax, currency, cancelUrl, exchan
|
|
|
34609
34512
|
const { t } = useTranslation();
|
|
34610
34513
|
const subtotal = lineItems.reduce((acc, item) => {
|
|
34611
34514
|
var _a, _b;
|
|
34612
|
-
const variant = (_a = item.product) === null || _a === void 0 ? void 0 : _a.productVariants.find((variant) =>
|
|
34515
|
+
const variant = (_a = item.product) === null || _a === void 0 ? void 0 : _a.productVariants.find((variant) => {
|
|
34516
|
+
if (!variant.variantOptions || !item.variantOptions)
|
|
34517
|
+
return false;
|
|
34518
|
+
if (variant.variantOptions.length !== item.variantOptions.length)
|
|
34519
|
+
return false;
|
|
34520
|
+
return variant.variantOptions.every((vOpt) => item.variantOptions.some((iOpt) => vOpt.name === iOpt.name && vOpt.value === iOpt.value));
|
|
34521
|
+
});
|
|
34613
34522
|
const productItem = variant || item.product;
|
|
34614
34523
|
return acc + ((_b = productItem === null || productItem === void 0 ? void 0 : productItem.priceInCents) !== null && _b !== void 0 ? _b : 0) * item.quantity;
|
|
34615
34524
|
}, 0);
|
|
@@ -34653,7 +34562,13 @@ function CheckoutSummary({ lineItems, shipping, tax, currency, cancelUrl, exchan
|
|
|
34653
34562
|
grid: isOpen,
|
|
34654
34563
|
}) }, lineItems.map((item, index) => {
|
|
34655
34564
|
var _a, _b, _c, _d, _e;
|
|
34656
|
-
const variant = (_a = item.product) === null || _a === void 0 ? void 0 : _a.productVariants.find((variant) =>
|
|
34565
|
+
const variant = (_a = item.product) === null || _a === void 0 ? void 0 : _a.productVariants.find((variant) => {
|
|
34566
|
+
if (!variant.variantOptions || !item.variantOptions)
|
|
34567
|
+
return false;
|
|
34568
|
+
if (variant.variantOptions.length !== item.variantOptions.length)
|
|
34569
|
+
return false;
|
|
34570
|
+
return variant.variantOptions.every((vOpt) => item.variantOptions.some((iOpt) => vOpt.name === iOpt.name && vOpt.value === iOpt.value));
|
|
34571
|
+
});
|
|
34657
34572
|
const productItem = variant || item.product;
|
|
34658
34573
|
return (React.createElement("div", { key: index, className: "flex items-center" },
|
|
34659
34574
|
React.createElement("div", { className: "relative" },
|