framer-motion 7.8.0 → 7.8.1
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/dist/cjs/index.js +1930 -1900
- package/dist/es/animation/animate.mjs +2 -2
- package/dist/es/animation/create-accelerated-animation.mjs +8 -0
- package/dist/es/animation/create-instant-animation.mjs +12 -0
- package/dist/es/animation/{animation-controls.mjs → hooks/animation-controls.mjs} +2 -2
- package/dist/es/animation/{use-animated-state.mjs → hooks/use-animated-state.mjs} +6 -6
- package/dist/es/animation/{use-animation.mjs → hooks/use-animation.mjs} +1 -1
- package/dist/es/animation/index.mjs +121 -0
- package/dist/es/animation/legacy-popmotion/decay.mjs +11 -4
- package/dist/es/animation/legacy-popmotion/index.mjs +20 -12
- package/dist/es/animation/legacy-popmotion/inertia.mjs +14 -8
- package/dist/es/animation/legacy-popmotion/keyframes.mjs +21 -13
- package/dist/es/animation/legacy-popmotion/spring.mjs +13 -11
- package/dist/es/animation/utils/default-transitions.mjs +9 -14
- package/dist/es/animation/utils/keyframes.mjs +41 -0
- package/dist/es/animation/utils/transitions.mjs +1 -166
- package/dist/es/gestures/drag/VisualElementDragControls.mjs +2 -2
- package/dist/es/index.mjs +3 -3
- package/dist/es/render/utils/animation.mjs +2 -2
- package/dist/es/render/utils/motion-values.mjs +1 -1
- package/dist/es/value/index.mjs +2 -2
- package/dist/es/value/use-spring.mjs +1 -2
- package/dist/framer-motion.dev.js +1942 -1912
- package/dist/framer-motion.js +1 -1
- package/dist/index.d.ts +401 -348
- package/dist/projection.dev.js +1595 -1552
- package/dist/size-rollup-dom-animation-assets.js +1 -1
- package/dist/size-rollup-dom-animation.js +1 -1
- package/dist/size-rollup-dom-max-assets.js +1 -1
- package/dist/size-rollup-dom-max.js +1 -1
- package/dist/size-rollup-motion.js +1 -1
- package/dist/size-webpack-dom-animation.js +1 -1
- package/dist/size-webpack-dom-max.js +1 -1
- package/dist/three-entry.d.ts +286 -281
- package/package.json +6 -6
package/dist/cjs/index.js
CHANGED
|
@@ -1869,1312 +1869,984 @@ function shallowCompare(next, prev) {
|
|
|
1869
1869
|
}
|
|
1870
1870
|
|
|
1871
1871
|
/**
|
|
1872
|
-
*
|
|
1873
|
-
*
|
|
1874
|
-
* @param seconds - Time in seconds.
|
|
1875
|
-
* @return milliseconds - Converted time in milliseconds.
|
|
1872
|
+
* Check if value is a numerical string, ie a string that is purely a number eg "100" or "-100.1"
|
|
1876
1873
|
*/
|
|
1877
|
-
const
|
|
1878
|
-
|
|
1879
|
-
const noop = (any) => any;
|
|
1880
|
-
|
|
1881
|
-
/*
|
|
1882
|
-
Bezier function generator
|
|
1883
|
-
This has been modified from Gaëtan Renaudeau's BezierEasing
|
|
1884
|
-
https://github.com/gre/bezier-easing/blob/master/src/index.js
|
|
1885
|
-
https://github.com/gre/bezier-easing/blob/master/LICENSE
|
|
1886
|
-
|
|
1887
|
-
I've removed the newtonRaphsonIterate algo because in benchmarking it
|
|
1888
|
-
wasn't noticiably faster than binarySubdivision, indeed removing it
|
|
1889
|
-
usually improved times, depending on the curve.
|
|
1890
|
-
I also removed the lookup table, as for the added bundle size and loop we're
|
|
1891
|
-
only cutting ~4 or so subdivision iterations. I bumped the max iterations up
|
|
1892
|
-
to 12 to compensate and this still tended to be faster for no perceivable
|
|
1893
|
-
loss in accuracy.
|
|
1894
|
-
Usage
|
|
1895
|
-
const easeOut = cubicBezier(.17,.67,.83,.67);
|
|
1896
|
-
const x = easeOut(0.5); // returns 0.627...
|
|
1897
|
-
*/
|
|
1898
|
-
// Returns x(t) given t, x1, and x2, or y(t) given t, y1, and y2.
|
|
1899
|
-
const calcBezier = (t, a1, a2) => (((1.0 - 3.0 * a2 + 3.0 * a1) * t + (3.0 * a2 - 6.0 * a1)) * t + 3.0 * a1) *
|
|
1900
|
-
t;
|
|
1901
|
-
const subdivisionPrecision = 0.0000001;
|
|
1902
|
-
const subdivisionMaxIterations = 12;
|
|
1903
|
-
function binarySubdivide(x, lowerBound, upperBound, mX1, mX2) {
|
|
1904
|
-
let currentX;
|
|
1905
|
-
let currentT;
|
|
1906
|
-
let i = 0;
|
|
1907
|
-
do {
|
|
1908
|
-
currentT = lowerBound + (upperBound - lowerBound) / 2.0;
|
|
1909
|
-
currentX = calcBezier(currentT, mX1, mX2) - x;
|
|
1910
|
-
if (currentX > 0.0) {
|
|
1911
|
-
upperBound = currentT;
|
|
1912
|
-
}
|
|
1913
|
-
else {
|
|
1914
|
-
lowerBound = currentT;
|
|
1915
|
-
}
|
|
1916
|
-
} while (Math.abs(currentX) > subdivisionPrecision &&
|
|
1917
|
-
++i < subdivisionMaxIterations);
|
|
1918
|
-
return currentT;
|
|
1919
|
-
}
|
|
1920
|
-
function cubicBezier(mX1, mY1, mX2, mY2) {
|
|
1921
|
-
// If this is a linear gradient, return linear easing
|
|
1922
|
-
if (mX1 === mY1 && mX2 === mY2)
|
|
1923
|
-
return noop;
|
|
1924
|
-
const getTForX = (aX) => binarySubdivide(aX, 0, 1, mX1, mX2);
|
|
1925
|
-
// If animation is at start/end, return t without easing
|
|
1926
|
-
return (t) => t === 0 || t === 1 ? t : calcBezier(getTForX(t), mY1, mY2);
|
|
1927
|
-
}
|
|
1928
|
-
|
|
1929
|
-
// Accepts an easing function and returns a new one that outputs mirrored values for
|
|
1930
|
-
// the second half of the animation. Turns easeIn into easeInOut.
|
|
1931
|
-
const mirrorEasing = (easing) => (p) => p <= 0.5 ? easing(2 * p) / 2 : (2 - easing(2 * (1 - p))) / 2;
|
|
1932
|
-
|
|
1933
|
-
// Accepts an easing function and returns a new one that outputs reversed values.
|
|
1934
|
-
// Turns easeIn into easeOut.
|
|
1935
|
-
const reverseEasing = (easing) => (p) => 1 - easing(1 - p);
|
|
1936
|
-
|
|
1937
|
-
const easeIn = (p) => p * p;
|
|
1938
|
-
const easeOut = reverseEasing(easeIn);
|
|
1939
|
-
const easeInOut = mirrorEasing(easeIn);
|
|
1940
|
-
|
|
1941
|
-
const circIn = (p) => 1 - Math.sin(Math.acos(p));
|
|
1942
|
-
const circOut = reverseEasing(circIn);
|
|
1943
|
-
const circInOut = mirrorEasing(circOut);
|
|
1944
|
-
|
|
1945
|
-
const createBackIn = (power = 1.525) => (p) => p * p * ((power + 1) * p - power);
|
|
1946
|
-
const backIn = createBackIn();
|
|
1947
|
-
const backOut = reverseEasing(backIn);
|
|
1948
|
-
const backInOut = mirrorEasing(backIn);
|
|
1949
|
-
|
|
1950
|
-
const createAnticipate = (power) => {
|
|
1951
|
-
const backEasing = createBackIn(power);
|
|
1952
|
-
return (p) => (p *= 2) < 1
|
|
1953
|
-
? 0.5 * backEasing(p)
|
|
1954
|
-
: 0.5 * (2 - Math.pow(2, -10 * (p - 1)));
|
|
1955
|
-
};
|
|
1956
|
-
const anticipate = createAnticipate();
|
|
1957
|
-
|
|
1958
|
-
const easingLookup = {
|
|
1959
|
-
linear: noop,
|
|
1960
|
-
easeIn,
|
|
1961
|
-
easeInOut,
|
|
1962
|
-
easeOut,
|
|
1963
|
-
circIn,
|
|
1964
|
-
circInOut,
|
|
1965
|
-
circOut,
|
|
1966
|
-
backIn,
|
|
1967
|
-
backInOut,
|
|
1968
|
-
backOut,
|
|
1969
|
-
anticipate,
|
|
1970
|
-
};
|
|
1971
|
-
const easingDefinitionToFunction = (definition) => {
|
|
1972
|
-
if (Array.isArray(definition)) {
|
|
1973
|
-
// If cubic bezier definition, create bezier curve
|
|
1974
|
-
heyListen.invariant(definition.length === 4, `Cubic bezier arrays must contain four numerical values.`);
|
|
1975
|
-
const [x1, y1, x2, y2] = definition;
|
|
1976
|
-
return cubicBezier(x1, y1, x2, y2);
|
|
1977
|
-
}
|
|
1978
|
-
else if (typeof definition === "string") {
|
|
1979
|
-
// Else lookup from table
|
|
1980
|
-
heyListen.invariant(easingLookup[definition] !== undefined, `Invalid easing type '${definition}'`);
|
|
1981
|
-
return easingLookup[definition];
|
|
1982
|
-
}
|
|
1983
|
-
return definition;
|
|
1984
|
-
};
|
|
1985
|
-
const isEasingArray = (ease) => {
|
|
1986
|
-
return Array.isArray(ease) && typeof ease[0] !== "number";
|
|
1987
|
-
};
|
|
1874
|
+
const isNumericalString = (v) => /^\-?\d*\.?\d+$/.test(v);
|
|
1988
1875
|
|
|
1989
1876
|
/**
|
|
1990
|
-
*
|
|
1991
|
-
* but false if a number or multiple colors
|
|
1877
|
+
* Check if the value is a zero value string like "0px" or "0%"
|
|
1992
1878
|
*/
|
|
1993
|
-
const
|
|
1994
|
-
return Boolean((isString(v) && singleColorRegex.test(v) && v.startsWith(type)) ||
|
|
1995
|
-
(testProp && Object.prototype.hasOwnProperty.call(v, testProp)));
|
|
1996
|
-
};
|
|
1997
|
-
const splitColor = (aName, bName, cName) => (v) => {
|
|
1998
|
-
if (!isString(v))
|
|
1999
|
-
return v;
|
|
2000
|
-
const [a, b, c, alpha] = v.match(floatRegex);
|
|
2001
|
-
return {
|
|
2002
|
-
[aName]: parseFloat(a),
|
|
2003
|
-
[bName]: parseFloat(b),
|
|
2004
|
-
[cName]: parseFloat(c),
|
|
2005
|
-
alpha: alpha !== undefined ? parseFloat(alpha) : 1,
|
|
2006
|
-
};
|
|
2007
|
-
};
|
|
2008
|
-
|
|
2009
|
-
const clampRgbUnit = (v) => clamp(0, 255, v);
|
|
2010
|
-
const rgbUnit = {
|
|
2011
|
-
...number,
|
|
2012
|
-
transform: (v) => Math.round(clampRgbUnit(v)),
|
|
2013
|
-
};
|
|
2014
|
-
const rgba = {
|
|
2015
|
-
test: isColorString("rgb", "red"),
|
|
2016
|
-
parse: splitColor("red", "green", "blue"),
|
|
2017
|
-
transform: ({ red, green, blue, alpha: alpha$1 = 1 }) => "rgba(" +
|
|
2018
|
-
rgbUnit.transform(red) +
|
|
2019
|
-
", " +
|
|
2020
|
-
rgbUnit.transform(green) +
|
|
2021
|
-
", " +
|
|
2022
|
-
rgbUnit.transform(blue) +
|
|
2023
|
-
", " +
|
|
2024
|
-
sanitize(alpha.transform(alpha$1)) +
|
|
2025
|
-
")",
|
|
2026
|
-
};
|
|
2027
|
-
|
|
2028
|
-
function parseHex(v) {
|
|
2029
|
-
let r = "";
|
|
2030
|
-
let g = "";
|
|
2031
|
-
let b = "";
|
|
2032
|
-
let a = "";
|
|
2033
|
-
// If we have 6 characters, ie #FF0000
|
|
2034
|
-
if (v.length > 5) {
|
|
2035
|
-
r = v.substring(1, 3);
|
|
2036
|
-
g = v.substring(3, 5);
|
|
2037
|
-
b = v.substring(5, 7);
|
|
2038
|
-
a = v.substring(7, 9);
|
|
2039
|
-
// Or we have 3 characters, ie #F00
|
|
2040
|
-
}
|
|
2041
|
-
else {
|
|
2042
|
-
r = v.substring(1, 2);
|
|
2043
|
-
g = v.substring(2, 3);
|
|
2044
|
-
b = v.substring(3, 4);
|
|
2045
|
-
a = v.substring(4, 5);
|
|
2046
|
-
r += r;
|
|
2047
|
-
g += g;
|
|
2048
|
-
b += b;
|
|
2049
|
-
a += a;
|
|
2050
|
-
}
|
|
2051
|
-
return {
|
|
2052
|
-
red: parseInt(r, 16),
|
|
2053
|
-
green: parseInt(g, 16),
|
|
2054
|
-
blue: parseInt(b, 16),
|
|
2055
|
-
alpha: a ? parseInt(a, 16) / 255 : 1,
|
|
2056
|
-
};
|
|
2057
|
-
}
|
|
2058
|
-
const hex = {
|
|
2059
|
-
test: isColorString("#"),
|
|
2060
|
-
parse: parseHex,
|
|
2061
|
-
transform: rgba.transform,
|
|
2062
|
-
};
|
|
2063
|
-
|
|
2064
|
-
const hsla = {
|
|
2065
|
-
test: isColorString("hsl", "hue"),
|
|
2066
|
-
parse: splitColor("hue", "saturation", "lightness"),
|
|
2067
|
-
transform: ({ hue, saturation, lightness, alpha: alpha$1 = 1 }) => {
|
|
2068
|
-
return ("hsla(" +
|
|
2069
|
-
Math.round(hue) +
|
|
2070
|
-
", " +
|
|
2071
|
-
percent.transform(sanitize(saturation)) +
|
|
2072
|
-
", " +
|
|
2073
|
-
percent.transform(sanitize(lightness)) +
|
|
2074
|
-
", " +
|
|
2075
|
-
sanitize(alpha.transform(alpha$1)) +
|
|
2076
|
-
")");
|
|
2077
|
-
},
|
|
2078
|
-
};
|
|
1879
|
+
const isZeroValueString = (v) => /^0[^.\s]+$/.test(v);
|
|
2079
1880
|
|
|
2080
|
-
const
|
|
2081
|
-
|
|
2082
|
-
|
|
2083
|
-
if (rgba.test(v)) {
|
|
2084
|
-
return rgba.parse(v);
|
|
2085
|
-
}
|
|
2086
|
-
else if (hsla.test(v)) {
|
|
2087
|
-
return hsla.parse(v);
|
|
2088
|
-
}
|
|
2089
|
-
else {
|
|
2090
|
-
return hex.parse(v);
|
|
2091
|
-
}
|
|
2092
|
-
},
|
|
2093
|
-
transform: (v) => {
|
|
2094
|
-
return isString(v)
|
|
2095
|
-
? v
|
|
2096
|
-
: v.hasOwnProperty("red")
|
|
2097
|
-
? rgba.transform(v)
|
|
2098
|
-
: hsla.transform(v);
|
|
2099
|
-
},
|
|
1881
|
+
const frameData = {
|
|
1882
|
+
delta: 0,
|
|
1883
|
+
timestamp: 0,
|
|
2100
1884
|
};
|
|
2101
1885
|
|
|
2102
|
-
|
|
2103
|
-
|
|
2104
|
-
function test(v) {
|
|
2105
|
-
var _a, _b;
|
|
2106
|
-
return (isNaN(v) &&
|
|
2107
|
-
isString(v) &&
|
|
2108
|
-
(((_a = v.match(floatRegex)) === null || _a === void 0 ? void 0 : _a.length) || 0) +
|
|
2109
|
-
(((_b = v.match(colorRegex)) === null || _b === void 0 ? void 0 : _b.length) || 0) >
|
|
2110
|
-
0);
|
|
2111
|
-
}
|
|
2112
|
-
function analyseComplexValue(v) {
|
|
2113
|
-
if (typeof v === "number")
|
|
2114
|
-
v = `${v}`;
|
|
2115
|
-
const values = [];
|
|
2116
|
-
let numColors = 0;
|
|
2117
|
-
let numNumbers = 0;
|
|
2118
|
-
const colors = v.match(colorRegex);
|
|
2119
|
-
if (colors) {
|
|
2120
|
-
numColors = colors.length;
|
|
2121
|
-
// Strip colors from input so they're not picked up by number regex.
|
|
2122
|
-
// There's a better way to combine these regex searches, but its beyond my regex skills
|
|
2123
|
-
v = v.replace(colorRegex, colorToken);
|
|
2124
|
-
values.push(...colors.map(color.parse));
|
|
2125
|
-
}
|
|
2126
|
-
const numbers = v.match(floatRegex);
|
|
2127
|
-
if (numbers) {
|
|
2128
|
-
numNumbers = numbers.length;
|
|
2129
|
-
v = v.replace(floatRegex, numberToken);
|
|
2130
|
-
values.push(...numbers.map(number.parse));
|
|
2131
|
-
}
|
|
2132
|
-
return { values, numColors, numNumbers, tokenised: v };
|
|
2133
|
-
}
|
|
2134
|
-
function parse(v) {
|
|
2135
|
-
return analyseComplexValue(v).values;
|
|
2136
|
-
}
|
|
2137
|
-
function createTransformer(source) {
|
|
2138
|
-
const { values, numColors, tokenised } = analyseComplexValue(source);
|
|
2139
|
-
const numValues = values.length;
|
|
2140
|
-
return (v) => {
|
|
2141
|
-
let output = tokenised;
|
|
2142
|
-
for (let i = 0; i < numValues; i++) {
|
|
2143
|
-
output = output.replace(i < numColors ? colorToken : numberToken, i < numColors
|
|
2144
|
-
? color.transform(v[i])
|
|
2145
|
-
: sanitize(v[i]));
|
|
2146
|
-
}
|
|
2147
|
-
return output;
|
|
2148
|
-
};
|
|
2149
|
-
}
|
|
2150
|
-
const convertNumbersToZero = (v) => typeof v === "number" ? 0 : v;
|
|
2151
|
-
function getAnimatableNone$1(v) {
|
|
2152
|
-
const parsed = parse(v);
|
|
2153
|
-
const transformer = createTransformer(v);
|
|
2154
|
-
return transformer(parsed.map(convertNumbersToZero));
|
|
2155
|
-
}
|
|
2156
|
-
const complex = { test, parse, createTransformer, getAnimatableNone: getAnimatableNone$1 };
|
|
2157
|
-
|
|
2158
|
-
/**
|
|
2159
|
-
* Check if a value is animatable. Examples:
|
|
2160
|
-
*
|
|
2161
|
-
* ✅: 100, "100px", "#fff"
|
|
2162
|
-
* ❌: "block", "url(2.jpg)"
|
|
2163
|
-
* @param value
|
|
2164
|
-
*
|
|
2165
|
-
* @internal
|
|
1886
|
+
/*
|
|
1887
|
+
Detect and load appropriate clock setting for the execution environment
|
|
2166
1888
|
*/
|
|
2167
|
-
const
|
|
2168
|
-
|
|
2169
|
-
|
|
2170
|
-
|
|
2171
|
-
|
|
2172
|
-
|
|
2173
|
-
|
|
2174
|
-
if (typeof value === "number" || Array.isArray(value))
|
|
2175
|
-
return true;
|
|
2176
|
-
if (typeof value === "string" && // It's animatable if we have a string
|
|
2177
|
-
complex.test(value) && // And it contains numbers and/or colors
|
|
2178
|
-
!value.startsWith("url(") // Unless it starts with "url("
|
|
2179
|
-
) {
|
|
2180
|
-
return true;
|
|
2181
|
-
}
|
|
2182
|
-
return false;
|
|
2183
|
-
};
|
|
2184
|
-
|
|
2185
|
-
const underDampedSpring = () => ({
|
|
2186
|
-
type: "spring",
|
|
2187
|
-
stiffness: 500,
|
|
2188
|
-
damping: 25,
|
|
2189
|
-
restSpeed: 10,
|
|
2190
|
-
});
|
|
2191
|
-
const criticallyDampedSpring = (to) => ({
|
|
2192
|
-
type: "spring",
|
|
2193
|
-
stiffness: 550,
|
|
2194
|
-
damping: to === 0 ? 2 * Math.sqrt(550) : 30,
|
|
2195
|
-
restSpeed: 10,
|
|
2196
|
-
});
|
|
2197
|
-
const linearTween = () => ({
|
|
2198
|
-
type: "keyframes",
|
|
2199
|
-
ease: "linear",
|
|
2200
|
-
duration: 0.3,
|
|
2201
|
-
});
|
|
2202
|
-
const keyframes$1 = (values) => ({
|
|
2203
|
-
type: "keyframes",
|
|
2204
|
-
duration: 0.8,
|
|
2205
|
-
values,
|
|
2206
|
-
});
|
|
2207
|
-
const defaultTransitions = {
|
|
2208
|
-
x: underDampedSpring,
|
|
2209
|
-
y: underDampedSpring,
|
|
2210
|
-
z: underDampedSpring,
|
|
2211
|
-
rotate: underDampedSpring,
|
|
2212
|
-
rotateX: underDampedSpring,
|
|
2213
|
-
rotateY: underDampedSpring,
|
|
2214
|
-
rotateZ: underDampedSpring,
|
|
2215
|
-
scaleX: criticallyDampedSpring,
|
|
2216
|
-
scaleY: criticallyDampedSpring,
|
|
2217
|
-
scale: criticallyDampedSpring,
|
|
2218
|
-
opacity: linearTween,
|
|
2219
|
-
backgroundColor: linearTween,
|
|
2220
|
-
color: linearTween,
|
|
2221
|
-
default: criticallyDampedSpring,
|
|
2222
|
-
};
|
|
2223
|
-
const getDefaultTransition = (valueKey, to) => {
|
|
2224
|
-
let transitionFactory;
|
|
2225
|
-
if (isKeyframesTarget(to)) {
|
|
2226
|
-
transitionFactory = keyframes$1;
|
|
2227
|
-
}
|
|
2228
|
-
else {
|
|
2229
|
-
transitionFactory =
|
|
2230
|
-
defaultTransitions[valueKey] || defaultTransitions.default;
|
|
2231
|
-
}
|
|
2232
|
-
return { to, ...transitionFactory(to) };
|
|
2233
|
-
};
|
|
1889
|
+
const defaultTimestep = (1 / 60) * 1000;
|
|
1890
|
+
const getCurrentTime = typeof performance !== "undefined"
|
|
1891
|
+
? () => performance.now()
|
|
1892
|
+
: () => Date.now();
|
|
1893
|
+
const onNextFrame = typeof window !== "undefined"
|
|
1894
|
+
? (callback) => window.requestAnimationFrame(callback)
|
|
1895
|
+
: (callback) => setTimeout(() => callback(getCurrentTime()), defaultTimestep);
|
|
2234
1896
|
|
|
2235
|
-
|
|
2236
|
-
|
|
2237
|
-
|
|
2238
|
-
|
|
2239
|
-
|
|
2240
|
-
|
|
2241
|
-
|
|
2242
|
-
|
|
2243
|
-
|
|
2244
|
-
|
|
2245
|
-
|
|
2246
|
-
|
|
2247
|
-
|
|
2248
|
-
|
|
2249
|
-
|
|
2250
|
-
|
|
1897
|
+
function createRenderStep(runNextFrame) {
|
|
1898
|
+
/**
|
|
1899
|
+
* We create and reuse two arrays, one to queue jobs for the current frame
|
|
1900
|
+
* and one for the next. We reuse to avoid triggering GC after x frames.
|
|
1901
|
+
*/
|
|
1902
|
+
let toRun = [];
|
|
1903
|
+
let toRunNextFrame = [];
|
|
1904
|
+
/**
|
|
1905
|
+
*
|
|
1906
|
+
*/
|
|
1907
|
+
let numToRun = 0;
|
|
1908
|
+
/**
|
|
1909
|
+
* Track whether we're currently processing jobs in this step. This way
|
|
1910
|
+
* we can decide whether to schedule new jobs for this frame or next.
|
|
1911
|
+
*/
|
|
1912
|
+
let isProcessing = false;
|
|
1913
|
+
let flushNextFrame = false;
|
|
1914
|
+
/**
|
|
1915
|
+
* A set of processes which were marked keepAlive when scheduled.
|
|
1916
|
+
*/
|
|
1917
|
+
const toKeepAlive = new WeakSet();
|
|
1918
|
+
const step = {
|
|
1919
|
+
/**
|
|
1920
|
+
* Schedule a process to run on the next frame.
|
|
1921
|
+
*/
|
|
1922
|
+
schedule: (callback, keepAlive = false, immediate = false) => {
|
|
1923
|
+
const addToCurrentFrame = immediate && isProcessing;
|
|
1924
|
+
const buffer = addToCurrentFrame ? toRun : toRunNextFrame;
|
|
1925
|
+
if (keepAlive)
|
|
1926
|
+
toKeepAlive.add(callback);
|
|
1927
|
+
// If the buffer doesn't already contain this callback, add it
|
|
1928
|
+
if (buffer.indexOf(callback) === -1) {
|
|
1929
|
+
buffer.push(callback);
|
|
1930
|
+
// If we're adding it to the currently running buffer, update its measured size
|
|
1931
|
+
if (addToCurrentFrame && isProcessing)
|
|
1932
|
+
numToRun = toRun.length;
|
|
1933
|
+
}
|
|
1934
|
+
return callback;
|
|
1935
|
+
},
|
|
1936
|
+
/**
|
|
1937
|
+
* Cancel the provided callback from running on the next frame.
|
|
1938
|
+
*/
|
|
1939
|
+
cancel: (callback) => {
|
|
1940
|
+
const index = toRunNextFrame.indexOf(callback);
|
|
1941
|
+
if (index !== -1)
|
|
1942
|
+
toRunNextFrame.splice(index, 1);
|
|
1943
|
+
toKeepAlive.delete(callback);
|
|
1944
|
+
},
|
|
1945
|
+
/**
|
|
1946
|
+
* Execute all schedule callbacks.
|
|
1947
|
+
*/
|
|
1948
|
+
process: (frameData) => {
|
|
1949
|
+
/**
|
|
1950
|
+
* If we're already processing we've probably been triggered by a flushSync
|
|
1951
|
+
* inside an existing process. Instead of executing, mark flushNextFrame
|
|
1952
|
+
* as true and ensure we flush the following frame at the end of this one.
|
|
1953
|
+
*/
|
|
1954
|
+
if (isProcessing) {
|
|
1955
|
+
flushNextFrame = true;
|
|
1956
|
+
return;
|
|
1957
|
+
}
|
|
1958
|
+
isProcessing = true;
|
|
1959
|
+
[toRun, toRunNextFrame] = [toRunNextFrame, toRun];
|
|
1960
|
+
// Clear the next frame list
|
|
1961
|
+
toRunNextFrame.length = 0;
|
|
1962
|
+
// Execute this frame
|
|
1963
|
+
numToRun = toRun.length;
|
|
1964
|
+
if (numToRun) {
|
|
1965
|
+
for (let i = 0; i < numToRun; i++) {
|
|
1966
|
+
const callback = toRun[i];
|
|
1967
|
+
callback(frameData);
|
|
1968
|
+
if (toKeepAlive.has(callback)) {
|
|
1969
|
+
step.schedule(callback);
|
|
1970
|
+
runNextFrame();
|
|
1971
|
+
}
|
|
1972
|
+
}
|
|
1973
|
+
}
|
|
1974
|
+
isProcessing = false;
|
|
1975
|
+
if (flushNextFrame) {
|
|
1976
|
+
flushNextFrame = false;
|
|
1977
|
+
step.process(frameData);
|
|
1978
|
+
}
|
|
1979
|
+
},
|
|
1980
|
+
};
|
|
1981
|
+
return step;
|
|
2251
1982
|
}
|
|
2252
|
-
const functionRegex = /([a-z-]*)\(.*?\)/g;
|
|
2253
|
-
const filter = {
|
|
2254
|
-
...complex,
|
|
2255
|
-
getAnimatableNone: (v) => {
|
|
2256
|
-
const functions = v.match(functionRegex);
|
|
2257
|
-
return functions ? functions.map(applyDefaultFilter).join(" ") : v;
|
|
2258
|
-
},
|
|
2259
|
-
};
|
|
2260
1983
|
|
|
2261
|
-
|
|
2262
|
-
|
|
2263
|
-
|
|
2264
|
-
|
|
2265
|
-
|
|
2266
|
-
|
|
2267
|
-
|
|
2268
|
-
|
|
2269
|
-
|
|
2270
|
-
|
|
2271
|
-
|
|
2272
|
-
|
|
2273
|
-
|
|
2274
|
-
|
|
2275
|
-
|
|
2276
|
-
|
|
2277
|
-
|
|
2278
|
-
|
|
2279
|
-
|
|
1984
|
+
const maxElapsed = 40;
|
|
1985
|
+
let useDefaultElapsed = true;
|
|
1986
|
+
let runNextFrame = false;
|
|
1987
|
+
let isProcessing = false;
|
|
1988
|
+
const stepsOrder = [
|
|
1989
|
+
"read",
|
|
1990
|
+
"update",
|
|
1991
|
+
"preRender",
|
|
1992
|
+
"render",
|
|
1993
|
+
"postRender",
|
|
1994
|
+
];
|
|
1995
|
+
const steps = stepsOrder.reduce((acc, key) => {
|
|
1996
|
+
acc[key] = createRenderStep(() => (runNextFrame = true));
|
|
1997
|
+
return acc;
|
|
1998
|
+
}, {});
|
|
1999
|
+
const sync = stepsOrder.reduce((acc, key) => {
|
|
2000
|
+
const step = steps[key];
|
|
2001
|
+
acc[key] = (process, keepAlive = false, immediate = false) => {
|
|
2002
|
+
if (!runNextFrame)
|
|
2003
|
+
startLoop();
|
|
2004
|
+
return step.schedule(process, keepAlive, immediate);
|
|
2005
|
+
};
|
|
2006
|
+
return acc;
|
|
2007
|
+
}, {});
|
|
2008
|
+
const cancelSync = stepsOrder.reduce((acc, key) => {
|
|
2009
|
+
acc[key] = steps[key].cancel;
|
|
2010
|
+
return acc;
|
|
2011
|
+
}, {});
|
|
2012
|
+
const flushSync = stepsOrder.reduce((acc, key) => {
|
|
2013
|
+
acc[key] = () => steps[key].process(frameData);
|
|
2014
|
+
return acc;
|
|
2015
|
+
}, {});
|
|
2016
|
+
const processStep = (stepId) => steps[stepId].process(frameData);
|
|
2017
|
+
const processFrame = (timestamp) => {
|
|
2018
|
+
runNextFrame = false;
|
|
2019
|
+
frameData.delta = useDefaultElapsed
|
|
2020
|
+
? defaultTimestep
|
|
2021
|
+
: Math.max(Math.min(timestamp - frameData.timestamp, maxElapsed), 1);
|
|
2022
|
+
frameData.timestamp = timestamp;
|
|
2023
|
+
isProcessing = true;
|
|
2024
|
+
stepsOrder.forEach(processStep);
|
|
2025
|
+
isProcessing = false;
|
|
2026
|
+
if (runNextFrame) {
|
|
2027
|
+
useDefaultElapsed = false;
|
|
2028
|
+
onNextFrame(processFrame);
|
|
2029
|
+
}
|
|
2280
2030
|
};
|
|
2281
|
-
|
|
2282
|
-
|
|
2283
|
-
|
|
2284
|
-
|
|
2285
|
-
|
|
2286
|
-
function getAnimatableNone(key, value) {
|
|
2287
|
-
var _a;
|
|
2288
|
-
let defaultValueType = getDefaultValueType(key);
|
|
2289
|
-
if (defaultValueType !== filter)
|
|
2290
|
-
defaultValueType = complex;
|
|
2291
|
-
// If value is not recognised as animatable, ie "none", create an animatable version origin based on the target
|
|
2292
|
-
return (_a = defaultValueType.getAnimatableNone) === null || _a === void 0 ? void 0 : _a.call(defaultValueType, value);
|
|
2293
|
-
}
|
|
2294
|
-
|
|
2295
|
-
const instantAnimationState = {
|
|
2296
|
-
current: false,
|
|
2031
|
+
const startLoop = () => {
|
|
2032
|
+
runNextFrame = true;
|
|
2033
|
+
useDefaultElapsed = true;
|
|
2034
|
+
if (!isProcessing)
|
|
2035
|
+
onNextFrame(processFrame);
|
|
2297
2036
|
};
|
|
2298
2037
|
|
|
2299
|
-
|
|
2300
|
-
|
|
2301
|
-
|
|
2302
|
-
Given a lower limit and an upper limit, we return the value within
|
|
2303
|
-
that range as expressed by progress (usually a number from 0 to 1)
|
|
2304
|
-
|
|
2305
|
-
So progress = 0.5 would change
|
|
2306
|
-
|
|
2307
|
-
from -------- to
|
|
2308
|
-
|
|
2309
|
-
to
|
|
2310
|
-
|
|
2311
|
-
from ---- to
|
|
2312
|
-
|
|
2313
|
-
E.g. from = 10, to = 20, progress = 0.5 => 15
|
|
2314
|
-
|
|
2315
|
-
@param [number]: Lower limit of range
|
|
2316
|
-
@param [number]: Upper limit of range
|
|
2317
|
-
@param [number]: The progress between lower and upper limits expressed 0-1
|
|
2318
|
-
@return [number]: Value as calculated from progress within range (not limited within range)
|
|
2319
|
-
*/
|
|
2320
|
-
const mix = (from, to, progress) => -progress * from + progress * to + from;
|
|
2321
|
-
|
|
2322
|
-
// Adapted from https://gist.github.com/mjackson/5311256
|
|
2323
|
-
function hueToRgb(p, q, t) {
|
|
2324
|
-
if (t < 0)
|
|
2325
|
-
t += 1;
|
|
2326
|
-
if (t > 1)
|
|
2327
|
-
t -= 1;
|
|
2328
|
-
if (t < 1 / 6)
|
|
2329
|
-
return p + (q - p) * 6 * t;
|
|
2330
|
-
if (t < 1 / 2)
|
|
2331
|
-
return q;
|
|
2332
|
-
if (t < 2 / 3)
|
|
2333
|
-
return p + (q - p) * (2 / 3 - t) * 6;
|
|
2334
|
-
return p;
|
|
2038
|
+
function addUniqueItem(arr, item) {
|
|
2039
|
+
if (arr.indexOf(item) === -1)
|
|
2040
|
+
arr.push(item);
|
|
2335
2041
|
}
|
|
2336
|
-
function
|
|
2337
|
-
|
|
2338
|
-
|
|
2339
|
-
|
|
2340
|
-
let red = 0;
|
|
2341
|
-
let green = 0;
|
|
2342
|
-
let blue = 0;
|
|
2343
|
-
if (!saturation) {
|
|
2344
|
-
red = green = blue = lightness;
|
|
2345
|
-
}
|
|
2346
|
-
else {
|
|
2347
|
-
const q = lightness < 0.5
|
|
2348
|
-
? lightness * (1 + saturation)
|
|
2349
|
-
: lightness + saturation - lightness * saturation;
|
|
2350
|
-
const p = 2 * lightness - q;
|
|
2351
|
-
red = hueToRgb(p, q, hue + 1 / 3);
|
|
2352
|
-
green = hueToRgb(p, q, hue);
|
|
2353
|
-
blue = hueToRgb(p, q, hue - 1 / 3);
|
|
2354
|
-
}
|
|
2355
|
-
return {
|
|
2356
|
-
red: Math.round(red * 255),
|
|
2357
|
-
green: Math.round(green * 255),
|
|
2358
|
-
blue: Math.round(blue * 255),
|
|
2359
|
-
alpha,
|
|
2360
|
-
};
|
|
2042
|
+
function removeItem(arr, item) {
|
|
2043
|
+
const index = arr.indexOf(item);
|
|
2044
|
+
if (index > -1)
|
|
2045
|
+
arr.splice(index, 1);
|
|
2361
2046
|
}
|
|
2362
|
-
|
|
2363
|
-
|
|
2364
|
-
|
|
2365
|
-
|
|
2366
|
-
const
|
|
2367
|
-
|
|
2368
|
-
|
|
2369
|
-
};
|
|
2370
|
-
const colorTypes = [hex, rgba, hsla];
|
|
2371
|
-
const getColorType = (v) => colorTypes.find((type) => type.test(v));
|
|
2372
|
-
function asRGBA(color) {
|
|
2373
|
-
const type = getColorType(color);
|
|
2374
|
-
heyListen.invariant(Boolean(type), `'${color}' is not an animatable color. Use the equivalent color code instead.`);
|
|
2375
|
-
let model = type.parse(color);
|
|
2376
|
-
if (type === hsla) {
|
|
2377
|
-
// TODO Remove this cast - needed since Framer Motion's stricter typing
|
|
2378
|
-
model = hslaToRgba(model);
|
|
2047
|
+
// Adapted from array-move
|
|
2048
|
+
function moveItem([...arr], fromIndex, toIndex) {
|
|
2049
|
+
const startIndex = fromIndex < 0 ? arr.length + fromIndex : fromIndex;
|
|
2050
|
+
if (startIndex >= 0 && startIndex < arr.length) {
|
|
2051
|
+
const endIndex = toIndex < 0 ? arr.length + toIndex : toIndex;
|
|
2052
|
+
const [item] = arr.splice(fromIndex, 1);
|
|
2053
|
+
arr.splice(endIndex, 0, item);
|
|
2379
2054
|
}
|
|
2380
|
-
return
|
|
2055
|
+
return arr;
|
|
2381
2056
|
}
|
|
2382
|
-
const mixColor = (from, to) => {
|
|
2383
|
-
const fromRGBA = asRGBA(from);
|
|
2384
|
-
const toRGBA = asRGBA(to);
|
|
2385
|
-
const blended = { ...fromRGBA };
|
|
2386
|
-
return (v) => {
|
|
2387
|
-
blended.red = mixLinearColor(fromRGBA.red, toRGBA.red, v);
|
|
2388
|
-
blended.green = mixLinearColor(fromRGBA.green, toRGBA.green, v);
|
|
2389
|
-
blended.blue = mixLinearColor(fromRGBA.blue, toRGBA.blue, v);
|
|
2390
|
-
blended.alpha = mix(fromRGBA.alpha, toRGBA.alpha, v);
|
|
2391
|
-
return rgba.transform(blended);
|
|
2392
|
-
};
|
|
2393
|
-
};
|
|
2394
2057
|
|
|
2395
|
-
|
|
2396
|
-
|
|
2397
|
-
|
|
2398
|
-
}
|
|
2399
|
-
else if (color.test(origin)) {
|
|
2400
|
-
return mixColor(origin, target);
|
|
2401
|
-
}
|
|
2402
|
-
else {
|
|
2403
|
-
return mixComplex(origin, target);
|
|
2058
|
+
class SubscriptionManager {
|
|
2059
|
+
constructor() {
|
|
2060
|
+
this.subscriptions = [];
|
|
2404
2061
|
}
|
|
2405
|
-
|
|
2406
|
-
|
|
2407
|
-
|
|
2408
|
-
|
|
2409
|
-
|
|
2410
|
-
|
|
2411
|
-
|
|
2412
|
-
|
|
2062
|
+
add(handler) {
|
|
2063
|
+
addUniqueItem(this.subscriptions, handler);
|
|
2064
|
+
return () => removeItem(this.subscriptions, handler);
|
|
2065
|
+
}
|
|
2066
|
+
notify(a, b, c) {
|
|
2067
|
+
const numSubscriptions = this.subscriptions.length;
|
|
2068
|
+
if (!numSubscriptions)
|
|
2069
|
+
return;
|
|
2070
|
+
if (numSubscriptions === 1) {
|
|
2071
|
+
/**
|
|
2072
|
+
* If there's only a single handler we can just call it without invoking a loop.
|
|
2073
|
+
*/
|
|
2074
|
+
this.subscriptions[0](a, b, c);
|
|
2413
2075
|
}
|
|
2414
|
-
|
|
2415
|
-
|
|
2416
|
-
|
|
2417
|
-
|
|
2418
|
-
|
|
2419
|
-
|
|
2420
|
-
|
|
2421
|
-
|
|
2422
|
-
|
|
2076
|
+
else {
|
|
2077
|
+
for (let i = 0; i < numSubscriptions; i++) {
|
|
2078
|
+
/**
|
|
2079
|
+
* Check whether the handler exists before firing as it's possible
|
|
2080
|
+
* the subscriptions were modified during this loop running.
|
|
2081
|
+
*/
|
|
2082
|
+
const handler = this.subscriptions[i];
|
|
2083
|
+
handler && handler(a, b, c);
|
|
2084
|
+
}
|
|
2423
2085
|
}
|
|
2424
2086
|
}
|
|
2425
|
-
|
|
2426
|
-
|
|
2427
|
-
output[key] = blendValue[key](v);
|
|
2428
|
-
}
|
|
2429
|
-
return output;
|
|
2430
|
-
};
|
|
2431
|
-
};
|
|
2432
|
-
const mixComplex = (origin, target) => {
|
|
2433
|
-
const template = complex.createTransformer(target);
|
|
2434
|
-
const originStats = analyseComplexValue(origin);
|
|
2435
|
-
const targetStats = analyseComplexValue(target);
|
|
2436
|
-
const canInterpolate = originStats.numColors === targetStats.numColors &&
|
|
2437
|
-
originStats.numNumbers >= targetStats.numNumbers;
|
|
2438
|
-
if (canInterpolate) {
|
|
2439
|
-
return pipe(mixArray(originStats.values, targetStats.values), template);
|
|
2087
|
+
getSize() {
|
|
2088
|
+
return this.subscriptions.length;
|
|
2440
2089
|
}
|
|
2441
|
-
|
|
2442
|
-
|
|
2443
|
-
return (p) => `${p > 0 ? target : origin}`;
|
|
2090
|
+
clear() {
|
|
2091
|
+
this.subscriptions.length = 0;
|
|
2444
2092
|
}
|
|
2445
|
-
}
|
|
2093
|
+
}
|
|
2446
2094
|
|
|
2447
2095
|
/*
|
|
2448
|
-
|
|
2449
|
-
|
|
2450
|
-
Given a lower limit and an upper limit, we return the progress
|
|
2451
|
-
(expressed as a number 0-1) represented by the given value, and
|
|
2452
|
-
limit that progress to within 0-1.
|
|
2096
|
+
Convert velocity into velocity per second
|
|
2453
2097
|
|
|
2454
|
-
@param [number]:
|
|
2455
|
-
@param [number]:
|
|
2456
|
-
@param [number]: Value to find progress within given range
|
|
2457
|
-
@return [number]: Progress of value within range as expressed 0-1
|
|
2098
|
+
@param [number]: Unit per frame
|
|
2099
|
+
@param [number]: Frame duration in ms
|
|
2458
2100
|
*/
|
|
2459
|
-
|
|
2460
|
-
|
|
2461
|
-
|
|
2462
|
-
};
|
|
2101
|
+
function velocityPerSecond(velocity, frameDuration) {
|
|
2102
|
+
return frameDuration ? velocity * (1000 / frameDuration) : 0;
|
|
2103
|
+
}
|
|
2463
2104
|
|
|
2464
|
-
const
|
|
2465
|
-
|
|
2466
|
-
|
|
2467
|
-
|
|
2105
|
+
const isFloat = (value) => {
|
|
2106
|
+
return !isNaN(parseFloat(value));
|
|
2107
|
+
};
|
|
2108
|
+
/**
|
|
2109
|
+
* `MotionValue` is used to track the state and velocity of motion values.
|
|
2110
|
+
*
|
|
2111
|
+
* @public
|
|
2112
|
+
*/
|
|
2113
|
+
class MotionValue {
|
|
2114
|
+
/**
|
|
2115
|
+
* @param init - The initiating value
|
|
2116
|
+
* @param config - Optional configuration options
|
|
2117
|
+
*
|
|
2118
|
+
* - `transformer`: A function to transform incoming values with.
|
|
2119
|
+
*
|
|
2120
|
+
* @internal
|
|
2121
|
+
*/
|
|
2122
|
+
constructor(init) {
|
|
2123
|
+
/**
|
|
2124
|
+
* This will be replaced by the build step with the latest version number.
|
|
2125
|
+
* When MotionValues are provided to motion components, warn if versions are mixed.
|
|
2126
|
+
*/
|
|
2127
|
+
this.version = "7.8.1";
|
|
2128
|
+
/**
|
|
2129
|
+
* Duration, in milliseconds, since last updating frame.
|
|
2130
|
+
*
|
|
2131
|
+
* @internal
|
|
2132
|
+
*/
|
|
2133
|
+
this.timeDelta = 0;
|
|
2134
|
+
/**
|
|
2135
|
+
* Timestamp of the last time this `MotionValue` was updated.
|
|
2136
|
+
*
|
|
2137
|
+
* @internal
|
|
2138
|
+
*/
|
|
2139
|
+
this.lastUpdated = 0;
|
|
2140
|
+
/**
|
|
2141
|
+
* Functions to notify when the `MotionValue` updates.
|
|
2142
|
+
*
|
|
2143
|
+
* @internal
|
|
2144
|
+
*/
|
|
2145
|
+
this.updateSubscribers = new SubscriptionManager();
|
|
2146
|
+
/**
|
|
2147
|
+
* Functions to notify when the velocity updates.
|
|
2148
|
+
*
|
|
2149
|
+
* @internal
|
|
2150
|
+
*/
|
|
2151
|
+
this.velocityUpdateSubscribers = new SubscriptionManager();
|
|
2152
|
+
/**
|
|
2153
|
+
* Functions to notify when the `MotionValue` updates and `render` is set to `true`.
|
|
2154
|
+
*
|
|
2155
|
+
* @internal
|
|
2156
|
+
*/
|
|
2157
|
+
this.renderSubscribers = new SubscriptionManager();
|
|
2158
|
+
/**
|
|
2159
|
+
* Tracks whether this value can output a velocity. Currently this is only true
|
|
2160
|
+
* if the value is numerical, but we might be able to widen the scope here and support
|
|
2161
|
+
* other value types.
|
|
2162
|
+
*
|
|
2163
|
+
* @internal
|
|
2164
|
+
*/
|
|
2165
|
+
this.canTrackVelocity = false;
|
|
2166
|
+
this.updateAndNotify = (v, render = true) => {
|
|
2167
|
+
this.prev = this.current;
|
|
2168
|
+
this.current = v;
|
|
2169
|
+
// Update timestamp
|
|
2170
|
+
const { delta, timestamp } = frameData;
|
|
2171
|
+
if (this.lastUpdated !== timestamp) {
|
|
2172
|
+
this.timeDelta = delta;
|
|
2173
|
+
this.lastUpdated = timestamp;
|
|
2174
|
+
sync.postRender(this.scheduleVelocityCheck);
|
|
2175
|
+
}
|
|
2176
|
+
// Update update subscribers
|
|
2177
|
+
if (this.prev !== this.current) {
|
|
2178
|
+
this.updateSubscribers.notify(this.current);
|
|
2179
|
+
}
|
|
2180
|
+
// Update velocity subscribers
|
|
2181
|
+
if (this.velocityUpdateSubscribers.getSize()) {
|
|
2182
|
+
this.velocityUpdateSubscribers.notify(this.getVelocity());
|
|
2183
|
+
}
|
|
2184
|
+
// Update render subscribers
|
|
2185
|
+
if (render) {
|
|
2186
|
+
this.renderSubscribers.notify(this.current);
|
|
2187
|
+
}
|
|
2188
|
+
};
|
|
2189
|
+
/**
|
|
2190
|
+
* Schedule a velocity check for the next frame.
|
|
2191
|
+
*
|
|
2192
|
+
* This is an instanced and bound function to prevent generating a new
|
|
2193
|
+
* function once per frame.
|
|
2194
|
+
*
|
|
2195
|
+
* @internal
|
|
2196
|
+
*/
|
|
2197
|
+
this.scheduleVelocityCheck = () => sync.postRender(this.velocityCheck);
|
|
2198
|
+
/**
|
|
2199
|
+
* Updates `prev` with `current` if the value hasn't been updated this frame.
|
|
2200
|
+
* This ensures velocity calculations return `0`.
|
|
2201
|
+
*
|
|
2202
|
+
* This is an instanced and bound function to prevent generating a new
|
|
2203
|
+
* function once per frame.
|
|
2204
|
+
*
|
|
2205
|
+
* @internal
|
|
2206
|
+
*/
|
|
2207
|
+
this.velocityCheck = ({ timestamp }) => {
|
|
2208
|
+
if (timestamp !== this.lastUpdated) {
|
|
2209
|
+
this.prev = this.current;
|
|
2210
|
+
this.velocityUpdateSubscribers.notify(this.getVelocity());
|
|
2211
|
+
}
|
|
2212
|
+
};
|
|
2213
|
+
this.hasAnimated = false;
|
|
2214
|
+
this.prev = this.current = init;
|
|
2215
|
+
this.canTrackVelocity = isFloat(this.current);
|
|
2216
|
+
}
|
|
2217
|
+
/**
|
|
2218
|
+
* Adds a function that will be notified when the `MotionValue` is updated.
|
|
2219
|
+
*
|
|
2220
|
+
* It returns a function that, when called, will cancel the subscription.
|
|
2221
|
+
*
|
|
2222
|
+
* When calling `onChange` inside a React component, it should be wrapped with the
|
|
2223
|
+
* `useEffect` hook. As it returns an unsubscribe function, this should be returned
|
|
2224
|
+
* from the `useEffect` function to ensure you don't add duplicate subscribers..
|
|
2225
|
+
*
|
|
2226
|
+
* ```jsx
|
|
2227
|
+
* export const MyComponent = () => {
|
|
2228
|
+
* const x = useMotionValue(0)
|
|
2229
|
+
* const y = useMotionValue(0)
|
|
2230
|
+
* const opacity = useMotionValue(1)
|
|
2231
|
+
*
|
|
2232
|
+
* useEffect(() => {
|
|
2233
|
+
* function updateOpacity() {
|
|
2234
|
+
* const maxXY = Math.max(x.get(), y.get())
|
|
2235
|
+
* const newOpacity = transform(maxXY, [0, 100], [1, 0])
|
|
2236
|
+
* opacity.set(newOpacity)
|
|
2237
|
+
* }
|
|
2238
|
+
*
|
|
2239
|
+
* const unsubscribeX = x.onChange(updateOpacity)
|
|
2240
|
+
* const unsubscribeY = y.onChange(updateOpacity)
|
|
2241
|
+
*
|
|
2242
|
+
* return () => {
|
|
2243
|
+
* unsubscribeX()
|
|
2244
|
+
* unsubscribeY()
|
|
2245
|
+
* }
|
|
2246
|
+
* }, [])
|
|
2247
|
+
*
|
|
2248
|
+
* return <motion.div style={{ x }} />
|
|
2249
|
+
* }
|
|
2250
|
+
* ```
|
|
2251
|
+
*
|
|
2252
|
+
* @privateRemarks
|
|
2253
|
+
*
|
|
2254
|
+
* We could look into a `useOnChange` hook if the above lifecycle management proves confusing.
|
|
2255
|
+
*
|
|
2256
|
+
* ```jsx
|
|
2257
|
+
* useOnChange(x, () => {})
|
|
2258
|
+
* ```
|
|
2259
|
+
*
|
|
2260
|
+
* @param subscriber - A function that receives the latest value.
|
|
2261
|
+
* @returns A function that, when called, will cancel this subscription.
|
|
2262
|
+
*
|
|
2263
|
+
* @public
|
|
2264
|
+
*/
|
|
2265
|
+
onChange(subscription) {
|
|
2266
|
+
return this.updateSubscribers.add(subscription);
|
|
2267
|
+
}
|
|
2268
|
+
clearListeners() {
|
|
2269
|
+
this.updateSubscribers.clear();
|
|
2270
|
+
}
|
|
2271
|
+
/**
|
|
2272
|
+
* Adds a function that will be notified when the `MotionValue` requests a render.
|
|
2273
|
+
*
|
|
2274
|
+
* @param subscriber - A function that's provided the latest value.
|
|
2275
|
+
* @returns A function that, when called, will cancel this subscription.
|
|
2276
|
+
*
|
|
2277
|
+
* @internal
|
|
2278
|
+
*/
|
|
2279
|
+
onRenderRequest(subscription) {
|
|
2280
|
+
// Render immediately
|
|
2281
|
+
subscription(this.get());
|
|
2282
|
+
return this.renderSubscribers.add(subscription);
|
|
2283
|
+
}
|
|
2284
|
+
/**
|
|
2285
|
+
* Attaches a passive effect to the `MotionValue`.
|
|
2286
|
+
*
|
|
2287
|
+
* @internal
|
|
2288
|
+
*/
|
|
2289
|
+
attach(passiveEffect) {
|
|
2290
|
+
this.passiveEffect = passiveEffect;
|
|
2468
2291
|
}
|
|
2469
|
-
|
|
2470
|
-
|
|
2471
|
-
|
|
2292
|
+
/**
|
|
2293
|
+
* Sets the state of the `MotionValue`.
|
|
2294
|
+
*
|
|
2295
|
+
* @remarks
|
|
2296
|
+
*
|
|
2297
|
+
* ```jsx
|
|
2298
|
+
* const x = useMotionValue(0)
|
|
2299
|
+
* x.set(10)
|
|
2300
|
+
* ```
|
|
2301
|
+
*
|
|
2302
|
+
* @param latest - Latest value to set.
|
|
2303
|
+
* @param render - Whether to notify render subscribers. Defaults to `true`
|
|
2304
|
+
*
|
|
2305
|
+
* @public
|
|
2306
|
+
*/
|
|
2307
|
+
set(v, render = true) {
|
|
2308
|
+
if (!render || !this.passiveEffect) {
|
|
2309
|
+
this.updateAndNotify(v, render);
|
|
2472
2310
|
}
|
|
2473
2311
|
else {
|
|
2474
|
-
|
|
2475
|
-
}
|
|
2476
|
-
}
|
|
2477
|
-
else if (Array.isArray(v)) {
|
|
2478
|
-
return mixArray;
|
|
2479
|
-
}
|
|
2480
|
-
else if (typeof v === "object") {
|
|
2481
|
-
return mixObject;
|
|
2482
|
-
}
|
|
2483
|
-
return mixNumber;
|
|
2484
|
-
}
|
|
2485
|
-
function createMixers(output, ease, customMixer) {
|
|
2486
|
-
const mixers = [];
|
|
2487
|
-
const mixerFactory = customMixer || detectMixerFactory(output[0]);
|
|
2488
|
-
const numMixers = output.length - 1;
|
|
2489
|
-
for (let i = 0; i < numMixers; i++) {
|
|
2490
|
-
let mixer = mixerFactory(output[i], output[i + 1]);
|
|
2491
|
-
if (ease) {
|
|
2492
|
-
const easingFunction = Array.isArray(ease) ? ease[i] : ease;
|
|
2493
|
-
mixer = pipe(easingFunction, mixer);
|
|
2312
|
+
this.passiveEffect(v, this.updateAndNotify);
|
|
2494
2313
|
}
|
|
2495
|
-
mixers.push(mixer);
|
|
2496
|
-
}
|
|
2497
|
-
return mixers;
|
|
2498
|
-
}
|
|
2499
|
-
/**
|
|
2500
|
-
* Create a function that maps from a numerical input array to a generic output array.
|
|
2501
|
-
*
|
|
2502
|
-
* Accepts:
|
|
2503
|
-
* - Numbers
|
|
2504
|
-
* - Colors (hex, hsl, hsla, rgb, rgba)
|
|
2505
|
-
* - Complex (combinations of one or more numbers or strings)
|
|
2506
|
-
*
|
|
2507
|
-
* ```jsx
|
|
2508
|
-
* const mixColor = interpolate([0, 1], ['#fff', '#000'])
|
|
2509
|
-
*
|
|
2510
|
-
* mixColor(0.5) // 'rgba(128, 128, 128, 1)'
|
|
2511
|
-
* ```
|
|
2512
|
-
*
|
|
2513
|
-
* TODO Revist this approach once we've moved to data models for values,
|
|
2514
|
-
* probably not needed to pregenerate mixer functions.
|
|
2515
|
-
*
|
|
2516
|
-
* @public
|
|
2517
|
-
*/
|
|
2518
|
-
function interpolate(input, output, { clamp: isClamp = true, ease, mixer } = {}) {
|
|
2519
|
-
const inputLength = input.length;
|
|
2520
|
-
heyListen.invariant(inputLength === output.length, "Both input and output ranges must be the same length");
|
|
2521
|
-
heyListen.invariant(!ease || !Array.isArray(ease) || ease.length === inputLength - 1, "Array of easing functions must be of length `input.length - 1`, as it applies to the transitions **between** the defined values.");
|
|
2522
|
-
// If input runs highest -> lowest, reverse both arrays
|
|
2523
|
-
if (input[0] > input[inputLength - 1]) {
|
|
2524
|
-
input = [...input].reverse();
|
|
2525
|
-
output = [...output].reverse();
|
|
2526
2314
|
}
|
|
2527
|
-
const mixers = createMixers(output, ease, mixer);
|
|
2528
|
-
const numMixers = mixers.length;
|
|
2529
|
-
const interpolator = (v) => {
|
|
2530
|
-
let i = 0;
|
|
2531
|
-
if (numMixers > 1) {
|
|
2532
|
-
for (; i < input.length - 2; i++) {
|
|
2533
|
-
if (v < input[i + 1])
|
|
2534
|
-
break;
|
|
2535
|
-
}
|
|
2536
|
-
}
|
|
2537
|
-
const progressInRange = progress(input[i], input[i + 1], v);
|
|
2538
|
-
return mixers[i](progressInRange);
|
|
2539
|
-
};
|
|
2540
|
-
return isClamp
|
|
2541
|
-
? (v) => interpolator(clamp(input[0], input[inputLength - 1], v))
|
|
2542
|
-
: interpolator;
|
|
2543
|
-
}
|
|
2544
|
-
|
|
2545
|
-
function defaultEasing(values, easing) {
|
|
2546
|
-
return values.map(() => easing || easeInOut).splice(0, values.length - 1);
|
|
2547
|
-
}
|
|
2548
|
-
function defaultOffset(values) {
|
|
2549
|
-
const numValues = values.length;
|
|
2550
|
-
return values.map((_value, i) => i !== 0 ? i / (numValues - 1) : 0);
|
|
2551
|
-
}
|
|
2552
|
-
function convertOffsetToTimes(offset, duration) {
|
|
2553
|
-
return offset.map((o) => o * duration);
|
|
2554
|
-
}
|
|
2555
|
-
function keyframes({ from = 0, to = 1, ease, offset, duration = 300, }) {
|
|
2556
2315
|
/**
|
|
2557
|
-
*
|
|
2558
|
-
*
|
|
2316
|
+
* Returns the latest state of `MotionValue`
|
|
2317
|
+
*
|
|
2318
|
+
* @returns - The latest state of `MotionValue`
|
|
2319
|
+
*
|
|
2320
|
+
* @public
|
|
2559
2321
|
*/
|
|
2560
|
-
|
|
2322
|
+
get() {
|
|
2323
|
+
return this.current;
|
|
2324
|
+
}
|
|
2561
2325
|
/**
|
|
2562
|
-
*
|
|
2326
|
+
* @public
|
|
2563
2327
|
*/
|
|
2564
|
-
|
|
2328
|
+
getPrevious() {
|
|
2329
|
+
return this.prev;
|
|
2330
|
+
}
|
|
2565
2331
|
/**
|
|
2566
|
-
*
|
|
2332
|
+
* Returns the latest velocity of `MotionValue`
|
|
2333
|
+
*
|
|
2334
|
+
* @returns - The latest velocity of `MotionValue`. Returns `0` if the state is non-numerical.
|
|
2335
|
+
*
|
|
2336
|
+
* @public
|
|
2567
2337
|
*/
|
|
2568
|
-
|
|
2569
|
-
|
|
2570
|
-
|
|
2571
|
-
|
|
2572
|
-
|
|
2573
|
-
|
|
2574
|
-
|
|
2575
|
-
return interpolate(times, values, {
|
|
2576
|
-
ease: Array.isArray(ease) ? ease : defaultEasing(values, ease),
|
|
2577
|
-
});
|
|
2338
|
+
getVelocity() {
|
|
2339
|
+
// This could be isFloat(this.prev) && isFloat(this.current), but that would be wasteful
|
|
2340
|
+
return this.canTrackVelocity
|
|
2341
|
+
? // These casts could be avoided if parseFloat would be typed better
|
|
2342
|
+
velocityPerSecond(parseFloat(this.current) -
|
|
2343
|
+
parseFloat(this.prev), this.timeDelta)
|
|
2344
|
+
: 0;
|
|
2578
2345
|
}
|
|
2579
|
-
let interpolator = createInterpolator();
|
|
2580
|
-
return {
|
|
2581
|
-
next: (t) => {
|
|
2582
|
-
state.value = interpolator(t);
|
|
2583
|
-
state.done = t >= duration;
|
|
2584
|
-
return state;
|
|
2585
|
-
},
|
|
2586
|
-
flipTarget: () => {
|
|
2587
|
-
values.reverse();
|
|
2588
|
-
interpolator = createInterpolator();
|
|
2589
|
-
},
|
|
2590
|
-
};
|
|
2591
|
-
}
|
|
2592
|
-
|
|
2593
|
-
const safeMin = 0.001;
|
|
2594
|
-
const minDuration = 0.01;
|
|
2595
|
-
const maxDuration = 10.0;
|
|
2596
|
-
const minDamping = 0.05;
|
|
2597
|
-
const maxDamping = 1;
|
|
2598
|
-
function findSpring({ duration = 800, bounce = 0.25, velocity = 0, mass = 1, }) {
|
|
2599
|
-
let envelope;
|
|
2600
|
-
let derivative;
|
|
2601
|
-
heyListen.warning(duration <= maxDuration * 1000, "Spring duration must be 10 seconds or less");
|
|
2602
|
-
let dampingRatio = 1 - bounce;
|
|
2603
2346
|
/**
|
|
2604
|
-
*
|
|
2347
|
+
* Registers a new animation to control this `MotionValue`. Only one
|
|
2348
|
+
* animation can drive a `MotionValue` at one time.
|
|
2349
|
+
*
|
|
2350
|
+
* ```jsx
|
|
2351
|
+
* value.start()
|
|
2352
|
+
* ```
|
|
2353
|
+
*
|
|
2354
|
+
* @param animation - A function that starts the provided animation
|
|
2355
|
+
*
|
|
2356
|
+
* @internal
|
|
2605
2357
|
*/
|
|
2606
|
-
|
|
2607
|
-
|
|
2608
|
-
|
|
2609
|
-
|
|
2610
|
-
|
|
2611
|
-
|
|
2612
|
-
envelope = (undampedFreq) => {
|
|
2613
|
-
const exponentialDecay = undampedFreq * dampingRatio;
|
|
2614
|
-
const delta = exponentialDecay * duration;
|
|
2615
|
-
const a = exponentialDecay - velocity;
|
|
2616
|
-
const b = calcAngularFreq(undampedFreq, dampingRatio);
|
|
2617
|
-
const c = Math.exp(-delta);
|
|
2618
|
-
return safeMin - (a / b) * c;
|
|
2619
|
-
};
|
|
2620
|
-
derivative = (undampedFreq) => {
|
|
2621
|
-
const exponentialDecay = undampedFreq * dampingRatio;
|
|
2622
|
-
const delta = exponentialDecay * duration;
|
|
2623
|
-
const d = delta * velocity + velocity;
|
|
2624
|
-
const e = Math.pow(dampingRatio, 2) * Math.pow(undampedFreq, 2) * duration;
|
|
2625
|
-
const f = Math.exp(-delta);
|
|
2626
|
-
const g = calcAngularFreq(Math.pow(undampedFreq, 2), dampingRatio);
|
|
2627
|
-
const factor = -envelope(undampedFreq) + safeMin > 0 ? -1 : 1;
|
|
2628
|
-
return (factor * ((d - e) * f)) / g;
|
|
2629
|
-
};
|
|
2358
|
+
start(animation) {
|
|
2359
|
+
this.stop();
|
|
2360
|
+
return new Promise((resolve) => {
|
|
2361
|
+
this.hasAnimated = true;
|
|
2362
|
+
this.stopAnimation = animation(resolve);
|
|
2363
|
+
}).then(() => this.clearAnimation());
|
|
2630
2364
|
}
|
|
2631
|
-
|
|
2632
|
-
|
|
2633
|
-
|
|
2634
|
-
|
|
2635
|
-
|
|
2636
|
-
|
|
2637
|
-
|
|
2638
|
-
|
|
2639
|
-
|
|
2640
|
-
derivative = (undampedFreq) => {
|
|
2641
|
-
const a = Math.exp(-undampedFreq * duration);
|
|
2642
|
-
const b = (velocity - undampedFreq) * (duration * duration);
|
|
2643
|
-
return a * b;
|
|
2644
|
-
};
|
|
2365
|
+
/**
|
|
2366
|
+
* Stop the currently active animation.
|
|
2367
|
+
*
|
|
2368
|
+
* @public
|
|
2369
|
+
*/
|
|
2370
|
+
stop() {
|
|
2371
|
+
if (this.stopAnimation)
|
|
2372
|
+
this.stopAnimation();
|
|
2373
|
+
this.clearAnimation();
|
|
2645
2374
|
}
|
|
2646
|
-
|
|
2647
|
-
|
|
2648
|
-
|
|
2649
|
-
|
|
2650
|
-
|
|
2651
|
-
|
|
2652
|
-
|
|
2653
|
-
duration,
|
|
2654
|
-
};
|
|
2375
|
+
/**
|
|
2376
|
+
* Returns `true` if this value is currently animating.
|
|
2377
|
+
*
|
|
2378
|
+
* @public
|
|
2379
|
+
*/
|
|
2380
|
+
isAnimating() {
|
|
2381
|
+
return !!this.stopAnimation;
|
|
2655
2382
|
}
|
|
2656
|
-
|
|
2657
|
-
|
|
2658
|
-
return {
|
|
2659
|
-
stiffness,
|
|
2660
|
-
damping: dampingRatio * 2 * Math.sqrt(mass * stiffness),
|
|
2661
|
-
duration,
|
|
2662
|
-
};
|
|
2383
|
+
clearAnimation() {
|
|
2384
|
+
this.stopAnimation = null;
|
|
2663
2385
|
}
|
|
2664
|
-
|
|
2665
|
-
|
|
2666
|
-
|
|
2667
|
-
|
|
2668
|
-
|
|
2669
|
-
|
|
2386
|
+
/**
|
|
2387
|
+
* Destroy and clean up subscribers to this `MotionValue`.
|
|
2388
|
+
*
|
|
2389
|
+
* The `MotionValue` hooks like `useMotionValue` and `useTransform` automatically
|
|
2390
|
+
* handle the lifecycle of the returned `MotionValue`, so this method is only necessary if you've manually
|
|
2391
|
+
* created a `MotionValue` via the `motionValue` function.
|
|
2392
|
+
*
|
|
2393
|
+
* @public
|
|
2394
|
+
*/
|
|
2395
|
+
destroy() {
|
|
2396
|
+
this.updateSubscribers.clear();
|
|
2397
|
+
this.renderSubscribers.clear();
|
|
2398
|
+
this.stop();
|
|
2670
2399
|
}
|
|
2671
|
-
return result;
|
|
2672
2400
|
}
|
|
2673
|
-
function
|
|
2674
|
-
return
|
|
2401
|
+
function motionValue(init) {
|
|
2402
|
+
return new MotionValue(init);
|
|
2675
2403
|
}
|
|
2676
2404
|
|
|
2677
|
-
|
|
2678
|
-
|
|
2405
|
+
/**
|
|
2406
|
+
* Returns true if the provided string is a color, ie rgba(0,0,0,0) or #000,
|
|
2407
|
+
* but false if a number or multiple colors
|
|
2408
|
+
*/
|
|
2409
|
+
const isColorString = (type, testProp) => (v) => {
|
|
2410
|
+
return Boolean((isString(v) && singleColorRegex.test(v) && v.startsWith(type)) ||
|
|
2411
|
+
(testProp && Object.prototype.hasOwnProperty.call(v, testProp)));
|
|
2412
|
+
};
|
|
2413
|
+
const splitColor = (aName, bName, cName) => (v) => {
|
|
2414
|
+
if (!isString(v))
|
|
2415
|
+
return v;
|
|
2416
|
+
const [a, b, c, alpha] = v.match(floatRegex);
|
|
2417
|
+
return {
|
|
2418
|
+
[aName]: parseFloat(a),
|
|
2419
|
+
[bName]: parseFloat(b),
|
|
2420
|
+
[cName]: parseFloat(c),
|
|
2421
|
+
alpha: alpha !== undefined ? parseFloat(alpha) : 1,
|
|
2422
|
+
};
|
|
2423
|
+
};
|
|
2679
2424
|
|
|
2680
|
-
|
|
2681
|
-
|
|
2682
|
-
|
|
2683
|
-
|
|
2684
|
-
|
|
2685
|
-
|
|
2425
|
+
const clampRgbUnit = (v) => clamp(0, 255, v);
|
|
2426
|
+
const rgbUnit = {
|
|
2427
|
+
...number,
|
|
2428
|
+
transform: (v) => Math.round(clampRgbUnit(v)),
|
|
2429
|
+
};
|
|
2430
|
+
const rgba = {
|
|
2431
|
+
test: isColorString("rgb", "red"),
|
|
2432
|
+
parse: splitColor("red", "green", "blue"),
|
|
2433
|
+
transform: ({ red, green, blue, alpha: alpha$1 = 1 }) => "rgba(" +
|
|
2434
|
+
rgbUnit.transform(red) +
|
|
2435
|
+
", " +
|
|
2436
|
+
rgbUnit.transform(green) +
|
|
2437
|
+
", " +
|
|
2438
|
+
rgbUnit.transform(blue) +
|
|
2439
|
+
", " +
|
|
2440
|
+
sanitize(alpha.transform(alpha$1)) +
|
|
2441
|
+
")",
|
|
2442
|
+
};
|
|
2686
2443
|
|
|
2687
|
-
|
|
2688
|
-
|
|
2689
|
-
|
|
2690
|
-
|
|
2691
|
-
|
|
2692
|
-
|
|
2693
|
-
|
|
2694
|
-
|
|
2695
|
-
|
|
2696
|
-
|
|
2697
|
-
|
|
2698
|
-
|
|
2699
|
-
...options,
|
|
2700
|
-
};
|
|
2701
|
-
// stiffness/damping/mass overrides duration/bounce
|
|
2702
|
-
if (!isSpringType(options, physicsKeys) &&
|
|
2703
|
-
isSpringType(options, durationKeys)) {
|
|
2704
|
-
const derived = findSpring(options);
|
|
2705
|
-
springOptions = {
|
|
2706
|
-
...springOptions,
|
|
2707
|
-
...derived,
|
|
2708
|
-
velocity: 0.0,
|
|
2709
|
-
mass: 1.0,
|
|
2710
|
-
};
|
|
2711
|
-
springOptions.isResolvedFromDuration = true;
|
|
2444
|
+
function parseHex(v) {
|
|
2445
|
+
let r = "";
|
|
2446
|
+
let g = "";
|
|
2447
|
+
let b = "";
|
|
2448
|
+
let a = "";
|
|
2449
|
+
// If we have 6 characters, ie #FF0000
|
|
2450
|
+
if (v.length > 5) {
|
|
2451
|
+
r = v.substring(1, 3);
|
|
2452
|
+
g = v.substring(3, 5);
|
|
2453
|
+
b = v.substring(5, 7);
|
|
2454
|
+
a = v.substring(7, 9);
|
|
2455
|
+
// Or we have 3 characters, ie #F00
|
|
2712
2456
|
}
|
|
2713
|
-
|
|
2457
|
+
else {
|
|
2458
|
+
r = v.substring(1, 2);
|
|
2459
|
+
g = v.substring(2, 3);
|
|
2460
|
+
b = v.substring(3, 4);
|
|
2461
|
+
a = v.substring(4, 5);
|
|
2462
|
+
r += r;
|
|
2463
|
+
g += g;
|
|
2464
|
+
b += b;
|
|
2465
|
+
a += a;
|
|
2466
|
+
}
|
|
2467
|
+
return {
|
|
2468
|
+
red: parseInt(r, 16),
|
|
2469
|
+
green: parseInt(g, 16),
|
|
2470
|
+
blue: parseInt(b, 16),
|
|
2471
|
+
alpha: a ? parseInt(a, 16) / 255 : 1,
|
|
2472
|
+
};
|
|
2714
2473
|
}
|
|
2715
|
-
const
|
|
2716
|
-
|
|
2717
|
-
|
|
2718
|
-
|
|
2719
|
-
|
|
2720
|
-
|
|
2721
|
-
|
|
2722
|
-
|
|
2723
|
-
|
|
2724
|
-
|
|
2725
|
-
|
|
2726
|
-
|
|
2727
|
-
|
|
2728
|
-
|
|
2729
|
-
|
|
2730
|
-
|
|
2731
|
-
|
|
2732
|
-
|
|
2733
|
-
|
|
2734
|
-
|
|
2735
|
-
|
|
2736
|
-
|
|
2737
|
-
|
|
2738
|
-
|
|
2739
|
-
|
|
2740
|
-
|
|
2741
|
-
|
|
2742
|
-
resolveSpring = (t) => {
|
|
2743
|
-
const envelope = Math.exp(-dampingRatio * undampedAngularFreq * t);
|
|
2744
|
-
return (to -
|
|
2745
|
-
envelope *
|
|
2746
|
-
(((initialVelocity +
|
|
2747
|
-
dampingRatio * undampedAngularFreq * initialDelta) /
|
|
2748
|
-
angularFreq) *
|
|
2749
|
-
Math.sin(angularFreq * t) +
|
|
2750
|
-
initialDelta * Math.cos(angularFreq * t)));
|
|
2751
|
-
};
|
|
2474
|
+
const hex = {
|
|
2475
|
+
test: isColorString("#"),
|
|
2476
|
+
parse: parseHex,
|
|
2477
|
+
transform: rgba.transform,
|
|
2478
|
+
};
|
|
2479
|
+
|
|
2480
|
+
const hsla = {
|
|
2481
|
+
test: isColorString("hsl", "hue"),
|
|
2482
|
+
parse: splitColor("hue", "saturation", "lightness"),
|
|
2483
|
+
transform: ({ hue, saturation, lightness, alpha: alpha$1 = 1 }) => {
|
|
2484
|
+
return ("hsla(" +
|
|
2485
|
+
Math.round(hue) +
|
|
2486
|
+
", " +
|
|
2487
|
+
percent.transform(sanitize(saturation)) +
|
|
2488
|
+
", " +
|
|
2489
|
+
percent.transform(sanitize(lightness)) +
|
|
2490
|
+
", " +
|
|
2491
|
+
sanitize(alpha.transform(alpha$1)) +
|
|
2492
|
+
")");
|
|
2493
|
+
},
|
|
2494
|
+
};
|
|
2495
|
+
|
|
2496
|
+
const color = {
|
|
2497
|
+
test: (v) => rgba.test(v) || hex.test(v) || hsla.test(v),
|
|
2498
|
+
parse: (v) => {
|
|
2499
|
+
if (rgba.test(v)) {
|
|
2500
|
+
return rgba.parse(v);
|
|
2752
2501
|
}
|
|
2753
|
-
else if (
|
|
2754
|
-
|
|
2755
|
-
resolveSpring = (t) => to -
|
|
2756
|
-
Math.exp(-undampedAngularFreq * t) *
|
|
2757
|
-
(initialDelta +
|
|
2758
|
-
(initialVelocity + undampedAngularFreq * initialDelta) *
|
|
2759
|
-
t);
|
|
2502
|
+
else if (hsla.test(v)) {
|
|
2503
|
+
return hsla.parse(v);
|
|
2760
2504
|
}
|
|
2761
2505
|
else {
|
|
2762
|
-
|
|
2763
|
-
const dampedAngularFreq = undampedAngularFreq * Math.sqrt(dampingRatio * dampingRatio - 1);
|
|
2764
|
-
resolveSpring = (t) => {
|
|
2765
|
-
const envelope = Math.exp(-dampingRatio * undampedAngularFreq * t);
|
|
2766
|
-
// When performing sinh or cosh values can hit Infinity so we cap them here
|
|
2767
|
-
const freqForT = Math.min(dampedAngularFreq * t, 300);
|
|
2768
|
-
return (to -
|
|
2769
|
-
(envelope *
|
|
2770
|
-
((initialVelocity +
|
|
2771
|
-
dampingRatio * undampedAngularFreq * initialDelta) *
|
|
2772
|
-
Math.sinh(freqForT) +
|
|
2773
|
-
dampedAngularFreq *
|
|
2774
|
-
initialDelta *
|
|
2775
|
-
Math.cosh(freqForT))) /
|
|
2776
|
-
dampedAngularFreq);
|
|
2777
|
-
};
|
|
2506
|
+
return hex.parse(v);
|
|
2778
2507
|
}
|
|
2779
|
-
}
|
|
2780
|
-
|
|
2781
|
-
|
|
2782
|
-
|
|
2783
|
-
|
|
2784
|
-
|
|
2785
|
-
|
|
2786
|
-
|
|
2787
|
-
|
|
2788
|
-
|
|
2789
|
-
|
|
2790
|
-
|
|
2791
|
-
|
|
2792
|
-
|
|
2793
|
-
|
|
2794
|
-
|
|
2795
|
-
|
|
2796
|
-
|
|
2797
|
-
|
|
2798
|
-
|
|
2799
|
-
|
|
2800
|
-
|
|
2801
|
-
|
|
2802
|
-
|
|
2803
|
-
|
|
2804
|
-
|
|
2805
|
-
|
|
2806
|
-
|
|
2807
|
-
|
|
2808
|
-
|
|
2809
|
-
|
|
2810
|
-
|
|
2811
|
-
|
|
2812
|
-
|
|
2813
|
-
|
|
2814
|
-
|
|
2815
|
-
|
|
2816
|
-
|
|
2508
|
+
},
|
|
2509
|
+
transform: (v) => {
|
|
2510
|
+
return isString(v)
|
|
2511
|
+
? v
|
|
2512
|
+
: v.hasOwnProperty("red")
|
|
2513
|
+
? rgba.transform(v)
|
|
2514
|
+
: hsla.transform(v);
|
|
2515
|
+
},
|
|
2516
|
+
};
|
|
2517
|
+
|
|
2518
|
+
const colorToken = "${c}";
|
|
2519
|
+
const numberToken = "${n}";
|
|
2520
|
+
function test(v) {
|
|
2521
|
+
var _a, _b;
|
|
2522
|
+
return (isNaN(v) &&
|
|
2523
|
+
isString(v) &&
|
|
2524
|
+
(((_a = v.match(floatRegex)) === null || _a === void 0 ? void 0 : _a.length) || 0) +
|
|
2525
|
+
(((_b = v.match(colorRegex)) === null || _b === void 0 ? void 0 : _b.length) || 0) >
|
|
2526
|
+
0);
|
|
2527
|
+
}
|
|
2528
|
+
function analyseComplexValue(v) {
|
|
2529
|
+
if (typeof v === "number")
|
|
2530
|
+
v = `${v}`;
|
|
2531
|
+
const values = [];
|
|
2532
|
+
let numColors = 0;
|
|
2533
|
+
let numNumbers = 0;
|
|
2534
|
+
const colors = v.match(colorRegex);
|
|
2535
|
+
if (colors) {
|
|
2536
|
+
numColors = colors.length;
|
|
2537
|
+
// Strip colors from input so they're not picked up by number regex.
|
|
2538
|
+
// There's a better way to combine these regex searches, but its beyond my regex skills
|
|
2539
|
+
v = v.replace(colorRegex, colorToken);
|
|
2540
|
+
values.push(...colors.map(color.parse));
|
|
2541
|
+
}
|
|
2542
|
+
const numbers = v.match(floatRegex);
|
|
2543
|
+
if (numbers) {
|
|
2544
|
+
numNumbers = numbers.length;
|
|
2545
|
+
v = v.replace(floatRegex, numberToken);
|
|
2546
|
+
values.push(...numbers.map(number.parse));
|
|
2547
|
+
}
|
|
2548
|
+
return { values, numColors, numNumbers, tokenised: v };
|
|
2817
2549
|
}
|
|
2818
|
-
|
|
2819
|
-
|
|
2820
|
-
|
|
2821
|
-
function
|
|
2822
|
-
|
|
2823
|
-
|
|
2824
|
-
|
|
2825
|
-
|
|
2826
|
-
|
|
2827
|
-
|
|
2828
|
-
|
|
2829
|
-
|
|
2830
|
-
|
|
2831
|
-
|
|
2832
|
-
* the animation will start from the wrong position.
|
|
2833
|
-
*/
|
|
2834
|
-
if (target !== ideal)
|
|
2835
|
-
amplitude = target - from;
|
|
2836
|
-
return {
|
|
2837
|
-
next: (t) => {
|
|
2838
|
-
const delta = -amplitude * Math.exp(-t / timeConstant);
|
|
2839
|
-
state.done = !(delta > restDelta || delta < -restDelta);
|
|
2840
|
-
state.value = state.done ? target : target + delta;
|
|
2841
|
-
return state;
|
|
2842
|
-
},
|
|
2843
|
-
flipTarget: () => { },
|
|
2550
|
+
function parse(v) {
|
|
2551
|
+
return analyseComplexValue(v).values;
|
|
2552
|
+
}
|
|
2553
|
+
function createTransformer(source) {
|
|
2554
|
+
const { values, numColors, tokenised } = analyseComplexValue(source);
|
|
2555
|
+
const numValues = values.length;
|
|
2556
|
+
return (v) => {
|
|
2557
|
+
let output = tokenised;
|
|
2558
|
+
for (let i = 0; i < numValues; i++) {
|
|
2559
|
+
output = output.replace(i < numColors ? colorToken : numberToken, i < numColors
|
|
2560
|
+
? color.transform(v[i])
|
|
2561
|
+
: sanitize(v[i]));
|
|
2562
|
+
}
|
|
2563
|
+
return output;
|
|
2844
2564
|
};
|
|
2845
2565
|
}
|
|
2566
|
+
const convertNumbersToZero = (v) => typeof v === "number" ? 0 : v;
|
|
2567
|
+
function getAnimatableNone$1(v) {
|
|
2568
|
+
const parsed = parse(v);
|
|
2569
|
+
const transformer = createTransformer(v);
|
|
2570
|
+
return transformer(parsed.map(convertNumbersToZero));
|
|
2571
|
+
}
|
|
2572
|
+
const complex = { test, parse, createTransformer, getAnimatableNone: getAnimatableNone$1 };
|
|
2846
2573
|
|
|
2847
|
-
|
|
2848
|
-
|
|
2574
|
+
/**
|
|
2575
|
+
* Properties that should default to 1 or 100%
|
|
2849
2576
|
*/
|
|
2850
|
-
const
|
|
2851
|
-
|
|
2852
|
-
|
|
2853
|
-
|
|
2854
|
-
|
|
2855
|
-
|
|
2856
|
-
|
|
2857
|
-
|
|
2858
|
-
|
|
2859
|
-
|
|
2860
|
-
|
|
2861
|
-
|
|
2862
|
-
|
|
2863
|
-
let toRun = [];
|
|
2864
|
-
let toRunNextFrame = [];
|
|
2865
|
-
/**
|
|
2866
|
-
*
|
|
2867
|
-
*/
|
|
2868
|
-
let numToRun = 0;
|
|
2869
|
-
/**
|
|
2870
|
-
* Track whether we're currently processing jobs in this step. This way
|
|
2871
|
-
* we can decide whether to schedule new jobs for this frame or next.
|
|
2872
|
-
*/
|
|
2873
|
-
let isProcessing = false;
|
|
2874
|
-
let flushNextFrame = false;
|
|
2875
|
-
/**
|
|
2876
|
-
* A set of processes which were marked keepAlive when scheduled.
|
|
2877
|
-
*/
|
|
2878
|
-
const toKeepAlive = new WeakSet();
|
|
2879
|
-
const step = {
|
|
2880
|
-
/**
|
|
2881
|
-
* Schedule a process to run on the next frame.
|
|
2882
|
-
*/
|
|
2883
|
-
schedule: (callback, keepAlive = false, immediate = false) => {
|
|
2884
|
-
const addToCurrentFrame = immediate && isProcessing;
|
|
2885
|
-
const buffer = addToCurrentFrame ? toRun : toRunNextFrame;
|
|
2886
|
-
if (keepAlive)
|
|
2887
|
-
toKeepAlive.add(callback);
|
|
2888
|
-
// If the buffer doesn't already contain this callback, add it
|
|
2889
|
-
if (buffer.indexOf(callback) === -1) {
|
|
2890
|
-
buffer.push(callback);
|
|
2891
|
-
// If we're adding it to the currently running buffer, update its measured size
|
|
2892
|
-
if (addToCurrentFrame && isProcessing)
|
|
2893
|
-
numToRun = toRun.length;
|
|
2894
|
-
}
|
|
2895
|
-
return callback;
|
|
2896
|
-
},
|
|
2897
|
-
/**
|
|
2898
|
-
* Cancel the provided callback from running on the next frame.
|
|
2899
|
-
*/
|
|
2900
|
-
cancel: (callback) => {
|
|
2901
|
-
const index = toRunNextFrame.indexOf(callback);
|
|
2902
|
-
if (index !== -1)
|
|
2903
|
-
toRunNextFrame.splice(index, 1);
|
|
2904
|
-
toKeepAlive.delete(callback);
|
|
2905
|
-
},
|
|
2906
|
-
/**
|
|
2907
|
-
* Execute all schedule callbacks.
|
|
2908
|
-
*/
|
|
2909
|
-
process: (frameData) => {
|
|
2910
|
-
/**
|
|
2911
|
-
* If we're already processing we've probably been triggered by a flushSync
|
|
2912
|
-
* inside an existing process. Instead of executing, mark flushNextFrame
|
|
2913
|
-
* as true and ensure we flush the following frame at the end of this one.
|
|
2914
|
-
*/
|
|
2915
|
-
if (isProcessing) {
|
|
2916
|
-
flushNextFrame = true;
|
|
2917
|
-
return;
|
|
2918
|
-
}
|
|
2919
|
-
isProcessing = true;
|
|
2920
|
-
[toRun, toRunNextFrame] = [toRunNextFrame, toRun];
|
|
2921
|
-
// Clear the next frame list
|
|
2922
|
-
toRunNextFrame.length = 0;
|
|
2923
|
-
// Execute this frame
|
|
2924
|
-
numToRun = toRun.length;
|
|
2925
|
-
if (numToRun) {
|
|
2926
|
-
for (let i = 0; i < numToRun; i++) {
|
|
2927
|
-
const callback = toRun[i];
|
|
2928
|
-
callback(frameData);
|
|
2929
|
-
if (toKeepAlive.has(callback)) {
|
|
2930
|
-
step.schedule(callback);
|
|
2931
|
-
runNextFrame();
|
|
2932
|
-
}
|
|
2933
|
-
}
|
|
2934
|
-
}
|
|
2935
|
-
isProcessing = false;
|
|
2936
|
-
if (flushNextFrame) {
|
|
2937
|
-
flushNextFrame = false;
|
|
2938
|
-
step.process(frameData);
|
|
2939
|
-
}
|
|
2940
|
-
},
|
|
2941
|
-
};
|
|
2942
|
-
return step;
|
|
2577
|
+
const maxDefaults = new Set(["brightness", "contrast", "saturate", "opacity"]);
|
|
2578
|
+
function applyDefaultFilter(v) {
|
|
2579
|
+
const [name, value] = v.slice(0, -1).split("(");
|
|
2580
|
+
if (name === "drop-shadow")
|
|
2581
|
+
return v;
|
|
2582
|
+
const [number] = value.match(floatRegex) || [];
|
|
2583
|
+
if (!number)
|
|
2584
|
+
return v;
|
|
2585
|
+
const unit = value.replace(number, "");
|
|
2586
|
+
let defaultValue = maxDefaults.has(name) ? 1 : 0;
|
|
2587
|
+
if (number !== value)
|
|
2588
|
+
defaultValue *= 100;
|
|
2589
|
+
return name + "(" + defaultValue + unit + ")";
|
|
2943
2590
|
}
|
|
2944
|
-
|
|
2945
|
-
const
|
|
2946
|
-
|
|
2947
|
-
|
|
2591
|
+
const functionRegex = /([a-z-]*)\(.*?\)/g;
|
|
2592
|
+
const filter = {
|
|
2593
|
+
...complex,
|
|
2594
|
+
getAnimatableNone: (v) => {
|
|
2595
|
+
const functions = v.match(functionRegex);
|
|
2596
|
+
return functions ? functions.map(applyDefaultFilter).join(" ") : v;
|
|
2597
|
+
},
|
|
2948
2598
|
};
|
|
2949
2599
|
|
|
2950
|
-
|
|
2951
|
-
|
|
2952
|
-
|
|
2953
|
-
|
|
2954
|
-
|
|
2955
|
-
|
|
2956
|
-
|
|
2957
|
-
|
|
2958
|
-
|
|
2959
|
-
|
|
2960
|
-
|
|
2961
|
-
|
|
2962
|
-
|
|
2963
|
-
|
|
2964
|
-
|
|
2965
|
-
|
|
2966
|
-
|
|
2967
|
-
|
|
2968
|
-
|
|
2969
|
-
startLoop();
|
|
2970
|
-
return step.schedule(process, keepAlive, immediate);
|
|
2971
|
-
};
|
|
2972
|
-
return acc;
|
|
2973
|
-
}, {});
|
|
2974
|
-
const cancelSync = stepsOrder.reduce((acc, key) => {
|
|
2975
|
-
acc[key] = steps[key].cancel;
|
|
2976
|
-
return acc;
|
|
2977
|
-
}, {});
|
|
2978
|
-
const flushSync = stepsOrder.reduce((acc, key) => {
|
|
2979
|
-
acc[key] = () => steps[key].process(frameData);
|
|
2980
|
-
return acc;
|
|
2981
|
-
}, {});
|
|
2982
|
-
const processStep = (stepId) => steps[stepId].process(frameData);
|
|
2983
|
-
const processFrame = (timestamp) => {
|
|
2984
|
-
runNextFrame = false;
|
|
2985
|
-
frameData.delta = useDefaultElapsed
|
|
2986
|
-
? defaultTimestep
|
|
2987
|
-
: Math.max(Math.min(timestamp - frameData.timestamp, maxElapsed), 1);
|
|
2988
|
-
frameData.timestamp = timestamp;
|
|
2989
|
-
isProcessing = true;
|
|
2990
|
-
stepsOrder.forEach(processStep);
|
|
2991
|
-
isProcessing = false;
|
|
2992
|
-
if (runNextFrame) {
|
|
2993
|
-
useDefaultElapsed = false;
|
|
2994
|
-
onNextFrame(processFrame);
|
|
2995
|
-
}
|
|
2600
|
+
/**
|
|
2601
|
+
* A map of default value types for common values
|
|
2602
|
+
*/
|
|
2603
|
+
const defaultValueTypes = {
|
|
2604
|
+
...numberValueTypes,
|
|
2605
|
+
// Color props
|
|
2606
|
+
color,
|
|
2607
|
+
backgroundColor: color,
|
|
2608
|
+
outlineColor: color,
|
|
2609
|
+
fill: color,
|
|
2610
|
+
stroke: color,
|
|
2611
|
+
// Border props
|
|
2612
|
+
borderColor: color,
|
|
2613
|
+
borderTopColor: color,
|
|
2614
|
+
borderRightColor: color,
|
|
2615
|
+
borderBottomColor: color,
|
|
2616
|
+
borderLeftColor: color,
|
|
2617
|
+
filter,
|
|
2618
|
+
WebkitFilter: filter,
|
|
2996
2619
|
};
|
|
2997
|
-
|
|
2998
|
-
|
|
2999
|
-
|
|
3000
|
-
|
|
3001
|
-
|
|
2620
|
+
/**
|
|
2621
|
+
* Gets the default ValueType for the provided value key
|
|
2622
|
+
*/
|
|
2623
|
+
const getDefaultValueType = (key) => defaultValueTypes[key];
|
|
2624
|
+
|
|
2625
|
+
function getAnimatableNone(key, value) {
|
|
2626
|
+
var _a;
|
|
2627
|
+
let defaultValueType = getDefaultValueType(key);
|
|
2628
|
+
if (defaultValueType !== filter)
|
|
2629
|
+
defaultValueType = complex;
|
|
2630
|
+
// If value is not recognised as animatable, ie "none", create an animatable version origin based on the target
|
|
2631
|
+
return (_a = defaultValueType.getAnimatableNone) === null || _a === void 0 ? void 0 : _a.call(defaultValueType, value);
|
|
2632
|
+
}
|
|
2633
|
+
|
|
2634
|
+
/**
|
|
2635
|
+
* Tests a provided value against a ValueType
|
|
2636
|
+
*/
|
|
2637
|
+
const testValueType = (v) => (type) => type.test(v);
|
|
2638
|
+
|
|
2639
|
+
/**
|
|
2640
|
+
* ValueType for "auto"
|
|
2641
|
+
*/
|
|
2642
|
+
const auto = {
|
|
2643
|
+
test: (v) => v === "auto",
|
|
2644
|
+
parse: (v) => v,
|
|
3002
2645
|
};
|
|
3003
2646
|
|
|
3004
|
-
|
|
3005
|
-
|
|
3006
|
-
|
|
2647
|
+
/**
|
|
2648
|
+
* A list of value types commonly used for dimensions
|
|
2649
|
+
*/
|
|
2650
|
+
const dimensionValueTypes = [number, px, percent, degrees, vw, vh, auto];
|
|
2651
|
+
/**
|
|
2652
|
+
* Tests a dimensional value against the list of dimension ValueTypes
|
|
2653
|
+
*/
|
|
2654
|
+
const findDimensionValueType = (v) => dimensionValueTypes.find(testValueType(v));
|
|
2655
|
+
|
|
2656
|
+
/**
|
|
2657
|
+
* A list of all ValueTypes
|
|
2658
|
+
*/
|
|
2659
|
+
const valueTypes = [...dimensionValueTypes, color, complex];
|
|
2660
|
+
/**
|
|
2661
|
+
* Tests a value against the list of ValueTypes
|
|
2662
|
+
*/
|
|
2663
|
+
const findValueType = (v) => valueTypes.find(testValueType(v));
|
|
2664
|
+
|
|
2665
|
+
/**
|
|
2666
|
+
* Creates an object containing the latest state of every MotionValue on a VisualElement
|
|
2667
|
+
*/
|
|
2668
|
+
function getCurrent(visualElement) {
|
|
2669
|
+
const current = {};
|
|
2670
|
+
visualElement.values.forEach((value, key) => (current[key] = value.get()));
|
|
2671
|
+
return current;
|
|
3007
2672
|
}
|
|
3008
|
-
|
|
3009
|
-
|
|
3010
|
-
|
|
3011
|
-
|
|
2673
|
+
/**
|
|
2674
|
+
* Creates an object containing the latest velocity of every MotionValue on a VisualElement
|
|
2675
|
+
*/
|
|
2676
|
+
function getVelocity$1(visualElement) {
|
|
2677
|
+
const velocity = {};
|
|
2678
|
+
visualElement.values.forEach((value, key) => (velocity[key] = value.getVelocity()));
|
|
2679
|
+
return velocity;
|
|
3012
2680
|
}
|
|
3013
|
-
function
|
|
3014
|
-
|
|
2681
|
+
function resolveVariant(visualElement, definition, custom) {
|
|
2682
|
+
const props = visualElement.getProps();
|
|
2683
|
+
return resolveVariantFromProps(props, definition, custom !== undefined ? custom : props.custom, getCurrent(visualElement), getVelocity$1(visualElement));
|
|
3015
2684
|
}
|
|
3016
|
-
|
|
3017
|
-
|
|
3018
|
-
|
|
3019
|
-
|
|
3020
|
-
|
|
3021
|
-
|
|
3022
|
-
|
|
3023
|
-
|
|
3024
|
-
var _a, _b;
|
|
3025
|
-
let { to } = options;
|
|
3026
|
-
let driverControls;
|
|
3027
|
-
let repeatCount = 0;
|
|
3028
|
-
let computedDuration = options
|
|
3029
|
-
.duration;
|
|
3030
|
-
let latest;
|
|
3031
|
-
let isComplete = false;
|
|
3032
|
-
let isForwardPlayback = true;
|
|
3033
|
-
let interpolateFromNumber;
|
|
3034
|
-
const animator = types[Array.isArray(to) ? "keyframes" : type];
|
|
3035
|
-
if ((_b = (_a = animator).needsInterpolation) === null || _b === void 0 ? void 0 : _b.call(_a, from, to)) {
|
|
3036
|
-
interpolateFromNumber = interpolate([0, 100], [from, to], {
|
|
3037
|
-
clamp: false,
|
|
3038
|
-
});
|
|
3039
|
-
from = 0;
|
|
3040
|
-
to = 100;
|
|
3041
|
-
}
|
|
3042
|
-
const animation = animator({ ...options, from, to });
|
|
3043
|
-
function repeat() {
|
|
3044
|
-
repeatCount++;
|
|
3045
|
-
if (repeatType === "reverse") {
|
|
3046
|
-
isForwardPlayback = repeatCount % 2 === 0;
|
|
3047
|
-
elapsed = reverseElapsed(elapsed, computedDuration, repeatDelay, isForwardPlayback);
|
|
3048
|
-
}
|
|
3049
|
-
else {
|
|
3050
|
-
elapsed = loopElapsed(elapsed, computedDuration, repeatDelay);
|
|
3051
|
-
if (repeatType === "mirror")
|
|
3052
|
-
animation.flipTarget();
|
|
3053
|
-
}
|
|
3054
|
-
isComplete = false;
|
|
3055
|
-
onRepeat && onRepeat();
|
|
3056
|
-
}
|
|
3057
|
-
function complete() {
|
|
3058
|
-
driverControls.stop();
|
|
3059
|
-
onComplete && onComplete();
|
|
2685
|
+
|
|
2686
|
+
/**
|
|
2687
|
+
* Set VisualElement's MotionValue, creating a new MotionValue for it if
|
|
2688
|
+
* it doesn't exist.
|
|
2689
|
+
*/
|
|
2690
|
+
function setMotionValue(visualElement, key, value) {
|
|
2691
|
+
if (visualElement.hasValue(key)) {
|
|
2692
|
+
visualElement.getValue(key).set(value);
|
|
3060
2693
|
}
|
|
3061
|
-
|
|
3062
|
-
|
|
3063
|
-
delta = -delta;
|
|
3064
|
-
elapsed += delta;
|
|
3065
|
-
if (!isComplete) {
|
|
3066
|
-
const state = animation.next(Math.max(0, elapsed));
|
|
3067
|
-
latest = state.value;
|
|
3068
|
-
if (interpolateFromNumber)
|
|
3069
|
-
latest = interpolateFromNumber(latest);
|
|
3070
|
-
isComplete = isForwardPlayback ? state.done : elapsed <= 0;
|
|
3071
|
-
}
|
|
3072
|
-
onUpdate && onUpdate(latest);
|
|
3073
|
-
if (isComplete) {
|
|
3074
|
-
if (repeatCount === 0) {
|
|
3075
|
-
computedDuration =
|
|
3076
|
-
computedDuration !== undefined ? computedDuration : elapsed;
|
|
3077
|
-
}
|
|
3078
|
-
if (repeatCount < repeatMax) {
|
|
3079
|
-
hasRepeatDelayElapsed(elapsed, computedDuration, repeatDelay, isForwardPlayback) && repeat();
|
|
3080
|
-
}
|
|
3081
|
-
else {
|
|
3082
|
-
complete();
|
|
3083
|
-
}
|
|
3084
|
-
}
|
|
2694
|
+
else {
|
|
2695
|
+
visualElement.addValue(key, motionValue(value));
|
|
3085
2696
|
}
|
|
3086
|
-
|
|
3087
|
-
|
|
3088
|
-
|
|
3089
|
-
|
|
2697
|
+
}
|
|
2698
|
+
function setTarget(visualElement, definition) {
|
|
2699
|
+
const resolved = resolveVariant(visualElement, definition);
|
|
2700
|
+
let { transitionEnd = {}, transition = {}, ...target } = resolved ? visualElement.makeTargetAnimatable(resolved, false) : {};
|
|
2701
|
+
target = { ...target, ...transitionEnd };
|
|
2702
|
+
for (const key in target) {
|
|
2703
|
+
const value = resolveFinalValueInKeyframes(target[key]);
|
|
2704
|
+
setMotionValue(visualElement, key, value);
|
|
3090
2705
|
}
|
|
3091
|
-
autoplay && play();
|
|
3092
|
-
return {
|
|
3093
|
-
stop: () => {
|
|
3094
|
-
onStop && onStop();
|
|
3095
|
-
driverControls.stop();
|
|
3096
|
-
},
|
|
3097
|
-
};
|
|
3098
2706
|
}
|
|
3099
|
-
|
|
3100
|
-
|
|
3101
|
-
|
|
3102
|
-
|
|
3103
|
-
|
|
2707
|
+
function setVariants(visualElement, variantLabels) {
|
|
2708
|
+
const reversedLabels = [...variantLabels].reverse();
|
|
2709
|
+
reversedLabels.forEach((key) => {
|
|
2710
|
+
var _a;
|
|
2711
|
+
const variant = visualElement.getVariant(key);
|
|
2712
|
+
variant && setTarget(visualElement, variant);
|
|
2713
|
+
(_a = visualElement.variantChildren) === null || _a === void 0 ? void 0 : _a.forEach((child) => {
|
|
2714
|
+
setVariants(child, variantLabels);
|
|
2715
|
+
});
|
|
2716
|
+
});
|
|
2717
|
+
}
|
|
2718
|
+
function setValues(visualElement, definition) {
|
|
2719
|
+
if (Array.isArray(definition)) {
|
|
2720
|
+
return setVariants(visualElement, definition);
|
|
3104
2721
|
}
|
|
3105
|
-
|
|
3106
|
-
|
|
3107
|
-
return max;
|
|
3108
|
-
if (max === undefined)
|
|
3109
|
-
return min;
|
|
3110
|
-
return Math.abs(min - v) < Math.abs(max - v) ? min : max;
|
|
2722
|
+
else if (typeof definition === "string") {
|
|
2723
|
+
return setVariants(visualElement, [definition]);
|
|
3111
2724
|
}
|
|
3112
|
-
|
|
3113
|
-
|
|
3114
|
-
currentAnimation = animate$1({
|
|
3115
|
-
...options,
|
|
3116
|
-
driver,
|
|
3117
|
-
onUpdate: (v) => {
|
|
3118
|
-
var _a;
|
|
3119
|
-
onUpdate === null || onUpdate === void 0 ? void 0 : onUpdate(v);
|
|
3120
|
-
(_a = options.onUpdate) === null || _a === void 0 ? void 0 : _a.call(options, v);
|
|
3121
|
-
},
|
|
3122
|
-
onComplete,
|
|
3123
|
-
onStop,
|
|
3124
|
-
});
|
|
2725
|
+
else {
|
|
2726
|
+
setTarget(visualElement, definition);
|
|
3125
2727
|
}
|
|
3126
|
-
|
|
3127
|
-
|
|
3128
|
-
|
|
3129
|
-
|
|
3130
|
-
|
|
3131
|
-
|
|
3132
|
-
|
|
3133
|
-
|
|
2728
|
+
}
|
|
2729
|
+
function checkTargetForNewValues(visualElement, target, origin) {
|
|
2730
|
+
var _a, _b;
|
|
2731
|
+
const newValueKeys = Object.keys(target).filter((key) => !visualElement.hasValue(key));
|
|
2732
|
+
const numNewValues = newValueKeys.length;
|
|
2733
|
+
if (!numNewValues)
|
|
2734
|
+
return;
|
|
2735
|
+
for (let i = 0; i < numNewValues; i++) {
|
|
2736
|
+
const key = newValueKeys[i];
|
|
2737
|
+
const targetValue = target[key];
|
|
2738
|
+
let value = null;
|
|
2739
|
+
/**
|
|
2740
|
+
* If the target is a series of keyframes, we can use the first value
|
|
2741
|
+
* in the array. If this first value is null, we'll still need to read from the DOM.
|
|
2742
|
+
*/
|
|
2743
|
+
if (Array.isArray(targetValue)) {
|
|
2744
|
+
value = targetValue[0];
|
|
2745
|
+
}
|
|
2746
|
+
/**
|
|
2747
|
+
* If the target isn't keyframes, or the first keyframe was null, we need to
|
|
2748
|
+
* first check if an origin value was explicitly defined in the transition as "from",
|
|
2749
|
+
* if not read the value from the DOM. As an absolute fallback, take the defined target value.
|
|
2750
|
+
*/
|
|
2751
|
+
if (value === null) {
|
|
2752
|
+
value = (_b = (_a = origin[key]) !== null && _a !== void 0 ? _a : visualElement.readValue(key)) !== null && _b !== void 0 ? _b : target[key];
|
|
2753
|
+
}
|
|
2754
|
+
/**
|
|
2755
|
+
* If value is still undefined or null, ignore it. Preferably this would throw,
|
|
2756
|
+
* but this was causing issues in Framer.
|
|
2757
|
+
*/
|
|
2758
|
+
if (value === undefined || value === null)
|
|
2759
|
+
continue;
|
|
2760
|
+
if (typeof value === "string" &&
|
|
2761
|
+
(isNumericalString(value) || isZeroValueString(value))) {
|
|
2762
|
+
// If this is a number read as a string, ie "0" or "200", convert it to a number
|
|
2763
|
+
value = parseFloat(value);
|
|
2764
|
+
}
|
|
2765
|
+
else if (!findValueType(value) && complex.test(targetValue)) {
|
|
2766
|
+
value = getAnimatableNone(key, targetValue);
|
|
2767
|
+
}
|
|
2768
|
+
visualElement.addValue(key, motionValue(value));
|
|
2769
|
+
if (origin[key] === undefined) {
|
|
2770
|
+
origin[key] = value;
|
|
2771
|
+
}
|
|
2772
|
+
if (value !== null)
|
|
2773
|
+
visualElement.setBaseTarget(key, value);
|
|
3134
2774
|
}
|
|
3135
|
-
|
|
3136
|
-
|
|
3137
|
-
|
|
2775
|
+
}
|
|
2776
|
+
function getOriginFromTransition(key, transition) {
|
|
2777
|
+
if (!transition)
|
|
2778
|
+
return;
|
|
2779
|
+
const valueTransition = transition[key] || transition["default"] || transition;
|
|
2780
|
+
return valueTransition.from;
|
|
2781
|
+
}
|
|
2782
|
+
function getOrigin(target, transition, visualElement) {
|
|
2783
|
+
var _a;
|
|
2784
|
+
const origin = {};
|
|
2785
|
+
for (const key in target) {
|
|
2786
|
+
const transitionOrigin = getOriginFromTransition(key, transition);
|
|
2787
|
+
origin[key] =
|
|
2788
|
+
transitionOrigin !== undefined
|
|
2789
|
+
? transitionOrigin
|
|
2790
|
+
: (_a = visualElement.getValue(key)) === null || _a === void 0 ? void 0 : _a.get();
|
|
3138
2791
|
}
|
|
3139
|
-
|
|
2792
|
+
return origin;
|
|
2793
|
+
}
|
|
2794
|
+
|
|
2795
|
+
function isWillChangeMotionValue(value) {
|
|
2796
|
+
return Boolean(isMotionValue(value) && value.add);
|
|
2797
|
+
}
|
|
2798
|
+
|
|
2799
|
+
const appearStoreId = (id, value) => `${id}: ${value}`;
|
|
2800
|
+
|
|
2801
|
+
function handoffOptimizedAppearAnimation(id, name) {
|
|
2802
|
+
const { MotionAppearAnimations } = window;
|
|
2803
|
+
const animationId = appearStoreId(id, transformProps.has(name) ? "transform" : name);
|
|
2804
|
+
const animation = MotionAppearAnimations && MotionAppearAnimations.get(animationId);
|
|
2805
|
+
if (animation) {
|
|
3140
2806
|
/**
|
|
3141
|
-
*
|
|
3142
|
-
*
|
|
3143
|
-
*
|
|
3144
|
-
*
|
|
3145
|
-
*
|
|
3146
|
-
* animation
|
|
2807
|
+
* We allow the animation to persist until the next frame:
|
|
2808
|
+
* 1. So it continues to play until Framer Motion is ready to render
|
|
2809
|
+
* (avoiding a potential flash of the element's original state)
|
|
2810
|
+
* 2. As all independent transforms share a single transform animation, stopping
|
|
2811
|
+
* it synchronously would prevent subsequent transforms from handing off.
|
|
3147
2812
|
*/
|
|
3148
|
-
|
|
3149
|
-
|
|
3150
|
-
|
|
3151
|
-
|
|
3152
|
-
|
|
3153
|
-
|
|
3154
|
-
|
|
3155
|
-
const checkBoundary = (v) => {
|
|
3156
|
-
prev = current;
|
|
3157
|
-
current = v;
|
|
3158
|
-
velocity = velocityPerSecond(v - prev, frameData.delta);
|
|
3159
|
-
if ((heading === 1 && v > boundary) ||
|
|
3160
|
-
(heading === -1 && v < boundary)) {
|
|
3161
|
-
startSpring({ from: v, to: boundary, velocity });
|
|
2813
|
+
sync.render(() => {
|
|
2814
|
+
/**
|
|
2815
|
+
* Animation.cancel() throws so it needs to be wrapped in a try/catch
|
|
2816
|
+
*/
|
|
2817
|
+
try {
|
|
2818
|
+
animation.cancel();
|
|
2819
|
+
MotionAppearAnimations.delete(animationId);
|
|
3162
2820
|
}
|
|
3163
|
-
|
|
3164
|
-
startAnimation({
|
|
3165
|
-
type: "decay",
|
|
3166
|
-
from,
|
|
3167
|
-
velocity,
|
|
3168
|
-
timeConstant,
|
|
3169
|
-
power,
|
|
3170
|
-
restDelta,
|
|
3171
|
-
modifyTarget,
|
|
3172
|
-
onUpdate: isOutOfBounds(target) ? checkBoundary : undefined,
|
|
2821
|
+
catch (e) { }
|
|
3173
2822
|
});
|
|
2823
|
+
return animation.currentTime || 0;
|
|
3174
2824
|
}
|
|
3175
|
-
|
|
3176
|
-
|
|
3177
|
-
}
|
|
2825
|
+
else {
|
|
2826
|
+
return 0;
|
|
2827
|
+
}
|
|
2828
|
+
}
|
|
2829
|
+
|
|
2830
|
+
const optimizedAppearDataId = "framerAppearId";
|
|
2831
|
+
const optimizedAppearDataAttribute = "data-" + camelToDash(optimizedAppearDataId);
|
|
2832
|
+
|
|
2833
|
+
/**
|
|
2834
|
+
* Converts seconds to milliseconds
|
|
2835
|
+
*
|
|
2836
|
+
* @param seconds - Time in seconds.
|
|
2837
|
+
* @return milliseconds - Converted time in milliseconds.
|
|
2838
|
+
*/
|
|
2839
|
+
const secondsToMilliseconds = (seconds) => seconds * 1000;
|
|
2840
|
+
|
|
2841
|
+
const instantAnimationState = {
|
|
2842
|
+
current: false,
|
|
2843
|
+
};
|
|
2844
|
+
|
|
2845
|
+
/**
|
|
2846
|
+
*
|
|
2847
|
+
*/
|
|
2848
|
+
function createAcceleratedAnimation() {
|
|
2849
|
+
return () => { };
|
|
3178
2850
|
}
|
|
3179
2851
|
|
|
3180
2852
|
/**
|
|
@@ -3193,749 +2865,1124 @@ function delay(callback, timeout) {
|
|
|
3193
2865
|
return () => cancelSync.read(checkElapsed);
|
|
3194
2866
|
}
|
|
3195
2867
|
|
|
3196
|
-
|
|
3197
|
-
|
|
3198
|
-
|
|
3199
|
-
|
|
3200
|
-
|
|
3201
|
-
|
|
3202
|
-
return
|
|
3203
|
-
}
|
|
3204
|
-
|
|
3205
|
-
|
|
3206
|
-
|
|
3207
|
-
|
|
3208
|
-
|
|
3209
|
-
|
|
3210
|
-
|
|
3211
|
-
|
|
3212
|
-
|
|
3213
|
-
|
|
3214
|
-
|
|
3215
|
-
|
|
3216
|
-
|
|
3217
|
-
|
|
3218
|
-
|
|
3219
|
-
|
|
3220
|
-
|
|
3221
|
-
|
|
3222
|
-
|
|
3223
|
-
|
|
3224
|
-
|
|
2868
|
+
function createInstantAnimation({ keyframes, elapsed, onUpdate, onComplete, }) {
|
|
2869
|
+
const setValue = () => {
|
|
2870
|
+
onUpdate && onUpdate(keyframes[keyframes.length - 1]);
|
|
2871
|
+
onComplete && onComplete();
|
|
2872
|
+
return () => { };
|
|
2873
|
+
};
|
|
2874
|
+
return elapsed ? delay(setValue, -elapsed) : setValue();
|
|
2875
|
+
}
|
|
2876
|
+
|
|
2877
|
+
// Accepts an easing function and returns a new one that outputs mirrored values for
|
|
2878
|
+
// the second half of the animation. Turns easeIn into easeInOut.
|
|
2879
|
+
const mirrorEasing = (easing) => (p) => p <= 0.5 ? easing(2 * p) / 2 : (2 - easing(2 * (1 - p))) / 2;
|
|
2880
|
+
|
|
2881
|
+
// Accepts an easing function and returns a new one that outputs reversed values.
|
|
2882
|
+
// Turns easeIn into easeOut.
|
|
2883
|
+
const reverseEasing = (easing) => (p) => 1 - easing(1 - p);
|
|
2884
|
+
|
|
2885
|
+
const easeIn = (p) => p * p;
|
|
2886
|
+
const easeOut = reverseEasing(easeIn);
|
|
2887
|
+
const easeInOut = mirrorEasing(easeIn);
|
|
2888
|
+
|
|
2889
|
+
/*
|
|
2890
|
+
Value in range from progress
|
|
2891
|
+
|
|
2892
|
+
Given a lower limit and an upper limit, we return the value within
|
|
2893
|
+
that range as expressed by progress (usually a number from 0 to 1)
|
|
2894
|
+
|
|
2895
|
+
So progress = 0.5 would change
|
|
2896
|
+
|
|
2897
|
+
from -------- to
|
|
2898
|
+
|
|
2899
|
+
to
|
|
2900
|
+
|
|
2901
|
+
from ---- to
|
|
2902
|
+
|
|
2903
|
+
E.g. from = 10, to = 20, progress = 0.5 => 15
|
|
2904
|
+
|
|
2905
|
+
@param [number]: Lower limit of range
|
|
2906
|
+
@param [number]: Upper limit of range
|
|
2907
|
+
@param [number]: The progress between lower and upper limits expressed 0-1
|
|
2908
|
+
@return [number]: Value as calculated from progress within range (not limited within range)
|
|
2909
|
+
*/
|
|
2910
|
+
const mix = (from, to, progress) => -progress * from + progress * to + from;
|
|
2911
|
+
|
|
2912
|
+
// Adapted from https://gist.github.com/mjackson/5311256
|
|
2913
|
+
function hueToRgb(p, q, t) {
|
|
2914
|
+
if (t < 0)
|
|
2915
|
+
t += 1;
|
|
2916
|
+
if (t > 1)
|
|
2917
|
+
t -= 1;
|
|
2918
|
+
if (t < 1 / 6)
|
|
2919
|
+
return p + (q - p) * 6 * t;
|
|
2920
|
+
if (t < 1 / 2)
|
|
2921
|
+
return q;
|
|
2922
|
+
if (t < 2 / 3)
|
|
2923
|
+
return p + (q - p) * (2 / 3 - t) * 6;
|
|
2924
|
+
return p;
|
|
2925
|
+
}
|
|
2926
|
+
function hslaToRgba({ hue, saturation, lightness, alpha }) {
|
|
2927
|
+
hue /= 360;
|
|
2928
|
+
saturation /= 100;
|
|
2929
|
+
lightness /= 100;
|
|
2930
|
+
let red = 0;
|
|
2931
|
+
let green = 0;
|
|
2932
|
+
let blue = 0;
|
|
2933
|
+
if (!saturation) {
|
|
2934
|
+
red = green = blue = lightness;
|
|
2935
|
+
}
|
|
2936
|
+
else {
|
|
2937
|
+
const q = lightness < 0.5
|
|
2938
|
+
? lightness * (1 + saturation)
|
|
2939
|
+
: lightness + saturation - lightness * saturation;
|
|
2940
|
+
const p = 2 * lightness - q;
|
|
2941
|
+
red = hueToRgb(p, q, hue + 1 / 3);
|
|
2942
|
+
green = hueToRgb(p, q, hue);
|
|
2943
|
+
blue = hueToRgb(p, q, hue - 1 / 3);
|
|
2944
|
+
}
|
|
2945
|
+
return {
|
|
2946
|
+
red: Math.round(red * 255),
|
|
2947
|
+
green: Math.round(green * 255),
|
|
2948
|
+
blue: Math.round(blue * 255),
|
|
2949
|
+
alpha,
|
|
2950
|
+
};
|
|
2951
|
+
}
|
|
2952
|
+
|
|
2953
|
+
// Linear color space blending
|
|
2954
|
+
// Explained https://www.youtube.com/watch?v=LKnqECcg6Gw
|
|
2955
|
+
// Demonstrated http://codepen.io/osublake/pen/xGVVaN
|
|
2956
|
+
const mixLinearColor = (from, to, v) => {
|
|
2957
|
+
const fromExpo = from * from;
|
|
2958
|
+
return Math.sqrt(Math.max(0, v * (to * to - fromExpo) + fromExpo));
|
|
2959
|
+
};
|
|
2960
|
+
const colorTypes = [hex, rgba, hsla];
|
|
2961
|
+
const getColorType = (v) => colorTypes.find((type) => type.test(v));
|
|
2962
|
+
function asRGBA(color) {
|
|
2963
|
+
const type = getColorType(color);
|
|
2964
|
+
heyListen.invariant(Boolean(type), `'${color}' is not an animatable color. Use the equivalent color code instead.`);
|
|
2965
|
+
let model = type.parse(color);
|
|
2966
|
+
if (type === hsla) {
|
|
2967
|
+
// TODO Remove this cast - needed since Framer Motion's stricter typing
|
|
2968
|
+
model = hslaToRgba(model);
|
|
2969
|
+
}
|
|
2970
|
+
return model;
|
|
2971
|
+
}
|
|
2972
|
+
const mixColor = (from, to) => {
|
|
2973
|
+
const fromRGBA = asRGBA(from);
|
|
2974
|
+
const toRGBA = asRGBA(to);
|
|
2975
|
+
const blended = { ...fromRGBA };
|
|
2976
|
+
return (v) => {
|
|
2977
|
+
blended.red = mixLinearColor(fromRGBA.red, toRGBA.red, v);
|
|
2978
|
+
blended.green = mixLinearColor(fromRGBA.green, toRGBA.green, v);
|
|
2979
|
+
blended.blue = mixLinearColor(fromRGBA.blue, toRGBA.blue, v);
|
|
2980
|
+
blended.alpha = mix(fromRGBA.alpha, toRGBA.alpha, v);
|
|
2981
|
+
return rgba.transform(blended);
|
|
2982
|
+
};
|
|
2983
|
+
};
|
|
2984
|
+
|
|
2985
|
+
function getMixer$1(origin, target) {
|
|
2986
|
+
if (typeof origin === "number") {
|
|
2987
|
+
return (v) => mix(origin, target, v);
|
|
2988
|
+
}
|
|
2989
|
+
else if (color.test(origin)) {
|
|
2990
|
+
return mixColor(origin, target);
|
|
2991
|
+
}
|
|
2992
|
+
else {
|
|
2993
|
+
return mixComplex(origin, target);
|
|
2994
|
+
}
|
|
2995
|
+
}
|
|
2996
|
+
const mixArray = (from, to) => {
|
|
2997
|
+
const output = [...from];
|
|
2998
|
+
const numValues = output.length;
|
|
2999
|
+
const blendValue = from.map((fromThis, i) => getMixer$1(fromThis, to[i]));
|
|
3000
|
+
return (v) => {
|
|
3001
|
+
for (let i = 0; i < numValues; i++) {
|
|
3002
|
+
output[i] = blendValue[i](v);
|
|
3003
|
+
}
|
|
3004
|
+
return output;
|
|
3005
|
+
};
|
|
3006
|
+
};
|
|
3007
|
+
const mixObject = (origin, target) => {
|
|
3008
|
+
const output = { ...origin, ...target };
|
|
3009
|
+
const blendValue = {};
|
|
3010
|
+
for (const key in output) {
|
|
3011
|
+
if (origin[key] !== undefined && target[key] !== undefined) {
|
|
3012
|
+
blendValue[key] = getMixer$1(origin[key], target[key]);
|
|
3013
|
+
}
|
|
3014
|
+
}
|
|
3015
|
+
return (v) => {
|
|
3016
|
+
for (const key in blendValue) {
|
|
3017
|
+
output[key] = blendValue[key](v);
|
|
3018
|
+
}
|
|
3019
|
+
return output;
|
|
3020
|
+
};
|
|
3021
|
+
};
|
|
3022
|
+
const mixComplex = (origin, target) => {
|
|
3023
|
+
const template = complex.createTransformer(target);
|
|
3024
|
+
const originStats = analyseComplexValue(origin);
|
|
3025
|
+
const targetStats = analyseComplexValue(target);
|
|
3026
|
+
const canInterpolate = originStats.numColors === targetStats.numColors &&
|
|
3027
|
+
originStats.numNumbers >= targetStats.numNumbers;
|
|
3028
|
+
if (canInterpolate) {
|
|
3029
|
+
return pipe(mixArray(originStats.values, targetStats.values), template);
|
|
3030
|
+
}
|
|
3031
|
+
else {
|
|
3032
|
+
heyListen.warning(true, `Complex values '${origin}' and '${target}' too different to mix. Ensure all colors are of the same type, and that each contains the same quantity of number and color values. Falling back to instant transition.`);
|
|
3033
|
+
return (p) => `${p > 0 ? target : origin}`;
|
|
3034
|
+
}
|
|
3035
|
+
};
|
|
3036
|
+
|
|
3037
|
+
/*
|
|
3038
|
+
Progress within given range
|
|
3039
|
+
|
|
3040
|
+
Given a lower limit and an upper limit, we return the progress
|
|
3041
|
+
(expressed as a number 0-1) represented by the given value, and
|
|
3042
|
+
limit that progress to within 0-1.
|
|
3043
|
+
|
|
3044
|
+
@param [number]: Lower limit
|
|
3045
|
+
@param [number]: Upper limit
|
|
3046
|
+
@param [number]: Value to find progress within given range
|
|
3047
|
+
@return [number]: Progress of value within range as expressed 0-1
|
|
3048
|
+
*/
|
|
3049
|
+
const progress = (from, to, value) => {
|
|
3050
|
+
const toFromDifference = to - from;
|
|
3051
|
+
return toFromDifference === 0 ? 1 : (value - from) / toFromDifference;
|
|
3052
|
+
};
|
|
3053
|
+
|
|
3054
|
+
const mixNumber = (from, to) => (p) => mix(from, to, p);
|
|
3055
|
+
function detectMixerFactory(v) {
|
|
3056
|
+
if (typeof v === "number") {
|
|
3057
|
+
return mixNumber;
|
|
3225
3058
|
}
|
|
3226
|
-
|
|
3227
|
-
|
|
3228
|
-
|
|
3229
|
-
|
|
3230
|
-
|
|
3231
|
-
|
|
3232
|
-
|
|
3233
|
-
* a keyframes or spring animation, but does so by detecting velocity and other spring options.
|
|
3234
|
-
* It'd be good to introduce a similar thing here.
|
|
3235
|
-
*/
|
|
3236
|
-
if (transition.type !== "spring")
|
|
3237
|
-
options.type = "keyframes";
|
|
3238
|
-
return options;
|
|
3239
|
-
}
|
|
3240
|
-
/**
|
|
3241
|
-
* Get the delay for a value by checking Transition with decreasing specificity.
|
|
3242
|
-
*/
|
|
3243
|
-
function getDelayFromTransition(transition, key) {
|
|
3244
|
-
const valueTransition = getValueTransition(transition, key) || {};
|
|
3245
|
-
return valueTransition.delay !== undefined
|
|
3246
|
-
? valueTransition.delay
|
|
3247
|
-
: transition.delay !== undefined
|
|
3248
|
-
? transition.delay
|
|
3249
|
-
: 0;
|
|
3250
|
-
}
|
|
3251
|
-
function hydrateKeyframes(options) {
|
|
3252
|
-
if (Array.isArray(options.to) && options.to[0] === null) {
|
|
3253
|
-
options.to = [...options.to];
|
|
3254
|
-
options.to[0] = options.from;
|
|
3059
|
+
else if (typeof v === "string") {
|
|
3060
|
+
if (color.test(v)) {
|
|
3061
|
+
return mixColor;
|
|
3062
|
+
}
|
|
3063
|
+
else {
|
|
3064
|
+
return mixComplex;
|
|
3065
|
+
}
|
|
3255
3066
|
}
|
|
3256
|
-
|
|
3257
|
-
|
|
3258
|
-
function getPopmotionAnimationOptions(transition, options, key) {
|
|
3259
|
-
if (Array.isArray(options.to) && transition.duration === undefined) {
|
|
3260
|
-
transition.duration = 0.8;
|
|
3067
|
+
else if (Array.isArray(v)) {
|
|
3068
|
+
return mixArray;
|
|
3261
3069
|
}
|
|
3262
|
-
|
|
3263
|
-
|
|
3264
|
-
* Get a default transition if none is determined to be defined.
|
|
3265
|
-
*/
|
|
3266
|
-
if (!isTransitionDefined(transition)) {
|
|
3267
|
-
transition = {
|
|
3268
|
-
...transition,
|
|
3269
|
-
...getDefaultTransition(key, options.to),
|
|
3270
|
-
};
|
|
3070
|
+
else if (typeof v === "object") {
|
|
3071
|
+
return mixObject;
|
|
3271
3072
|
}
|
|
3272
|
-
return
|
|
3273
|
-
|
|
3274
|
-
|
|
3275
|
-
|
|
3073
|
+
return mixNumber;
|
|
3074
|
+
}
|
|
3075
|
+
function createMixers(output, ease, customMixer) {
|
|
3076
|
+
const mixers = [];
|
|
3077
|
+
const mixerFactory = customMixer || detectMixerFactory(output[0]);
|
|
3078
|
+
const numMixers = output.length - 1;
|
|
3079
|
+
for (let i = 0; i < numMixers; i++) {
|
|
3080
|
+
let mixer = mixerFactory(output[i], output[i + 1]);
|
|
3081
|
+
if (ease) {
|
|
3082
|
+
const easingFunction = Array.isArray(ease) ? ease[i] : ease;
|
|
3083
|
+
mixer = pipe(easingFunction, mixer);
|
|
3084
|
+
}
|
|
3085
|
+
mixers.push(mixer);
|
|
3086
|
+
}
|
|
3087
|
+
return mixers;
|
|
3276
3088
|
}
|
|
3277
3089
|
/**
|
|
3090
|
+
* Create a function that maps from a numerical input array to a generic output array.
|
|
3091
|
+
*
|
|
3092
|
+
* Accepts:
|
|
3093
|
+
* - Numbers
|
|
3094
|
+
* - Colors (hex, hsl, hsla, rgb, rgba)
|
|
3095
|
+
* - Complex (combinations of one or more numbers or strings)
|
|
3096
|
+
*
|
|
3097
|
+
* ```jsx
|
|
3098
|
+
* const mixColor = interpolate([0, 1], ['#fff', '#000'])
|
|
3099
|
+
*
|
|
3100
|
+
* mixColor(0.5) // 'rgba(128, 128, 128, 1)'
|
|
3101
|
+
* ```
|
|
3278
3102
|
*
|
|
3103
|
+
* TODO Revist this approach once we've moved to data models for values,
|
|
3104
|
+
* probably not needed to pregenerate mixer functions.
|
|
3105
|
+
*
|
|
3106
|
+
* @public
|
|
3279
3107
|
*/
|
|
3280
|
-
function
|
|
3281
|
-
const
|
|
3282
|
-
|
|
3283
|
-
|
|
3284
|
-
|
|
3285
|
-
|
|
3286
|
-
|
|
3287
|
-
|
|
3288
|
-
/**
|
|
3289
|
-
* If we're trying to animate from "none", try and get an animatable version
|
|
3290
|
-
* of the target. This could be improved to work both ways.
|
|
3291
|
-
*/
|
|
3292
|
-
origin = getAnimatableNone(key, target);
|
|
3293
|
-
}
|
|
3294
|
-
else if (isZero(origin) && typeof target === "string") {
|
|
3295
|
-
origin = getZeroUnit(target);
|
|
3296
|
-
}
|
|
3297
|
-
else if (!Array.isArray(target) &&
|
|
3298
|
-
isZero(target) &&
|
|
3299
|
-
typeof origin === "string") {
|
|
3300
|
-
target = getZeroUnit(origin);
|
|
3301
|
-
}
|
|
3302
|
-
const isOriginAnimatable = isAnimatable(key, origin);
|
|
3303
|
-
heyListen.warning(isOriginAnimatable === isTargetAnimatable, `You are trying to animate ${key} from "${origin}" to "${target}". ${origin} is not an animatable value - to enable this animation set ${origin} to a value animatable to ${target} via the \`style\` property.`);
|
|
3304
|
-
function start() {
|
|
3305
|
-
const options = {
|
|
3306
|
-
from: origin,
|
|
3307
|
-
to: target,
|
|
3308
|
-
velocity: value.getVelocity(),
|
|
3309
|
-
onComplete,
|
|
3310
|
-
onUpdate: (v) => value.set(v),
|
|
3311
|
-
};
|
|
3312
|
-
const animation = valueTransition.type === "inertia" ||
|
|
3313
|
-
valueTransition.type === "decay"
|
|
3314
|
-
? inertia({ ...options, ...valueTransition })
|
|
3315
|
-
: animate$1({
|
|
3316
|
-
...getPopmotionAnimationOptions(valueTransition, options, key),
|
|
3317
|
-
onUpdate: (v) => {
|
|
3318
|
-
options.onUpdate(v);
|
|
3319
|
-
valueTransition.onUpdate &&
|
|
3320
|
-
valueTransition.onUpdate(v);
|
|
3321
|
-
},
|
|
3322
|
-
onComplete: () => {
|
|
3323
|
-
options.onComplete();
|
|
3324
|
-
valueTransition.onComplete &&
|
|
3325
|
-
valueTransition.onComplete();
|
|
3326
|
-
},
|
|
3327
|
-
});
|
|
3328
|
-
return () => animation.stop();
|
|
3329
|
-
}
|
|
3330
|
-
function set() {
|
|
3331
|
-
const finalTarget = resolveFinalValueInKeyframes(target);
|
|
3332
|
-
value.set(finalTarget);
|
|
3333
|
-
onComplete();
|
|
3334
|
-
valueTransition.onUpdate && valueTransition.onUpdate(finalTarget);
|
|
3335
|
-
valueTransition.onComplete && valueTransition.onComplete();
|
|
3336
|
-
return () => { };
|
|
3108
|
+
function interpolate(input, output, { clamp: isClamp = true, ease, mixer } = {}) {
|
|
3109
|
+
const inputLength = input.length;
|
|
3110
|
+
heyListen.invariant(inputLength === output.length, "Both input and output ranges must be the same length");
|
|
3111
|
+
heyListen.invariant(!ease || !Array.isArray(ease) || ease.length === inputLength - 1, "Array of easing functions must be of length `input.length - 1`, as it applies to the transitions **between** the defined values.");
|
|
3112
|
+
// If input runs highest -> lowest, reverse both arrays
|
|
3113
|
+
if (input[0] > input[inputLength - 1]) {
|
|
3114
|
+
input = [...input].reverse();
|
|
3115
|
+
output = [...output].reverse();
|
|
3337
3116
|
}
|
|
3338
|
-
const
|
|
3339
|
-
|
|
3340
|
-
|
|
3341
|
-
|
|
3342
|
-
|
|
3343
|
-
|
|
3344
|
-
|
|
3345
|
-
|
|
3346
|
-
}
|
|
3347
|
-
|
|
3348
|
-
|
|
3349
|
-
(
|
|
3350
|
-
|
|
3351
|
-
|
|
3352
|
-
|
|
3353
|
-
|
|
3354
|
-
return typeof potentialUnitType === "number"
|
|
3355
|
-
? 0
|
|
3356
|
-
: getAnimatableNone("", potentialUnitType);
|
|
3117
|
+
const mixers = createMixers(output, ease, mixer);
|
|
3118
|
+
const numMixers = mixers.length;
|
|
3119
|
+
const interpolator = (v) => {
|
|
3120
|
+
let i = 0;
|
|
3121
|
+
if (numMixers > 1) {
|
|
3122
|
+
for (; i < input.length - 2; i++) {
|
|
3123
|
+
if (v < input[i + 1])
|
|
3124
|
+
break;
|
|
3125
|
+
}
|
|
3126
|
+
}
|
|
3127
|
+
const progressInRange = progress(input[i], input[i + 1], v);
|
|
3128
|
+
return mixers[i](progressInRange);
|
|
3129
|
+
};
|
|
3130
|
+
return isClamp
|
|
3131
|
+
? (v) => interpolator(clamp(input[0], input[inputLength - 1], v))
|
|
3132
|
+
: interpolator;
|
|
3357
3133
|
}
|
|
3358
|
-
|
|
3359
|
-
|
|
3134
|
+
|
|
3135
|
+
const noop = (any) => any;
|
|
3136
|
+
|
|
3137
|
+
/*
|
|
3138
|
+
Bezier function generator
|
|
3139
|
+
This has been modified from Gaëtan Renaudeau's BezierEasing
|
|
3140
|
+
https://github.com/gre/bezier-easing/blob/master/src/index.js
|
|
3141
|
+
https://github.com/gre/bezier-easing/blob/master/LICENSE
|
|
3142
|
+
|
|
3143
|
+
I've removed the newtonRaphsonIterate algo because in benchmarking it
|
|
3144
|
+
wasn't noticiably faster than binarySubdivision, indeed removing it
|
|
3145
|
+
usually improved times, depending on the curve.
|
|
3146
|
+
I also removed the lookup table, as for the added bundle size and loop we're
|
|
3147
|
+
only cutting ~4 or so subdivision iterations. I bumped the max iterations up
|
|
3148
|
+
to 12 to compensate and this still tended to be faster for no perceivable
|
|
3149
|
+
loss in accuracy.
|
|
3150
|
+
Usage
|
|
3151
|
+
const easeOut = cubicBezier(.17,.67,.83,.67);
|
|
3152
|
+
const x = easeOut(0.5); // returns 0.627...
|
|
3153
|
+
*/
|
|
3154
|
+
// Returns x(t) given t, x1, and x2, or y(t) given t, y1, and y2.
|
|
3155
|
+
const calcBezier = (t, a1, a2) => (((1.0 - 3.0 * a2 + 3.0 * a1) * t + (3.0 * a2 - 6.0 * a1)) * t + 3.0 * a1) *
|
|
3156
|
+
t;
|
|
3157
|
+
const subdivisionPrecision = 0.0000001;
|
|
3158
|
+
const subdivisionMaxIterations = 12;
|
|
3159
|
+
function binarySubdivide(x, lowerBound, upperBound, mX1, mX2) {
|
|
3160
|
+
let currentX;
|
|
3161
|
+
let currentT;
|
|
3162
|
+
let i = 0;
|
|
3163
|
+
do {
|
|
3164
|
+
currentT = lowerBound + (upperBound - lowerBound) / 2.0;
|
|
3165
|
+
currentX = calcBezier(currentT, mX1, mX2) - x;
|
|
3166
|
+
if (currentX > 0.0) {
|
|
3167
|
+
upperBound = currentT;
|
|
3168
|
+
}
|
|
3169
|
+
else {
|
|
3170
|
+
lowerBound = currentT;
|
|
3171
|
+
}
|
|
3172
|
+
} while (Math.abs(currentX) > subdivisionPrecision &&
|
|
3173
|
+
++i < subdivisionMaxIterations);
|
|
3174
|
+
return currentT;
|
|
3360
3175
|
}
|
|
3361
|
-
|
|
3362
|
-
|
|
3363
|
-
|
|
3364
|
-
|
|
3365
|
-
|
|
3366
|
-
|
|
3367
|
-
|
|
3368
|
-
}
|
|
3369
|
-
return value.start((onComplete) => {
|
|
3370
|
-
return getAnimation(key, value, target, { ...transition, delay: getDelayFromTransition(transition, key) }, onComplete);
|
|
3371
|
-
});
|
|
3176
|
+
function cubicBezier(mX1, mY1, mX2, mY2) {
|
|
3177
|
+
// If this is a linear gradient, return linear easing
|
|
3178
|
+
if (mX1 === mY1 && mX2 === mY2)
|
|
3179
|
+
return noop;
|
|
3180
|
+
const getTForX = (aX) => binarySubdivide(aX, 0, 1, mX1, mX2);
|
|
3181
|
+
// If animation is at start/end, return t without easing
|
|
3182
|
+
return (t) => t === 0 || t === 1 ? t : calcBezier(getTForX(t), mY1, mY2);
|
|
3372
3183
|
}
|
|
3373
3184
|
|
|
3374
|
-
|
|
3375
|
-
|
|
3376
|
-
|
|
3377
|
-
const isNumericalString = (v) => /^\-?\d*\.?\d+$/.test(v);
|
|
3185
|
+
const circIn = (p) => 1 - Math.sin(Math.acos(p));
|
|
3186
|
+
const circOut = reverseEasing(circIn);
|
|
3187
|
+
const circInOut = mirrorEasing(circOut);
|
|
3378
3188
|
|
|
3379
|
-
|
|
3380
|
-
|
|
3381
|
-
|
|
3382
|
-
const
|
|
3189
|
+
const createBackIn = (power = 1.525) => (p) => p * p * ((power + 1) * p - power);
|
|
3190
|
+
const backIn = createBackIn();
|
|
3191
|
+
const backOut = reverseEasing(backIn);
|
|
3192
|
+
const backInOut = mirrorEasing(backIn);
|
|
3383
3193
|
|
|
3384
|
-
|
|
3385
|
-
|
|
3386
|
-
|
|
3194
|
+
const createAnticipate = (power) => {
|
|
3195
|
+
const backEasing = createBackIn(power);
|
|
3196
|
+
return (p) => (p *= 2) < 1
|
|
3197
|
+
? 0.5 * backEasing(p)
|
|
3198
|
+
: 0.5 * (2 - Math.pow(2, -10 * (p - 1)));
|
|
3199
|
+
};
|
|
3200
|
+
const anticipate = createAnticipate();
|
|
3201
|
+
|
|
3202
|
+
const easingLookup = {
|
|
3203
|
+
linear: noop,
|
|
3204
|
+
easeIn,
|
|
3205
|
+
easeInOut,
|
|
3206
|
+
easeOut,
|
|
3207
|
+
circIn,
|
|
3208
|
+
circInOut,
|
|
3209
|
+
circOut,
|
|
3210
|
+
backIn,
|
|
3211
|
+
backInOut,
|
|
3212
|
+
backOut,
|
|
3213
|
+
anticipate,
|
|
3214
|
+
};
|
|
3215
|
+
const easingDefinitionToFunction = (definition) => {
|
|
3216
|
+
if (Array.isArray(definition)) {
|
|
3217
|
+
// If cubic bezier definition, create bezier curve
|
|
3218
|
+
heyListen.invariant(definition.length === 4, `Cubic bezier arrays must contain four numerical values.`);
|
|
3219
|
+
const [x1, y1, x2, y2] = definition;
|
|
3220
|
+
return cubicBezier(x1, y1, x2, y2);
|
|
3221
|
+
}
|
|
3222
|
+
else if (typeof definition === "string") {
|
|
3223
|
+
// Else lookup from table
|
|
3224
|
+
heyListen.invariant(easingLookup[definition] !== undefined, `Invalid easing type '${definition}'`);
|
|
3225
|
+
return easingLookup[definition];
|
|
3226
|
+
}
|
|
3227
|
+
return definition;
|
|
3228
|
+
};
|
|
3229
|
+
const isEasingArray = (ease) => {
|
|
3230
|
+
return Array.isArray(ease) && typeof ease[0] !== "number";
|
|
3231
|
+
};
|
|
3232
|
+
|
|
3233
|
+
function defaultEasing(values, easing) {
|
|
3234
|
+
return values.map(() => easing || easeInOut).splice(0, values.length - 1);
|
|
3387
3235
|
}
|
|
3388
|
-
function
|
|
3389
|
-
const
|
|
3390
|
-
|
|
3391
|
-
arr.splice(index, 1);
|
|
3236
|
+
function defaultOffset(values) {
|
|
3237
|
+
const numValues = values.length;
|
|
3238
|
+
return values.map((_value, i) => i !== 0 ? i / (numValues - 1) : 0);
|
|
3392
3239
|
}
|
|
3393
|
-
|
|
3394
|
-
|
|
3395
|
-
const startIndex = fromIndex < 0 ? arr.length + fromIndex : fromIndex;
|
|
3396
|
-
if (startIndex >= 0 && startIndex < arr.length) {
|
|
3397
|
-
const endIndex = toIndex < 0 ? arr.length + toIndex : toIndex;
|
|
3398
|
-
const [item] = arr.splice(fromIndex, 1);
|
|
3399
|
-
arr.splice(endIndex, 0, item);
|
|
3400
|
-
}
|
|
3401
|
-
return arr;
|
|
3240
|
+
function convertOffsetToTimes(offset, duration) {
|
|
3241
|
+
return offset.map((o) => o * duration);
|
|
3402
3242
|
}
|
|
3403
|
-
|
|
3404
|
-
|
|
3405
|
-
|
|
3406
|
-
|
|
3407
|
-
|
|
3408
|
-
|
|
3409
|
-
|
|
3410
|
-
|
|
3411
|
-
|
|
3412
|
-
|
|
3413
|
-
|
|
3414
|
-
|
|
3415
|
-
|
|
3416
|
-
|
|
3417
|
-
|
|
3418
|
-
|
|
3419
|
-
|
|
3420
|
-
|
|
3421
|
-
|
|
3422
|
-
|
|
3423
|
-
|
|
3424
|
-
|
|
3425
|
-
|
|
3426
|
-
|
|
3427
|
-
|
|
3428
|
-
|
|
3429
|
-
|
|
3430
|
-
|
|
3431
|
-
|
|
3432
|
-
|
|
3433
|
-
getSize() {
|
|
3434
|
-
return this.subscriptions.length;
|
|
3435
|
-
}
|
|
3436
|
-
clear() {
|
|
3437
|
-
this.subscriptions.length = 0;
|
|
3243
|
+
function keyframes({ keyframes: keyframeValues, ease = easeInOut, times, duration = 300, }) {
|
|
3244
|
+
keyframeValues = [...keyframeValues];
|
|
3245
|
+
const origin = keyframes[0];
|
|
3246
|
+
/**
|
|
3247
|
+
* Easing functions can be externally defined as strings. Here we convert them
|
|
3248
|
+
* into actual functions.
|
|
3249
|
+
*/
|
|
3250
|
+
const easingFunctions = isEasingArray(ease)
|
|
3251
|
+
? ease.map(easingDefinitionToFunction)
|
|
3252
|
+
: easingDefinitionToFunction(ease);
|
|
3253
|
+
/**
|
|
3254
|
+
* This is the Iterator-spec return value. We ensure it's mutable rather than using a generator
|
|
3255
|
+
* to reduce GC during animation.
|
|
3256
|
+
*/
|
|
3257
|
+
const state = { done: false, value: origin };
|
|
3258
|
+
/**
|
|
3259
|
+
* Create a times array based on the provided 0-1 offsets
|
|
3260
|
+
*/
|
|
3261
|
+
const absoluteTimes = convertOffsetToTimes(
|
|
3262
|
+
// Only use the provided offsets if they're the correct length
|
|
3263
|
+
// TODO Maybe we should warn here if there's a length mismatch
|
|
3264
|
+
times && times.length === keyframes.length
|
|
3265
|
+
? times
|
|
3266
|
+
: defaultOffset(keyframeValues), duration);
|
|
3267
|
+
function createInterpolator() {
|
|
3268
|
+
return interpolate(absoluteTimes, keyframeValues, {
|
|
3269
|
+
ease: Array.isArray(easingFunctions)
|
|
3270
|
+
? easingFunctions
|
|
3271
|
+
: defaultEasing(keyframeValues, easingFunctions),
|
|
3272
|
+
});
|
|
3438
3273
|
}
|
|
3274
|
+
let interpolator = createInterpolator();
|
|
3275
|
+
return {
|
|
3276
|
+
next: (t) => {
|
|
3277
|
+
state.value = interpolator(t);
|
|
3278
|
+
state.done = t >= duration;
|
|
3279
|
+
return state;
|
|
3280
|
+
},
|
|
3281
|
+
flipTarget: () => {
|
|
3282
|
+
keyframeValues.reverse();
|
|
3283
|
+
interpolator = createInterpolator();
|
|
3284
|
+
},
|
|
3285
|
+
};
|
|
3439
3286
|
}
|
|
3440
3287
|
|
|
3441
|
-
const
|
|
3442
|
-
|
|
3443
|
-
|
|
3444
|
-
|
|
3445
|
-
|
|
3446
|
-
|
|
3447
|
-
|
|
3448
|
-
|
|
3449
|
-
|
|
3288
|
+
const safeMin = 0.001;
|
|
3289
|
+
const minDuration = 0.01;
|
|
3290
|
+
const maxDuration = 10.0;
|
|
3291
|
+
const minDamping = 0.05;
|
|
3292
|
+
const maxDamping = 1;
|
|
3293
|
+
function findSpring({ duration = 800, bounce = 0.25, velocity = 0, mass = 1, }) {
|
|
3294
|
+
let envelope;
|
|
3295
|
+
let derivative;
|
|
3296
|
+
heyListen.warning(duration <= maxDuration * 1000, "Spring duration must be 10 seconds or less");
|
|
3297
|
+
let dampingRatio = 1 - bounce;
|
|
3450
3298
|
/**
|
|
3451
|
-
*
|
|
3452
|
-
* @param config - Optional configuration options
|
|
3453
|
-
*
|
|
3454
|
-
* - `transformer`: A function to transform incoming values with.
|
|
3455
|
-
*
|
|
3456
|
-
* @internal
|
|
3299
|
+
* Restrict dampingRatio and duration to within acceptable ranges.
|
|
3457
3300
|
*/
|
|
3458
|
-
|
|
3459
|
-
|
|
3460
|
-
|
|
3461
|
-
* When MotionValues are provided to motion components, warn if versions are mixed.
|
|
3462
|
-
*/
|
|
3463
|
-
this.version = "7.8.0";
|
|
3464
|
-
/**
|
|
3465
|
-
* Duration, in milliseconds, since last updating frame.
|
|
3466
|
-
*
|
|
3467
|
-
* @internal
|
|
3468
|
-
*/
|
|
3469
|
-
this.timeDelta = 0;
|
|
3470
|
-
/**
|
|
3471
|
-
* Timestamp of the last time this `MotionValue` was updated.
|
|
3472
|
-
*
|
|
3473
|
-
* @internal
|
|
3474
|
-
*/
|
|
3475
|
-
this.lastUpdated = 0;
|
|
3476
|
-
/**
|
|
3477
|
-
* Functions to notify when the `MotionValue` updates.
|
|
3478
|
-
*
|
|
3479
|
-
* @internal
|
|
3480
|
-
*/
|
|
3481
|
-
this.updateSubscribers = new SubscriptionManager();
|
|
3482
|
-
/**
|
|
3483
|
-
* Functions to notify when the velocity updates.
|
|
3484
|
-
*
|
|
3485
|
-
* @internal
|
|
3486
|
-
*/
|
|
3487
|
-
this.velocityUpdateSubscribers = new SubscriptionManager();
|
|
3488
|
-
/**
|
|
3489
|
-
* Functions to notify when the `MotionValue` updates and `render` is set to `true`.
|
|
3490
|
-
*
|
|
3491
|
-
* @internal
|
|
3492
|
-
*/
|
|
3493
|
-
this.renderSubscribers = new SubscriptionManager();
|
|
3301
|
+
dampingRatio = clamp(minDamping, maxDamping, dampingRatio);
|
|
3302
|
+
duration = clamp(minDuration, maxDuration, duration / 1000);
|
|
3303
|
+
if (dampingRatio < 1) {
|
|
3494
3304
|
/**
|
|
3495
|
-
*
|
|
3496
|
-
* if the value is numerical, but we might be able to widen the scope here and support
|
|
3497
|
-
* other value types.
|
|
3498
|
-
*
|
|
3499
|
-
* @internal
|
|
3305
|
+
* Underdamped spring
|
|
3500
3306
|
*/
|
|
3501
|
-
|
|
3502
|
-
|
|
3503
|
-
|
|
3504
|
-
|
|
3505
|
-
|
|
3506
|
-
const
|
|
3507
|
-
|
|
3508
|
-
this.timeDelta = delta;
|
|
3509
|
-
this.lastUpdated = timestamp;
|
|
3510
|
-
sync.postRender(this.scheduleVelocityCheck);
|
|
3511
|
-
}
|
|
3512
|
-
// Update update subscribers
|
|
3513
|
-
if (this.prev !== this.current) {
|
|
3514
|
-
this.updateSubscribers.notify(this.current);
|
|
3515
|
-
}
|
|
3516
|
-
// Update velocity subscribers
|
|
3517
|
-
if (this.velocityUpdateSubscribers.getSize()) {
|
|
3518
|
-
this.velocityUpdateSubscribers.notify(this.getVelocity());
|
|
3519
|
-
}
|
|
3520
|
-
// Update render subscribers
|
|
3521
|
-
if (render) {
|
|
3522
|
-
this.renderSubscribers.notify(this.current);
|
|
3523
|
-
}
|
|
3307
|
+
envelope = (undampedFreq) => {
|
|
3308
|
+
const exponentialDecay = undampedFreq * dampingRatio;
|
|
3309
|
+
const delta = exponentialDecay * duration;
|
|
3310
|
+
const a = exponentialDecay - velocity;
|
|
3311
|
+
const b = calcAngularFreq(undampedFreq, dampingRatio);
|
|
3312
|
+
const c = Math.exp(-delta);
|
|
3313
|
+
return safeMin - (a / b) * c;
|
|
3524
3314
|
};
|
|
3315
|
+
derivative = (undampedFreq) => {
|
|
3316
|
+
const exponentialDecay = undampedFreq * dampingRatio;
|
|
3317
|
+
const delta = exponentialDecay * duration;
|
|
3318
|
+
const d = delta * velocity + velocity;
|
|
3319
|
+
const e = Math.pow(dampingRatio, 2) * Math.pow(undampedFreq, 2) * duration;
|
|
3320
|
+
const f = Math.exp(-delta);
|
|
3321
|
+
const g = calcAngularFreq(Math.pow(undampedFreq, 2), dampingRatio);
|
|
3322
|
+
const factor = -envelope(undampedFreq) + safeMin > 0 ? -1 : 1;
|
|
3323
|
+
return (factor * ((d - e) * f)) / g;
|
|
3324
|
+
};
|
|
3325
|
+
}
|
|
3326
|
+
else {
|
|
3525
3327
|
/**
|
|
3526
|
-
*
|
|
3527
|
-
*
|
|
3528
|
-
* This is an instanced and bound function to prevent generating a new
|
|
3529
|
-
* function once per frame.
|
|
3530
|
-
*
|
|
3531
|
-
* @internal
|
|
3532
|
-
*/
|
|
3533
|
-
this.scheduleVelocityCheck = () => sync.postRender(this.velocityCheck);
|
|
3534
|
-
/**
|
|
3535
|
-
* Updates `prev` with `current` if the value hasn't been updated this frame.
|
|
3536
|
-
* This ensures velocity calculations return `0`.
|
|
3537
|
-
*
|
|
3538
|
-
* This is an instanced and bound function to prevent generating a new
|
|
3539
|
-
* function once per frame.
|
|
3540
|
-
*
|
|
3541
|
-
* @internal
|
|
3328
|
+
* Critically-damped spring
|
|
3542
3329
|
*/
|
|
3543
|
-
|
|
3544
|
-
|
|
3545
|
-
|
|
3546
|
-
|
|
3547
|
-
|
|
3330
|
+
envelope = (undampedFreq) => {
|
|
3331
|
+
const a = Math.exp(-undampedFreq * duration);
|
|
3332
|
+
const b = (undampedFreq - velocity) * duration + 1;
|
|
3333
|
+
return -safeMin + a * b;
|
|
3334
|
+
};
|
|
3335
|
+
derivative = (undampedFreq) => {
|
|
3336
|
+
const a = Math.exp(-undampedFreq * duration);
|
|
3337
|
+
const b = (velocity - undampedFreq) * (duration * duration);
|
|
3338
|
+
return a * b;
|
|
3339
|
+
};
|
|
3340
|
+
}
|
|
3341
|
+
const initialGuess = 5 / duration;
|
|
3342
|
+
const undampedFreq = approximateRoot(envelope, derivative, initialGuess);
|
|
3343
|
+
duration = duration * 1000;
|
|
3344
|
+
if (isNaN(undampedFreq)) {
|
|
3345
|
+
return {
|
|
3346
|
+
stiffness: 100,
|
|
3347
|
+
damping: 10,
|
|
3348
|
+
duration,
|
|
3349
|
+
};
|
|
3350
|
+
}
|
|
3351
|
+
else {
|
|
3352
|
+
const stiffness = Math.pow(undampedFreq, 2) * mass;
|
|
3353
|
+
return {
|
|
3354
|
+
stiffness,
|
|
3355
|
+
damping: dampingRatio * 2 * Math.sqrt(mass * stiffness),
|
|
3356
|
+
duration,
|
|
3357
|
+
};
|
|
3358
|
+
}
|
|
3359
|
+
}
|
|
3360
|
+
const rootIterations = 12;
|
|
3361
|
+
function approximateRoot(envelope, derivative, initialGuess) {
|
|
3362
|
+
let result = initialGuess;
|
|
3363
|
+
for (let i = 1; i < rootIterations; i++) {
|
|
3364
|
+
result = result - envelope(result) / derivative(result);
|
|
3365
|
+
}
|
|
3366
|
+
return result;
|
|
3367
|
+
}
|
|
3368
|
+
function calcAngularFreq(undampedFreq, dampingRatio) {
|
|
3369
|
+
return undampedFreq * Math.sqrt(1 - dampingRatio * dampingRatio);
|
|
3370
|
+
}
|
|
3371
|
+
|
|
3372
|
+
const durationKeys = ["duration", "bounce"];
|
|
3373
|
+
const physicsKeys = ["stiffness", "damping", "mass"];
|
|
3374
|
+
function isSpringType(options, keys) {
|
|
3375
|
+
return keys.some((key) => options[key] !== undefined);
|
|
3376
|
+
}
|
|
3377
|
+
function getSpringOptions(options) {
|
|
3378
|
+
let springOptions = {
|
|
3379
|
+
velocity: 0.0,
|
|
3380
|
+
stiffness: 100,
|
|
3381
|
+
damping: 10,
|
|
3382
|
+
mass: 1.0,
|
|
3383
|
+
isResolvedFromDuration: false,
|
|
3384
|
+
...options,
|
|
3385
|
+
};
|
|
3386
|
+
// stiffness/damping/mass overrides duration/bounce
|
|
3387
|
+
if (!isSpringType(options, physicsKeys) &&
|
|
3388
|
+
isSpringType(options, durationKeys)) {
|
|
3389
|
+
const derived = findSpring(options);
|
|
3390
|
+
springOptions = {
|
|
3391
|
+
...springOptions,
|
|
3392
|
+
...derived,
|
|
3393
|
+
velocity: 0.0,
|
|
3394
|
+
mass: 1.0,
|
|
3548
3395
|
};
|
|
3549
|
-
|
|
3550
|
-
this.prev = this.current = init;
|
|
3551
|
-
this.canTrackVelocity = isFloat(this.current);
|
|
3396
|
+
springOptions.isResolvedFromDuration = true;
|
|
3552
3397
|
}
|
|
3398
|
+
return springOptions;
|
|
3399
|
+
}
|
|
3400
|
+
const velocitySampleDuration = 5;
|
|
3401
|
+
/**
|
|
3402
|
+
* This is based on the spring implementation of Wobble https://github.com/skevy/wobble
|
|
3403
|
+
*/
|
|
3404
|
+
function spring({ keyframes, restSpeed = 2, restDelta = 0.01, ...options }) {
|
|
3405
|
+
let origin = keyframes[0];
|
|
3406
|
+
let target = keyframes[keyframes.length - 1];
|
|
3553
3407
|
/**
|
|
3554
|
-
*
|
|
3555
|
-
*
|
|
3556
|
-
* It returns a function that, when called, will cancel the subscription.
|
|
3557
|
-
*
|
|
3558
|
-
* When calling `onChange` inside a React component, it should be wrapped with the
|
|
3559
|
-
* `useEffect` hook. As it returns an unsubscribe function, this should be returned
|
|
3560
|
-
* from the `useEffect` function to ensure you don't add duplicate subscribers..
|
|
3561
|
-
*
|
|
3562
|
-
* ```jsx
|
|
3563
|
-
* export const MyComponent = () => {
|
|
3564
|
-
* const x = useMotionValue(0)
|
|
3565
|
-
* const y = useMotionValue(0)
|
|
3566
|
-
* const opacity = useMotionValue(1)
|
|
3567
|
-
*
|
|
3568
|
-
* useEffect(() => {
|
|
3569
|
-
* function updateOpacity() {
|
|
3570
|
-
* const maxXY = Math.max(x.get(), y.get())
|
|
3571
|
-
* const newOpacity = transform(maxXY, [0, 100], [1, 0])
|
|
3572
|
-
* opacity.set(newOpacity)
|
|
3573
|
-
* }
|
|
3574
|
-
*
|
|
3575
|
-
* const unsubscribeX = x.onChange(updateOpacity)
|
|
3576
|
-
* const unsubscribeY = y.onChange(updateOpacity)
|
|
3577
|
-
*
|
|
3578
|
-
* return () => {
|
|
3579
|
-
* unsubscribeX()
|
|
3580
|
-
* unsubscribeY()
|
|
3581
|
-
* }
|
|
3582
|
-
* }, [])
|
|
3583
|
-
*
|
|
3584
|
-
* return <motion.div style={{ x }} />
|
|
3585
|
-
* }
|
|
3586
|
-
* ```
|
|
3587
|
-
*
|
|
3588
|
-
* @privateRemarks
|
|
3589
|
-
*
|
|
3590
|
-
* We could look into a `useOnChange` hook if the above lifecycle management proves confusing.
|
|
3591
|
-
*
|
|
3592
|
-
* ```jsx
|
|
3593
|
-
* useOnChange(x, () => {})
|
|
3594
|
-
* ```
|
|
3595
|
-
*
|
|
3596
|
-
* @param subscriber - A function that receives the latest value.
|
|
3597
|
-
* @returns A function that, when called, will cancel this subscription.
|
|
3598
|
-
*
|
|
3599
|
-
* @public
|
|
3408
|
+
* This is the Iterator-spec return value. We ensure it's mutable rather than using a generator
|
|
3409
|
+
* to reduce GC during animation.
|
|
3600
3410
|
*/
|
|
3601
|
-
|
|
3602
|
-
|
|
3603
|
-
|
|
3604
|
-
|
|
3605
|
-
|
|
3411
|
+
const state = { done: false, value: origin };
|
|
3412
|
+
const { stiffness, damping, mass, velocity, duration, isResolvedFromDuration, } = getSpringOptions(options);
|
|
3413
|
+
let resolveSpring = zero;
|
|
3414
|
+
let initialVelocity = velocity ? -(velocity / 1000) : 0.0;
|
|
3415
|
+
const dampingRatio = damping / (2 * Math.sqrt(stiffness * mass));
|
|
3416
|
+
function createSpring() {
|
|
3417
|
+
const initialDelta = target - origin;
|
|
3418
|
+
const undampedAngularFreq = Math.sqrt(stiffness / mass) / 1000;
|
|
3419
|
+
/**
|
|
3420
|
+
* If we're working within what looks like a 0-1 range, change the default restDelta
|
|
3421
|
+
* to 0.01
|
|
3422
|
+
*/
|
|
3423
|
+
if (restDelta === undefined) {
|
|
3424
|
+
restDelta = Math.min(Math.abs(target - origin) / 100, 0.4);
|
|
3425
|
+
}
|
|
3426
|
+
if (dampingRatio < 1) {
|
|
3427
|
+
const angularFreq = calcAngularFreq(undampedAngularFreq, dampingRatio);
|
|
3428
|
+
// Underdamped spring
|
|
3429
|
+
resolveSpring = (t) => {
|
|
3430
|
+
const envelope = Math.exp(-dampingRatio * undampedAngularFreq * t);
|
|
3431
|
+
return (target -
|
|
3432
|
+
envelope *
|
|
3433
|
+
(((initialVelocity +
|
|
3434
|
+
dampingRatio * undampedAngularFreq * initialDelta) /
|
|
3435
|
+
angularFreq) *
|
|
3436
|
+
Math.sin(angularFreq * t) +
|
|
3437
|
+
initialDelta * Math.cos(angularFreq * t)));
|
|
3438
|
+
};
|
|
3439
|
+
}
|
|
3440
|
+
else if (dampingRatio === 1) {
|
|
3441
|
+
// Critically damped spring
|
|
3442
|
+
resolveSpring = (t) => target -
|
|
3443
|
+
Math.exp(-undampedAngularFreq * t) *
|
|
3444
|
+
(initialDelta +
|
|
3445
|
+
(initialVelocity + undampedAngularFreq * initialDelta) *
|
|
3446
|
+
t);
|
|
3447
|
+
}
|
|
3448
|
+
else {
|
|
3449
|
+
// Overdamped spring
|
|
3450
|
+
const dampedAngularFreq = undampedAngularFreq * Math.sqrt(dampingRatio * dampingRatio - 1);
|
|
3451
|
+
resolveSpring = (t) => {
|
|
3452
|
+
const envelope = Math.exp(-dampingRatio * undampedAngularFreq * t);
|
|
3453
|
+
// When performing sinh or cosh values can hit Infinity so we cap them here
|
|
3454
|
+
const freqForT = Math.min(dampedAngularFreq * t, 300);
|
|
3455
|
+
return (target -
|
|
3456
|
+
(envelope *
|
|
3457
|
+
((initialVelocity +
|
|
3458
|
+
dampingRatio * undampedAngularFreq * initialDelta) *
|
|
3459
|
+
Math.sinh(freqForT) +
|
|
3460
|
+
dampedAngularFreq *
|
|
3461
|
+
initialDelta *
|
|
3462
|
+
Math.cosh(freqForT))) /
|
|
3463
|
+
dampedAngularFreq);
|
|
3464
|
+
};
|
|
3465
|
+
}
|
|
3606
3466
|
}
|
|
3467
|
+
createSpring();
|
|
3468
|
+
return {
|
|
3469
|
+
next: (t) => {
|
|
3470
|
+
const current = resolveSpring(t);
|
|
3471
|
+
if (!isResolvedFromDuration) {
|
|
3472
|
+
let currentVelocity = initialVelocity;
|
|
3473
|
+
if (t !== 0) {
|
|
3474
|
+
/**
|
|
3475
|
+
* We only need to calculate velocity for under-damped springs
|
|
3476
|
+
* as over- and critically-damped springs can't overshoot, so
|
|
3477
|
+
* checking only for displacement is enough.
|
|
3478
|
+
*/
|
|
3479
|
+
if (dampingRatio < 1) {
|
|
3480
|
+
const prevT = Math.max(0, t - velocitySampleDuration);
|
|
3481
|
+
currentVelocity = velocityPerSecond(current - resolveSpring(prevT), t - prevT);
|
|
3482
|
+
}
|
|
3483
|
+
else {
|
|
3484
|
+
currentVelocity = 0;
|
|
3485
|
+
}
|
|
3486
|
+
}
|
|
3487
|
+
const isBelowVelocityThreshold = Math.abs(currentVelocity) <= restSpeed;
|
|
3488
|
+
const isBelowDisplacementThreshold = Math.abs(target - current) <= restDelta;
|
|
3489
|
+
state.done =
|
|
3490
|
+
isBelowVelocityThreshold && isBelowDisplacementThreshold;
|
|
3491
|
+
}
|
|
3492
|
+
else {
|
|
3493
|
+
state.done = t >= duration;
|
|
3494
|
+
}
|
|
3495
|
+
state.value = state.done ? target : current;
|
|
3496
|
+
return state;
|
|
3497
|
+
},
|
|
3498
|
+
flipTarget: () => {
|
|
3499
|
+
initialVelocity = -initialVelocity;
|
|
3500
|
+
[origin, target] = [target, origin];
|
|
3501
|
+
createSpring();
|
|
3502
|
+
},
|
|
3503
|
+
};
|
|
3504
|
+
}
|
|
3505
|
+
spring.needsInterpolation = (a, b) => typeof a === "string" || typeof b === "string";
|
|
3506
|
+
const zero = (_t) => 0;
|
|
3507
|
+
|
|
3508
|
+
function decay({
|
|
3509
|
+
/**
|
|
3510
|
+
* The decay animation dynamically calculates an end of the animation
|
|
3511
|
+
* based on the initial keyframe, so we only need to define a single keyframe
|
|
3512
|
+
* as default.
|
|
3513
|
+
*/
|
|
3514
|
+
keyframes = [0], velocity = 0, power = 0.8, timeConstant = 350, restDelta = 0.5, modifyTarget, }) {
|
|
3515
|
+
const origin = keyframes[0];
|
|
3607
3516
|
/**
|
|
3608
|
-
*
|
|
3609
|
-
*
|
|
3610
|
-
* @param subscriber - A function that's provided the latest value.
|
|
3611
|
-
* @returns A function that, when called, will cancel this subscription.
|
|
3612
|
-
*
|
|
3613
|
-
* @internal
|
|
3517
|
+
* This is the Iterator-spec return value. We ensure it's mutable rather than using a generator
|
|
3518
|
+
* to reduce GC during animation.
|
|
3614
3519
|
*/
|
|
3615
|
-
|
|
3616
|
-
|
|
3617
|
-
|
|
3618
|
-
|
|
3619
|
-
}
|
|
3520
|
+
const state = { done: false, value: origin };
|
|
3521
|
+
let amplitude = power * velocity;
|
|
3522
|
+
const ideal = origin + amplitude;
|
|
3523
|
+
const target = modifyTarget === undefined ? ideal : modifyTarget(ideal);
|
|
3620
3524
|
/**
|
|
3621
|
-
*
|
|
3622
|
-
*
|
|
3623
|
-
* @internal
|
|
3525
|
+
* If the target has changed we need to re-calculate the amplitude, otherwise
|
|
3526
|
+
* the animation will start from the wrong position.
|
|
3624
3527
|
*/
|
|
3625
|
-
|
|
3626
|
-
|
|
3528
|
+
if (target !== ideal)
|
|
3529
|
+
amplitude = target - origin;
|
|
3530
|
+
return {
|
|
3531
|
+
next: (t) => {
|
|
3532
|
+
const delta = -amplitude * Math.exp(-t / timeConstant);
|
|
3533
|
+
state.done = !(delta > restDelta || delta < -restDelta);
|
|
3534
|
+
state.value = state.done ? target : target + delta;
|
|
3535
|
+
return state;
|
|
3536
|
+
},
|
|
3537
|
+
flipTarget: () => { },
|
|
3538
|
+
};
|
|
3539
|
+
}
|
|
3540
|
+
|
|
3541
|
+
const types = {
|
|
3542
|
+
decay,
|
|
3543
|
+
keyframes: keyframes,
|
|
3544
|
+
tween: keyframes,
|
|
3545
|
+
spring,
|
|
3546
|
+
};
|
|
3547
|
+
function loopElapsed(elapsed, duration, delay = 0) {
|
|
3548
|
+
return elapsed - duration - delay;
|
|
3549
|
+
}
|
|
3550
|
+
function reverseElapsed(elapsed, duration = 0, delay = 0, isForwardPlayback = true) {
|
|
3551
|
+
return isForwardPlayback
|
|
3552
|
+
? loopElapsed(duration + -elapsed, duration, delay)
|
|
3553
|
+
: duration - (elapsed - duration) + delay;
|
|
3554
|
+
}
|
|
3555
|
+
function hasRepeatDelayElapsed(elapsed, duration, delay, isForwardPlayback) {
|
|
3556
|
+
return isForwardPlayback ? elapsed >= duration + delay : elapsed <= -delay;
|
|
3557
|
+
}
|
|
3558
|
+
const framesync = (update) => {
|
|
3559
|
+
const passTimestamp = ({ delta }) => update(delta);
|
|
3560
|
+
return {
|
|
3561
|
+
start: () => sync.update(passTimestamp, true),
|
|
3562
|
+
stop: () => cancelSync.update(passTimestamp),
|
|
3563
|
+
};
|
|
3564
|
+
};
|
|
3565
|
+
function animate$1({ duration, driver = framesync, elapsed = 0, repeat: repeatMax = 0, repeatType = "loop", repeatDelay = 0, keyframes, onPlay, onStop, onComplete, onRepeat, onUpdate, type = "keyframes", ...options }) {
|
|
3566
|
+
var _a, _b;
|
|
3567
|
+
let driverControls;
|
|
3568
|
+
let repeatCount = 0;
|
|
3569
|
+
let computedDuration = duration;
|
|
3570
|
+
let latest;
|
|
3571
|
+
let isComplete = false;
|
|
3572
|
+
let isForwardPlayback = true;
|
|
3573
|
+
let interpolateFromNumber;
|
|
3574
|
+
const animator = types[keyframes.length > 2 ? "keyframes" : type];
|
|
3575
|
+
const origin = keyframes[0];
|
|
3576
|
+
const target = keyframes[keyframes.length - 1];
|
|
3577
|
+
if ((_b = (_a = animator).needsInterpolation) === null || _b === void 0 ? void 0 : _b.call(_a, origin, target)) {
|
|
3578
|
+
interpolateFromNumber = interpolate([0, 100], [origin, target], {
|
|
3579
|
+
clamp: false,
|
|
3580
|
+
});
|
|
3581
|
+
keyframes = [0, 100];
|
|
3627
3582
|
}
|
|
3628
|
-
|
|
3629
|
-
|
|
3630
|
-
|
|
3631
|
-
|
|
3632
|
-
|
|
3633
|
-
|
|
3634
|
-
|
|
3635
|
-
|
|
3636
|
-
|
|
3637
|
-
|
|
3638
|
-
* @param latest - Latest value to set.
|
|
3639
|
-
* @param render - Whether to notify render subscribers. Defaults to `true`
|
|
3640
|
-
*
|
|
3641
|
-
* @public
|
|
3642
|
-
*/
|
|
3643
|
-
set(v, render = true) {
|
|
3644
|
-
if (!render || !this.passiveEffect) {
|
|
3645
|
-
this.updateAndNotify(v, render);
|
|
3583
|
+
const animation = animator({
|
|
3584
|
+
...options,
|
|
3585
|
+
duration,
|
|
3586
|
+
keyframes,
|
|
3587
|
+
});
|
|
3588
|
+
function repeat() {
|
|
3589
|
+
repeatCount++;
|
|
3590
|
+
if (repeatType === "reverse") {
|
|
3591
|
+
isForwardPlayback = repeatCount % 2 === 0;
|
|
3592
|
+
elapsed = reverseElapsed(elapsed, computedDuration, repeatDelay, isForwardPlayback);
|
|
3646
3593
|
}
|
|
3647
3594
|
else {
|
|
3648
|
-
|
|
3595
|
+
elapsed = loopElapsed(elapsed, computedDuration, repeatDelay);
|
|
3596
|
+
if (repeatType === "mirror")
|
|
3597
|
+
animation.flipTarget();
|
|
3649
3598
|
}
|
|
3599
|
+
isComplete = false;
|
|
3600
|
+
onRepeat && onRepeat();
|
|
3650
3601
|
}
|
|
3651
|
-
|
|
3652
|
-
|
|
3653
|
-
|
|
3654
|
-
* @returns - The latest state of `MotionValue`
|
|
3655
|
-
*
|
|
3656
|
-
* @public
|
|
3657
|
-
*/
|
|
3658
|
-
get() {
|
|
3659
|
-
return this.current;
|
|
3602
|
+
function complete() {
|
|
3603
|
+
driverControls.stop();
|
|
3604
|
+
onComplete && onComplete();
|
|
3660
3605
|
}
|
|
3661
|
-
|
|
3662
|
-
|
|
3663
|
-
|
|
3664
|
-
|
|
3665
|
-
|
|
3606
|
+
function update(delta) {
|
|
3607
|
+
if (!isForwardPlayback)
|
|
3608
|
+
delta = -delta;
|
|
3609
|
+
elapsed += delta;
|
|
3610
|
+
if (!isComplete) {
|
|
3611
|
+
const state = animation.next(Math.max(0, elapsed));
|
|
3612
|
+
latest = state.value;
|
|
3613
|
+
if (interpolateFromNumber)
|
|
3614
|
+
latest = interpolateFromNumber(latest);
|
|
3615
|
+
isComplete = isForwardPlayback ? state.done : elapsed <= 0;
|
|
3616
|
+
}
|
|
3617
|
+
onUpdate && onUpdate(latest);
|
|
3618
|
+
if (isComplete) {
|
|
3619
|
+
if (repeatCount === 0) {
|
|
3620
|
+
computedDuration =
|
|
3621
|
+
computedDuration !== undefined ? computedDuration : elapsed;
|
|
3622
|
+
}
|
|
3623
|
+
if (repeatCount < repeatMax) {
|
|
3624
|
+
hasRepeatDelayElapsed(elapsed, computedDuration, repeatDelay, isForwardPlayback) && repeat();
|
|
3625
|
+
}
|
|
3626
|
+
else {
|
|
3627
|
+
complete();
|
|
3628
|
+
}
|
|
3629
|
+
}
|
|
3666
3630
|
}
|
|
3667
|
-
|
|
3668
|
-
|
|
3669
|
-
|
|
3670
|
-
|
|
3671
|
-
*
|
|
3672
|
-
* @public
|
|
3673
|
-
*/
|
|
3674
|
-
getVelocity() {
|
|
3675
|
-
// This could be isFloat(this.prev) && isFloat(this.current), but that would be wasteful
|
|
3676
|
-
return this.canTrackVelocity
|
|
3677
|
-
? // These casts could be avoided if parseFloat would be typed better
|
|
3678
|
-
velocityPerSecond(parseFloat(this.current) -
|
|
3679
|
-
parseFloat(this.prev), this.timeDelta)
|
|
3680
|
-
: 0;
|
|
3631
|
+
function play() {
|
|
3632
|
+
onPlay && onPlay();
|
|
3633
|
+
driverControls = driver(update);
|
|
3634
|
+
driverControls.start();
|
|
3681
3635
|
}
|
|
3682
|
-
|
|
3683
|
-
|
|
3684
|
-
|
|
3685
|
-
|
|
3686
|
-
|
|
3687
|
-
|
|
3688
|
-
|
|
3689
|
-
|
|
3690
|
-
|
|
3691
|
-
|
|
3692
|
-
|
|
3693
|
-
|
|
3694
|
-
|
|
3695
|
-
|
|
3696
|
-
return new Promise((resolve) => {
|
|
3697
|
-
this.hasAnimated = true;
|
|
3698
|
-
this.stopAnimation = animation(resolve);
|
|
3699
|
-
}).then(() => this.clearAnimation());
|
|
3636
|
+
play();
|
|
3637
|
+
return {
|
|
3638
|
+
stop: () => {
|
|
3639
|
+
onStop && onStop();
|
|
3640
|
+
driverControls.stop();
|
|
3641
|
+
},
|
|
3642
|
+
};
|
|
3643
|
+
}
|
|
3644
|
+
|
|
3645
|
+
function inertia({ keyframes, velocity = 0, min, max, power = 0.8, timeConstant = 750, bounceStiffness = 500, bounceDamping = 10, restDelta = 1, modifyTarget, driver, onUpdate, onComplete, onStop, }) {
|
|
3646
|
+
const origin = keyframes[0];
|
|
3647
|
+
let currentAnimation;
|
|
3648
|
+
function isOutOfBounds(v) {
|
|
3649
|
+
return (min !== undefined && v < min) || (max !== undefined && v > max);
|
|
3700
3650
|
}
|
|
3701
|
-
|
|
3702
|
-
|
|
3703
|
-
|
|
3704
|
-
|
|
3705
|
-
|
|
3706
|
-
|
|
3707
|
-
if (this.stopAnimation)
|
|
3708
|
-
this.stopAnimation();
|
|
3709
|
-
this.clearAnimation();
|
|
3651
|
+
function findNearestBoundary(v) {
|
|
3652
|
+
if (min === undefined)
|
|
3653
|
+
return max;
|
|
3654
|
+
if (max === undefined)
|
|
3655
|
+
return min;
|
|
3656
|
+
return Math.abs(min - v) < Math.abs(max - v) ? min : max;
|
|
3710
3657
|
}
|
|
3711
|
-
|
|
3712
|
-
|
|
3713
|
-
|
|
3714
|
-
|
|
3715
|
-
|
|
3716
|
-
|
|
3717
|
-
|
|
3658
|
+
function startAnimation(options) {
|
|
3659
|
+
currentAnimation === null || currentAnimation === void 0 ? void 0 : currentAnimation.stop();
|
|
3660
|
+
currentAnimation = animate$1({
|
|
3661
|
+
keyframes: [0, 1],
|
|
3662
|
+
velocity: 0,
|
|
3663
|
+
...options,
|
|
3664
|
+
driver,
|
|
3665
|
+
onUpdate: (v) => {
|
|
3666
|
+
var _a;
|
|
3667
|
+
onUpdate === null || onUpdate === void 0 ? void 0 : onUpdate(v);
|
|
3668
|
+
(_a = options.onUpdate) === null || _a === void 0 ? void 0 : _a.call(options, v);
|
|
3669
|
+
},
|
|
3670
|
+
onComplete,
|
|
3671
|
+
onStop,
|
|
3672
|
+
});
|
|
3718
3673
|
}
|
|
3719
|
-
|
|
3720
|
-
|
|
3674
|
+
function startSpring(options) {
|
|
3675
|
+
startAnimation({
|
|
3676
|
+
type: "spring",
|
|
3677
|
+
stiffness: bounceStiffness,
|
|
3678
|
+
damping: bounceDamping,
|
|
3679
|
+
restDelta,
|
|
3680
|
+
...options,
|
|
3681
|
+
});
|
|
3721
3682
|
}
|
|
3722
|
-
|
|
3723
|
-
|
|
3724
|
-
|
|
3725
|
-
|
|
3726
|
-
|
|
3727
|
-
|
|
3728
|
-
*
|
|
3729
|
-
* @public
|
|
3730
|
-
*/
|
|
3731
|
-
destroy() {
|
|
3732
|
-
this.updateSubscribers.clear();
|
|
3733
|
-
this.renderSubscribers.clear();
|
|
3734
|
-
this.stop();
|
|
3683
|
+
if (isOutOfBounds(origin)) {
|
|
3684
|
+
// Start the animation with spring if outside the defined boundaries
|
|
3685
|
+
startSpring({
|
|
3686
|
+
velocity,
|
|
3687
|
+
keyframes: [origin, findNearestBoundary(origin)],
|
|
3688
|
+
});
|
|
3735
3689
|
}
|
|
3690
|
+
else {
|
|
3691
|
+
/**
|
|
3692
|
+
* Or if the value is out of bounds, simulate the inertia movement
|
|
3693
|
+
* with the decay animation.
|
|
3694
|
+
*
|
|
3695
|
+
* Pre-calculate the target so we can detect if it's out-of-bounds.
|
|
3696
|
+
* If it is, we want to check per frame when to switch to a spring
|
|
3697
|
+
* animation
|
|
3698
|
+
*/
|
|
3699
|
+
let target = power * velocity + origin;
|
|
3700
|
+
if (typeof modifyTarget !== "undefined")
|
|
3701
|
+
target = modifyTarget(target);
|
|
3702
|
+
const boundary = findNearestBoundary(target);
|
|
3703
|
+
const heading = boundary === min ? -1 : 1;
|
|
3704
|
+
let prev;
|
|
3705
|
+
let current;
|
|
3706
|
+
const checkBoundary = (v) => {
|
|
3707
|
+
prev = current;
|
|
3708
|
+
current = v;
|
|
3709
|
+
velocity = velocityPerSecond(v - prev, frameData.delta);
|
|
3710
|
+
if ((heading === 1 && v > boundary) ||
|
|
3711
|
+
(heading === -1 && v < boundary)) {
|
|
3712
|
+
startSpring({ keyframes: [v, boundary], velocity });
|
|
3713
|
+
}
|
|
3714
|
+
};
|
|
3715
|
+
startAnimation({
|
|
3716
|
+
type: "decay",
|
|
3717
|
+
keyframes: [origin, 0],
|
|
3718
|
+
velocity,
|
|
3719
|
+
timeConstant,
|
|
3720
|
+
power,
|
|
3721
|
+
restDelta,
|
|
3722
|
+
modifyTarget,
|
|
3723
|
+
onUpdate: isOutOfBounds(target) ? checkBoundary : undefined,
|
|
3724
|
+
});
|
|
3725
|
+
}
|
|
3726
|
+
return {
|
|
3727
|
+
stop: () => currentAnimation === null || currentAnimation === void 0 ? void 0 : currentAnimation.stop(),
|
|
3728
|
+
};
|
|
3736
3729
|
}
|
|
3737
|
-
function motionValue(init) {
|
|
3738
|
-
return new MotionValue(init);
|
|
3739
|
-
}
|
|
3740
|
-
|
|
3741
|
-
/**
|
|
3742
|
-
* Tests a provided value against a ValueType
|
|
3743
|
-
*/
|
|
3744
|
-
const testValueType = (v) => (type) => type.test(v);
|
|
3745
3730
|
|
|
3746
|
-
|
|
3747
|
-
|
|
3748
|
-
|
|
3749
|
-
|
|
3750
|
-
|
|
3751
|
-
|
|
3731
|
+
const underDampedSpring = () => ({
|
|
3732
|
+
type: "spring",
|
|
3733
|
+
stiffness: 500,
|
|
3734
|
+
damping: 25,
|
|
3735
|
+
restSpeed: 10,
|
|
3736
|
+
});
|
|
3737
|
+
const criticallyDampedSpring = (target) => ({
|
|
3738
|
+
type: "spring",
|
|
3739
|
+
stiffness: 550,
|
|
3740
|
+
damping: target === 0 ? 2 * Math.sqrt(550) : 30,
|
|
3741
|
+
restSpeed: 10,
|
|
3742
|
+
});
|
|
3743
|
+
const linearTween = () => ({
|
|
3744
|
+
type: "keyframes",
|
|
3745
|
+
ease: "linear",
|
|
3746
|
+
duration: 0.3,
|
|
3747
|
+
});
|
|
3748
|
+
const keyframesTransition = {
|
|
3749
|
+
type: "keyframes",
|
|
3750
|
+
duration: 0.8,
|
|
3751
|
+
};
|
|
3752
|
+
const defaultTransitions = {
|
|
3753
|
+
x: underDampedSpring,
|
|
3754
|
+
y: underDampedSpring,
|
|
3755
|
+
z: underDampedSpring,
|
|
3756
|
+
rotate: underDampedSpring,
|
|
3757
|
+
rotateX: underDampedSpring,
|
|
3758
|
+
rotateY: underDampedSpring,
|
|
3759
|
+
rotateZ: underDampedSpring,
|
|
3760
|
+
scaleX: criticallyDampedSpring,
|
|
3761
|
+
scaleY: criticallyDampedSpring,
|
|
3762
|
+
scale: criticallyDampedSpring,
|
|
3763
|
+
opacity: linearTween,
|
|
3764
|
+
backgroundColor: linearTween,
|
|
3765
|
+
color: linearTween,
|
|
3766
|
+
default: criticallyDampedSpring,
|
|
3767
|
+
};
|
|
3768
|
+
const getDefaultTransition = (valueKey, { keyframes }) => {
|
|
3769
|
+
if (keyframes.length > 2) {
|
|
3770
|
+
return keyframesTransition;
|
|
3771
|
+
}
|
|
3772
|
+
else {
|
|
3773
|
+
const factory = defaultTransitions[valueKey] || defaultTransitions.default;
|
|
3774
|
+
return factory(keyframes[1]);
|
|
3775
|
+
}
|
|
3752
3776
|
};
|
|
3753
3777
|
|
|
3754
3778
|
/**
|
|
3755
|
-
*
|
|
3756
|
-
|
|
3757
|
-
|
|
3758
|
-
|
|
3759
|
-
*
|
|
3760
|
-
|
|
3761
|
-
|
|
3762
|
-
|
|
3763
|
-
/**
|
|
3764
|
-
* A list of all ValueTypes
|
|
3765
|
-
*/
|
|
3766
|
-
const valueTypes = [...dimensionValueTypes, color, complex];
|
|
3767
|
-
/**
|
|
3768
|
-
* Tests a value against the list of ValueTypes
|
|
3779
|
+
* Check if a value is animatable. Examples:
|
|
3780
|
+
*
|
|
3781
|
+
* ✅: 100, "100px", "#fff"
|
|
3782
|
+
* ❌: "block", "url(2.jpg)"
|
|
3783
|
+
* @param value
|
|
3784
|
+
*
|
|
3785
|
+
* @internal
|
|
3769
3786
|
*/
|
|
3770
|
-
const
|
|
3787
|
+
const isAnimatable = (key, value) => {
|
|
3788
|
+
// If the list of keys tat might be non-animatable grows, replace with Set
|
|
3789
|
+
if (key === "zIndex")
|
|
3790
|
+
return false;
|
|
3791
|
+
// If it's a number or a keyframes array, we can animate it. We might at some point
|
|
3792
|
+
// need to do a deep isAnimatable check of keyframes, or let Popmotion handle this,
|
|
3793
|
+
// but for now lets leave it like this for performance reasons
|
|
3794
|
+
if (typeof value === "number" || Array.isArray(value))
|
|
3795
|
+
return true;
|
|
3796
|
+
if (typeof value === "string" && // It's animatable if we have a string
|
|
3797
|
+
complex.test(value) && // And it contains numbers and/or colors
|
|
3798
|
+
!value.startsWith("url(") // Unless it starts with "url("
|
|
3799
|
+
) {
|
|
3800
|
+
return true;
|
|
3801
|
+
}
|
|
3802
|
+
return false;
|
|
3803
|
+
};
|
|
3771
3804
|
|
|
3772
3805
|
/**
|
|
3773
|
-
*
|
|
3806
|
+
* Decide whether a transition is defined on a given Transition.
|
|
3807
|
+
* This filters out orchestration options and returns true
|
|
3808
|
+
* if any options are left.
|
|
3774
3809
|
*/
|
|
3775
|
-
function
|
|
3776
|
-
|
|
3777
|
-
visualElement.values.forEach((value, key) => (current[key] = value.get()));
|
|
3778
|
-
return current;
|
|
3810
|
+
function isTransitionDefined({ when, delay: _delay, delayChildren, staggerChildren, staggerDirection, repeat, repeatType, repeatDelay, from, ...transition }) {
|
|
3811
|
+
return !!Object.keys(transition).length;
|
|
3779
3812
|
}
|
|
3780
|
-
|
|
3781
|
-
|
|
3782
|
-
|
|
3783
|
-
|
|
3784
|
-
|
|
3785
|
-
visualElement.values.forEach((value, key) => (velocity[key] = value.getVelocity()));
|
|
3786
|
-
return velocity;
|
|
3813
|
+
function isZero(value) {
|
|
3814
|
+
return (value === 0 ||
|
|
3815
|
+
(typeof value === "string" &&
|
|
3816
|
+
parseFloat(value) === 0 &&
|
|
3817
|
+
value.indexOf(" ") === -1));
|
|
3787
3818
|
}
|
|
3788
|
-
function
|
|
3789
|
-
|
|
3790
|
-
|
|
3819
|
+
function getZeroUnit(potentialUnitType) {
|
|
3820
|
+
return typeof potentialUnitType === "number"
|
|
3821
|
+
? 0
|
|
3822
|
+
: getAnimatableNone("", potentialUnitType);
|
|
3823
|
+
}
|
|
3824
|
+
function getValueTransition(transition, key) {
|
|
3825
|
+
return transition[key] || transition["default"] || transition;
|
|
3791
3826
|
}
|
|
3792
3827
|
|
|
3793
|
-
|
|
3794
|
-
|
|
3795
|
-
|
|
3796
|
-
|
|
3797
|
-
|
|
3798
|
-
|
|
3799
|
-
|
|
3800
|
-
|
|
3801
|
-
|
|
3802
|
-
visualElement.addValue(key, motionValue(value));
|
|
3828
|
+
function getKeyframes(value, valueName, target, transition) {
|
|
3829
|
+
const isTargetAnimatable = isAnimatable(valueName, target);
|
|
3830
|
+
let origin = transition.from !== undefined ? transition.from : value.get();
|
|
3831
|
+
if (origin === "none" && isTargetAnimatable && typeof target === "string") {
|
|
3832
|
+
/**
|
|
3833
|
+
* If we're trying to animate from "none", try and get an animatable version
|
|
3834
|
+
* of the target. This could be improved to work both ways.
|
|
3835
|
+
*/
|
|
3836
|
+
origin = getAnimatableNone(valueName, target);
|
|
3803
3837
|
}
|
|
3804
|
-
|
|
3805
|
-
|
|
3806
|
-
const resolved = resolveVariant(visualElement, definition);
|
|
3807
|
-
let { transitionEnd = {}, transition = {}, ...target } = resolved ? visualElement.makeTargetAnimatable(resolved, false) : {};
|
|
3808
|
-
target = { ...target, ...transitionEnd };
|
|
3809
|
-
for (const key in target) {
|
|
3810
|
-
const value = resolveFinalValueInKeyframes(target[key]);
|
|
3811
|
-
setMotionValue(visualElement, key, value);
|
|
3838
|
+
else if (isZero(origin) && typeof target === "string") {
|
|
3839
|
+
origin = getZeroUnit(target);
|
|
3812
3840
|
}
|
|
3813
|
-
|
|
3814
|
-
|
|
3815
|
-
|
|
3816
|
-
|
|
3817
|
-
var _a;
|
|
3818
|
-
const variant = visualElement.getVariant(key);
|
|
3819
|
-
variant && setTarget(visualElement, variant);
|
|
3820
|
-
(_a = visualElement.variantChildren) === null || _a === void 0 ? void 0 : _a.forEach((child) => {
|
|
3821
|
-
setVariants(child, variantLabels);
|
|
3822
|
-
});
|
|
3823
|
-
});
|
|
3824
|
-
}
|
|
3825
|
-
function setValues(visualElement, definition) {
|
|
3826
|
-
if (Array.isArray(definition)) {
|
|
3827
|
-
return setVariants(visualElement, definition);
|
|
3841
|
+
else if (!Array.isArray(target) &&
|
|
3842
|
+
isZero(target) &&
|
|
3843
|
+
typeof origin === "string") {
|
|
3844
|
+
target = getZeroUnit(origin);
|
|
3828
3845
|
}
|
|
3829
|
-
|
|
3830
|
-
|
|
3846
|
+
/**
|
|
3847
|
+
* If the target has been defined as a series of keyframes
|
|
3848
|
+
*/
|
|
3849
|
+
if (Array.isArray(target)) {
|
|
3850
|
+
/**
|
|
3851
|
+
* Ensure an initial wildcard keyframe is hydrated by the origin.
|
|
3852
|
+
* TODO: Support extra wildcard keyframes i.e [1, null, 0]
|
|
3853
|
+
*/
|
|
3854
|
+
if (target[0] === null) {
|
|
3855
|
+
target[0] = origin;
|
|
3856
|
+
}
|
|
3857
|
+
return target;
|
|
3831
3858
|
}
|
|
3832
3859
|
else {
|
|
3833
|
-
|
|
3860
|
+
return [origin, target];
|
|
3834
3861
|
}
|
|
3835
3862
|
}
|
|
3836
|
-
|
|
3837
|
-
|
|
3838
|
-
|
|
3839
|
-
|
|
3840
|
-
|
|
3841
|
-
|
|
3842
|
-
|
|
3843
|
-
|
|
3844
|
-
|
|
3845
|
-
|
|
3863
|
+
|
|
3864
|
+
const featureTests = {
|
|
3865
|
+
waapi: () => Object.hasOwnProperty.call(Element.prototype, "animate"),
|
|
3866
|
+
};
|
|
3867
|
+
const results = {};
|
|
3868
|
+
const supports = {};
|
|
3869
|
+
/**
|
|
3870
|
+
* Generate features tests that cache their results.
|
|
3871
|
+
*/
|
|
3872
|
+
for (const key in featureTests) {
|
|
3873
|
+
supports[key] = () => {
|
|
3874
|
+
if (results[key] === undefined)
|
|
3875
|
+
results[key] = featureTests[key]();
|
|
3876
|
+
return results[key];
|
|
3877
|
+
};
|
|
3878
|
+
}
|
|
3879
|
+
|
|
3880
|
+
/**
|
|
3881
|
+
* A list of values that can be hardware-accelerated.
|
|
3882
|
+
*/
|
|
3883
|
+
const acceleratedValues = new Set([]);
|
|
3884
|
+
const createMotionValueAnimation = (valueName, value, target, transition = {}) => {
|
|
3885
|
+
return (onComplete) => {
|
|
3886
|
+
const valueTransition = getValueTransition(transition, valueName) || {};
|
|
3846
3887
|
/**
|
|
3847
|
-
*
|
|
3848
|
-
*
|
|
3888
|
+
* Most transition values are currently completely overwritten by value-specific
|
|
3889
|
+
* transitions. In the future it'd be nicer to blend these transitions. But for now
|
|
3890
|
+
* delay actually does inherit from the root transition if not value-specific.
|
|
3849
3891
|
*/
|
|
3850
|
-
|
|
3851
|
-
value = targetValue[0];
|
|
3852
|
-
}
|
|
3892
|
+
const delay = valueTransition.delay || transition.delay || 0;
|
|
3853
3893
|
/**
|
|
3854
|
-
*
|
|
3855
|
-
*
|
|
3856
|
-
* if not read the value from the DOM. As an absolute fallback, take the defined target value.
|
|
3894
|
+
* Elapsed isn't a public transition option but can be passed through from
|
|
3895
|
+
* optimized appear effects in milliseconds.
|
|
3857
3896
|
*/
|
|
3858
|
-
|
|
3859
|
-
|
|
3860
|
-
|
|
3897
|
+
let { elapsed = 0 } = transition;
|
|
3898
|
+
elapsed = elapsed - secondsToMilliseconds(delay);
|
|
3899
|
+
const keyframes = getKeyframes(value, valueName, target, valueTransition);
|
|
3861
3900
|
/**
|
|
3862
|
-
*
|
|
3863
|
-
*
|
|
3901
|
+
* Check if we're able to animate between the start and end keyframes,
|
|
3902
|
+
* and throw a warning if we're attempting to animate between one that's
|
|
3903
|
+
* animatable and another that isn't.
|
|
3864
3904
|
*/
|
|
3865
|
-
|
|
3866
|
-
|
|
3867
|
-
|
|
3868
|
-
|
|
3869
|
-
|
|
3870
|
-
|
|
3905
|
+
const originKeyframe = keyframes[0];
|
|
3906
|
+
const targetKeyframe = keyframes[keyframes.length - 1];
|
|
3907
|
+
const isOriginAnimatable = isAnimatable(valueName, originKeyframe);
|
|
3908
|
+
const isTargetAnimatable = isAnimatable(valueName, targetKeyframe);
|
|
3909
|
+
heyListen.warning(isOriginAnimatable === isTargetAnimatable, `You are trying to animate ${valueName} 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.`);
|
|
3910
|
+
let options = {
|
|
3911
|
+
keyframes,
|
|
3912
|
+
velocity: value.getVelocity(),
|
|
3913
|
+
...valueTransition,
|
|
3914
|
+
elapsed,
|
|
3915
|
+
onUpdate: (v) => {
|
|
3916
|
+
value.set(v);
|
|
3917
|
+
valueTransition.onUpdate && valueTransition.onUpdate(v);
|
|
3918
|
+
},
|
|
3919
|
+
onComplete: () => {
|
|
3920
|
+
onComplete();
|
|
3921
|
+
valueTransition.onComplete && valueTransition.onComplete();
|
|
3922
|
+
},
|
|
3923
|
+
};
|
|
3924
|
+
if (!isOriginAnimatable ||
|
|
3925
|
+
!isTargetAnimatable ||
|
|
3926
|
+
instantAnimationState.current ||
|
|
3927
|
+
valueTransition.type === false) {
|
|
3928
|
+
/**
|
|
3929
|
+
* If we can't animate this value, or the global instant animation flag is set,
|
|
3930
|
+
* or this is simply defined as an instant transition, return an instant transition.
|
|
3931
|
+
*/
|
|
3932
|
+
return createInstantAnimation(options);
|
|
3871
3933
|
}
|
|
3872
|
-
else if (
|
|
3873
|
-
|
|
3934
|
+
else if (valueTransition.type === "inertia") {
|
|
3935
|
+
/**
|
|
3936
|
+
* If this is an inertia animation, we currently don't support pre-generating
|
|
3937
|
+
* keyframes for this as such it must always run on the main thread.
|
|
3938
|
+
*/
|
|
3939
|
+
const animation = inertia(options);
|
|
3940
|
+
return () => animation.stop();
|
|
3874
3941
|
}
|
|
3875
|
-
|
|
3876
|
-
|
|
3877
|
-
|
|
3942
|
+
/**
|
|
3943
|
+
* If there's no transition defined for this value, we can generate
|
|
3944
|
+
* unqiue transition settings for this value.
|
|
3945
|
+
*/
|
|
3946
|
+
if (!isTransitionDefined(valueTransition)) {
|
|
3947
|
+
options = {
|
|
3948
|
+
...options,
|
|
3949
|
+
...getDefaultTransition(valueName, options),
|
|
3950
|
+
};
|
|
3878
3951
|
}
|
|
3879
|
-
if (value !== null)
|
|
3880
|
-
visualElement.setBaseTarget(key, value);
|
|
3881
|
-
}
|
|
3882
|
-
}
|
|
3883
|
-
function getOriginFromTransition(key, transition) {
|
|
3884
|
-
if (!transition)
|
|
3885
|
-
return;
|
|
3886
|
-
const valueTransition = transition[key] || transition["default"] || transition;
|
|
3887
|
-
return valueTransition.from;
|
|
3888
|
-
}
|
|
3889
|
-
function getOrigin(target, transition, visualElement) {
|
|
3890
|
-
var _a;
|
|
3891
|
-
const origin = {};
|
|
3892
|
-
for (const key in target) {
|
|
3893
|
-
const transitionOrigin = getOriginFromTransition(key, transition);
|
|
3894
|
-
origin[key] =
|
|
3895
|
-
transitionOrigin !== undefined
|
|
3896
|
-
? transitionOrigin
|
|
3897
|
-
: (_a = visualElement.getValue(key)) === null || _a === void 0 ? void 0 : _a.get();
|
|
3898
|
-
}
|
|
3899
|
-
return origin;
|
|
3900
|
-
}
|
|
3901
|
-
|
|
3902
|
-
function isWillChangeMotionValue(value) {
|
|
3903
|
-
return Boolean(isMotionValue(value) && value.add);
|
|
3904
|
-
}
|
|
3905
|
-
|
|
3906
|
-
const appearStoreId = (id, value) => `${id}: ${value}`;
|
|
3907
|
-
|
|
3908
|
-
function handoffOptimizedAppearAnimation(id, name) {
|
|
3909
|
-
const { MotionAppearAnimations } = window;
|
|
3910
|
-
const animationId = appearStoreId(id, transformProps.has(name) ? "transform" : name);
|
|
3911
|
-
const animation = MotionAppearAnimations && MotionAppearAnimations.get(animationId);
|
|
3912
|
-
if (animation) {
|
|
3913
3952
|
/**
|
|
3914
|
-
*
|
|
3915
|
-
*
|
|
3916
|
-
*
|
|
3917
|
-
* 2. As all independent transforms share a single transform animation, stopping
|
|
3918
|
-
* it synchronously would prevent subsequent transforms from handing off.
|
|
3953
|
+
* Both WAAPI and our internal animation functions use durations
|
|
3954
|
+
* as defined by milliseconds, while our external API defines them
|
|
3955
|
+
* as seconds.
|
|
3919
3956
|
*/
|
|
3920
|
-
|
|
3957
|
+
if (options.duration) {
|
|
3958
|
+
options.duration = secondsToMilliseconds(options.duration);
|
|
3959
|
+
}
|
|
3960
|
+
if (options.repeatDelay) {
|
|
3961
|
+
options.repeatDelay = secondsToMilliseconds(options.repeatDelay);
|
|
3962
|
+
}
|
|
3963
|
+
const canAccelerateAnimation = acceleratedValues.has(valueName) &&
|
|
3964
|
+
supports.waapi() &&
|
|
3965
|
+
value.owner &&
|
|
3966
|
+
!value.owner.getProps().onUpdate &&
|
|
3967
|
+
!options.repeat;
|
|
3968
|
+
if (canAccelerateAnimation) {
|
|
3921
3969
|
/**
|
|
3922
|
-
*
|
|
3970
|
+
* If this animation is capable of being run via WAAPI, then do so.
|
|
3971
|
+
*
|
|
3972
|
+
* TODO: Currently no values are hardware accelerated so this clause
|
|
3973
|
+
* will never trigger. Animation to be added in subsequent PR.
|
|
3923
3974
|
*/
|
|
3924
|
-
|
|
3925
|
-
|
|
3926
|
-
|
|
3927
|
-
|
|
3928
|
-
|
|
3929
|
-
|
|
3930
|
-
|
|
3931
|
-
|
|
3932
|
-
|
|
3933
|
-
|
|
3934
|
-
|
|
3935
|
-
}
|
|
3936
|
-
|
|
3937
|
-
const optimizedAppearDataId = "framerAppearId";
|
|
3938
|
-
const optimizedAppearDataAttribute = "data-" + camelToDash(optimizedAppearDataId);
|
|
3975
|
+
return createAcceleratedAnimation();
|
|
3976
|
+
}
|
|
3977
|
+
else {
|
|
3978
|
+
/**
|
|
3979
|
+
* Otherwise, fall back to the main thread.
|
|
3980
|
+
*/
|
|
3981
|
+
const animation = animate$1(options);
|
|
3982
|
+
return () => animation.stop();
|
|
3983
|
+
}
|
|
3984
|
+
};
|
|
3985
|
+
};
|
|
3939
3986
|
|
|
3940
3987
|
function animateVisualElement(visualElement, definition, options = {}) {
|
|
3941
3988
|
visualElement.notify("AnimationStart", definition);
|
|
@@ -4035,7 +4082,7 @@ function animateTarget(visualElement, definition, { delay = 0, transitionOverrid
|
|
|
4035
4082
|
valueTransition.elapsed = handoffOptimizedAppearAnimation(appearId, key);
|
|
4036
4083
|
}
|
|
4037
4084
|
}
|
|
4038
|
-
let animation =
|
|
4085
|
+
let animation = value.start(createMotionValueAnimation(key, value, valueTarget, valueTransition));
|
|
4039
4086
|
if (isWillChangeMotionValue(willChange)) {
|
|
4040
4087
|
willChange.add(key);
|
|
4041
4088
|
animation = animation.then(() => willChange.remove(key));
|
|
@@ -5191,7 +5238,7 @@ class VisualElementDragControls {
|
|
|
5191
5238
|
}
|
|
5192
5239
|
startAxisValueAnimation(axis, transition) {
|
|
5193
5240
|
const axisValue = this.getAxisMotionValue(axis);
|
|
5194
|
-
return
|
|
5241
|
+
return axisValue.start(createMotionValueAnimation(axis, axisValue, 0, transition));
|
|
5195
5242
|
}
|
|
5196
5243
|
stopAnimation() {
|
|
5197
5244
|
eachAxis((axis) => this.getAxisMotionValue(axis).stop());
|
|
@@ -5786,7 +5833,7 @@ function updateMotionValuesFromProps(element, next, prev) {
|
|
|
5786
5833
|
* and warn against mismatches.
|
|
5787
5834
|
*/
|
|
5788
5835
|
if (process.env.NODE_ENV === "development") {
|
|
5789
|
-
warnOnce(nextValue.version === "7.8.
|
|
5836
|
+
warnOnce(nextValue.version === "7.8.1", `Attempting to mix Framer Motion versions ${nextValue.version} with 7.8.1 may not work as expected.`);
|
|
5790
5837
|
}
|
|
5791
5838
|
}
|
|
5792
5839
|
else if (isMotionValue(prevValue)) {
|
|
@@ -6633,7 +6680,7 @@ const layoutFeatures = {
|
|
|
6633
6680
|
*/
|
|
6634
6681
|
function animate(from, to, transition = {}) {
|
|
6635
6682
|
const value = isMotionValue(from) ? from : motionValue(from);
|
|
6636
|
-
|
|
6683
|
+
value.start(createMotionValueAnimation("", value, to, transition));
|
|
6637
6684
|
return {
|
|
6638
6685
|
stop: () => value.stop(),
|
|
6639
6686
|
isAnimating: () => value.isAnimating(),
|
|
@@ -9092,8 +9139,7 @@ function useSpring(source, config = {}) {
|
|
|
9092
9139
|
activeSpringAnimation.current.stop();
|
|
9093
9140
|
}
|
|
9094
9141
|
activeSpringAnimation.current = animate$1({
|
|
9095
|
-
|
|
9096
|
-
to: v,
|
|
9142
|
+
keyframes: [value.get(), v],
|
|
9097
9143
|
velocity: value.getVelocity(),
|
|
9098
9144
|
type: "spring",
|
|
9099
9145
|
...config,
|
|
@@ -9606,22 +9652,6 @@ function useResetProjection() {
|
|
|
9606
9652
|
return reset;
|
|
9607
9653
|
}
|
|
9608
9654
|
|
|
9609
|
-
const featureTests = {
|
|
9610
|
-
waapi: () => Object.hasOwnProperty.call(Element.prototype, "animate"),
|
|
9611
|
-
};
|
|
9612
|
-
const results = {};
|
|
9613
|
-
const supports = {};
|
|
9614
|
-
/**
|
|
9615
|
-
* Generate features tests that cache their results.
|
|
9616
|
-
*/
|
|
9617
|
-
for (const key in featureTests) {
|
|
9618
|
-
supports[key] = () => {
|
|
9619
|
-
if (results[key] === undefined)
|
|
9620
|
-
results[key] = featureTests[key]();
|
|
9621
|
-
return results[key];
|
|
9622
|
-
};
|
|
9623
|
-
}
|
|
9624
|
-
|
|
9625
9655
|
const cubicBezierAsString = ([a, b, c, d]) => `cubic-bezier(${a}, ${b}, ${c}, ${d})`;
|
|
9626
9656
|
|
|
9627
9657
|
function animateStyle(element, valueName, keyframes, { delay, duration, ease }) {
|