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