UNPKG

framer-motion

Version:

A simple and powerful React animation library

1,277 lines (1,221 loc) • 418 kB
(function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('react')) : typeof define === 'function' && define.amd ? define(['exports', 'react'], factory) : (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.Motion = {}, global.React)); })(this, (function (exports, React) { 'use strict'; function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; } function _interopNamespace(e) { if (e && e.__esModule) return e; var n = Object.create(null); if (e) { Object.keys(e).forEach(function (k) { if (k !== 'default') { var d = Object.getOwnPropertyDescriptor(e, k); Object.defineProperty(n, k, d.get ? d : { enumerable: true, get: function () { return e[k]; } }); } }); } n["default"] = e; return Object.freeze(n); } var React__namespace = /*#__PURE__*/_interopNamespace(React); var React__default = /*#__PURE__*/_interopDefaultLegacy(React); /*! ***************************************************************************** Copyright (c) Microsoft Corporation. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ***************************************************************************** */ /* global Reflect, Promise */ var extendStatics = function(d, b) { extendStatics = Object.setPrototypeOf || ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; }; return extendStatics(d, b); }; function __extends(d, b) { if (typeof b !== "function" && b !== null) throw new TypeError("Class extends value " + String(b) + " is not a constructor or null"); extendStatics(d, b); function __() { this.constructor = d; } d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); } var __assign = function() { __assign = Object.assign || function __assign(t) { for (var s, i = 1, n = arguments.length; i < n; i++) { s = arguments[i]; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]; } return t; }; return __assign.apply(this, arguments); }; function __rest(s, e) { var t = {}; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) t[p] = s[p]; if (s != null && typeof Object.getOwnPropertySymbols === "function") for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) { if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i])) t[p[i]] = s[p[i]]; } return t; } function __values(o) { var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0; if (m) return m.call(o); if (o && typeof o.length === "number") return { next: function () { if (o && i >= o.length) o = void 0; return { value: o && o[i++], done: !o }; } }; throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined."); } function __read(o, n) { var m = typeof Symbol === "function" && o[Symbol.iterator]; if (!m) return o; var i = m.call(o), r, ar = [], e; try { while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value); } catch (error) { e = { error: error }; } finally { try { if (r && !r.done && (m = i["return"])) m.call(i); } finally { if (e) throw e.error; } } return ar; } function __spreadArray(to, from, pack) { if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) { if (ar || !(i in from)) { if (!ar) ar = Array.prototype.slice.call(from, 0, i); ar[i] = from[i]; } } return to.concat(ar || Array.prototype.slice.call(from)); } var createDefinition = function (propNames) { return ({ isEnabled: function (props) { return propNames.some(function (name) { return !!props[name]; }); }, }); }; var featureDefinitions = { measureLayout: createDefinition(["layout", "layoutId", "drag"]), animation: createDefinition([ "animate", "exit", "variants", "whileHover", "whileTap", "whileFocus", "whileDrag", "whileInView", ]), exit: createDefinition(["exit"]), drag: createDefinition(["drag", "dragControls"]), focus: createDefinition(["whileFocus"]), hover: createDefinition(["whileHover", "onHoverStart", "onHoverEnd"]), tap: createDefinition(["whileTap", "onTap", "onTapStart", "onTapCancel"]), pan: createDefinition([ "onPan", "onPanStart", "onPanSessionStart", "onPanEnd", ]), inView: createDefinition([ "whileInView", "onViewportEnter", "onViewportLeave", ]), }; function loadFeatures(features) { for (var key in features) { if (features[key] === null) continue; if (key === "projectionNodeConstructor") { featureDefinitions.projectionNodeConstructor = features[key]; } else { featureDefinitions[key].Component = features[key]; } } } var warning = function () { }; var invariant = function () { }; { warning = function (check, message) { if (!check && typeof console !== 'undefined') { console.warn(message); } }; invariant = function (check, message) { if (!check) { throw new Error(message); } }; } var LazyContext = React.createContext({ strict: false }); var featureNames = Object.keys(featureDefinitions); var numFeatures = featureNames.length; /** * Load features via renderless components based on the provided MotionProps. */ function useFeatures(props, visualElement, preloadedFeatures) { var features = []; var lazyContext = React.useContext(LazyContext); if (!visualElement) return null; /** * If we're in development mode, check to make sure we're not rendering a motion component * as a child of LazyMotion, as this will break the file-size benefits of using it. */ if (preloadedFeatures && lazyContext.strict) { invariant(false, "You have rendered a `motion` component within a `LazyMotion` component. This will break tree shaking. Import and render a `m` component instead."); } for (var i = 0; i < numFeatures; i++) { var name_1 = featureNames[i]; var _a = featureDefinitions[name_1], isEnabled = _a.isEnabled, Component = _a.Component; /** * It might be possible in the future to use this moment to * dynamically request functionality. In initial tests this * was producing a lot of duplication amongst bundles. */ if (isEnabled(props) && Component) { features.push(React__namespace.createElement(Component, __assign({ key: name_1 }, props, { visualElement: visualElement }))); } } return features; } /** * @public */ var MotionConfigContext = React.createContext({ transformPagePoint: function (p) { return p; }, isStatic: false, reducedMotion: "never", }); var MotionContext = React.createContext({}); function useVisualElementContext() { return React.useContext(MotionContext).visualElement; } /** * @public */ var PresenceContext = React.createContext(null); var isBrowser = typeof document !== "undefined"; var useIsomorphicLayoutEffect = isBrowser ? React.useLayoutEffect : React.useEffect; // Does this device prefer reduced motion? Returns `null` server-side. var prefersReducedMotion = { current: null }; var hasDetected = false; function initPrefersReducedMotion() { hasDetected = true; if (!isBrowser) return; if (window.matchMedia) { var motionMediaQuery_1 = window.matchMedia("(prefers-reduced-motion)"); var setReducedMotionPreferences = function () { return (prefersReducedMotion.current = motionMediaQuery_1.matches); }; motionMediaQuery_1.addListener(setReducedMotionPreferences); setReducedMotionPreferences(); } else { prefersReducedMotion.current = false; } } /** * A hook that returns `true` if we should be using reduced motion based on the current device's Reduced Motion setting. * * This can be used to implement changes to your UI based on Reduced Motion. For instance, replacing motion-sickness inducing * `x`/`y` animations with `opacity`, disabling the autoplay of background videos, or turning off parallax motion. * * It will actively respond to changes and re-render your components with the latest setting. * * ```jsx * export function Sidebar({ isOpen }) { * const shouldReduceMotion = useReducedMotion() * const closedX = shouldReduceMotion ? 0 : "-100%" * * return ( * <motion.div animate={{ * opacity: isOpen ? 1 : 0, * x: isOpen ? 0 : closedX * }} /> * ) * } * ``` * * @return boolean * * @public */ function useReducedMotion() { /** * Lazy initialisation of prefersReducedMotion */ !hasDetected && initPrefersReducedMotion(); var _a = __read(React.useState(prefersReducedMotion.current), 1), shouldReduceMotion = _a[0]; /** * TODO See if people miss automatically updating shouldReduceMotion setting */ return shouldReduceMotion; } function useReducedMotionConfig() { var reducedMotionPreference = useReducedMotion(); var reducedMotion = React.useContext(MotionConfigContext).reducedMotion; if (reducedMotion === "never") { return false; } else if (reducedMotion === "always") { return true; } else { return reducedMotionPreference; } } function useVisualElement(Component, visualState, props, createVisualElement) { var lazyContext = React.useContext(LazyContext); var parent = useVisualElementContext(); var presenceContext = React.useContext(PresenceContext); var shouldReduceMotion = useReducedMotionConfig(); var visualElementRef = React.useRef(undefined); /** * If we haven't preloaded a renderer, check to see if we have one lazy-loaded */ if (!createVisualElement) createVisualElement = lazyContext.renderer; if (!visualElementRef.current && createVisualElement) { visualElementRef.current = createVisualElement(Component, { visualState: visualState, parent: parent, props: props, presenceId: presenceContext === null || presenceContext === void 0 ? void 0 : presenceContext.id, blockInitialAnimation: (presenceContext === null || presenceContext === void 0 ? void 0 : presenceContext.initial) === false, shouldReduceMotion: shouldReduceMotion, }); } var visualElement = visualElementRef.current; useIsomorphicLayoutEffect(function () { visualElement === null || visualElement === void 0 ? void 0 : visualElement.syncRender(); }); React.useEffect(function () { var _a; (_a = visualElement === null || visualElement === void 0 ? void 0 : visualElement.animationState) === null || _a === void 0 ? void 0 : _a.animateChanges(); }); useIsomorphicLayoutEffect(function () { return function () { return visualElement === null || visualElement === void 0 ? void 0 : visualElement.notifyUnmount(); }; }, []); return visualElement; } function isRefObject(ref) { return (typeof ref === "object" && Object.prototype.hasOwnProperty.call(ref, "current")); } /** * Creates a ref function that, when called, hydrates the provided * external ref and VisualElement. */ function useMotionRef(visualState, visualElement, externalRef) { return React.useCallback(function (instance) { var _a; instance && ((_a = visualState.mount) === null || _a === void 0 ? void 0 : _a.call(visualState, instance)); if (visualElement) { instance ? visualElement.mount(instance) : visualElement.unmount(); } if (externalRef) { if (typeof externalRef === "function") { externalRef(instance); } else if (isRefObject(externalRef)) { externalRef.current = instance; } } }, /** * Only pass a new ref callback to React if we've received a visual element * factory. Otherwise we'll be mounting/remounting every time externalRef * or other dependencies change. */ [visualElement]); } /** * Decides if the supplied variable is an array of variant labels */ function isVariantLabels(v) { return Array.isArray(v); } /** * Decides if the supplied variable is variant label */ function isVariantLabel(v) { return typeof v === "string" || isVariantLabels(v); } /** * Creates an object containing the latest state of every MotionValue on a VisualElement */ function getCurrent(visualElement) { var current = {}; visualElement.forEachValue(function (value, key) { return (current[key] = value.get()); }); return current; } /** * Creates an object containing the latest velocity of every MotionValue on a VisualElement */ function getVelocity$1(visualElement) { var velocity = {}; visualElement.forEachValue(function (value, key) { return (velocity[key] = value.getVelocity()); }); return velocity; } function resolveVariantFromProps(props, definition, custom, currentValues, currentVelocity) { var _a; if (currentValues === void 0) { currentValues = {}; } if (currentVelocity === void 0) { currentVelocity = {}; } /** * If the variant definition is a function, resolve. */ if (typeof definition === "function") { definition = definition(custom !== null && custom !== void 0 ? custom : props.custom, currentValues, currentVelocity); } /** * If the variant definition is a variant label, or * the function returned a variant label, resolve. */ if (typeof definition === "string") { definition = (_a = props.variants) === null || _a === void 0 ? void 0 : _a[definition]; } /** * At this point we've resolved both functions and variant labels, * but the resolved variant label might itself have been a function. * If so, resolve. This can only have returned a valid target object. */ if (typeof definition === "function") { definition = definition(custom !== null && custom !== void 0 ? custom : props.custom, currentValues, currentVelocity); } return definition; } function resolveVariant(visualElement, definition, custom) { var props = visualElement.getProps(); return resolveVariantFromProps(props, definition, custom !== null && custom !== void 0 ? custom : props.custom, getCurrent(visualElement), getVelocity$1(visualElement)); } function checkIfControllingVariants(props) { var _a; return (typeof ((_a = props.animate) === null || _a === void 0 ? void 0 : _a.start) === "function" || isVariantLabel(props.initial) || isVariantLabel(props.animate) || isVariantLabel(props.whileHover) || isVariantLabel(props.whileDrag) || isVariantLabel(props.whileTap) || isVariantLabel(props.whileFocus) || isVariantLabel(props.exit)); } function checkIfVariantNode(props) { return Boolean(checkIfControllingVariants(props) || props.variants); } function getCurrentTreeVariants(props, context) { if (checkIfControllingVariants(props)) { var initial = props.initial, animate = props.animate; return { initial: initial === false || isVariantLabel(initial) ? initial : undefined, animate: isVariantLabel(animate) ? animate : undefined, }; } return props.inherit !== false ? context : {}; } function useCreateMotionContext(props) { var _a = getCurrentTreeVariants(props, React.useContext(MotionContext)), initial = _a.initial, animate = _a.animate; return React.useMemo(function () { return ({ initial: initial, animate: animate }); }, [variantLabelsAsDependency(initial), variantLabelsAsDependency(animate)]); } function variantLabelsAsDependency(prop) { return Array.isArray(prop) ? prop.join(" ") : prop; } /** * Creates a constant value over the lifecycle of a component. * * Even if `useMemo` is provided an empty array as its final argument, it doesn't offer * a guarantee that it won't re-run for performance reasons later on. By using `useConstant` * you can ensure that initialisers don't execute twice or more. */ function useConstant(init) { var ref = React.useRef(null); if (ref.current === null) { ref.current = init(); } return ref.current; } const defaultTimestep = (1 / 60) * 1000; const getCurrentTime$1 = typeof performance !== "undefined" ? () => performance.now() : () => Date.now(); const onNextFrame = typeof window !== "undefined" ? (callback) => window.requestAnimationFrame(callback) : (callback) => setTimeout(() => callback(getCurrentTime$1()), defaultTimestep); function createRenderStep(runNextFrame) { let toRun = []; let toRunNextFrame = []; let numToRun = 0; let isProcessing = false; let flushNextFrame = false; const toKeepAlive = new WeakSet(); const step = { schedule: (callback, keepAlive = false, immediate = false) => { const addToCurrentFrame = immediate && isProcessing; const buffer = addToCurrentFrame ? toRun : toRunNextFrame; if (keepAlive) toKeepAlive.add(callback); if (buffer.indexOf(callback) === -1) { buffer.push(callback); if (addToCurrentFrame && isProcessing) numToRun = toRun.length; } return callback; }, cancel: (callback) => { const index = toRunNextFrame.indexOf(callback); if (index !== -1) toRunNextFrame.splice(index, 1); toKeepAlive.delete(callback); }, process: (frameData) => { if (isProcessing) { flushNextFrame = true; return; } isProcessing = true; [toRun, toRunNextFrame] = [toRunNextFrame, toRun]; toRunNextFrame.length = 0; numToRun = toRun.length; if (numToRun) { for (let i = 0; i < numToRun; i++) { const callback = toRun[i]; callback(frameData); if (toKeepAlive.has(callback)) { step.schedule(callback); runNextFrame(); } } } isProcessing = false; if (flushNextFrame) { flushNextFrame = false; step.process(frameData); } }, }; return step; } const maxElapsed = 40; let useDefaultElapsed = true; let runNextFrame = false; let isProcessing = false; const frame = { delta: 0, timestamp: 0, }; const stepsOrder = [ "read", "update", "preRender", "render", "postRender", ]; const steps = stepsOrder.reduce((acc, key) => { acc[key] = createRenderStep(() => (runNextFrame = true)); return acc; }, {}); const sync = stepsOrder.reduce((acc, key) => { const step = steps[key]; acc[key] = (process, keepAlive = false, immediate = false) => { if (!runNextFrame) startLoop(); return step.schedule(process, keepAlive, immediate); }; return acc; }, {}); const cancelSync = stepsOrder.reduce((acc, key) => { acc[key] = steps[key].cancel; return acc; }, {}); const flushSync = stepsOrder.reduce((acc, key) => { acc[key] = () => steps[key].process(frame); return acc; }, {}); const processStep = (stepId) => steps[stepId].process(frame); const processFrame = (timestamp) => { runNextFrame = false; frame.delta = useDefaultElapsed ? defaultTimestep : Math.max(Math.min(timestamp - frame.timestamp, maxElapsed), 1); frame.timestamp = timestamp; isProcessing = true; stepsOrder.forEach(processStep); isProcessing = false; if (runNextFrame) { useDefaultElapsed = false; onNextFrame(processFrame); } }; const startLoop = () => { runNextFrame = true; useDefaultElapsed = true; if (!isProcessing) onNextFrame(processFrame); }; const getFrameData = () => frame; const clamp$1 = (min, max, v) => Math.min(Math.max(v, min), max); const safeMin = 0.001; const minDuration = 0.01; const maxDuration = 10.0; const minDamping = 0.05; const maxDamping = 1; function findSpring({ duration = 800, bounce = 0.25, velocity = 0, mass = 1, }) { let envelope; let derivative; warning(duration <= maxDuration * 1000, "Spring duration must be 10 seconds or less"); let dampingRatio = 1 - bounce; dampingRatio = clamp$1(minDamping, maxDamping, dampingRatio); duration = clamp$1(minDuration, maxDuration, duration / 1000); if (dampingRatio < 1) { envelope = (undampedFreq) => { const exponentialDecay = undampedFreq * dampingRatio; const delta = exponentialDecay * duration; const a = exponentialDecay - velocity; const b = calcAngularFreq(undampedFreq, dampingRatio); const c = Math.exp(-delta); return safeMin - (a / b) * c; }; derivative = (undampedFreq) => { const exponentialDecay = undampedFreq * dampingRatio; const delta = exponentialDecay * duration; const d = delta * velocity + velocity; const e = Math.pow(dampingRatio, 2) * Math.pow(undampedFreq, 2) * duration; const f = Math.exp(-delta); const g = calcAngularFreq(Math.pow(undampedFreq, 2), dampingRatio); const factor = -envelope(undampedFreq) + safeMin > 0 ? -1 : 1; return (factor * ((d - e) * f)) / g; }; } else { envelope = (undampedFreq) => { const a = Math.exp(-undampedFreq * duration); const b = (undampedFreq - velocity) * duration + 1; return -safeMin + a * b; }; derivative = (undampedFreq) => { const a = Math.exp(-undampedFreq * duration); const b = (velocity - undampedFreq) * (duration * duration); return a * b; }; } const initialGuess = 5 / duration; const undampedFreq = approximateRoot(envelope, derivative, initialGuess); duration = duration * 1000; if (isNaN(undampedFreq)) { return { stiffness: 100, damping: 10, duration, }; } else { const stiffness = Math.pow(undampedFreq, 2) * mass; return { stiffness, damping: dampingRatio * 2 * Math.sqrt(mass * stiffness), duration, }; } } const rootIterations = 12; function approximateRoot(envelope, derivative, initialGuess) { let result = initialGuess; for (let i = 1; i < rootIterations; i++) { result = result - envelope(result) / derivative(result); } return result; } function calcAngularFreq(undampedFreq, dampingRatio) { return undampedFreq * Math.sqrt(1 - dampingRatio * dampingRatio); } const durationKeys = ["duration", "bounce"]; const physicsKeys = ["stiffness", "damping", "mass"]; function isSpringType(options, keys) { return keys.some((key) => options[key] !== undefined); } function getSpringOptions(options) { let springOptions = Object.assign({ velocity: 0.0, stiffness: 100, damping: 10, mass: 1.0, isResolvedFromDuration: false }, options); if (!isSpringType(options, physicsKeys) && isSpringType(options, durationKeys)) { const derived = findSpring(options); springOptions = Object.assign(Object.assign(Object.assign({}, springOptions), derived), { velocity: 0.0, mass: 1.0 }); springOptions.isResolvedFromDuration = true; } return springOptions; } function spring(_a) { var { from = 0.0, to = 1.0, restSpeed = 2, restDelta } = _a, options = __rest(_a, ["from", "to", "restSpeed", "restDelta"]); const state = { done: false, value: from }; let { stiffness, damping, mass, velocity, duration, isResolvedFromDuration, } = getSpringOptions(options); let resolveSpring = zero; let resolveVelocity = zero; function createSpring() { const initialVelocity = velocity ? -(velocity / 1000) : 0.0; const initialDelta = to - from; const dampingRatio = damping / (2 * Math.sqrt(stiffness * mass)); const undampedAngularFreq = Math.sqrt(stiffness / mass) / 1000; if (restDelta === undefined) { restDelta = Math.min(Math.abs(to - from) / 100, 0.4); } if (dampingRatio < 1) { const angularFreq = calcAngularFreq(undampedAngularFreq, dampingRatio); resolveSpring = (t) => { const envelope = Math.exp(-dampingRatio * undampedAngularFreq * t); return (to - envelope * (((initialVelocity + dampingRatio * undampedAngularFreq * initialDelta) / angularFreq) * Math.sin(angularFreq * t) + initialDelta * Math.cos(angularFreq * t))); }; resolveVelocity = (t) => { const envelope = Math.exp(-dampingRatio * undampedAngularFreq * t); return (dampingRatio * undampedAngularFreq * envelope * ((Math.sin(angularFreq * t) * (initialVelocity + dampingRatio * undampedAngularFreq * initialDelta)) / angularFreq + initialDelta * Math.cos(angularFreq * t)) - envelope * (Math.cos(angularFreq * t) * (initialVelocity + dampingRatio * undampedAngularFreq * initialDelta) - angularFreq * initialDelta * Math.sin(angularFreq * t))); }; } else if (dampingRatio === 1) { resolveSpring = (t) => to - Math.exp(-undampedAngularFreq * t) * (initialDelta + (initialVelocity + undampedAngularFreq * initialDelta) * t); } else { const dampedAngularFreq = undampedAngularFreq * Math.sqrt(dampingRatio * dampingRatio - 1); resolveSpring = (t) => { const envelope = Math.exp(-dampingRatio * undampedAngularFreq * t); const freqForT = Math.min(dampedAngularFreq * t, 300); return (to - (envelope * ((initialVelocity + dampingRatio * undampedAngularFreq * initialDelta) * Math.sinh(freqForT) + dampedAngularFreq * initialDelta * Math.cosh(freqForT))) / dampedAngularFreq); }; } } createSpring(); return { next: (t) => { const current = resolveSpring(t); if (!isResolvedFromDuration) { const currentVelocity = resolveVelocity(t) * 1000; const isBelowVelocityThreshold = Math.abs(currentVelocity) <= restSpeed; const isBelowDisplacementThreshold = Math.abs(to - current) <= restDelta; state.done = isBelowVelocityThreshold && isBelowDisplacementThreshold; } else { state.done = t >= duration; } state.value = state.done ? to : current; return state; }, flipTarget: () => { velocity = -velocity; [from, to] = [to, from]; createSpring(); }, }; } spring.needsInterpolation = (a, b) => typeof a === "string" || typeof b === "string"; const zero = (_t) => 0; const progress = (from, to, value) => { const toFromDifference = to - from; return toFromDifference === 0 ? 1 : (value - from) / toFromDifference; }; const mix = (from, to, progress) => -progress * from + progress * to + from; const clamp = (min, max) => (v) => Math.max(Math.min(v, max), min); const sanitize = (v) => (v % 1 ? Number(v.toFixed(5)) : v); const floatRegex = /(-)?([\d]*\.?[\d])+/g; const colorRegex = /(#[0-9a-f]{6}|#[0-9a-f]{3}|#(?:[0-9a-f]{2}){2,4}|(rgb|hsl)a?\((-?[\d\.]+%?[,\s]+){2,3}\s*\/*\s*[\d\.]+%?\))/gi; const singleColorRegex = /^(#[0-9a-f]{3}|#(?:[0-9a-f]{2}){2,4}|(rgb|hsl)a?\((-?[\d\.]+%?[,\s]+){2,3}\s*\/*\s*[\d\.]+%?\))$/i; function isString(v) { return typeof v === 'string'; } const number = { test: (v) => typeof v === 'number', parse: parseFloat, transform: (v) => v, }; const alpha = Object.assign(Object.assign({}, number), { transform: clamp(0, 1) }); const scale = Object.assign(Object.assign({}, number), { default: 1 }); const createUnitType = (unit) => ({ test: (v) => isString(v) && v.endsWith(unit) && v.split(' ').length === 1, parse: parseFloat, transform: (v) => `${v}${unit}`, }); const degrees = createUnitType('deg'); const percent = createUnitType('%'); const px = createUnitType('px'); const vh = createUnitType('vh'); const vw = createUnitType('vw'); const progressPercentage = Object.assign(Object.assign({}, percent), { parse: (v) => percent.parse(v) / 100, transform: (v) => percent.transform(v * 100) }); const isColorString = (type, testProp) => (v) => { return Boolean((isString(v) && singleColorRegex.test(v) && v.startsWith(type)) || (testProp && Object.prototype.hasOwnProperty.call(v, testProp))); }; const splitColor = (aName, bName, cName) => (v) => { if (!isString(v)) return v; const [a, b, c, alpha] = v.match(floatRegex); return { [aName]: parseFloat(a), [bName]: parseFloat(b), [cName]: parseFloat(c), alpha: alpha !== undefined ? parseFloat(alpha) : 1, }; }; const hsla = { test: isColorString('hsl', 'hue'), parse: splitColor('hue', 'saturation', 'lightness'), transform: ({ hue, saturation, lightness, alpha: alpha$1 = 1 }) => { return ('hsla(' + Math.round(hue) + ', ' + percent.transform(sanitize(saturation)) + ', ' + percent.transform(sanitize(lightness)) + ', ' + sanitize(alpha.transform(alpha$1)) + ')'); }, }; const clampRgbUnit = clamp(0, 255); const rgbUnit = Object.assign(Object.assign({}, number), { transform: (v) => Math.round(clampRgbUnit(v)) }); const rgba = { test: isColorString('rgb', 'red'), parse: splitColor('red', 'green', 'blue'), transform: ({ red, green, blue, alpha: alpha$1 = 1 }) => 'rgba(' + rgbUnit.transform(red) + ', ' + rgbUnit.transform(green) + ', ' + rgbUnit.transform(blue) + ', ' + sanitize(alpha.transform(alpha$1)) + ')', }; function parseHex(v) { let r = ''; let g = ''; let b = ''; let a = ''; if (v.length > 5) { r = v.substr(1, 2); g = v.substr(3, 2); b = v.substr(5, 2); a = v.substr(7, 2); } else { r = v.substr(1, 1); g = v.substr(2, 1); b = v.substr(3, 1); a = v.substr(4, 1); r += r; g += g; b += b; a += a; } return { red: parseInt(r, 16), green: parseInt(g, 16), blue: parseInt(b, 16), alpha: a ? parseInt(a, 16) / 255 : 1, }; } const hex = { test: isColorString('#'), parse: parseHex, transform: rgba.transform, }; const color = { test: (v) => rgba.test(v) || hex.test(v) || hsla.test(v), parse: (v) => { if (rgba.test(v)) { return rgba.parse(v); } else if (hsla.test(v)) { return hsla.parse(v); } else { return hex.parse(v); } }, transform: (v) => { return isString(v) ? v : v.hasOwnProperty('red') ? rgba.transform(v) : hsla.transform(v); }, }; const colorToken = '${c}'; const numberToken = '${n}'; function test(v) { var _a, _b, _c, _d; return (isNaN(v) && isString(v) && ((_b = (_a = v.match(floatRegex)) === null || _a === void 0 ? void 0 : _a.length) !== null && _b !== void 0 ? _b : 0) + ((_d = (_c = v.match(colorRegex)) === null || _c === void 0 ? void 0 : _c.length) !== null && _d !== void 0 ? _d : 0) > 0); } function analyse$1(v) { if (typeof v === 'number') v = `${v}`; const values = []; let numColors = 0; const colors = v.match(colorRegex); if (colors) { numColors = colors.length; v = v.replace(colorRegex, colorToken); values.push(...colors.map(color.parse)); } const numbers = v.match(floatRegex); if (numbers) { v = v.replace(floatRegex, numberToken); values.push(...numbers.map(number.parse)); } return { values, numColors, tokenised: v }; } function parse(v) { return analyse$1(v).values; } function createTransformer(v) { const { values, numColors, tokenised } = analyse$1(v); const numValues = values.length; return (v) => { let output = tokenised; for (let i = 0; i < numValues; i++) { output = output.replace(i < numColors ? colorToken : numberToken, i < numColors ? color.transform(v[i]) : sanitize(v[i])); } return output; }; } const convertNumbersToZero = (v) => typeof v === 'number' ? 0 : v; function getAnimatableNone$1(v) { const parsed = parse(v); const transformer = createTransformer(v); return transformer(parsed.map(convertNumbersToZero)); } const complex = { test, parse, createTransformer, getAnimatableNone: getAnimatableNone$1 }; const maxDefaults = new Set(['brightness', 'contrast', 'saturate', 'opacity']); function applyDefaultFilter(v) { let [name, value] = v.slice(0, -1).split('('); if (name === 'drop-shadow') return v; const [number] = value.match(floatRegex) || []; if (!number) return v; const unit = value.replace(number, ''); let defaultValue = maxDefaults.has(name) ? 1 : 0; if (number !== value) defaultValue *= 100; return name + '(' + defaultValue + unit + ')'; } const functionRegex = /([a-z-]*)\(.*?\)/g; const filter = Object.assign(Object.assign({}, complex), { getAnimatableNone: (v) => { const functions = v.match(functionRegex); return functions ? functions.map(applyDefaultFilter).join(' ') : v; } }); function hueToRgb(p, q, t) { if (t < 0) t += 1; if (t > 1) t -= 1; if (t < 1 / 6) return p + (q - p) * 6 * t; if (t < 1 / 2) return q; if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6; return p; } function hslaToRgba({ hue, saturation, lightness, alpha }) { hue /= 360; saturation /= 100; lightness /= 100; let red = 0; let green = 0; let blue = 0; if (!saturation) { red = green = blue = lightness; } else { const q = lightness < 0.5 ? lightness * (1 + saturation) : lightness + saturation - lightness * saturation; const p = 2 * lightness - q; red = hueToRgb(p, q, hue + 1 / 3); green = hueToRgb(p, q, hue); blue = hueToRgb(p, q, hue - 1 / 3); } return { red: Math.round(red * 255), green: Math.round(green * 255), blue: Math.round(blue * 255), alpha, }; } const mixLinearColor = (from, to, v) => { const fromExpo = from * from; const toExpo = to * to; return Math.sqrt(Math.max(0, v * (toExpo - fromExpo) + fromExpo)); }; const colorTypes = [hex, rgba, hsla]; const getColorType = (v) => colorTypes.find((type) => type.test(v)); const notAnimatable = (color) => `'${color}' is not an animatable color. Use the equivalent color code instead.`; const mixColor = (from, to) => { let fromColorType = getColorType(from); let toColorType = getColorType(to); invariant(!!fromColorType, notAnimatable(from)); invariant(!!toColorType, notAnimatable(to)); let fromColor = fromColorType.parse(from); let toColor = toColorType.parse(to); if (fromColorType === hsla) { fromColor = hslaToRgba(fromColor); fromColorType = rgba; } if (toColorType === hsla) { toColor = hslaToRgba(toColor); toColorType = rgba; } const blended = Object.assign({}, fromColor); return (v) => { for (const key in blended) { if (key !== "alpha") { blended[key] = mixLinearColor(fromColor[key], toColor[key], v); } } blended.alpha = mix(fromColor.alpha, toColor.alpha, v); return fromColorType.transform(blended); }; }; const isNum = (v) => typeof v === 'number'; const combineFunctions = (a, b) => (v) => b(a(v)); const pipe = (...transformers) => transformers.reduce(combineFunctions); function getMixer$1(origin, target) { if (isNum(origin)) { return (v) => mix(origin, target, v); } else if (color.test(origin)) { return mixColor(origin, target); } else { return mixComplex(origin, target); } } const mixArray = (from, to) => { const output = [...from]; const numValues = output.length; const blendValue = from.map((fromThis, i) => getMixer$1(fromThis, to[i])); return (v) => { for (let i = 0; i < numValues; i++) { output[i] = blendValue[i](v); } return output; }; }; const mixObject = (origin, target) => { const output = Object.assign(Object.assign({}, origin), target); const blendValue = {}; for (const key in output) { if (origin[key] !== undefined && target[key] !== undefined) { blendValue[key] = getMixer$1(origin[key], target[key]); } } return (v) => { for (const key in blendValue) { output[key] = blendValue[key](v); } return output; }; }; function analyse(value) { const parsed = complex.parse(value); const numValues = parsed.length; let numNumbers = 0; let numRGB = 0; let numHSL = 0; for (let i = 0; i < numValues; i++) { if (numNumbers || typeof parsed[i] === "number") { numNumbers++; } else { if (parsed[i].hue !== undefined) { numHSL++; } else { numRGB++; } } } return { parsed, numNumbers, numRGB, numHSL }; } const mixComplex = (origin, target) => { const template = complex.createTransformer(target); const originStats = analyse(origin); const targetStats = analyse(target); const canInterpolate = originStats.numHSL === targetStats.numHSL && originStats.numRGB === targetStats.numRGB && originStats.numNumbers >= targetStats.numNumbers; if (canInterpolate) { return pipe(mixArray(originStats.parsed, targetStats.parsed), template); } else { 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.`); return (p) => `${p > 0 ? target : origin}`; } }; const mixNumber = (from, to) => (p) => mix(from, to, p); function detectMixerFactory(v) { if (typeof v === 'number') { return mixNumber; } else if (typeof v === 'string') { if (color.test(v)) { return mixColor; } else { return mixComplex; } } else if (Array.isArray(v)) { return mixArray; } else if (typeof v === 'object') { return mixObject; } } function createMixers(output, ease, customMixer) { const mixers = []; const mixerFactory = customMixer || detectMixerFactory(output[0]); const numMixers = output.length - 1; for (let i = 0; i < numMixers; i++) { let mixer = mixerFactory(output[i], output[i + 1]); if (ease) { const easingFunction = Array.isArray(ease) ? ease[i] : ease; mixer = pipe(easingFunction, mixer); } mixers.push(mixer); } return mixers; } function fastInterpolate([from, to], [mixer]) { return (v) => mixer(progress(from, to, v)); } function slowInterpolate(input, mixers) { const inputLength = input.length; const lastInputIndex = inputLength - 1; return (v) => { let mixerIndex = 0; let foundMixerIndex = false; if (v <= input[0]) { foundMixerIndex = true; } else if (v >= input[lastInputIndex]) { mixerIndex = lastInputIndex - 1; foundMixerIndex = true; } if (!foundMixerIndex) { let i = 1; for (; i < inputLength; i++) { if (input[i] > v || i === lastInputIndex) { break; } } mixerIndex = i - 1; } const progressInRange = progress(input[mixerIndex], input[mixerIndex + 1], v); return mixers[mixerIndex](progressInRange); }; } function interpolate(input, output, { clamp: isClamp = true, ease, mixer } = {}) { const inputLength = input.length; invariant(inputLength === output.length, 'Both input and output ranges must be the same length'); 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.'); if (input[0] > input[inputLength - 1]) { input = [].concat(input); output = [].concat(output); input.reverse(); output.reverse(); } const mixers = createMixers(output, ease, mixer); const interpolator = inputLength === 2 ? fastInterpolate(input, mixers) : slowInterpolate(input, mixers); return isClamp ? (v) => interpolator(clamp$1(input[0], input[inputLength - 1], v)) : interpolator; } const reverseEasing = easing => p => 1 - easing(1 - p); const mirrorEasing = easing => p => p <= 0.5 ? easing(2 * p) / 2 : (2 - easing(2 * (1 - p))) / 2; const createExpoIn = (power) => p => Math.pow(p, power); const createBackIn = (power) => p => p * p * ((power + 1) * p - power); const createAnticipate = (power) => { const backEasing = createBackIn(power); return p => (p *= 2) < 1 ? 0.5 * backEasing(p) : 0.5 * (2 - Math.pow(2, -10 * (p - 1))); }; const DEFAULT_OVERSHOOT_STRENGTH = 1.525; const BOUNCE_FIRST_THRESHOLD = 4.0 / 11.0; const BOUNCE_SECOND_THRESHOLD = 8.0 / 11.0; const BOUNCE_THIRD_THRESHOLD = 9.0 / 10.0; const linear = p => p; const easeIn = createExpoIn(2); const easeOut = reverseEasing(easeIn); const easeInOut = mirrorEasing(easeIn); const circIn = p => 1 - Math.sin(Math.acos(p)); const circOut = reverseEasing(circIn); const circInOut = mirrorEasing(circOut); const backIn = createBackIn(DEFAULT_OVERSHOOT_STRENGTH); const backOut = reverseEasing(backIn); const backInOut = mirrorEasing(backIn); const anticipate = createAnticipate(DEFAULT_OVERSHOOT_STRENGTH); const ca = 4356.0 / 361.0; const cb = 35442.0 / 1805.0; const cc = 16061.0 / 1805.0; const bounceOut = (p) => { if (p ===