framer-motion 7.8.0 → 7.9.0

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