framer-motion 7.7.3 → 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 +1966 -1860
- 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 +23 -15
- 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 +33 -39
- package/dist/es/animation/optimized-appear/data-id.mjs +6 -0
- package/dist/es/animation/optimized-appear/handoff.mjs +34 -0
- package/dist/es/animation/optimized-appear/start.mjs +15 -0
- package/dist/es/animation/optimized-appear/store-id.mjs +3 -0
- 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 -171
- package/dist/es/animation/waapi/easing.mjs +3 -0
- package/dist/es/animation/waapi/index.mjs +16 -0
- package/dist/es/animation/waapi/supports.mjs +17 -0
- package/dist/es/gestures/drag/VisualElementDragControls.mjs +2 -2
- package/dist/es/index.mjs +6 -3
- package/dist/es/render/utils/animation.mjs +15 -3
- package/dist/es/render/utils/motion-values.mjs +1 -1
- package/dist/es/utils/delay.mjs +3 -0
- package/dist/es/value/index.mjs +2 -2
- package/dist/es/value/use-spring.mjs +1 -2
- package/dist/framer-motion.dev.js +1971 -1865
- package/dist/framer-motion.js +1 -1
- package/dist/index.d.ts +424 -341
- package/dist/projection.dev.js +1655 -1623
- 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 +289 -282
- package/package.json +11 -9
package/dist/cjs/index.js
CHANGED
|
@@ -1869,2050 +1869,2120 @@ 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
|
-
};
|
|
1879
|
+
const isZeroValueString = (v) => /^0[^.\s]+$/.test(v);
|
|
2063
1880
|
|
|
2064
|
-
const
|
|
2065
|
-
|
|
2066
|
-
|
|
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
|
-
},
|
|
1881
|
+
const frameData = {
|
|
1882
|
+
delta: 0,
|
|
1883
|
+
timestamp: 0,
|
|
2078
1884
|
};
|
|
2079
1885
|
|
|
2080
|
-
|
|
2081
|
-
|
|
2082
|
-
|
|
2083
|
-
|
|
2084
|
-
|
|
2085
|
-
|
|
2086
|
-
|
|
2087
|
-
|
|
2088
|
-
|
|
2089
|
-
|
|
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
|
-
},
|
|
2100
|
-
};
|
|
1886
|
+
/*
|
|
1887
|
+
Detect and load appropriate clock setting for the execution environment
|
|
1888
|
+
*/
|
|
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);
|
|
2101
1896
|
|
|
2102
|
-
|
|
2103
|
-
|
|
2104
|
-
|
|
2105
|
-
|
|
2106
|
-
|
|
2107
|
-
|
|
2108
|
-
|
|
2109
|
-
|
|
2110
|
-
|
|
2111
|
-
|
|
2112
|
-
|
|
2113
|
-
|
|
2114
|
-
|
|
2115
|
-
|
|
2116
|
-
|
|
2117
|
-
let
|
|
2118
|
-
|
|
2119
|
-
|
|
2120
|
-
|
|
2121
|
-
|
|
2122
|
-
|
|
2123
|
-
|
|
2124
|
-
|
|
2125
|
-
|
|
2126
|
-
|
|
2127
|
-
|
|
2128
|
-
|
|
2129
|
-
|
|
2130
|
-
|
|
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;
|
|
1982
|
+
}
|
|
1983
|
+
|
|
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);
|
|
2131
2029
|
}
|
|
2132
|
-
|
|
2030
|
+
};
|
|
2031
|
+
const startLoop = () => {
|
|
2032
|
+
runNextFrame = true;
|
|
2033
|
+
useDefaultElapsed = true;
|
|
2034
|
+
if (!isProcessing)
|
|
2035
|
+
onNextFrame(processFrame);
|
|
2036
|
+
};
|
|
2037
|
+
|
|
2038
|
+
function addUniqueItem(arr, item) {
|
|
2039
|
+
if (arr.indexOf(item) === -1)
|
|
2040
|
+
arr.push(item);
|
|
2133
2041
|
}
|
|
2134
|
-
function
|
|
2135
|
-
|
|
2042
|
+
function removeItem(arr, item) {
|
|
2043
|
+
const index = arr.indexOf(item);
|
|
2044
|
+
if (index > -1)
|
|
2045
|
+
arr.splice(index, 1);
|
|
2136
2046
|
}
|
|
2137
|
-
|
|
2138
|
-
|
|
2139
|
-
const
|
|
2140
|
-
|
|
2141
|
-
|
|
2142
|
-
|
|
2143
|
-
|
|
2144
|
-
|
|
2145
|
-
|
|
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);
|
|
2054
|
+
}
|
|
2055
|
+
return arr;
|
|
2056
|
+
}
|
|
2057
|
+
|
|
2058
|
+
class SubscriptionManager {
|
|
2059
|
+
constructor() {
|
|
2060
|
+
this.subscriptions = [];
|
|
2061
|
+
}
|
|
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);
|
|
2146
2075
|
}
|
|
2147
|
-
|
|
2148
|
-
|
|
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
|
+
}
|
|
2085
|
+
}
|
|
2086
|
+
}
|
|
2087
|
+
getSize() {
|
|
2088
|
+
return this.subscriptions.length;
|
|
2089
|
+
}
|
|
2090
|
+
clear() {
|
|
2091
|
+
this.subscriptions.length = 0;
|
|
2092
|
+
}
|
|
2149
2093
|
}
|
|
2150
|
-
|
|
2151
|
-
|
|
2152
|
-
|
|
2153
|
-
|
|
2154
|
-
|
|
2094
|
+
|
|
2095
|
+
/*
|
|
2096
|
+
Convert velocity into velocity per second
|
|
2097
|
+
|
|
2098
|
+
@param [number]: Unit per frame
|
|
2099
|
+
@param [number]: Frame duration in ms
|
|
2100
|
+
*/
|
|
2101
|
+
function velocityPerSecond(velocity, frameDuration) {
|
|
2102
|
+
return frameDuration ? velocity * (1000 / frameDuration) : 0;
|
|
2155
2103
|
}
|
|
2156
|
-
const complex = { test, parse, createTransformer, getAnimatableNone: getAnimatableNone$1 };
|
|
2157
2104
|
|
|
2105
|
+
const isFloat = (value) => {
|
|
2106
|
+
return !isNaN(parseFloat(value));
|
|
2107
|
+
};
|
|
2158
2108
|
/**
|
|
2159
|
-
*
|
|
2160
|
-
*
|
|
2161
|
-
* ✅: 100, "100px", "#fff"
|
|
2162
|
-
* ❌: "block", "url(2.jpg)"
|
|
2163
|
-
* @param value
|
|
2109
|
+
* `MotionValue` is used to track the state and velocity of motion values.
|
|
2164
2110
|
*
|
|
2165
|
-
* @
|
|
2111
|
+
* @public
|
|
2166
2112
|
*/
|
|
2167
|
-
|
|
2168
|
-
|
|
2169
|
-
|
|
2170
|
-
|
|
2171
|
-
|
|
2172
|
-
|
|
2173
|
-
|
|
2174
|
-
|
|
2175
|
-
|
|
2176
|
-
|
|
2177
|
-
|
|
2178
|
-
|
|
2179
|
-
|
|
2180
|
-
|
|
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);
|
|
2181
2216
|
}
|
|
2182
|
-
|
|
2183
|
-
|
|
2184
|
-
|
|
2185
|
-
|
|
2186
|
-
|
|
2187
|
-
|
|
2188
|
-
|
|
2189
|
-
|
|
2190
|
-
|
|
2191
|
-
|
|
2192
|
-
|
|
2193
|
-
|
|
2194
|
-
|
|
2195
|
-
|
|
2196
|
-
|
|
2197
|
-
|
|
2198
|
-
|
|
2199
|
-
|
|
2200
|
-
|
|
2201
|
-
|
|
2202
|
-
|
|
2203
|
-
|
|
2204
|
-
|
|
2205
|
-
|
|
2206
|
-
|
|
2207
|
-
|
|
2208
|
-
|
|
2209
|
-
|
|
2210
|
-
|
|
2211
|
-
|
|
2212
|
-
|
|
2213
|
-
|
|
2214
|
-
|
|
2215
|
-
|
|
2216
|
-
|
|
2217
|
-
|
|
2218
|
-
|
|
2219
|
-
|
|
2220
|
-
|
|
2221
|
-
|
|
2222
|
-
}
|
|
2223
|
-
|
|
2224
|
-
|
|
2225
|
-
|
|
2226
|
-
|
|
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);
|
|
2227
2267
|
}
|
|
2228
|
-
|
|
2229
|
-
|
|
2230
|
-
|
|
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;
|
|
2291
|
+
}
|
|
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);
|
|
2310
|
+
}
|
|
2311
|
+
else {
|
|
2312
|
+
this.passiveEffect(v, this.updateAndNotify);
|
|
2313
|
+
}
|
|
2314
|
+
}
|
|
2315
|
+
/**
|
|
2316
|
+
* Returns the latest state of `MotionValue`
|
|
2317
|
+
*
|
|
2318
|
+
* @returns - The latest state of `MotionValue`
|
|
2319
|
+
*
|
|
2320
|
+
* @public
|
|
2321
|
+
*/
|
|
2322
|
+
get() {
|
|
2323
|
+
return this.current;
|
|
2231
2324
|
}
|
|
2232
|
-
return { to, ...transitionFactory(to) };
|
|
2233
|
-
};
|
|
2234
|
-
|
|
2235
|
-
/**
|
|
2236
|
-
* Properties that should default to 1 or 100%
|
|
2237
|
-
*/
|
|
2238
|
-
const maxDefaults = new Set(["brightness", "contrast", "saturate", "opacity"]);
|
|
2239
|
-
function applyDefaultFilter(v) {
|
|
2240
|
-
const [name, value] = v.slice(0, -1).split("(");
|
|
2241
|
-
if (name === "drop-shadow")
|
|
2242
|
-
return v;
|
|
2243
|
-
const [number] = value.match(floatRegex) || [];
|
|
2244
|
-
if (!number)
|
|
2245
|
-
return v;
|
|
2246
|
-
const unit = value.replace(number, "");
|
|
2247
|
-
let defaultValue = maxDefaults.has(name) ? 1 : 0;
|
|
2248
|
-
if (number !== value)
|
|
2249
|
-
defaultValue *= 100;
|
|
2250
|
-
return name + "(" + defaultValue + unit + ")";
|
|
2251
|
-
}
|
|
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
|
-
|
|
2261
|
-
/**
|
|
2262
|
-
* A map of default value types for common values
|
|
2263
|
-
*/
|
|
2264
|
-
const defaultValueTypes = {
|
|
2265
|
-
...numberValueTypes,
|
|
2266
|
-
// Color props
|
|
2267
|
-
color,
|
|
2268
|
-
backgroundColor: color,
|
|
2269
|
-
outlineColor: color,
|
|
2270
|
-
fill: color,
|
|
2271
|
-
stroke: color,
|
|
2272
|
-
// Border props
|
|
2273
|
-
borderColor: color,
|
|
2274
|
-
borderTopColor: color,
|
|
2275
|
-
borderRightColor: color,
|
|
2276
|
-
borderBottomColor: color,
|
|
2277
|
-
borderLeftColor: color,
|
|
2278
|
-
filter,
|
|
2279
|
-
WebkitFilter: filter,
|
|
2280
|
-
};
|
|
2281
|
-
/**
|
|
2282
|
-
* Gets the default ValueType for the provided value key
|
|
2283
|
-
*/
|
|
2284
|
-
const getDefaultValueType = (key) => defaultValueTypes[key];
|
|
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,
|
|
2297
|
-
};
|
|
2298
|
-
|
|
2299
|
-
/*
|
|
2300
|
-
Detect and load appropriate clock setting for the execution environment
|
|
2301
|
-
*/
|
|
2302
|
-
const defaultTimestep = (1 / 60) * 1000;
|
|
2303
|
-
const getCurrentTime = typeof performance !== "undefined"
|
|
2304
|
-
? () => performance.now()
|
|
2305
|
-
: () => Date.now();
|
|
2306
|
-
const onNextFrame = typeof window !== "undefined"
|
|
2307
|
-
? (callback) => window.requestAnimationFrame(callback)
|
|
2308
|
-
: (callback) => setTimeout(() => callback(getCurrentTime()), defaultTimestep);
|
|
2309
|
-
|
|
2310
|
-
function createRenderStep(runNextFrame) {
|
|
2311
2325
|
/**
|
|
2312
|
-
*
|
|
2313
|
-
* and one for the next. We reuse to avoid triggering GC after x frames.
|
|
2326
|
+
* @public
|
|
2314
2327
|
*/
|
|
2315
|
-
|
|
2316
|
-
|
|
2328
|
+
getPrevious() {
|
|
2329
|
+
return this.prev;
|
|
2330
|
+
}
|
|
2317
2331
|
/**
|
|
2332
|
+
* Returns the latest velocity of `MotionValue`
|
|
2333
|
+
*
|
|
2334
|
+
* @returns - The latest velocity of `MotionValue`. Returns `0` if the state is non-numerical.
|
|
2318
2335
|
*
|
|
2336
|
+
* @public
|
|
2319
2337
|
*/
|
|
2320
|
-
|
|
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;
|
|
2345
|
+
}
|
|
2321
2346
|
/**
|
|
2322
|
-
*
|
|
2323
|
-
*
|
|
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
|
|
2324
2357
|
*/
|
|
2325
|
-
|
|
2326
|
-
|
|
2358
|
+
start(animation) {
|
|
2359
|
+
this.stop();
|
|
2360
|
+
return new Promise((resolve) => {
|
|
2361
|
+
this.hasAnimated = true;
|
|
2362
|
+
this.stopAnimation = animation(resolve);
|
|
2363
|
+
}).then(() => this.clearAnimation());
|
|
2364
|
+
}
|
|
2327
2365
|
/**
|
|
2328
|
-
*
|
|
2366
|
+
* Stop the currently active animation.
|
|
2367
|
+
*
|
|
2368
|
+
* @public
|
|
2329
2369
|
*/
|
|
2330
|
-
|
|
2331
|
-
|
|
2332
|
-
|
|
2333
|
-
|
|
2334
|
-
|
|
2335
|
-
|
|
2336
|
-
|
|
2337
|
-
|
|
2338
|
-
|
|
2339
|
-
|
|
2340
|
-
|
|
2341
|
-
|
|
2342
|
-
|
|
2343
|
-
|
|
2344
|
-
|
|
2345
|
-
|
|
2346
|
-
|
|
2347
|
-
|
|
2348
|
-
|
|
2349
|
-
|
|
2350
|
-
|
|
2351
|
-
|
|
2352
|
-
|
|
2353
|
-
|
|
2354
|
-
|
|
2355
|
-
|
|
2356
|
-
|
|
2357
|
-
|
|
2358
|
-
|
|
2359
|
-
|
|
2360
|
-
|
|
2361
|
-
|
|
2362
|
-
|
|
2363
|
-
* If we're already processing we've probably been triggered by a flushSync
|
|
2364
|
-
* inside an existing process. Instead of executing, mark flushNextFrame
|
|
2365
|
-
* as true and ensure we flush the following frame at the end of this one.
|
|
2366
|
-
*/
|
|
2367
|
-
if (isProcessing) {
|
|
2368
|
-
flushNextFrame = true;
|
|
2369
|
-
return;
|
|
2370
|
-
}
|
|
2371
|
-
isProcessing = true;
|
|
2372
|
-
[toRun, toRunNextFrame] = [toRunNextFrame, toRun];
|
|
2373
|
-
// Clear the next frame list
|
|
2374
|
-
toRunNextFrame.length = 0;
|
|
2375
|
-
// Execute this frame
|
|
2376
|
-
numToRun = toRun.length;
|
|
2377
|
-
if (numToRun) {
|
|
2378
|
-
for (let i = 0; i < numToRun; i++) {
|
|
2379
|
-
const callback = toRun[i];
|
|
2380
|
-
callback(frameData);
|
|
2381
|
-
if (toKeepAlive.has(callback)) {
|
|
2382
|
-
step.schedule(callback);
|
|
2383
|
-
runNextFrame();
|
|
2384
|
-
}
|
|
2385
|
-
}
|
|
2386
|
-
}
|
|
2387
|
-
isProcessing = false;
|
|
2388
|
-
if (flushNextFrame) {
|
|
2389
|
-
flushNextFrame = false;
|
|
2390
|
-
step.process(frameData);
|
|
2391
|
-
}
|
|
2392
|
-
},
|
|
2393
|
-
};
|
|
2394
|
-
return step;
|
|
2370
|
+
stop() {
|
|
2371
|
+
if (this.stopAnimation)
|
|
2372
|
+
this.stopAnimation();
|
|
2373
|
+
this.clearAnimation();
|
|
2374
|
+
}
|
|
2375
|
+
/**
|
|
2376
|
+
* Returns `true` if this value is currently animating.
|
|
2377
|
+
*
|
|
2378
|
+
* @public
|
|
2379
|
+
*/
|
|
2380
|
+
isAnimating() {
|
|
2381
|
+
return !!this.stopAnimation;
|
|
2382
|
+
}
|
|
2383
|
+
clearAnimation() {
|
|
2384
|
+
this.stopAnimation = null;
|
|
2385
|
+
}
|
|
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();
|
|
2399
|
+
}
|
|
2400
|
+
}
|
|
2401
|
+
function motionValue(init) {
|
|
2402
|
+
return new MotionValue(init);
|
|
2395
2403
|
}
|
|
2396
2404
|
|
|
2397
|
-
|
|
2398
|
-
|
|
2399
|
-
|
|
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)));
|
|
2400
2412
|
};
|
|
2401
|
-
|
|
2402
|
-
|
|
2403
|
-
|
|
2404
|
-
|
|
2405
|
-
|
|
2406
|
-
|
|
2407
|
-
|
|
2408
|
-
|
|
2409
|
-
|
|
2410
|
-
"render",
|
|
2411
|
-
"postRender",
|
|
2412
|
-
];
|
|
2413
|
-
const steps = stepsOrder.reduce((acc, key) => {
|
|
2414
|
-
acc[key] = createRenderStep(() => (runNextFrame = true));
|
|
2415
|
-
return acc;
|
|
2416
|
-
}, {});
|
|
2417
|
-
const sync = stepsOrder.reduce((acc, key) => {
|
|
2418
|
-
const step = steps[key];
|
|
2419
|
-
acc[key] = (process, keepAlive = false, immediate = false) => {
|
|
2420
|
-
if (!runNextFrame)
|
|
2421
|
-
startLoop();
|
|
2422
|
-
return step.schedule(process, keepAlive, immediate);
|
|
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,
|
|
2423
2422
|
};
|
|
2424
|
-
return acc;
|
|
2425
|
-
}, {});
|
|
2426
|
-
const cancelSync = stepsOrder.reduce((acc, key) => {
|
|
2427
|
-
acc[key] = steps[key].cancel;
|
|
2428
|
-
return acc;
|
|
2429
|
-
}, {});
|
|
2430
|
-
const flushSync = stepsOrder.reduce((acc, key) => {
|
|
2431
|
-
acc[key] = () => steps[key].process(frameData);
|
|
2432
|
-
return acc;
|
|
2433
|
-
}, {});
|
|
2434
|
-
const processStep = (stepId) => steps[stepId].process(frameData);
|
|
2435
|
-
const processFrame = (timestamp) => {
|
|
2436
|
-
runNextFrame = false;
|
|
2437
|
-
frameData.delta = useDefaultElapsed
|
|
2438
|
-
? defaultTimestep
|
|
2439
|
-
: Math.max(Math.min(timestamp - frameData.timestamp, maxElapsed), 1);
|
|
2440
|
-
frameData.timestamp = timestamp;
|
|
2441
|
-
isProcessing = true;
|
|
2442
|
-
stepsOrder.forEach(processStep);
|
|
2443
|
-
isProcessing = false;
|
|
2444
|
-
if (runNextFrame) {
|
|
2445
|
-
useDefaultElapsed = false;
|
|
2446
|
-
onNextFrame(processFrame);
|
|
2447
|
-
}
|
|
2448
|
-
};
|
|
2449
|
-
const startLoop = () => {
|
|
2450
|
-
runNextFrame = true;
|
|
2451
|
-
useDefaultElapsed = true;
|
|
2452
|
-
if (!isProcessing)
|
|
2453
|
-
onNextFrame(processFrame);
|
|
2454
2423
|
};
|
|
2455
2424
|
|
|
2456
|
-
|
|
2457
|
-
|
|
2458
|
-
|
|
2459
|
-
|
|
2460
|
-
|
|
2461
|
-
|
|
2462
|
-
|
|
2463
|
-
|
|
2464
|
-
}
|
|
2465
|
-
|
|
2466
|
-
|
|
2467
|
-
|
|
2468
|
-
|
|
2469
|
-
|
|
2470
|
-
|
|
2471
|
-
|
|
2472
|
-
|
|
2473
|
-
|
|
2474
|
-
|
|
2475
|
-
So progress = 0.5 would change
|
|
2476
|
-
|
|
2477
|
-
from -------- to
|
|
2478
|
-
|
|
2479
|
-
to
|
|
2480
|
-
|
|
2481
|
-
from ---- to
|
|
2482
|
-
|
|
2483
|
-
E.g. from = 10, to = 20, progress = 0.5 => 15
|
|
2484
|
-
|
|
2485
|
-
@param [number]: Lower limit of range
|
|
2486
|
-
@param [number]: Upper limit of range
|
|
2487
|
-
@param [number]: The progress between lower and upper limits expressed 0-1
|
|
2488
|
-
@return [number]: Value as calculated from progress within range (not limited within range)
|
|
2489
|
-
*/
|
|
2490
|
-
const mix = (from, to, progress) => -progress * from + progress * to + from;
|
|
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
|
+
};
|
|
2491
2443
|
|
|
2492
|
-
|
|
2493
|
-
|
|
2494
|
-
|
|
2495
|
-
|
|
2496
|
-
|
|
2497
|
-
|
|
2498
|
-
if (
|
|
2499
|
-
|
|
2500
|
-
|
|
2501
|
-
|
|
2502
|
-
|
|
2503
|
-
|
|
2504
|
-
return p;
|
|
2505
|
-
}
|
|
2506
|
-
function hslaToRgba({ hue, saturation, lightness, alpha }) {
|
|
2507
|
-
hue /= 360;
|
|
2508
|
-
saturation /= 100;
|
|
2509
|
-
lightness /= 100;
|
|
2510
|
-
let red = 0;
|
|
2511
|
-
let green = 0;
|
|
2512
|
-
let blue = 0;
|
|
2513
|
-
if (!saturation) {
|
|
2514
|
-
red = green = blue = lightness;
|
|
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
|
|
2515
2456
|
}
|
|
2516
2457
|
else {
|
|
2517
|
-
|
|
2518
|
-
|
|
2519
|
-
|
|
2520
|
-
|
|
2521
|
-
|
|
2522
|
-
|
|
2523
|
-
|
|
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;
|
|
2524
2466
|
}
|
|
2525
2467
|
return {
|
|
2526
|
-
red:
|
|
2527
|
-
green:
|
|
2528
|
-
blue:
|
|
2529
|
-
alpha,
|
|
2468
|
+
red: parseInt(r, 16),
|
|
2469
|
+
green: parseInt(g, 16),
|
|
2470
|
+
blue: parseInt(b, 16),
|
|
2471
|
+
alpha: a ? parseInt(a, 16) / 255 : 1,
|
|
2530
2472
|
};
|
|
2531
2473
|
}
|
|
2474
|
+
const hex = {
|
|
2475
|
+
test: isColorString("#"),
|
|
2476
|
+
parse: parseHex,
|
|
2477
|
+
transform: rgba.transform,
|
|
2478
|
+
};
|
|
2532
2479
|
|
|
2533
|
-
|
|
2534
|
-
|
|
2535
|
-
|
|
2536
|
-
|
|
2537
|
-
|
|
2538
|
-
|
|
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
|
+
},
|
|
2539
2494
|
};
|
|
2540
|
-
|
|
2541
|
-
const
|
|
2542
|
-
|
|
2543
|
-
|
|
2544
|
-
|
|
2545
|
-
|
|
2546
|
-
|
|
2547
|
-
|
|
2548
|
-
|
|
2549
|
-
|
|
2550
|
-
|
|
2551
|
-
|
|
2552
|
-
|
|
2553
|
-
|
|
2554
|
-
|
|
2555
|
-
|
|
2556
|
-
|
|
2557
|
-
|
|
2558
|
-
|
|
2559
|
-
|
|
2560
|
-
|
|
2561
|
-
return rgba.transform(blended);
|
|
2562
|
-
};
|
|
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);
|
|
2501
|
+
}
|
|
2502
|
+
else if (hsla.test(v)) {
|
|
2503
|
+
return hsla.parse(v);
|
|
2504
|
+
}
|
|
2505
|
+
else {
|
|
2506
|
+
return hex.parse(v);
|
|
2507
|
+
}
|
|
2508
|
+
},
|
|
2509
|
+
transform: (v) => {
|
|
2510
|
+
return isString(v)
|
|
2511
|
+
? v
|
|
2512
|
+
: v.hasOwnProperty("red")
|
|
2513
|
+
? rgba.transform(v)
|
|
2514
|
+
: hsla.transform(v);
|
|
2515
|
+
},
|
|
2563
2516
|
};
|
|
2564
2517
|
|
|
2565
|
-
|
|
2566
|
-
|
|
2567
|
-
|
|
2568
|
-
|
|
2569
|
-
|
|
2570
|
-
|
|
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));
|
|
2571
2541
|
}
|
|
2572
|
-
|
|
2573
|
-
|
|
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));
|
|
2574
2547
|
}
|
|
2548
|
+
return { values, numColors, numNumbers, tokenised: v };
|
|
2575
2549
|
}
|
|
2576
|
-
|
|
2577
|
-
|
|
2578
|
-
|
|
2579
|
-
|
|
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;
|
|
2580
2556
|
return (v) => {
|
|
2557
|
+
let output = tokenised;
|
|
2581
2558
|
for (let i = 0; i < numValues; i++) {
|
|
2582
|
-
output
|
|
2583
|
-
|
|
2584
|
-
|
|
2585
|
-
};
|
|
2586
|
-
};
|
|
2587
|
-
const mixObject = (origin, target) => {
|
|
2588
|
-
const output = { ...origin, ...target };
|
|
2589
|
-
const blendValue = {};
|
|
2590
|
-
for (const key in output) {
|
|
2591
|
-
if (origin[key] !== undefined && target[key] !== undefined) {
|
|
2592
|
-
blendValue[key] = getMixer$1(origin[key], target[key]);
|
|
2593
|
-
}
|
|
2594
|
-
}
|
|
2595
|
-
return (v) => {
|
|
2596
|
-
for (const key in blendValue) {
|
|
2597
|
-
output[key] = blendValue[key](v);
|
|
2559
|
+
output = output.replace(i < numColors ? colorToken : numberToken, i < numColors
|
|
2560
|
+
? color.transform(v[i])
|
|
2561
|
+
: sanitize(v[i]));
|
|
2598
2562
|
}
|
|
2599
2563
|
return output;
|
|
2600
2564
|
};
|
|
2601
|
-
}
|
|
2602
|
-
const
|
|
2603
|
-
|
|
2604
|
-
const
|
|
2605
|
-
const
|
|
2606
|
-
|
|
2607
|
-
|
|
2608
|
-
|
|
2609
|
-
|
|
2610
|
-
|
|
2611
|
-
|
|
2612
|
-
|
|
2613
|
-
|
|
2614
|
-
|
|
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 };
|
|
2573
|
+
|
|
2574
|
+
/**
|
|
2575
|
+
* Properties that should default to 1 or 100%
|
|
2576
|
+
*/
|
|
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 + ")";
|
|
2590
|
+
}
|
|
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
|
+
},
|
|
2615
2598
|
};
|
|
2616
2599
|
|
|
2617
|
-
|
|
2618
|
-
|
|
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,
|
|
2619
|
+
};
|
|
2620
|
+
/**
|
|
2621
|
+
* Gets the default ValueType for the provided value key
|
|
2622
|
+
*/
|
|
2623
|
+
const getDefaultValueType = (key) => defaultValueTypes[key];
|
|
2619
2624
|
|
|
2620
|
-
|
|
2621
|
-
|
|
2622
|
-
|
|
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
|
+
}
|
|
2623
2633
|
|
|
2624
|
-
|
|
2625
|
-
|
|
2626
|
-
|
|
2627
|
-
|
|
2628
|
-
|
|
2629
|
-
|
|
2630
|
-
|
|
2631
|
-
|
|
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,
|
|
2632
2645
|
};
|
|
2633
2646
|
|
|
2634
|
-
const mixNumber = (from, to) => (p) => mix(from, to, p);
|
|
2635
|
-
function detectMixerFactory(v) {
|
|
2636
|
-
if (typeof v === "number") {
|
|
2637
|
-
return mixNumber;
|
|
2638
|
-
}
|
|
2639
|
-
else if (typeof v === "string") {
|
|
2640
|
-
if (color.test(v)) {
|
|
2641
|
-
return mixColor;
|
|
2642
|
-
}
|
|
2643
|
-
else {
|
|
2644
|
-
return mixComplex;
|
|
2645
|
-
}
|
|
2646
|
-
}
|
|
2647
|
-
else if (Array.isArray(v)) {
|
|
2648
|
-
return mixArray;
|
|
2649
|
-
}
|
|
2650
|
-
else if (typeof v === "object") {
|
|
2651
|
-
return mixObject;
|
|
2652
|
-
}
|
|
2653
|
-
return mixNumber;
|
|
2654
|
-
}
|
|
2655
|
-
function createMixers(output, ease, customMixer) {
|
|
2656
|
-
const mixers = [];
|
|
2657
|
-
const mixerFactory = customMixer || detectMixerFactory(output[0]);
|
|
2658
|
-
const numMixers = output.length - 1;
|
|
2659
|
-
for (let i = 0; i < numMixers; i++) {
|
|
2660
|
-
let mixer = mixerFactory(output[i], output[i + 1]);
|
|
2661
|
-
if (ease) {
|
|
2662
|
-
const easingFunction = Array.isArray(ease) ? ease[i] : ease;
|
|
2663
|
-
mixer = pipe(easingFunction, mixer);
|
|
2664
|
-
}
|
|
2665
|
-
mixers.push(mixer);
|
|
2666
|
-
}
|
|
2667
|
-
return mixers;
|
|
2668
|
-
}
|
|
2669
2647
|
/**
|
|
2670
|
-
*
|
|
2671
|
-
*
|
|
2672
|
-
* Accepts:
|
|
2673
|
-
* - Numbers
|
|
2674
|
-
* - Colors (hex, hsl, hsla, rgb, rgba)
|
|
2675
|
-
* - Complex (combinations of one or more numbers or strings)
|
|
2676
|
-
*
|
|
2677
|
-
* ```jsx
|
|
2678
|
-
* const mixColor = interpolate([0, 1], ['#fff', '#000'])
|
|
2679
|
-
*
|
|
2680
|
-
* mixColor(0.5) // 'rgba(128, 128, 128, 1)'
|
|
2681
|
-
* ```
|
|
2682
|
-
*
|
|
2683
|
-
* TODO Revist this approach once we've moved to data models for values,
|
|
2684
|
-
* probably not needed to pregenerate mixer functions.
|
|
2685
|
-
*
|
|
2686
|
-
* @public
|
|
2648
|
+
* A list of value types commonly used for dimensions
|
|
2687
2649
|
*/
|
|
2688
|
-
|
|
2689
|
-
|
|
2690
|
-
|
|
2691
|
-
|
|
2692
|
-
|
|
2693
|
-
if (input[0] > input[inputLength - 1]) {
|
|
2694
|
-
input = [...input].reverse();
|
|
2695
|
-
output = [...output].reverse();
|
|
2696
|
-
}
|
|
2697
|
-
const mixers = createMixers(output, ease, mixer);
|
|
2698
|
-
const numMixers = mixers.length;
|
|
2699
|
-
const interpolator = (v) => {
|
|
2700
|
-
let i = 0;
|
|
2701
|
-
if (numMixers > 1) {
|
|
2702
|
-
for (; i < input.length - 2; i++) {
|
|
2703
|
-
if (v < input[i + 1])
|
|
2704
|
-
break;
|
|
2705
|
-
}
|
|
2706
|
-
}
|
|
2707
|
-
const progressInRange = progress(input[i], input[i + 1], v);
|
|
2708
|
-
return mixers[i](progressInRange);
|
|
2709
|
-
};
|
|
2710
|
-
return isClamp
|
|
2711
|
-
? (v) => interpolator(clamp(input[0], input[inputLength - 1], v))
|
|
2712
|
-
: interpolator;
|
|
2713
|
-
}
|
|
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));
|
|
2714
2655
|
|
|
2715
|
-
|
|
2716
|
-
|
|
2717
|
-
|
|
2718
|
-
|
|
2719
|
-
|
|
2720
|
-
|
|
2721
|
-
|
|
2722
|
-
|
|
2723
|
-
return offset.map((o) => o * duration);
|
|
2724
|
-
}
|
|
2725
|
-
function keyframes({ from = 0, to = 1, ease, offset, duration = 300, }) {
|
|
2726
|
-
/**
|
|
2727
|
-
* This is the Iterator-spec return value. We ensure it's mutable rather than using a generator
|
|
2728
|
-
* to reduce GC during animation.
|
|
2729
|
-
*/
|
|
2730
|
-
const state = { done: false, value: from };
|
|
2731
|
-
/**
|
|
2732
|
-
* Convert values to an array if they've been given as from/to options
|
|
2733
|
-
*/
|
|
2734
|
-
const values = Array.isArray(to) ? to : [from, to];
|
|
2735
|
-
/**
|
|
2736
|
-
* Create a times array based on the provided 0-1 offsets
|
|
2737
|
-
*/
|
|
2738
|
-
const times = convertOffsetToTimes(
|
|
2739
|
-
// Only use the provided offsets if they're the correct length
|
|
2740
|
-
// TODO Maybe we should warn here if there's a length mismatch
|
|
2741
|
-
offset && offset.length === values.length
|
|
2742
|
-
? offset
|
|
2743
|
-
: defaultOffset(values), duration);
|
|
2744
|
-
function createInterpolator() {
|
|
2745
|
-
return interpolate(times, values, {
|
|
2746
|
-
ease: Array.isArray(ease) ? ease : defaultEasing(values, ease),
|
|
2747
|
-
});
|
|
2748
|
-
}
|
|
2749
|
-
let interpolator = createInterpolator();
|
|
2750
|
-
return {
|
|
2751
|
-
next: (t) => {
|
|
2752
|
-
state.value = interpolator(t);
|
|
2753
|
-
state.done = t >= duration;
|
|
2754
|
-
return state;
|
|
2755
|
-
},
|
|
2756
|
-
flipTarget: () => {
|
|
2757
|
-
values.reverse();
|
|
2758
|
-
interpolator = createInterpolator();
|
|
2759
|
-
},
|
|
2760
|
-
};
|
|
2761
|
-
}
|
|
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));
|
|
2762
2664
|
|
|
2763
|
-
|
|
2764
|
-
|
|
2765
|
-
|
|
2766
|
-
|
|
2767
|
-
const
|
|
2768
|
-
|
|
2769
|
-
|
|
2770
|
-
|
|
2771
|
-
|
|
2772
|
-
|
|
2773
|
-
|
|
2774
|
-
|
|
2775
|
-
|
|
2776
|
-
|
|
2777
|
-
|
|
2778
|
-
|
|
2779
|
-
|
|
2780
|
-
|
|
2781
|
-
|
|
2782
|
-
|
|
2783
|
-
|
|
2784
|
-
|
|
2785
|
-
|
|
2786
|
-
|
|
2787
|
-
|
|
2788
|
-
|
|
2789
|
-
|
|
2790
|
-
|
|
2791
|
-
const exponentialDecay = undampedFreq * dampingRatio;
|
|
2792
|
-
const delta = exponentialDecay * duration;
|
|
2793
|
-
const d = delta * velocity + velocity;
|
|
2794
|
-
const e = Math.pow(dampingRatio, 2) * Math.pow(undampedFreq, 2) * duration;
|
|
2795
|
-
const f = Math.exp(-delta);
|
|
2796
|
-
const g = calcAngularFreq(Math.pow(undampedFreq, 2), dampingRatio);
|
|
2797
|
-
const factor = -envelope(undampedFreq) + safeMin > 0 ? -1 : 1;
|
|
2798
|
-
return (factor * ((d - e) * f)) / g;
|
|
2799
|
-
};
|
|
2800
|
-
}
|
|
2801
|
-
else {
|
|
2802
|
-
/**
|
|
2803
|
-
* Critically-damped spring
|
|
2804
|
-
*/
|
|
2805
|
-
envelope = (undampedFreq) => {
|
|
2806
|
-
const a = Math.exp(-undampedFreq * duration);
|
|
2807
|
-
const b = (undampedFreq - velocity) * duration + 1;
|
|
2808
|
-
return -safeMin + a * b;
|
|
2809
|
-
};
|
|
2810
|
-
derivative = (undampedFreq) => {
|
|
2811
|
-
const a = Math.exp(-undampedFreq * duration);
|
|
2812
|
-
const b = (velocity - undampedFreq) * (duration * duration);
|
|
2813
|
-
return a * b;
|
|
2814
|
-
};
|
|
2815
|
-
}
|
|
2816
|
-
const initialGuess = 5 / duration;
|
|
2817
|
-
const undampedFreq = approximateRoot(envelope, derivative, initialGuess);
|
|
2818
|
-
duration = duration * 1000;
|
|
2819
|
-
if (isNaN(undampedFreq)) {
|
|
2820
|
-
return {
|
|
2821
|
-
stiffness: 100,
|
|
2822
|
-
damping: 10,
|
|
2823
|
-
duration,
|
|
2824
|
-
};
|
|
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;
|
|
2672
|
+
}
|
|
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;
|
|
2680
|
+
}
|
|
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));
|
|
2684
|
+
}
|
|
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);
|
|
2825
2693
|
}
|
|
2826
2694
|
else {
|
|
2827
|
-
|
|
2828
|
-
return {
|
|
2829
|
-
stiffness,
|
|
2830
|
-
damping: dampingRatio * 2 * Math.sqrt(mass * stiffness),
|
|
2831
|
-
duration,
|
|
2832
|
-
};
|
|
2695
|
+
visualElement.addValue(key, motionValue(value));
|
|
2833
2696
|
}
|
|
2834
2697
|
}
|
|
2835
|
-
|
|
2836
|
-
|
|
2837
|
-
let
|
|
2838
|
-
|
|
2839
|
-
|
|
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);
|
|
2840
2705
|
}
|
|
2841
|
-
return result;
|
|
2842
2706
|
}
|
|
2843
|
-
function
|
|
2844
|
-
|
|
2845
|
-
|
|
2846
|
-
|
|
2847
|
-
const
|
|
2848
|
-
|
|
2849
|
-
|
|
2850
|
-
|
|
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
|
+
});
|
|
2851
2717
|
}
|
|
2852
|
-
function
|
|
2853
|
-
|
|
2854
|
-
|
|
2855
|
-
|
|
2856
|
-
|
|
2857
|
-
|
|
2858
|
-
|
|
2859
|
-
|
|
2860
|
-
|
|
2861
|
-
// stiffness/damping/mass overrides duration/bounce
|
|
2862
|
-
if (!isSpringType(options, physicsKeys) &&
|
|
2863
|
-
isSpringType(options, durationKeys)) {
|
|
2864
|
-
const derived = findSpring(options);
|
|
2865
|
-
springOptions = {
|
|
2866
|
-
...springOptions,
|
|
2867
|
-
...derived,
|
|
2868
|
-
velocity: 0.0,
|
|
2869
|
-
mass: 1.0,
|
|
2870
|
-
};
|
|
2871
|
-
springOptions.isResolvedFromDuration = true;
|
|
2718
|
+
function setValues(visualElement, definition) {
|
|
2719
|
+
if (Array.isArray(definition)) {
|
|
2720
|
+
return setVariants(visualElement, definition);
|
|
2721
|
+
}
|
|
2722
|
+
else if (typeof definition === "string") {
|
|
2723
|
+
return setVariants(visualElement, [definition]);
|
|
2724
|
+
}
|
|
2725
|
+
else {
|
|
2726
|
+
setTarget(visualElement, definition);
|
|
2872
2727
|
}
|
|
2873
|
-
return springOptions;
|
|
2874
2728
|
}
|
|
2875
|
-
|
|
2876
|
-
|
|
2877
|
-
|
|
2878
|
-
|
|
2879
|
-
|
|
2880
|
-
|
|
2881
|
-
|
|
2882
|
-
|
|
2883
|
-
|
|
2884
|
-
|
|
2885
|
-
let resolveSpring = zero;
|
|
2886
|
-
let resolveVelocity = zero;
|
|
2887
|
-
function createSpring() {
|
|
2888
|
-
const initialVelocity = velocity ? -(velocity / 1000) : 0.0;
|
|
2889
|
-
const initialDelta = to - from;
|
|
2890
|
-
const dampingRatio = damping / (2 * Math.sqrt(stiffness * mass));
|
|
2891
|
-
const undampedAngularFreq = Math.sqrt(stiffness / mass) / 1000;
|
|
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;
|
|
2892
2739
|
/**
|
|
2893
|
-
* If
|
|
2894
|
-
* to
|
|
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.
|
|
2895
2742
|
*/
|
|
2896
|
-
if (
|
|
2897
|
-
|
|
2743
|
+
if (Array.isArray(targetValue)) {
|
|
2744
|
+
value = targetValue[0];
|
|
2898
2745
|
}
|
|
2899
|
-
|
|
2900
|
-
|
|
2901
|
-
|
|
2902
|
-
|
|
2903
|
-
|
|
2904
|
-
|
|
2905
|
-
|
|
2906
|
-
(((initialVelocity +
|
|
2907
|
-
dampingRatio * undampedAngularFreq * initialDelta) /
|
|
2908
|
-
angularFreq) *
|
|
2909
|
-
Math.sin(angularFreq * t) +
|
|
2910
|
-
initialDelta * Math.cos(angularFreq * t)));
|
|
2911
|
-
};
|
|
2912
|
-
resolveVelocity = (t) => {
|
|
2913
|
-
// TODO Resolve these calculations with the above
|
|
2914
|
-
const envelope = Math.exp(-dampingRatio * undampedAngularFreq * t);
|
|
2915
|
-
return (dampingRatio *
|
|
2916
|
-
undampedAngularFreq *
|
|
2917
|
-
envelope *
|
|
2918
|
-
((Math.sin(angularFreq * t) *
|
|
2919
|
-
(initialVelocity +
|
|
2920
|
-
dampingRatio *
|
|
2921
|
-
undampedAngularFreq *
|
|
2922
|
-
initialDelta)) /
|
|
2923
|
-
angularFreq +
|
|
2924
|
-
initialDelta * Math.cos(angularFreq * t)) -
|
|
2925
|
-
envelope *
|
|
2926
|
-
(Math.cos(angularFreq * t) *
|
|
2927
|
-
(initialVelocity +
|
|
2928
|
-
dampingRatio *
|
|
2929
|
-
undampedAngularFreq *
|
|
2930
|
-
initialDelta) -
|
|
2931
|
-
angularFreq *
|
|
2932
|
-
initialDelta *
|
|
2933
|
-
Math.sin(angularFreq * t)));
|
|
2934
|
-
};
|
|
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];
|
|
2935
2753
|
}
|
|
2936
|
-
|
|
2937
|
-
|
|
2938
|
-
|
|
2939
|
-
|
|
2940
|
-
|
|
2941
|
-
|
|
2942
|
-
|
|
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);
|
|
2943
2764
|
}
|
|
2944
|
-
else {
|
|
2945
|
-
|
|
2946
|
-
|
|
2947
|
-
|
|
2948
|
-
|
|
2949
|
-
|
|
2950
|
-
const freqForT = Math.min(dampedAngularFreq * t, 300);
|
|
2951
|
-
return (to -
|
|
2952
|
-
(envelope *
|
|
2953
|
-
((initialVelocity +
|
|
2954
|
-
dampingRatio * undampedAngularFreq * initialDelta) *
|
|
2955
|
-
Math.sinh(freqForT) +
|
|
2956
|
-
dampedAngularFreq *
|
|
2957
|
-
initialDelta *
|
|
2958
|
-
Math.cosh(freqForT))) /
|
|
2959
|
-
dampedAngularFreq);
|
|
2960
|
-
};
|
|
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;
|
|
2961
2771
|
}
|
|
2772
|
+
if (value !== null)
|
|
2773
|
+
visualElement.setBaseTarget(key, value);
|
|
2962
2774
|
}
|
|
2963
|
-
|
|
2964
|
-
|
|
2965
|
-
|
|
2966
|
-
|
|
2967
|
-
|
|
2968
|
-
|
|
2969
|
-
|
|
2970
|
-
|
|
2971
|
-
|
|
2972
|
-
|
|
2973
|
-
|
|
2974
|
-
|
|
2975
|
-
|
|
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();
|
|
2791
|
+
}
|
|
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) {
|
|
2806
|
+
/**
|
|
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.
|
|
2812
|
+
*/
|
|
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);
|
|
2976
2820
|
}
|
|
2977
|
-
|
|
2978
|
-
|
|
2979
|
-
|
|
2980
|
-
|
|
2981
|
-
|
|
2982
|
-
|
|
2983
|
-
|
|
2984
|
-
|
|
2821
|
+
catch (e) { }
|
|
2822
|
+
});
|
|
2823
|
+
return animation.currentTime || 0;
|
|
2824
|
+
}
|
|
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 () => { };
|
|
2850
|
+
}
|
|
2851
|
+
|
|
2852
|
+
/**
|
|
2853
|
+
* Timeout defined in ms
|
|
2854
|
+
*/
|
|
2855
|
+
function delay(callback, timeout) {
|
|
2856
|
+
const start = performance.now();
|
|
2857
|
+
const checkElapsed = ({ timestamp }) => {
|
|
2858
|
+
const elapsed = timestamp - start;
|
|
2859
|
+
if (elapsed >= timeout) {
|
|
2860
|
+
cancelSync.read(checkElapsed);
|
|
2861
|
+
callback(elapsed - timeout);
|
|
2862
|
+
}
|
|
2985
2863
|
};
|
|
2864
|
+
sync.read(checkElapsed, true);
|
|
2865
|
+
return () => cancelSync.read(checkElapsed);
|
|
2986
2866
|
}
|
|
2987
|
-
spring.needsInterpolation = (a, b) => typeof a === "string" || typeof b === "string";
|
|
2988
|
-
const zero = (_t) => 0;
|
|
2989
2867
|
|
|
2990
|
-
function
|
|
2991
|
-
|
|
2992
|
-
|
|
2993
|
-
|
|
2994
|
-
|
|
2995
|
-
const state = { done: false, value: from };
|
|
2996
|
-
let amplitude = power * velocity;
|
|
2997
|
-
const ideal = from + amplitude;
|
|
2998
|
-
const target = modifyTarget === undefined ? ideal : modifyTarget(ideal);
|
|
2999
|
-
/**
|
|
3000
|
-
* If the target has changed we need to re-calculate the amplitude, otherwise
|
|
3001
|
-
* the animation will start from the wrong position.
|
|
3002
|
-
*/
|
|
3003
|
-
if (target !== ideal)
|
|
3004
|
-
amplitude = target - from;
|
|
3005
|
-
return {
|
|
3006
|
-
next: (t) => {
|
|
3007
|
-
const delta = -amplitude * Math.exp(-t / timeConstant);
|
|
3008
|
-
state.done = !(delta > restDelta || delta < -restDelta);
|
|
3009
|
-
state.value = state.done ? target : target + delta;
|
|
3010
|
-
return state;
|
|
3011
|
-
},
|
|
3012
|
-
flipTarget: () => { },
|
|
2868
|
+
function createInstantAnimation({ keyframes, elapsed, onUpdate, onComplete, }) {
|
|
2869
|
+
const setValue = () => {
|
|
2870
|
+
onUpdate && onUpdate(keyframes[keyframes.length - 1]);
|
|
2871
|
+
onComplete && onComplete();
|
|
2872
|
+
return () => { };
|
|
3013
2873
|
};
|
|
2874
|
+
return elapsed ? delay(setValue, -elapsed) : setValue();
|
|
3014
2875
|
}
|
|
3015
2876
|
|
|
3016
|
-
|
|
3017
|
-
|
|
3018
|
-
|
|
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;
|
|
3019
2925
|
}
|
|
3020
|
-
function
|
|
3021
|
-
|
|
3022
|
-
|
|
3023
|
-
|
|
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
|
+
};
|
|
3024
2951
|
}
|
|
3025
|
-
|
|
3026
|
-
|
|
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;
|
|
3027
2971
|
}
|
|
3028
|
-
const
|
|
3029
|
-
const
|
|
3030
|
-
|
|
3031
|
-
|
|
3032
|
-
|
|
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);
|
|
3033
2982
|
};
|
|
3034
2983
|
};
|
|
3035
|
-
|
|
3036
|
-
|
|
3037
|
-
|
|
3038
|
-
|
|
3039
|
-
let repeatCount = 0;
|
|
3040
|
-
let computedDuration = options
|
|
3041
|
-
.duration;
|
|
3042
|
-
let latest;
|
|
3043
|
-
let isComplete = false;
|
|
3044
|
-
let isForwardPlayback = true;
|
|
3045
|
-
let interpolateFromNumber;
|
|
3046
|
-
const animator = types[Array.isArray(to) ? "keyframes" : type];
|
|
3047
|
-
if ((_b = (_a = animator).needsInterpolation) === null || _b === void 0 ? void 0 : _b.call(_a, from, to)) {
|
|
3048
|
-
interpolateFromNumber = interpolate([0, 100], [from, to], {
|
|
3049
|
-
clamp: false,
|
|
3050
|
-
});
|
|
3051
|
-
from = 0;
|
|
3052
|
-
to = 100;
|
|
2984
|
+
|
|
2985
|
+
function getMixer$1(origin, target) {
|
|
2986
|
+
if (typeof origin === "number") {
|
|
2987
|
+
return (v) => mix(origin, target, v);
|
|
3053
2988
|
}
|
|
3054
|
-
|
|
3055
|
-
|
|
3056
|
-
|
|
3057
|
-
|
|
3058
|
-
|
|
3059
|
-
|
|
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;
|
|
3058
|
+
}
|
|
3059
|
+
else if (typeof v === "string") {
|
|
3060
|
+
if (color.test(v)) {
|
|
3061
|
+
return mixColor;
|
|
3060
3062
|
}
|
|
3061
3063
|
else {
|
|
3062
|
-
|
|
3063
|
-
|
|
3064
|
-
|
|
3064
|
+
return mixComplex;
|
|
3065
|
+
}
|
|
3066
|
+
}
|
|
3067
|
+
else if (Array.isArray(v)) {
|
|
3068
|
+
return mixArray;
|
|
3069
|
+
}
|
|
3070
|
+
else if (typeof v === "object") {
|
|
3071
|
+
return mixObject;
|
|
3072
|
+
}
|
|
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);
|
|
3065
3084
|
}
|
|
3066
|
-
|
|
3067
|
-
onRepeat && onRepeat();
|
|
3085
|
+
mixers.push(mixer);
|
|
3068
3086
|
}
|
|
3069
|
-
|
|
3070
|
-
|
|
3071
|
-
|
|
3087
|
+
return mixers;
|
|
3088
|
+
}
|
|
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
|
+
* ```
|
|
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
|
|
3107
|
+
*/
|
|
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();
|
|
3072
3116
|
}
|
|
3073
|
-
|
|
3074
|
-
|
|
3075
|
-
|
|
3076
|
-
|
|
3077
|
-
if (
|
|
3078
|
-
|
|
3079
|
-
|
|
3080
|
-
|
|
3081
|
-
latest = interpolateFromNumber(latest);
|
|
3082
|
-
isComplete = isForwardPlayback ? state.done : elapsed <= 0;
|
|
3083
|
-
}
|
|
3084
|
-
onUpdate === null || onUpdate === void 0 ? void 0 : onUpdate(latest);
|
|
3085
|
-
if (isComplete) {
|
|
3086
|
-
if (repeatCount === 0) {
|
|
3087
|
-
computedDuration =
|
|
3088
|
-
computedDuration !== undefined ? computedDuration : elapsed;
|
|
3089
|
-
}
|
|
3090
|
-
if (repeatCount < repeatMax) {
|
|
3091
|
-
hasRepeatDelayElapsed(elapsed, computedDuration, repeatDelay, isForwardPlayback) && repeat();
|
|
3092
|
-
}
|
|
3093
|
-
else {
|
|
3094
|
-
complete();
|
|
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;
|
|
3095
3125
|
}
|
|
3096
3126
|
}
|
|
3097
|
-
|
|
3098
|
-
|
|
3099
|
-
onPlay === null || onPlay === void 0 ? void 0 : onPlay();
|
|
3100
|
-
driverControls = driver(update);
|
|
3101
|
-
driverControls.start();
|
|
3102
|
-
}
|
|
3103
|
-
autoplay && play();
|
|
3104
|
-
return {
|
|
3105
|
-
stop: () => {
|
|
3106
|
-
onStop === null || onStop === void 0 ? void 0 : onStop();
|
|
3107
|
-
driverControls.stop();
|
|
3108
|
-
},
|
|
3127
|
+
const progressInRange = progress(input[i], input[i + 1], v);
|
|
3128
|
+
return mixers[i](progressInRange);
|
|
3109
3129
|
};
|
|
3130
|
+
return isClamp
|
|
3131
|
+
? (v) => interpolator(clamp(input[0], input[inputLength - 1], v))
|
|
3132
|
+
: interpolator;
|
|
3110
3133
|
}
|
|
3111
3134
|
|
|
3112
|
-
|
|
3113
|
-
Convert velocity into velocity per second
|
|
3135
|
+
const noop = (any) => any;
|
|
3114
3136
|
|
|
3115
|
-
|
|
3116
|
-
|
|
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...
|
|
3117
3153
|
*/
|
|
3118
|
-
|
|
3119
|
-
|
|
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;
|
|
3175
|
+
}
|
|
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);
|
|
3120
3183
|
}
|
|
3121
3184
|
|
|
3122
|
-
|
|
3123
|
-
|
|
3124
|
-
|
|
3125
|
-
|
|
3126
|
-
|
|
3127
|
-
|
|
3128
|
-
|
|
3129
|
-
|
|
3130
|
-
|
|
3131
|
-
|
|
3132
|
-
|
|
3133
|
-
|
|
3134
|
-
|
|
3135
|
-
|
|
3136
|
-
|
|
3137
|
-
|
|
3138
|
-
|
|
3139
|
-
|
|
3140
|
-
|
|
3141
|
-
|
|
3142
|
-
|
|
3143
|
-
|
|
3144
|
-
|
|
3145
|
-
|
|
3146
|
-
|
|
3147
|
-
|
|
3148
|
-
|
|
3149
|
-
|
|
3150
|
-
|
|
3151
|
-
|
|
3152
|
-
|
|
3153
|
-
|
|
3154
|
-
|
|
3155
|
-
|
|
3156
|
-
|
|
3157
|
-
|
|
3158
|
-
// Start the animation with spring if outside the defined boundaries
|
|
3159
|
-
startSpring({ from, velocity, to: boundaryNearest(from) });
|
|
3185
|
+
const circIn = (p) => 1 - Math.sin(Math.acos(p));
|
|
3186
|
+
const circOut = reverseEasing(circIn);
|
|
3187
|
+
const circInOut = mirrorEasing(circOut);
|
|
3188
|
+
|
|
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);
|
|
3193
|
+
|
|
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);
|
|
3160
3221
|
}
|
|
3161
|
-
else {
|
|
3162
|
-
|
|
3163
|
-
|
|
3164
|
-
|
|
3165
|
-
*
|
|
3166
|
-
* Pre-calculate the target so we can detect if it's out-of-bounds.
|
|
3167
|
-
* If it is, we want to check per frame when to switch to a spring
|
|
3168
|
-
* animation
|
|
3169
|
-
*/
|
|
3170
|
-
let target = power * velocity + from;
|
|
3171
|
-
if (typeof modifyTarget !== "undefined")
|
|
3172
|
-
target = modifyTarget(target);
|
|
3173
|
-
const boundary = boundaryNearest(target);
|
|
3174
|
-
const heading = boundary === min ? -1 : 1;
|
|
3175
|
-
let prev;
|
|
3176
|
-
let current;
|
|
3177
|
-
const checkBoundary = (v) => {
|
|
3178
|
-
prev = current;
|
|
3179
|
-
current = v;
|
|
3180
|
-
velocity = velocityPerSecond(v - prev, frameData.delta);
|
|
3181
|
-
if ((heading === 1 && v > boundary) ||
|
|
3182
|
-
(heading === -1 && v < boundary)) {
|
|
3183
|
-
startSpring({ from: v, to: boundary, velocity });
|
|
3184
|
-
}
|
|
3185
|
-
};
|
|
3186
|
-
startAnimation({
|
|
3187
|
-
type: "decay",
|
|
3188
|
-
from,
|
|
3189
|
-
velocity,
|
|
3190
|
-
timeConstant,
|
|
3191
|
-
power,
|
|
3192
|
-
restDelta,
|
|
3193
|
-
modifyTarget,
|
|
3194
|
-
onUpdate: isOutOfBounds(target) ? checkBoundary : undefined,
|
|
3195
|
-
});
|
|
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];
|
|
3196
3226
|
}
|
|
3197
|
-
return
|
|
3198
|
-
|
|
3199
|
-
|
|
3200
|
-
|
|
3227
|
+
return definition;
|
|
3228
|
+
};
|
|
3229
|
+
const isEasingArray = (ease) => {
|
|
3230
|
+
return Array.isArray(ease) && typeof ease[0] !== "number";
|
|
3231
|
+
};
|
|
3201
3232
|
|
|
3202
|
-
|
|
3203
|
-
|
|
3204
|
-
|
|
3205
|
-
|
|
3206
|
-
|
|
3207
|
-
|
|
3208
|
-
return !!Object.keys(transition).length;
|
|
3233
|
+
function defaultEasing(values, easing) {
|
|
3234
|
+
return values.map(() => easing || easeInOut).splice(0, values.length - 1);
|
|
3235
|
+
}
|
|
3236
|
+
function defaultOffset(values) {
|
|
3237
|
+
const numValues = values.length;
|
|
3238
|
+
return values.map((_value, i) => i !== 0 ? i / (numValues - 1) : 0);
|
|
3209
3239
|
}
|
|
3210
|
-
|
|
3211
|
-
|
|
3212
|
-
|
|
3213
|
-
function
|
|
3214
|
-
|
|
3215
|
-
|
|
3216
|
-
options["offset"] = times;
|
|
3217
|
-
/**
|
|
3218
|
-
* Convert any existing durations from seconds to milliseconds
|
|
3219
|
-
*/
|
|
3220
|
-
if (transition.duration)
|
|
3221
|
-
options["duration"] = secondsToMilliseconds(transition.duration);
|
|
3222
|
-
if (transition.repeatDelay)
|
|
3223
|
-
options.repeatDelay = secondsToMilliseconds(transition.repeatDelay);
|
|
3240
|
+
function convertOffsetToTimes(offset, duration) {
|
|
3241
|
+
return offset.map((o) => o * duration);
|
|
3242
|
+
}
|
|
3243
|
+
function keyframes({ keyframes: keyframeValues, ease = easeInOut, times, duration = 300, }) {
|
|
3244
|
+
keyframeValues = [...keyframeValues];
|
|
3245
|
+
const origin = keyframes[0];
|
|
3224
3246
|
/**
|
|
3225
|
-
*
|
|
3247
|
+
* Easing functions can be externally defined as strings. Here we convert them
|
|
3248
|
+
* into actual functions.
|
|
3226
3249
|
*/
|
|
3227
|
-
|
|
3228
|
-
|
|
3229
|
-
|
|
3230
|
-
: easingDefinitionToFunction(ease);
|
|
3231
|
-
}
|
|
3250
|
+
const easingFunctions = isEasingArray(ease)
|
|
3251
|
+
? ease.map(easingDefinitionToFunction)
|
|
3252
|
+
: easingDefinitionToFunction(ease);
|
|
3232
3253
|
/**
|
|
3233
|
-
*
|
|
3254
|
+
* This is the Iterator-spec return value. We ensure it's mutable rather than using a generator
|
|
3255
|
+
* to reduce GC during animation.
|
|
3234
3256
|
*/
|
|
3235
|
-
|
|
3236
|
-
options.type = "keyframes";
|
|
3257
|
+
const state = { done: false, value: origin };
|
|
3237
3258
|
/**
|
|
3238
|
-
*
|
|
3239
|
-
* a keyframes or spring animation, but does so by detecting velocity and other spring options.
|
|
3240
|
-
* It'd be good to introduce a similar thing here.
|
|
3259
|
+
* Create a times array based on the provided 0-1 offsets
|
|
3241
3260
|
*/
|
|
3242
|
-
|
|
3243
|
-
|
|
3244
|
-
|
|
3245
|
-
|
|
3246
|
-
|
|
3247
|
-
|
|
3248
|
-
|
|
3249
|
-
|
|
3250
|
-
|
|
3251
|
-
|
|
3252
|
-
|
|
3253
|
-
|
|
3254
|
-
? transition.delay
|
|
3255
|
-
: 0;
|
|
3256
|
-
}
|
|
3257
|
-
function hydrateKeyframes(options) {
|
|
3258
|
-
if (Array.isArray(options.to) && options.to[0] === null) {
|
|
3259
|
-
options.to = [...options.to];
|
|
3260
|
-
options.to[0] = options.from;
|
|
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
|
+
});
|
|
3261
3273
|
}
|
|
3262
|
-
|
|
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
|
+
};
|
|
3263
3286
|
}
|
|
3264
|
-
|
|
3265
|
-
|
|
3266
|
-
|
|
3267
|
-
|
|
3268
|
-
|
|
3287
|
+
|
|
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;
|
|
3269
3298
|
/**
|
|
3270
|
-
*
|
|
3299
|
+
* Restrict dampingRatio and duration to within acceptable ranges.
|
|
3271
3300
|
*/
|
|
3272
|
-
|
|
3273
|
-
|
|
3274
|
-
|
|
3275
|
-
|
|
3301
|
+
dampingRatio = clamp(minDamping, maxDamping, dampingRatio);
|
|
3302
|
+
duration = clamp(minDuration, maxDuration, duration / 1000);
|
|
3303
|
+
if (dampingRatio < 1) {
|
|
3304
|
+
/**
|
|
3305
|
+
* Underdamped spring
|
|
3306
|
+
*/
|
|
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;
|
|
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;
|
|
3276
3324
|
};
|
|
3277
3325
|
}
|
|
3278
|
-
|
|
3279
|
-
...options,
|
|
3280
|
-
...convertTransitionToAnimationOptions(transition),
|
|
3281
|
-
};
|
|
3282
|
-
}
|
|
3283
|
-
/**
|
|
3284
|
-
*
|
|
3285
|
-
*/
|
|
3286
|
-
function getAnimation(key, value, target, transition, onComplete) {
|
|
3287
|
-
const valueTransition = getValueTransition(transition, key) || {};
|
|
3288
|
-
let origin = valueTransition.from !== undefined ? valueTransition.from : value.get();
|
|
3289
|
-
const isTargetAnimatable = isAnimatable(key, target);
|
|
3290
|
-
if (origin === "none" && isTargetAnimatable && typeof target === "string") {
|
|
3326
|
+
else {
|
|
3291
3327
|
/**
|
|
3292
|
-
*
|
|
3293
|
-
* of the target. This could be improved to work both ways.
|
|
3328
|
+
* Critically-damped spring
|
|
3294
3329
|
*/
|
|
3295
|
-
|
|
3296
|
-
|
|
3297
|
-
|
|
3298
|
-
|
|
3299
|
-
|
|
3300
|
-
|
|
3301
|
-
|
|
3302
|
-
|
|
3303
|
-
|
|
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
|
+
};
|
|
3304
3340
|
}
|
|
3305
|
-
const
|
|
3306
|
-
|
|
3307
|
-
|
|
3308
|
-
|
|
3309
|
-
|
|
3310
|
-
|
|
3311
|
-
|
|
3312
|
-
|
|
3313
|
-
onUpdate: (v) => value.set(v),
|
|
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,
|
|
3314
3349
|
};
|
|
3315
|
-
return valueTransition.type === "inertia" ||
|
|
3316
|
-
valueTransition.type === "decay"
|
|
3317
|
-
? inertia({ ...options, ...valueTransition })
|
|
3318
|
-
: animate$1({
|
|
3319
|
-
...getPopmotionAnimationOptions(valueTransition, options, key),
|
|
3320
|
-
onUpdate: (v) => {
|
|
3321
|
-
options.onUpdate(v);
|
|
3322
|
-
valueTransition.onUpdate && valueTransition.onUpdate(v);
|
|
3323
|
-
},
|
|
3324
|
-
onComplete: () => {
|
|
3325
|
-
options.onComplete();
|
|
3326
|
-
valueTransition.onComplete && valueTransition.onComplete();
|
|
3327
|
-
},
|
|
3328
|
-
});
|
|
3329
3350
|
}
|
|
3330
|
-
|
|
3331
|
-
const
|
|
3332
|
-
|
|
3333
|
-
|
|
3334
|
-
|
|
3335
|
-
|
|
3336
|
-
|
|
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
|
+
};
|
|
3337
3358
|
}
|
|
3338
|
-
return !isOriginAnimatable ||
|
|
3339
|
-
!isTargetAnimatable ||
|
|
3340
|
-
valueTransition.type === false
|
|
3341
|
-
? set
|
|
3342
|
-
: start;
|
|
3343
3359
|
}
|
|
3344
|
-
|
|
3345
|
-
|
|
3346
|
-
|
|
3347
|
-
|
|
3348
|
-
|
|
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;
|
|
3349
3367
|
}
|
|
3350
|
-
function
|
|
3351
|
-
return
|
|
3352
|
-
? 0
|
|
3353
|
-
: getAnimatableNone("", potentialUnitType);
|
|
3368
|
+
function calcAngularFreq(undampedFreq, dampingRatio) {
|
|
3369
|
+
return undampedFreq * Math.sqrt(1 - dampingRatio * dampingRatio);
|
|
3354
3370
|
}
|
|
3355
|
-
|
|
3356
|
-
|
|
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);
|
|
3357
3376
|
}
|
|
3358
|
-
|
|
3359
|
-
|
|
3360
|
-
|
|
3361
|
-
|
|
3362
|
-
|
|
3363
|
-
|
|
3364
|
-
|
|
3365
|
-
|
|
3366
|
-
|
|
3367
|
-
|
|
3368
|
-
|
|
3369
|
-
|
|
3370
|
-
const
|
|
3371
|
-
|
|
3372
|
-
|
|
3373
|
-
|
|
3374
|
-
|
|
3375
|
-
|
|
3376
|
-
start();
|
|
3377
|
-
}
|
|
3378
|
-
return () => {
|
|
3379
|
-
cancelDelay && cancelDelay();
|
|
3380
|
-
controls && controls.stop();
|
|
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,
|
|
3381
3395
|
};
|
|
3382
|
-
|
|
3396
|
+
springOptions.isResolvedFromDuration = true;
|
|
3397
|
+
}
|
|
3398
|
+
return springOptions;
|
|
3383
3399
|
}
|
|
3384
|
-
|
|
3385
|
-
/**
|
|
3386
|
-
* Check if value is a numerical string, ie a string that is purely a number eg "100" or "-100.1"
|
|
3387
|
-
*/
|
|
3388
|
-
const isNumericalString = (v) => /^\-?\d*\.?\d+$/.test(v);
|
|
3389
|
-
|
|
3400
|
+
const velocitySampleDuration = 5;
|
|
3390
3401
|
/**
|
|
3391
|
-
*
|
|
3402
|
+
* This is based on the spring implementation of Wobble https://github.com/skevy/wobble
|
|
3392
3403
|
*/
|
|
3393
|
-
|
|
3394
|
-
|
|
3395
|
-
|
|
3396
|
-
|
|
3397
|
-
|
|
3398
|
-
|
|
3399
|
-
|
|
3400
|
-
const
|
|
3401
|
-
|
|
3402
|
-
|
|
3403
|
-
|
|
3404
|
-
|
|
3405
|
-
function
|
|
3406
|
-
|
|
3407
|
-
|
|
3408
|
-
|
|
3409
|
-
|
|
3410
|
-
|
|
3411
|
-
|
|
3412
|
-
|
|
3413
|
-
|
|
3414
|
-
|
|
3415
|
-
class SubscriptionManager {
|
|
3416
|
-
constructor() {
|
|
3417
|
-
this.subscriptions = [];
|
|
3418
|
-
}
|
|
3419
|
-
add(handler) {
|
|
3420
|
-
addUniqueItem(this.subscriptions, handler);
|
|
3421
|
-
return () => removeItem(this.subscriptions, handler);
|
|
3422
|
-
}
|
|
3423
|
-
notify(a, b, c) {
|
|
3424
|
-
const numSubscriptions = this.subscriptions.length;
|
|
3425
|
-
if (!numSubscriptions)
|
|
3426
|
-
return;
|
|
3427
|
-
if (numSubscriptions === 1) {
|
|
3428
|
-
/**
|
|
3429
|
-
* If there's only a single handler we can just call it without invoking a loop.
|
|
3430
|
-
*/
|
|
3431
|
-
this.subscriptions[0](a, b, c);
|
|
3404
|
+
function spring({ keyframes, restSpeed = 2, restDelta = 0.01, ...options }) {
|
|
3405
|
+
let origin = keyframes[0];
|
|
3406
|
+
let target = keyframes[keyframes.length - 1];
|
|
3407
|
+
/**
|
|
3408
|
+
* This is the Iterator-spec return value. We ensure it's mutable rather than using a generator
|
|
3409
|
+
* to reduce GC during animation.
|
|
3410
|
+
*/
|
|
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);
|
|
3432
3425
|
}
|
|
3433
|
-
|
|
3434
|
-
|
|
3435
|
-
|
|
3436
|
-
|
|
3437
|
-
|
|
3438
|
-
|
|
3439
|
-
|
|
3440
|
-
|
|
3441
|
-
|
|
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
|
+
};
|
|
3442
3465
|
}
|
|
3443
3466
|
}
|
|
3444
|
-
|
|
3445
|
-
|
|
3446
|
-
|
|
3447
|
-
|
|
3448
|
-
|
|
3449
|
-
|
|
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
|
+
};
|
|
3450
3504
|
}
|
|
3505
|
+
spring.needsInterpolation = (a, b) => typeof a === "string" || typeof b === "string";
|
|
3506
|
+
const zero = (_t) => 0;
|
|
3451
3507
|
|
|
3452
|
-
|
|
3453
|
-
return !isNaN(parseFloat(value));
|
|
3454
|
-
};
|
|
3508
|
+
function decay({
|
|
3455
3509
|
/**
|
|
3456
|
-
*
|
|
3457
|
-
*
|
|
3458
|
-
*
|
|
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.
|
|
3459
3513
|
*/
|
|
3460
|
-
|
|
3514
|
+
keyframes = [0], velocity = 0, power = 0.8, timeConstant = 350, restDelta = 0.5, modifyTarget, }) {
|
|
3515
|
+
const origin = keyframes[0];
|
|
3461
3516
|
/**
|
|
3462
|
-
*
|
|
3463
|
-
*
|
|
3464
|
-
*
|
|
3465
|
-
* - `transformer`: A function to transform incoming values with.
|
|
3466
|
-
*
|
|
3467
|
-
* @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.
|
|
3468
3519
|
*/
|
|
3469
|
-
|
|
3470
|
-
|
|
3471
|
-
|
|
3472
|
-
|
|
3473
|
-
*/
|
|
3474
|
-
this.version = "7.7.3";
|
|
3475
|
-
/**
|
|
3476
|
-
* Duration, in milliseconds, since last updating frame.
|
|
3477
|
-
*
|
|
3478
|
-
* @internal
|
|
3479
|
-
*/
|
|
3480
|
-
this.timeDelta = 0;
|
|
3481
|
-
/**
|
|
3482
|
-
* Timestamp of the last time this `MotionValue` was updated.
|
|
3483
|
-
*
|
|
3484
|
-
* @internal
|
|
3485
|
-
*/
|
|
3486
|
-
this.lastUpdated = 0;
|
|
3487
|
-
/**
|
|
3488
|
-
* Functions to notify when the `MotionValue` updates.
|
|
3489
|
-
*
|
|
3490
|
-
* @internal
|
|
3491
|
-
*/
|
|
3492
|
-
this.updateSubscribers = new SubscriptionManager();
|
|
3493
|
-
/**
|
|
3494
|
-
* Functions to notify when the velocity updates.
|
|
3495
|
-
*
|
|
3496
|
-
* @internal
|
|
3497
|
-
*/
|
|
3498
|
-
this.velocityUpdateSubscribers = new SubscriptionManager();
|
|
3499
|
-
/**
|
|
3500
|
-
* Functions to notify when the `MotionValue` updates and `render` is set to `true`.
|
|
3501
|
-
*
|
|
3502
|
-
* @internal
|
|
3503
|
-
*/
|
|
3504
|
-
this.renderSubscribers = new SubscriptionManager();
|
|
3505
|
-
/**
|
|
3506
|
-
* Tracks whether this value can output a velocity. Currently this is only true
|
|
3507
|
-
* if the value is numerical, but we might be able to widen the scope here and support
|
|
3508
|
-
* other value types.
|
|
3509
|
-
*
|
|
3510
|
-
* @internal
|
|
3511
|
-
*/
|
|
3512
|
-
this.canTrackVelocity = false;
|
|
3513
|
-
this.updateAndNotify = (v, render = true) => {
|
|
3514
|
-
this.prev = this.current;
|
|
3515
|
-
this.current = v;
|
|
3516
|
-
// Update timestamp
|
|
3517
|
-
const { delta, timestamp } = frameData;
|
|
3518
|
-
if (this.lastUpdated !== timestamp) {
|
|
3519
|
-
this.timeDelta = delta;
|
|
3520
|
-
this.lastUpdated = timestamp;
|
|
3521
|
-
sync.postRender(this.scheduleVelocityCheck);
|
|
3522
|
-
}
|
|
3523
|
-
// Update update subscribers
|
|
3524
|
-
if (this.prev !== this.current) {
|
|
3525
|
-
this.updateSubscribers.notify(this.current);
|
|
3526
|
-
}
|
|
3527
|
-
// Update velocity subscribers
|
|
3528
|
-
if (this.velocityUpdateSubscribers.getSize()) {
|
|
3529
|
-
this.velocityUpdateSubscribers.notify(this.getVelocity());
|
|
3530
|
-
}
|
|
3531
|
-
// Update render subscribers
|
|
3532
|
-
if (render) {
|
|
3533
|
-
this.renderSubscribers.notify(this.current);
|
|
3534
|
-
}
|
|
3535
|
-
};
|
|
3536
|
-
/**
|
|
3537
|
-
* Schedule a velocity check for the next frame.
|
|
3538
|
-
*
|
|
3539
|
-
* This is an instanced and bound function to prevent generating a new
|
|
3540
|
-
* function once per frame.
|
|
3541
|
-
*
|
|
3542
|
-
* @internal
|
|
3543
|
-
*/
|
|
3544
|
-
this.scheduleVelocityCheck = () => sync.postRender(this.velocityCheck);
|
|
3545
|
-
/**
|
|
3546
|
-
* Updates `prev` with `current` if the value hasn't been updated this frame.
|
|
3547
|
-
* This ensures velocity calculations return `0`.
|
|
3548
|
-
*
|
|
3549
|
-
* This is an instanced and bound function to prevent generating a new
|
|
3550
|
-
* function once per frame.
|
|
3551
|
-
*
|
|
3552
|
-
* @internal
|
|
3553
|
-
*/
|
|
3554
|
-
this.velocityCheck = ({ timestamp }) => {
|
|
3555
|
-
if (timestamp !== this.lastUpdated) {
|
|
3556
|
-
this.prev = this.current;
|
|
3557
|
-
this.velocityUpdateSubscribers.notify(this.getVelocity());
|
|
3558
|
-
}
|
|
3559
|
-
};
|
|
3560
|
-
this.hasAnimated = false;
|
|
3561
|
-
this.prev = this.current = init;
|
|
3562
|
-
this.canTrackVelocity = isFloat(this.current);
|
|
3563
|
-
}
|
|
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);
|
|
3564
3524
|
/**
|
|
3565
|
-
*
|
|
3566
|
-
*
|
|
3567
|
-
* It returns a function that, when called, will cancel the subscription.
|
|
3568
|
-
*
|
|
3569
|
-
* When calling `onChange` inside a React component, it should be wrapped with the
|
|
3570
|
-
* `useEffect` hook. As it returns an unsubscribe function, this should be returned
|
|
3571
|
-
* from the `useEffect` function to ensure you don't add duplicate subscribers..
|
|
3572
|
-
*
|
|
3573
|
-
* ```jsx
|
|
3574
|
-
* export const MyComponent = () => {
|
|
3575
|
-
* const x = useMotionValue(0)
|
|
3576
|
-
* const y = useMotionValue(0)
|
|
3577
|
-
* const opacity = useMotionValue(1)
|
|
3578
|
-
*
|
|
3579
|
-
* useEffect(() => {
|
|
3580
|
-
* function updateOpacity() {
|
|
3581
|
-
* const maxXY = Math.max(x.get(), y.get())
|
|
3582
|
-
* const newOpacity = transform(maxXY, [0, 100], [1, 0])
|
|
3583
|
-
* opacity.set(newOpacity)
|
|
3584
|
-
* }
|
|
3585
|
-
*
|
|
3586
|
-
* const unsubscribeX = x.onChange(updateOpacity)
|
|
3587
|
-
* const unsubscribeY = y.onChange(updateOpacity)
|
|
3588
|
-
*
|
|
3589
|
-
* return () => {
|
|
3590
|
-
* unsubscribeX()
|
|
3591
|
-
* unsubscribeY()
|
|
3592
|
-
* }
|
|
3593
|
-
* }, [])
|
|
3594
|
-
*
|
|
3595
|
-
* return <motion.div style={{ x }} />
|
|
3596
|
-
* }
|
|
3597
|
-
* ```
|
|
3598
|
-
*
|
|
3599
|
-
* @privateRemarks
|
|
3600
|
-
*
|
|
3601
|
-
* We could look into a `useOnChange` hook if the above lifecycle management proves confusing.
|
|
3602
|
-
*
|
|
3603
|
-
* ```jsx
|
|
3604
|
-
* useOnChange(x, () => {})
|
|
3605
|
-
* ```
|
|
3606
|
-
*
|
|
3607
|
-
* @param subscriber - A function that receives the latest value.
|
|
3608
|
-
* @returns A function that, when called, will cancel this subscription.
|
|
3609
|
-
*
|
|
3610
|
-
* @public
|
|
3525
|
+
* If the target has changed we need to re-calculate the amplitude, otherwise
|
|
3526
|
+
* the animation will start from the wrong position.
|
|
3611
3527
|
*/
|
|
3612
|
-
|
|
3613
|
-
|
|
3614
|
-
|
|
3615
|
-
|
|
3616
|
-
|
|
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];
|
|
3617
3582
|
}
|
|
3618
|
-
|
|
3619
|
-
|
|
3620
|
-
|
|
3621
|
-
|
|
3622
|
-
|
|
3623
|
-
|
|
3624
|
-
|
|
3625
|
-
|
|
3626
|
-
|
|
3627
|
-
|
|
3628
|
-
|
|
3629
|
-
|
|
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);
|
|
3593
|
+
}
|
|
3594
|
+
else {
|
|
3595
|
+
elapsed = loopElapsed(elapsed, computedDuration, repeatDelay);
|
|
3596
|
+
if (repeatType === "mirror")
|
|
3597
|
+
animation.flipTarget();
|
|
3598
|
+
}
|
|
3599
|
+
isComplete = false;
|
|
3600
|
+
onRepeat && onRepeat();
|
|
3630
3601
|
}
|
|
3631
|
-
|
|
3632
|
-
|
|
3633
|
-
|
|
3634
|
-
* @internal
|
|
3635
|
-
*/
|
|
3636
|
-
attach(passiveEffect) {
|
|
3637
|
-
this.passiveEffect = passiveEffect;
|
|
3602
|
+
function complete() {
|
|
3603
|
+
driverControls.stop();
|
|
3604
|
+
onComplete && onComplete();
|
|
3638
3605
|
}
|
|
3639
|
-
|
|
3640
|
-
|
|
3641
|
-
|
|
3642
|
-
|
|
3643
|
-
|
|
3644
|
-
|
|
3645
|
-
|
|
3646
|
-
|
|
3647
|
-
|
|
3648
|
-
|
|
3649
|
-
* @param latest - Latest value to set.
|
|
3650
|
-
* @param render - Whether to notify render subscribers. Defaults to `true`
|
|
3651
|
-
*
|
|
3652
|
-
* @public
|
|
3653
|
-
*/
|
|
3654
|
-
set(v, render = true) {
|
|
3655
|
-
if (!render || !this.passiveEffect) {
|
|
3656
|
-
this.updateAndNotify(v, render);
|
|
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;
|
|
3657
3616
|
}
|
|
3658
|
-
|
|
3659
|
-
|
|
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
|
+
}
|
|
3660
3629
|
}
|
|
3661
3630
|
}
|
|
3662
|
-
|
|
3663
|
-
|
|
3664
|
-
|
|
3665
|
-
|
|
3666
|
-
*
|
|
3667
|
-
* @public
|
|
3668
|
-
*/
|
|
3669
|
-
get() {
|
|
3670
|
-
return this.current;
|
|
3631
|
+
function play() {
|
|
3632
|
+
onPlay && onPlay();
|
|
3633
|
+
driverControls = driver(update);
|
|
3634
|
+
driverControls.start();
|
|
3671
3635
|
}
|
|
3672
|
-
|
|
3673
|
-
|
|
3674
|
-
|
|
3675
|
-
|
|
3676
|
-
|
|
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);
|
|
3677
3650
|
}
|
|
3678
|
-
|
|
3679
|
-
|
|
3680
|
-
|
|
3681
|
-
|
|
3682
|
-
|
|
3683
|
-
|
|
3684
|
-
*/
|
|
3685
|
-
getVelocity() {
|
|
3686
|
-
// This could be isFloat(this.prev) && isFloat(this.current), but that would be wasteful
|
|
3687
|
-
return this.canTrackVelocity
|
|
3688
|
-
? // These casts could be avoided if parseFloat would be typed better
|
|
3689
|
-
velocityPerSecond(parseFloat(this.current) -
|
|
3690
|
-
parseFloat(this.prev), this.timeDelta)
|
|
3691
|
-
: 0;
|
|
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;
|
|
3692
3657
|
}
|
|
3693
|
-
|
|
3694
|
-
|
|
3695
|
-
|
|
3696
|
-
|
|
3697
|
-
|
|
3698
|
-
|
|
3699
|
-
|
|
3700
|
-
|
|
3701
|
-
|
|
3702
|
-
|
|
3703
|
-
|
|
3704
|
-
|
|
3705
|
-
|
|
3706
|
-
|
|
3707
|
-
|
|
3708
|
-
this.hasAnimated = true;
|
|
3709
|
-
this.stopAnimation = animation(resolve);
|
|
3710
|
-
}).then(() => this.clearAnimation());
|
|
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
|
+
});
|
|
3711
3673
|
}
|
|
3712
|
-
|
|
3713
|
-
|
|
3714
|
-
|
|
3715
|
-
|
|
3716
|
-
|
|
3717
|
-
|
|
3718
|
-
|
|
3719
|
-
|
|
3720
|
-
this.clearAnimation();
|
|
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
|
-
return !!this.stopAnimation;
|
|
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
|
+
});
|
|
3729
3689
|
}
|
|
3730
|
-
|
|
3731
|
-
|
|
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
|
+
});
|
|
3732
3725
|
}
|
|
3733
|
-
|
|
3734
|
-
|
|
3735
|
-
|
|
3736
|
-
|
|
3737
|
-
|
|
3738
|
-
|
|
3739
|
-
|
|
3740
|
-
|
|
3741
|
-
|
|
3742
|
-
|
|
3743
|
-
|
|
3744
|
-
|
|
3745
|
-
|
|
3726
|
+
return {
|
|
3727
|
+
stop: () => currentAnimation === null || currentAnimation === void 0 ? void 0 : currentAnimation.stop(),
|
|
3728
|
+
};
|
|
3729
|
+
}
|
|
3730
|
+
|
|
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]);
|
|
3746
3775
|
}
|
|
3747
|
-
}
|
|
3748
|
-
function motionValue(init) {
|
|
3749
|
-
return new MotionValue(init);
|
|
3750
|
-
}
|
|
3751
|
-
|
|
3752
|
-
/**
|
|
3753
|
-
* Tests a provided value against a ValueType
|
|
3754
|
-
*/
|
|
3755
|
-
const testValueType = (v) => (type) => type.test(v);
|
|
3756
|
-
|
|
3757
|
-
/**
|
|
3758
|
-
* ValueType for "auto"
|
|
3759
|
-
*/
|
|
3760
|
-
const auto = {
|
|
3761
|
-
test: (v) => v === "auto",
|
|
3762
|
-
parse: (v) => v,
|
|
3763
3776
|
};
|
|
3764
3777
|
|
|
3765
3778
|
/**
|
|
3766
|
-
*
|
|
3767
|
-
|
|
3768
|
-
|
|
3769
|
-
|
|
3770
|
-
*
|
|
3771
|
-
|
|
3772
|
-
|
|
3773
|
-
|
|
3774
|
-
/**
|
|
3775
|
-
* A list of all ValueTypes
|
|
3776
|
-
*/
|
|
3777
|
-
const valueTypes = [...dimensionValueTypes, color, complex];
|
|
3778
|
-
/**
|
|
3779
|
-
* 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
|
|
3780
3786
|
*/
|
|
3781
|
-
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
|
+
};
|
|
3782
3804
|
|
|
3783
3805
|
/**
|
|
3784
|
-
*
|
|
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.
|
|
3785
3809
|
*/
|
|
3786
|
-
function
|
|
3787
|
-
|
|
3788
|
-
visualElement.values.forEach((value, key) => (current[key] = value.get()));
|
|
3789
|
-
return current;
|
|
3810
|
+
function isTransitionDefined({ when, delay: _delay, delayChildren, staggerChildren, staggerDirection, repeat, repeatType, repeatDelay, from, ...transition }) {
|
|
3811
|
+
return !!Object.keys(transition).length;
|
|
3790
3812
|
}
|
|
3791
|
-
|
|
3792
|
-
|
|
3793
|
-
|
|
3794
|
-
|
|
3795
|
-
|
|
3796
|
-
visualElement.values.forEach((value, key) => (velocity[key] = value.getVelocity()));
|
|
3797
|
-
return velocity;
|
|
3813
|
+
function isZero(value) {
|
|
3814
|
+
return (value === 0 ||
|
|
3815
|
+
(typeof value === "string" &&
|
|
3816
|
+
parseFloat(value) === 0 &&
|
|
3817
|
+
value.indexOf(" ") === -1));
|
|
3798
3818
|
}
|
|
3799
|
-
function
|
|
3800
|
-
|
|
3801
|
-
|
|
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;
|
|
3802
3826
|
}
|
|
3803
3827
|
|
|
3804
|
-
|
|
3805
|
-
|
|
3806
|
-
|
|
3807
|
-
|
|
3808
|
-
|
|
3809
|
-
|
|
3810
|
-
|
|
3811
|
-
|
|
3812
|
-
|
|
3813
|
-
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);
|
|
3814
3837
|
}
|
|
3815
|
-
|
|
3816
|
-
|
|
3817
|
-
const resolved = resolveVariant(visualElement, definition);
|
|
3818
|
-
let { transitionEnd = {}, transition = {}, ...target } = resolved ? visualElement.makeTargetAnimatable(resolved, false) : {};
|
|
3819
|
-
target = { ...target, ...transitionEnd };
|
|
3820
|
-
for (const key in target) {
|
|
3821
|
-
const value = resolveFinalValueInKeyframes(target[key]);
|
|
3822
|
-
setMotionValue(visualElement, key, value);
|
|
3838
|
+
else if (isZero(origin) && typeof target === "string") {
|
|
3839
|
+
origin = getZeroUnit(target);
|
|
3823
3840
|
}
|
|
3824
|
-
|
|
3825
|
-
|
|
3826
|
-
|
|
3827
|
-
|
|
3828
|
-
var _a;
|
|
3829
|
-
const variant = visualElement.getVariant(key);
|
|
3830
|
-
variant && setTarget(visualElement, variant);
|
|
3831
|
-
(_a = visualElement.variantChildren) === null || _a === void 0 ? void 0 : _a.forEach((child) => {
|
|
3832
|
-
setVariants(child, variantLabels);
|
|
3833
|
-
});
|
|
3834
|
-
});
|
|
3835
|
-
}
|
|
3836
|
-
function setValues(visualElement, definition) {
|
|
3837
|
-
if (Array.isArray(definition)) {
|
|
3838
|
-
return setVariants(visualElement, definition);
|
|
3841
|
+
else if (!Array.isArray(target) &&
|
|
3842
|
+
isZero(target) &&
|
|
3843
|
+
typeof origin === "string") {
|
|
3844
|
+
target = getZeroUnit(origin);
|
|
3839
3845
|
}
|
|
3840
|
-
|
|
3841
|
-
|
|
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;
|
|
3842
3858
|
}
|
|
3843
3859
|
else {
|
|
3844
|
-
|
|
3860
|
+
return [origin, target];
|
|
3845
3861
|
}
|
|
3846
3862
|
}
|
|
3847
|
-
|
|
3848
|
-
|
|
3849
|
-
|
|
3850
|
-
|
|
3851
|
-
|
|
3852
|
-
|
|
3853
|
-
|
|
3854
|
-
|
|
3855
|
-
|
|
3856
|
-
|
|
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) || {};
|
|
3857
3887
|
/**
|
|
3858
|
-
*
|
|
3859
|
-
*
|
|
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.
|
|
3860
3891
|
*/
|
|
3861
|
-
|
|
3862
|
-
value = targetValue[0];
|
|
3863
|
-
}
|
|
3892
|
+
const delay = valueTransition.delay || transition.delay || 0;
|
|
3864
3893
|
/**
|
|
3865
|
-
*
|
|
3866
|
-
*
|
|
3867
|
-
* 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.
|
|
3868
3896
|
*/
|
|
3869
|
-
|
|
3870
|
-
|
|
3897
|
+
let { elapsed = 0 } = transition;
|
|
3898
|
+
elapsed = elapsed - secondsToMilliseconds(delay);
|
|
3899
|
+
const keyframes = getKeyframes(value, valueName, target, valueTransition);
|
|
3900
|
+
/**
|
|
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.
|
|
3904
|
+
*/
|
|
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);
|
|
3933
|
+
}
|
|
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();
|
|
3871
3941
|
}
|
|
3872
3942
|
/**
|
|
3873
|
-
* If
|
|
3874
|
-
*
|
|
3943
|
+
* If there's no transition defined for this value, we can generate
|
|
3944
|
+
* unqiue transition settings for this value.
|
|
3875
3945
|
*/
|
|
3876
|
-
if (
|
|
3877
|
-
|
|
3878
|
-
|
|
3879
|
-
|
|
3880
|
-
|
|
3881
|
-
value = parseFloat(value);
|
|
3946
|
+
if (!isTransitionDefined(valueTransition)) {
|
|
3947
|
+
options = {
|
|
3948
|
+
...options,
|
|
3949
|
+
...getDefaultTransition(valueName, options),
|
|
3950
|
+
};
|
|
3882
3951
|
}
|
|
3883
|
-
|
|
3884
|
-
|
|
3952
|
+
/**
|
|
3953
|
+
* Both WAAPI and our internal animation functions use durations
|
|
3954
|
+
* as defined by milliseconds, while our external API defines them
|
|
3955
|
+
* as seconds.
|
|
3956
|
+
*/
|
|
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) {
|
|
3969
|
+
/**
|
|
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.
|
|
3974
|
+
*/
|
|
3975
|
+
return createAcceleratedAnimation();
|
|
3885
3976
|
}
|
|
3886
|
-
|
|
3887
|
-
|
|
3888
|
-
|
|
3977
|
+
else {
|
|
3978
|
+
/**
|
|
3979
|
+
* Otherwise, fall back to the main thread.
|
|
3980
|
+
*/
|
|
3981
|
+
const animation = animate$1(options);
|
|
3982
|
+
return () => animation.stop();
|
|
3889
3983
|
}
|
|
3890
|
-
|
|
3891
|
-
|
|
3892
|
-
}
|
|
3893
|
-
}
|
|
3894
|
-
function getOriginFromTransition(key, transition) {
|
|
3895
|
-
if (!transition)
|
|
3896
|
-
return;
|
|
3897
|
-
const valueTransition = transition[key] || transition["default"] || transition;
|
|
3898
|
-
return valueTransition.from;
|
|
3899
|
-
}
|
|
3900
|
-
function getOrigin(target, transition, visualElement) {
|
|
3901
|
-
var _a;
|
|
3902
|
-
const origin = {};
|
|
3903
|
-
for (const key in target) {
|
|
3904
|
-
const transitionOrigin = getOriginFromTransition(key, transition);
|
|
3905
|
-
origin[key] =
|
|
3906
|
-
transitionOrigin !== undefined
|
|
3907
|
-
? transitionOrigin
|
|
3908
|
-
: (_a = visualElement.getValue(key)) === null || _a === void 0 ? void 0 : _a.get();
|
|
3909
|
-
}
|
|
3910
|
-
return origin;
|
|
3911
|
-
}
|
|
3912
|
-
|
|
3913
|
-
function isWillChangeMotionValue(value) {
|
|
3914
|
-
return Boolean(isMotionValue(value) && value.add);
|
|
3915
|
-
}
|
|
3984
|
+
};
|
|
3985
|
+
};
|
|
3916
3986
|
|
|
3917
3987
|
function animateVisualElement(visualElement, definition, options = {}) {
|
|
3918
3988
|
visualElement.notify("AnimationStart", definition);
|
|
@@ -3991,7 +4061,7 @@ function animateTarget(visualElement, definition, { delay = 0, transitionOverrid
|
|
|
3991
4061
|
shouldBlockAnimation(animationTypeState, key))) {
|
|
3992
4062
|
continue;
|
|
3993
4063
|
}
|
|
3994
|
-
let valueTransition = { delay, ...transition };
|
|
4064
|
+
let valueTransition = { delay, elapsed: 0, ...transition };
|
|
3995
4065
|
/**
|
|
3996
4066
|
* Make animation instant if this is a transform prop and we should reduce motion.
|
|
3997
4067
|
*/
|
|
@@ -4002,7 +4072,17 @@ function animateTarget(visualElement, definition, { delay = 0, transitionOverrid
|
|
|
4002
4072
|
delay: 0,
|
|
4003
4073
|
};
|
|
4004
4074
|
}
|
|
4005
|
-
|
|
4075
|
+
/**
|
|
4076
|
+
* If this is the first time a value is being animated, check
|
|
4077
|
+
* to see if we're handling off from an existing animation.
|
|
4078
|
+
*/
|
|
4079
|
+
if (!value.hasAnimated) {
|
|
4080
|
+
const appearId = visualElement.getProps()[optimizedAppearDataAttribute];
|
|
4081
|
+
if (appearId) {
|
|
4082
|
+
valueTransition.elapsed = handoffOptimizedAppearAnimation(appearId, key);
|
|
4083
|
+
}
|
|
4084
|
+
}
|
|
4085
|
+
let animation = value.start(createMotionValueAnimation(key, value, valueTarget, valueTransition));
|
|
4006
4086
|
if (isWillChangeMotionValue(willChange)) {
|
|
4007
4087
|
willChange.add(key);
|
|
4008
4088
|
animation = animation.then(() => willChange.remove(key));
|
|
@@ -5158,7 +5238,7 @@ class VisualElementDragControls {
|
|
|
5158
5238
|
}
|
|
5159
5239
|
startAxisValueAnimation(axis, transition) {
|
|
5160
5240
|
const axisValue = this.getAxisMotionValue(axis);
|
|
5161
|
-
return
|
|
5241
|
+
return axisValue.start(createMotionValueAnimation(axis, axisValue, 0, transition));
|
|
5162
5242
|
}
|
|
5163
5243
|
stopAnimation() {
|
|
5164
5244
|
eachAxis((axis) => this.getAxisMotionValue(axis).stop());
|
|
@@ -5753,7 +5833,7 @@ function updateMotionValuesFromProps(element, next, prev) {
|
|
|
5753
5833
|
* and warn against mismatches.
|
|
5754
5834
|
*/
|
|
5755
5835
|
if (process.env.NODE_ENV === "development") {
|
|
5756
|
-
warnOnce(nextValue.version === "7.
|
|
5836
|
+
warnOnce(nextValue.version === "7.8.1", `Attempting to mix Framer Motion versions ${nextValue.version} with 7.8.1 may not work as expected.`);
|
|
5757
5837
|
}
|
|
5758
5838
|
}
|
|
5759
5839
|
else if (isMotionValue(prevValue)) {
|
|
@@ -6600,7 +6680,7 @@ const layoutFeatures = {
|
|
|
6600
6680
|
*/
|
|
6601
6681
|
function animate(from, to, transition = {}) {
|
|
6602
6682
|
const value = isMotionValue(from) ? from : motionValue(from);
|
|
6603
|
-
|
|
6683
|
+
value.start(createMotionValueAnimation("", value, to, transition));
|
|
6604
6684
|
return {
|
|
6605
6685
|
stop: () => value.stop(),
|
|
6606
6686
|
isAnimating: () => value.isAnimating(),
|
|
@@ -9059,8 +9139,7 @@ function useSpring(source, config = {}) {
|
|
|
9059
9139
|
activeSpringAnimation.current.stop();
|
|
9060
9140
|
}
|
|
9061
9141
|
activeSpringAnimation.current = animate$1({
|
|
9062
|
-
|
|
9063
|
-
to: v,
|
|
9142
|
+
keyframes: [value.get(), v],
|
|
9064
9143
|
velocity: value.getVelocity(),
|
|
9065
9144
|
type: "spring",
|
|
9066
9145
|
...config,
|
|
@@ -9573,6 +9652,30 @@ function useResetProjection() {
|
|
|
9573
9652
|
return reset;
|
|
9574
9653
|
}
|
|
9575
9654
|
|
|
9655
|
+
const cubicBezierAsString = ([a, b, c, d]) => `cubic-bezier(${a}, ${b}, ${c}, ${d})`;
|
|
9656
|
+
|
|
9657
|
+
function animateStyle(element, valueName, keyframes, { delay, duration, ease }) {
|
|
9658
|
+
if (!supports.waapi())
|
|
9659
|
+
return undefined;
|
|
9660
|
+
const animation = element.animate({ [valueName]: keyframes }, {
|
|
9661
|
+
delay,
|
|
9662
|
+
duration,
|
|
9663
|
+
easing: Array.isArray(ease) ? cubicBezierAsString(ease) : ease,
|
|
9664
|
+
fill: "both",
|
|
9665
|
+
});
|
|
9666
|
+
return animation;
|
|
9667
|
+
}
|
|
9668
|
+
|
|
9669
|
+
function startOptimizedAppearAnimation(element, name, keyframes, options) {
|
|
9670
|
+
window.MotionAppearAnimations || (window.MotionAppearAnimations = new Map());
|
|
9671
|
+
const id = element.dataset[optimizedAppearDataId];
|
|
9672
|
+
const animation = animateStyle(element, name, keyframes, options);
|
|
9673
|
+
if (id && animation) {
|
|
9674
|
+
window.MotionAppearAnimations.set(appearStoreId(id, name), animation);
|
|
9675
|
+
}
|
|
9676
|
+
return animation;
|
|
9677
|
+
}
|
|
9678
|
+
|
|
9576
9679
|
const createObject = () => ({});
|
|
9577
9680
|
class StateVisualElement extends VisualElement {
|
|
9578
9681
|
build() { }
|
|
@@ -9722,8 +9825,11 @@ exports.makeUseVisualState = makeUseVisualState;
|
|
|
9722
9825
|
exports.mix = mix;
|
|
9723
9826
|
exports.motion = motion;
|
|
9724
9827
|
exports.motionValue = motionValue;
|
|
9828
|
+
exports.optimizedAppearDataAttribute = optimizedAppearDataAttribute;
|
|
9725
9829
|
exports.pipe = pipe;
|
|
9726
9830
|
exports.resolveMotionValue = resolveMotionValue;
|
|
9831
|
+
exports.spring = spring;
|
|
9832
|
+
exports.startOptimizedAppearAnimation = startOptimizedAppearAnimation;
|
|
9727
9833
|
exports.transform = transform;
|
|
9728
9834
|
exports.unwrapMotionComponent = unwrapMotionComponent;
|
|
9729
9835
|
exports.useAnimation = useAnimation;
|