UNPKG

motion-v

Version:

<p align="center"> <img width="100" height="100" alt="Motion logo" src="https://user-images.githubusercontent.com/7850794/164965523-3eced4c4-6020-467e-acde-f11b7900ad62.png" /> </p> <h1 align="center">Motion for Vue</h1>

1,509 lines 335 kB
"use strict"; Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toStringTag]: { value: "Module" } }); const vue = require("vue"); const heyListen = require("hey-listen"); const core = require("@vueuse/core"); function addUniqueItem(arr, item) { if (arr.indexOf(item) === -1) arr.push(item); } function removeItem(arr, item) { const index = arr.indexOf(item); if (index > -1) arr.splice(index, 1); } const noop = /* @__NO_SIDE_EFFECTS__ */ (any) => any; let warning = noop; exports.invariant = noop; if (process.env.NODE_ENV !== "production") { warning = (check, message) => { if (!check && typeof console !== "undefined") { console.warn(message); } }; exports.invariant = (check, message) => { if (!check) { throw new Error(message); } }; } const MotionGlobalConfig = { skipAnimations: false, useManualTiming: false }; // @__NO_SIDE_EFFECTS__ function memo(callback) { let result; return () => { if (result === void 0) result = callback(); return result; }; } const progress$1 = /* @__NO_SIDE_EFFECTS__ */ (from, to, value) => { const toFromDifference = to - from; return toFromDifference === 0 ? 1 : (value - from) / toFromDifference; }; class SubscriptionManager { constructor() { this.subscriptions = []; } add(handler) { addUniqueItem(this.subscriptions, handler); return () => removeItem(this.subscriptions, handler); } notify(a, b, c) { const numSubscriptions = this.subscriptions.length; if (!numSubscriptions) return; if (numSubscriptions === 1) { this.subscriptions[0](a, b, c); } else { for (let i = 0; i < numSubscriptions; i++) { const handler = this.subscriptions[i]; handler && handler(a, b, c); } } } getSize() { return this.subscriptions.length; } clear() { this.subscriptions.length = 0; } } const secondsToMilliseconds$1 = /* @__NO_SIDE_EFFECTS__ */ (seconds) => seconds * 1e3; const millisecondsToSeconds$1 = /* @__NO_SIDE_EFFECTS__ */ (milliseconds) => milliseconds / 1e3; function velocityPerSecond(velocity, frameDuration) { return frameDuration ? velocity * (1e3 / frameDuration) : 0; } const warned = /* @__PURE__ */ new Set(); function warnOnce(condition, message, element) { if (condition || warned.has(message)) return; console.warn(message); warned.add(message); } const supportsScrollTimeline = /* @__PURE__ */ memo(() => window.ScrollTimeline !== void 0); class BaseGroupPlaybackControls { constructor(animations) { this.stop = () => this.runAll("stop"); this.animations = animations.filter(Boolean); } get finished() { return Promise.all(this.animations.map((animation) => "finished" in animation ? animation.finished : animation)); } /** * TODO: Filter out cancelled or stopped animations before returning */ getAll(propName) { return this.animations[0][propName]; } setAll(propName, newValue) { for (let i = 0; i < this.animations.length; i++) { this.animations[i][propName] = newValue; } } attachTimeline(timeline, fallback) { const subscriptions = this.animations.map((animation) => { if (supportsScrollTimeline() && animation.attachTimeline) { return animation.attachTimeline(timeline); } else if (typeof fallback === "function") { return fallback(animation); } }); return () => { subscriptions.forEach((cancel, i) => { cancel && cancel(); this.animations[i].stop(); }); }; } get time() { return this.getAll("time"); } set time(time2) { this.setAll("time", time2); } get speed() { return this.getAll("speed"); } set speed(speed) { this.setAll("speed", speed); } get startTime() { return this.getAll("startTime"); } get duration() { let max = 0; for (let i = 0; i < this.animations.length; i++) { max = Math.max(max, this.animations[i].duration); } return max; } runAll(methodName) { this.animations.forEach((controls) => controls[methodName]()); } flatten() { this.runAll("flatten"); } play() { this.runAll("play"); } pause() { this.runAll("pause"); } cancel() { this.runAll("cancel"); } complete() { this.runAll("complete"); } } class GroupPlaybackControls extends BaseGroupPlaybackControls { then(onResolve, onReject) { return Promise.all(this.animations).then(onResolve).catch(onReject); } } function getValueTransition$1(transition, key) { return transition ? transition[key] || transition["default"] || transition : void 0; } const maxGeneratorDuration = 2e4; function calcGeneratorDuration(generator) { let duration = 0; const timeStep = 50; let state2 = generator.next(duration); while (!state2.done && duration < maxGeneratorDuration) { duration += timeStep; state2 = generator.next(duration); } return duration >= maxGeneratorDuration ? Infinity : duration; } function createGeneratorEasing(options, scale2 = 100, createGenerator) { const generator = createGenerator({ ...options, keyframes: [0, scale2] }); const duration = Math.min(calcGeneratorDuration(generator), maxGeneratorDuration); return { type: "keyframes", ease: (progress2) => { return generator.next(duration * progress2).value / scale2; }, duration: /* @__PURE__ */ millisecondsToSeconds$1(duration) }; } function isGenerator(type) { return typeof type === "function"; } function attachTimeline(animation, timeline) { animation.timeline = timeline; animation.onfinish = null; } class NativeAnimationControls { constructor(animation) { this.animation = animation; } get duration() { var _a, _b, _c; const durationInMs = ((_b = (_a = this.animation) === null || _a === void 0 ? void 0 : _a.effect) === null || _b === void 0 ? void 0 : _b.getComputedTiming().duration) || ((_c = this.options) === null || _c === void 0 ? void 0 : _c.duration) || 300; return /* @__PURE__ */ millisecondsToSeconds$1(Number(durationInMs)); } get time() { var _a; if (this.animation) { return /* @__PURE__ */ millisecondsToSeconds$1(((_a = this.animation) === null || _a === void 0 ? void 0 : _a.currentTime) || 0); } return 0; } set time(newTime) { if (this.animation) { this.animation.currentTime = /* @__PURE__ */ secondsToMilliseconds$1(newTime); } } get speed() { return this.animation ? this.animation.playbackRate : 1; } set speed(newSpeed) { if (this.animation) { this.animation.playbackRate = newSpeed; } } get state() { return this.animation ? this.animation.playState : "finished"; } get startTime() { return this.animation ? this.animation.startTime : null; } get finished() { return this.animation ? this.animation.finished : Promise.resolve(); } play() { this.animation && this.animation.play(); } pause() { this.animation && this.animation.pause(); } stop() { if (!this.animation || this.state === "idle" || this.state === "finished") { return; } if (this.animation.commitStyles) { this.animation.commitStyles(); } this.cancel(); } flatten() { var _a, _b; if (!this.animation || !((_a = this.options) === null || _a === void 0 ? void 0 : _a.allowFlatten)) return; (_b = this.animation.effect) === null || _b === void 0 ? void 0 : _b.updateTiming({ easing: "linear" }); } attachTimeline(timeline) { if (this.animation) attachTimeline(this.animation, timeline); return noop; } complete() { this.animation && this.animation.finish(); } cancel() { try { this.animation && this.animation.cancel(); } catch (e) { } } } const isBezierDefinition = (easing) => Array.isArray(easing) && typeof easing[0] === "number"; const supportsFlags = { linearEasing: void 0 }; function memoSupports(callback, supportsFlag) { const memoized = /* @__PURE__ */ memo(callback); return () => { var _a; return (_a = supportsFlags[supportsFlag]) !== null && _a !== void 0 ? _a : memoized(); }; } const supportsLinearEasing = /* @__PURE__ */ memoSupports(() => { try { document.createElement("div").animate({ opacity: 0 }, { easing: "linear(0, 1)" }); } catch (e) { return false; } return true; }, "linearEasing"); const generateLinearEasing = (easing, duration, resolution = 10) => { let points = ""; const numPoints = Math.max(Math.round(duration / resolution), 2); for (let i = 0; i < numPoints; i++) { points += easing(/* @__PURE__ */ progress$1(0, numPoints - 1, i)) + ", "; } return `linear(${points.substring(0, points.length - 2)})`; }; function isWaapiSupportedEasing(easing) { return Boolean(typeof easing === "function" && supportsLinearEasing() || !easing || typeof easing === "string" && (easing in supportedWaapiEasing || supportsLinearEasing()) || isBezierDefinition(easing) || Array.isArray(easing) && easing.every(isWaapiSupportedEasing)); } const cubicBezierAsString = ([a, b, c, d]) => `cubic-bezier(${a}, ${b}, ${c}, ${d})`; const supportedWaapiEasing = { linear: "linear", ease: "ease", easeIn: "ease-in", easeOut: "ease-out", easeInOut: "ease-in-out", circIn: /* @__PURE__ */ cubicBezierAsString([0, 0.65, 0.55, 1]), circOut: /* @__PURE__ */ cubicBezierAsString([0.55, 0, 1, 0.45]), backIn: /* @__PURE__ */ cubicBezierAsString([0.31, 0.01, 0.66, -0.59]), backOut: /* @__PURE__ */ cubicBezierAsString([0.33, 1.53, 0.69, 0.99]) }; function mapEasingToNativeEasing(easing, duration) { if (!easing) { return void 0; } else if (typeof easing === "function" && supportsLinearEasing()) { return generateLinearEasing(easing, duration); } else if (isBezierDefinition(easing)) { return cubicBezierAsString(easing); } else if (Array.isArray(easing)) { return easing.map((segmentEasing) => mapEasingToNativeEasing(segmentEasing, duration) || supportedWaapiEasing.easeOut); } else { return supportedWaapiEasing[easing]; } } const stepsOrder = [ "read", // Read "resolveKeyframes", // Write/Read/Write/Read "update", // Compute "preRender", // Compute "render", // Write "postRender" // Compute ]; const statsBuffer = { value: null, addProjectionMetrics: null }; function createRenderStep(runNextFrame, stepName) { let thisFrame = /* @__PURE__ */ new Set(); let nextFrame = /* @__PURE__ */ new Set(); let isProcessing = false; let flushNextFrame = false; const toKeepAlive = /* @__PURE__ */ new WeakSet(); let latestFrameData = { delta: 0, timestamp: 0, isProcessing: false }; let numCalls = 0; function triggerCallback(callback) { if (toKeepAlive.has(callback)) { step.schedule(callback); runNextFrame(); } numCalls++; callback(latestFrameData); } const step = { /** * Schedule a process to run on the next frame. */ schedule: (callback, keepAlive = false, immediate = false) => { const addToCurrentFrame = immediate && isProcessing; const queue = addToCurrentFrame ? thisFrame : nextFrame; if (keepAlive) toKeepAlive.add(callback); if (!queue.has(callback)) queue.add(callback); return callback; }, /** * Cancel the provided callback from running on the next frame. */ cancel: (callback) => { nextFrame.delete(callback); toKeepAlive.delete(callback); }, /** * Execute all schedule callbacks. */ process: (frameData2) => { latestFrameData = frameData2; if (isProcessing) { flushNextFrame = true; return; } isProcessing = true; [thisFrame, nextFrame] = [nextFrame, thisFrame]; thisFrame.forEach(triggerCallback); if (stepName && statsBuffer.value) { statsBuffer.value.frameloop[stepName].push(numCalls); } numCalls = 0; thisFrame.clear(); isProcessing = false; if (flushNextFrame) { flushNextFrame = false; step.process(frameData2); } } }; return step; } const maxElapsed$1 = 40; function createRenderBatcher(scheduleNextBatch, allowKeepAlive) { let runNextFrame = false; let useDefaultElapsed = true; const state2 = { delta: 0, timestamp: 0, isProcessing: false }; const flagRunNextFrame = () => runNextFrame = true; const steps2 = stepsOrder.reduce((acc, key) => { acc[key] = createRenderStep(flagRunNextFrame, allowKeepAlive ? key : void 0); return acc; }, {}); const { read, resolveKeyframes, update, preRender, render, postRender } = steps2; const processBatch = () => { const timestamp = performance.now(); runNextFrame = false; { state2.delta = useDefaultElapsed ? 1e3 / 60 : Math.max(Math.min(timestamp - state2.timestamp, maxElapsed$1), 1); } state2.timestamp = timestamp; state2.isProcessing = true; read.process(state2); resolveKeyframes.process(state2); update.process(state2); preRender.process(state2); render.process(state2); postRender.process(state2); state2.isProcessing = false; if (runNextFrame && allowKeepAlive) { useDefaultElapsed = false; scheduleNextBatch(processBatch); } }; const wake = () => { runNextFrame = true; useDefaultElapsed = true; if (!state2.isProcessing) { scheduleNextBatch(processBatch); } }; const schedule = stepsOrder.reduce((acc, key) => { const step = steps2[key]; acc[key] = (process2, keepAlive = false, immediate = false) => { if (!runNextFrame) wake(); return step.schedule(process2, keepAlive, immediate); }; return acc; }, {}); const cancel = (process2) => { for (let i = 0; i < stepsOrder.length; i++) { steps2[stepsOrder[i]].cancel(process2); } }; return { schedule, cancel, state: state2, steps: steps2 }; } const { schedule: frame, cancel: cancelFrame, state: frameData, steps: frameSteps } = /* @__PURE__ */ createRenderBatcher(typeof requestAnimationFrame !== "undefined" ? requestAnimationFrame : noop, true); const { schedule: microtask, cancel: cancelMicrotask } = /* @__PURE__ */ createRenderBatcher(queueMicrotask, false); let now; function clearTime() { now = void 0; } const time = { now: () => { if (now === void 0) { time.set(frameData.isProcessing || MotionGlobalConfig.useManualTiming ? frameData.timestamp : performance.now()); } return now; }, set: (newTime) => { now = newTime; queueMicrotask(clearTime); } }; const isDragging = { x: false, y: false }; function isDragActive() { return isDragging.y; } function resolveElements(elementOrSelector, scope, selectorCache) { var _a; if (elementOrSelector instanceof EventTarget) { return [elementOrSelector]; } else if (typeof elementOrSelector === "string") { let root = document; if (scope) { root = scope.current; } const elements = (_a = selectorCache === null || selectorCache === void 0 ? void 0 : selectorCache[elementOrSelector]) !== null && _a !== void 0 ? _a : root.querySelectorAll(elementOrSelector); return elements ? Array.from(elements) : []; } return Array.from(elementOrSelector); } function setupGesture(elementOrSelector, options) { const elements = resolveElements(elementOrSelector); const gestureAbortController = new AbortController(); const eventOptions = { passive: true, ...options, signal: gestureAbortController.signal }; const cancel = () => gestureAbortController.abort(); return [elements, eventOptions, cancel]; } function isValidHover(event) { return !(event.pointerType === "touch" || isDragActive()); } function hover(elementOrSelector, onHoverStart, options = {}) { const [elements, eventOptions, cancel] = setupGesture(elementOrSelector, options); const onPointerEnter = (enterEvent) => { if (!isValidHover(enterEvent)) return; const { target } = enterEvent; const onHoverEnd = onHoverStart(target, enterEvent); if (typeof onHoverEnd !== "function" || !target) return; const onPointerLeave = (leaveEvent) => { if (!isValidHover(leaveEvent)) return; onHoverEnd(leaveEvent); target.removeEventListener("pointerleave", onPointerLeave); }; target.addEventListener("pointerleave", onPointerLeave, eventOptions); }; elements.forEach((element) => { element.addEventListener("pointerenter", onPointerEnter, eventOptions); }); return cancel; } function capturePointer(event, action) { const actionName = `${action}PointerCapture`; if (event.target instanceof Element && actionName in event.target && event.pointerId !== void 0) { try { event.target[actionName](event.pointerId); } catch (e) { } } } const isNodeOrChild = (parent, child) => { if (!child) { return false; } else if (parent === child) { return true; } else { return isNodeOrChild(parent, child.parentElement); } }; const isPrimaryPointer$1 = (event) => { if (event.pointerType === "mouse") { return typeof event.button !== "number" || event.button <= 0; } else { return event.isPrimary !== false; } }; const focusableElements = /* @__PURE__ */ new Set([ "BUTTON", "INPUT", "SELECT", "TEXTAREA", "A" ]); function isElementKeyboardAccessible(element) { return focusableElements.has(element.tagName) || element.tabIndex !== -1; } const isPressing = /* @__PURE__ */ new WeakSet(); function filterEvents(callback) { return (event) => { if (event.key !== "Enter") return; callback(event); }; } function firePointerEvent(target, type) { target.dispatchEvent(new PointerEvent("pointer" + type, { isPrimary: true, bubbles: true })); } const enableKeyboardPress = (focusEvent, eventOptions) => { const element = focusEvent.currentTarget; if (!element) return; const handleKeydown = filterEvents(() => { if (isPressing.has(element)) return; firePointerEvent(element, "down"); const handleKeyup = filterEvents(() => { firePointerEvent(element, "up"); }); const handleBlur = () => firePointerEvent(element, "cancel"); element.addEventListener("keyup", handleKeyup, eventOptions); element.addEventListener("blur", handleBlur, eventOptions); }); element.addEventListener("keydown", handleKeydown, eventOptions); element.addEventListener("blur", () => element.removeEventListener("keydown", handleKeydown), eventOptions); }; function isValidPressEvent(event) { return isPrimaryPointer$1(event) && !isDragActive(); } function press(targetOrSelector, onPressStart, options = {}) { const [targets, eventOptions, cancelEvents] = setupGesture(targetOrSelector, options); const startPress = (startEvent) => { const target = startEvent.currentTarget; if (!target || !isValidPressEvent(startEvent) || isPressing.has(target)) return; isPressing.add(target); capturePointer(startEvent, "set"); const onPressEnd = onPressStart(target, startEvent); const onPointerEnd = (endEvent, success) => { target.removeEventListener("pointerup", onPointerUp); target.removeEventListener("pointercancel", onPointerCancel); capturePointer(endEvent, "release"); if (!isValidPressEvent(endEvent) || !isPressing.has(target)) { return; } isPressing.delete(target); if (typeof onPressEnd === "function") { onPressEnd(endEvent, { success }); } }; const onPointerUp = (upEvent) => { const isOutside = !upEvent.isTrusted ? false : checkOutside(upEvent, target instanceof Element ? target.getBoundingClientRect() : { left: 0, top: 0, right: window.innerWidth, bottom: window.innerHeight }); if (isOutside) { onPointerEnd(upEvent, false); } else { onPointerEnd(upEvent, !(target instanceof Element) || isNodeOrChild(target, upEvent.target)); } }; const onPointerCancel = (cancelEvent) => { onPointerEnd(cancelEvent, false); }; target.addEventListener("pointerup", onPointerUp, eventOptions); target.addEventListener("pointercancel", onPointerCancel, eventOptions); target.addEventListener("lostpointercapture", onPointerCancel, eventOptions); }; targets.forEach((target) => { target = options.useGlobalTarget ? window : target; let canAddKeyboardAccessibility = false; if (target instanceof HTMLElement) { canAddKeyboardAccessibility = true; if (!isElementKeyboardAccessible(target) && target.getAttribute("tabindex") === null) { target.tabIndex = 0; } } target.addEventListener("pointerdown", startPress, eventOptions); if (canAddKeyboardAccessibility) { target.addEventListener("focus", (event) => enableKeyboardPress(event, eventOptions), eventOptions); } }); return cancelEvents; } function checkOutside(event, rect) { return event.clientX < rect.left || event.clientX > rect.right || event.clientY < rect.top || event.clientY > rect.bottom; } const MAX_VELOCITY_DELTA = 30; const isFloat = (value) => { return !isNaN(parseFloat(value)); }; const collectMotionValues = { current: void 0 }; class MotionValue { /** * @param init - The initiating value * @param config - Optional configuration options * * - `transformer`: A function to transform incoming values with. */ constructor(init, options = {}) { this.version = "12.5.0"; this.canTrackVelocity = null; this.events = {}; this.updateAndNotify = (v, render = true) => { const currentTime = time.now(); if (this.updatedAt !== currentTime) { this.setPrevFrameValue(); } this.prev = this.current; this.setCurrent(v); if (this.current !== this.prev && this.events.change) { this.events.change.notify(this.current); } if (render && this.events.renderRequest) { this.events.renderRequest.notify(this.current); } }; this.hasAnimated = false; this.setCurrent(init); this.owner = options.owner; } setCurrent(current) { this.current = current; this.updatedAt = time.now(); if (this.canTrackVelocity === null && current !== void 0) { this.canTrackVelocity = isFloat(this.current); } } setPrevFrameValue(prevFrameValue = this.current) { this.prevFrameValue = prevFrameValue; this.prevUpdatedAt = this.updatedAt; } /** * Adds a function that will be notified when the `MotionValue` is updated. * * It returns a function that, when called, will cancel the subscription. * * When calling `onChange` inside a React component, it should be wrapped with the * `useEffect` hook. As it returns an unsubscribe function, this should be returned * from the `useEffect` function to ensure you don't add duplicate subscribers.. * * ```jsx * export const MyComponent = () => { * const x = useMotionValue(0) * const y = useMotionValue(0) * const opacity = useMotionValue(1) * * useEffect(() => { * function updateOpacity() { * const maxXY = Math.max(x.get(), y.get()) * const newOpacity = transform(maxXY, [0, 100], [1, 0]) * opacity.set(newOpacity) * } * * const unsubscribeX = x.on("change", updateOpacity) * const unsubscribeY = y.on("change", updateOpacity) * * return () => { * unsubscribeX() * unsubscribeY() * } * }, []) * * return <motion.div style={{ x }} /> * } * ``` * * @param subscriber - A function that receives the latest value. * @returns A function that, when called, will cancel this subscription. * * @deprecated */ onChange(subscription) { if (process.env.NODE_ENV !== "production") { warnOnce(false, `value.onChange(callback) is deprecated. Switch to value.on("change", callback).`); } return this.on("change", subscription); } on(eventName, callback) { if (!this.events[eventName]) { this.events[eventName] = new SubscriptionManager(); } const unsubscribe = this.events[eventName].add(callback); if (eventName === "change") { return () => { unsubscribe(); frame.read(() => { if (!this.events.change.getSize()) { this.stop(); } }); }; } return unsubscribe; } clearListeners() { for (const eventManagers in this.events) { this.events[eventManagers].clear(); } } /** * Attaches a passive effect to the `MotionValue`. */ attach(passiveEffect, stopPassiveEffect) { this.passiveEffect = passiveEffect; this.stopPassiveEffect = stopPassiveEffect; } /** * Sets the state of the `MotionValue`. * * @remarks * * ```jsx * const x = useMotionValue(0) * x.set(10) * ``` * * @param latest - Latest value to set. * @param render - Whether to notify render subscribers. Defaults to `true` * * @public */ set(v, render = true) { if (!render || !this.passiveEffect) { this.updateAndNotify(v, render); } else { this.passiveEffect(v, this.updateAndNotify); } } setWithVelocity(prev, current, delta) { this.set(current); this.prev = void 0; this.prevFrameValue = prev; this.prevUpdatedAt = this.updatedAt - delta; } /** * Set the state of the `MotionValue`, stopping any active animations, * effects, and resets velocity to `0`. */ jump(v, endAnimation = true) { this.updateAndNotify(v); this.prev = v; this.prevUpdatedAt = this.prevFrameValue = void 0; endAnimation && this.stop(); if (this.stopPassiveEffect) this.stopPassiveEffect(); } /** * Returns the latest state of `MotionValue` * * @returns - The latest state of `MotionValue` * * @public */ get() { if (collectMotionValues.current) { collectMotionValues.current.push(this); } return this.current; } /** * @public */ getPrevious() { return this.prev; } /** * Returns the latest velocity of `MotionValue` * * @returns - The latest velocity of `MotionValue`. Returns `0` if the state is non-numerical. * * @public */ getVelocity() { const currentTime = time.now(); if (!this.canTrackVelocity || this.prevFrameValue === void 0 || currentTime - this.updatedAt > MAX_VELOCITY_DELTA) { return 0; } const delta = Math.min(this.updatedAt - this.prevUpdatedAt, MAX_VELOCITY_DELTA); return velocityPerSecond(parseFloat(this.current) - parseFloat(this.prevFrameValue), delta); } /** * Registers a new animation to control this `MotionValue`. Only one * animation can drive a `MotionValue` at one time. * * ```jsx * value.start() * ``` * * @param animation - A function that starts the provided animation */ start(startAnimation) { this.stop(); return new Promise((resolve) => { this.hasAnimated = true; this.animation = startAnimation(resolve); if (this.events.animationStart) { this.events.animationStart.notify(); } }).then(() => { if (this.events.animationComplete) { this.events.animationComplete.notify(); } this.clearAnimation(); }); } /** * Stop the currently active animation. * * @public */ stop() { if (this.animation) { this.animation.stop(); if (this.events.animationCancel) { this.events.animationCancel.notify(); } } this.clearAnimation(); } /** * Returns `true` if this value is currently animating. * * @public */ isAnimating() { return !!this.animation; } clearAnimation() { delete this.animation; } /** * Destroy and clean up subscribers to this `MotionValue`. * * The `MotionValue` hooks like `useMotionValue` and `useTransform` automatically * handle the lifecycle of the returned `MotionValue`, so this method is only necessary if you've manually * created a `MotionValue` via the `motionValue` function. * * @public */ destroy() { this.clearListeners(); this.stop(); if (this.stopPassiveEffect) { this.stopPassiveEffect(); } } } function motionValue(init, options) { return new MotionValue(init, options); } const clamp$1 = (min, max, v) => { if (v > max) return max; if (v < min) return min; return v; }; const velocitySampleDuration = 5; function calcGeneratorVelocity(resolveValue, t, current) { const prevT = Math.max(t - velocitySampleDuration, 0); return velocityPerSecond(current - resolveValue(prevT), t - prevT); } const springDefaults = { // Default spring physics stiffness: 100, damping: 10, mass: 1, velocity: 0, // Default duration/bounce-based options duration: 800, // in ms bounce: 0.3, visualDuration: 0.3, // in seconds // Rest thresholds restSpeed: { granular: 0.01, default: 2 }, restDelta: { granular: 5e-3, default: 0.5 }, // Limits minDuration: 0.01, // in seconds maxDuration: 10, // in seconds minDamping: 0.05, maxDamping: 1 }; const safeMin = 1e-3; function findSpring({ duration = springDefaults.duration, bounce = springDefaults.bounce, velocity = springDefaults.velocity, mass = springDefaults.mass }) { let envelope; let derivative; warning(duration <= /* @__PURE__ */ secondsToMilliseconds$1(springDefaults.maxDuration), "Spring duration must be 10 seconds or less"); let dampingRatio = 1 - bounce; dampingRatio = clamp$1(springDefaults.minDamping, springDefaults.maxDamping, dampingRatio); duration = clamp$1(springDefaults.minDuration, springDefaults.maxDuration, /* @__PURE__ */ millisecondsToSeconds$1(duration)); if (dampingRatio < 1) { envelope = (undampedFreq2) => { const exponentialDecay = undampedFreq2 * dampingRatio; const delta = exponentialDecay * duration; const a = exponentialDecay - velocity; const b = calcAngularFreq(undampedFreq2, dampingRatio); const c = Math.exp(-delta); return safeMin - a / b * c; }; derivative = (undampedFreq2) => { const exponentialDecay = undampedFreq2 * dampingRatio; const delta = exponentialDecay * duration; const d = delta * velocity + velocity; const e = Math.pow(dampingRatio, 2) * Math.pow(undampedFreq2, 2) * duration; const f = Math.exp(-delta); const g = calcAngularFreq(Math.pow(undampedFreq2, 2), dampingRatio); const factor = -envelope(undampedFreq2) + safeMin > 0 ? -1 : 1; return factor * ((d - e) * f) / g; }; } else { envelope = (undampedFreq2) => { const a = Math.exp(-undampedFreq2 * duration); const b = (undampedFreq2 - velocity) * duration + 1; return -safeMin + a * b; }; derivative = (undampedFreq2) => { const a = Math.exp(-undampedFreq2 * duration); const b = (velocity - undampedFreq2) * (duration * duration); return a * b; }; } const initialGuess = 5 / duration; const undampedFreq = approximateRoot(envelope, derivative, initialGuess); duration = /* @__PURE__ */ secondsToMilliseconds$1(duration); if (isNaN(undampedFreq)) { return { stiffness: springDefaults.stiffness, damping: springDefaults.damping, 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, keys2) { return keys2.some((key) => options[key] !== void 0); } function getSpringOptions(options) { let springOptions = { velocity: springDefaults.velocity, stiffness: springDefaults.stiffness, damping: springDefaults.damping, mass: springDefaults.mass, isResolvedFromDuration: false, ...options }; if (!isSpringType(options, physicsKeys) && isSpringType(options, durationKeys)) { if (options.visualDuration) { const visualDuration = options.visualDuration; const root = 2 * Math.PI / (visualDuration * 1.2); const stiffness = root * root; const damping = 2 * clamp$1(0.05, 1, 1 - (options.bounce || 0)) * Math.sqrt(stiffness); springOptions = { ...springOptions, mass: springDefaults.mass, stiffness, damping }; } else { const derived = findSpring(options); springOptions = { ...springOptions, ...derived, mass: springDefaults.mass }; springOptions.isResolvedFromDuration = true; } } return springOptions; } function spring(optionsOrVisualDuration = springDefaults.visualDuration, bounce = springDefaults.bounce) { const options = typeof optionsOrVisualDuration !== "object" ? { visualDuration: optionsOrVisualDuration, keyframes: [0, 1], bounce } : optionsOrVisualDuration; let { restSpeed, restDelta } = options; const origin = options.keyframes[0]; const target = options.keyframes[options.keyframes.length - 1]; const state2 = { done: false, value: origin }; const { stiffness, damping, mass, duration, velocity, isResolvedFromDuration } = getSpringOptions({ ...options, velocity: -/* @__PURE__ */ millisecondsToSeconds$1(options.velocity || 0) }); const initialVelocity = velocity || 0; const dampingRatio = damping / (2 * Math.sqrt(stiffness * mass)); const initialDelta = target - origin; const undampedAngularFreq = /* @__PURE__ */ millisecondsToSeconds$1(Math.sqrt(stiffness / mass)); const isGranularScale = Math.abs(initialDelta) < 5; restSpeed || (restSpeed = isGranularScale ? springDefaults.restSpeed.granular : springDefaults.restSpeed.default); restDelta || (restDelta = isGranularScale ? springDefaults.restDelta.granular : springDefaults.restDelta.default); let resolveSpring; if (dampingRatio < 1) { const angularFreq = calcAngularFreq(undampedAngularFreq, dampingRatio); resolveSpring = (t) => { const envelope = Math.exp(-dampingRatio * undampedAngularFreq * t); return target - envelope * ((initialVelocity + dampingRatio * undampedAngularFreq * initialDelta) / angularFreq * Math.sin(angularFreq * t) + initialDelta * Math.cos(angularFreq * t)); }; } else if (dampingRatio === 1) { resolveSpring = (t) => target - 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 target - envelope * ((initialVelocity + dampingRatio * undampedAngularFreq * initialDelta) * Math.sinh(freqForT) + dampedAngularFreq * initialDelta * Math.cosh(freqForT)) / dampedAngularFreq; }; } const generator = { calculatedDuration: isResolvedFromDuration ? duration || null : null, next: (t) => { const current = resolveSpring(t); if (!isResolvedFromDuration) { let currentVelocity = 0; if (dampingRatio < 1) { currentVelocity = t === 0 ? /* @__PURE__ */ secondsToMilliseconds$1(initialVelocity) : calcGeneratorVelocity(resolveSpring, t, current); } const isBelowVelocityThreshold = Math.abs(currentVelocity) <= restSpeed; const isBelowDisplacementThreshold = Math.abs(target - current) <= restDelta; state2.done = isBelowVelocityThreshold && isBelowDisplacementThreshold; } else { state2.done = t >= duration; } state2.value = state2.done ? target : current; return state2; }, toString: () => { const calculatedDuration = Math.min(calcGeneratorDuration(generator), maxGeneratorDuration); const easing = generateLinearEasing((progress2) => generator.next(calculatedDuration * progress2).value, calculatedDuration, 30); return calculatedDuration + "ms " + easing; } }; return generator; } const wrap = (min, max, v) => { const rangeSize = max - min; return ((v - min) % rangeSize + rangeSize) % rangeSize + min; }; const isEasingArray = (ease2) => { return Array.isArray(ease2) && typeof ease2[0] !== "number"; }; function getEasingForSegment(easing, i) { return isEasingArray(easing) ? easing[wrap(0, easing.length, i)] : easing; } const mixNumber$2 = (from, to, progress2) => { return from + (to - from) * progress2; }; function fillOffset(offset, remaining) { const min = offset[offset.length - 1]; for (let i = 1; i <= remaining; i++) { const offsetProgress = /* @__PURE__ */ progress$1(0, remaining, i); offset.push(mixNumber$2(min, 1, offsetProgress)); } } function defaultOffset$1(arr) { const offset = [0]; fillOffset(offset, arr.length - 1); return offset; } const isMotionValue$1 = (value) => Boolean(value && value.getVelocity); function isDOMKeyframes(keyframes2) { return typeof keyframes2 === "object" && !Array.isArray(keyframes2); } function resolveSubjects(subject, keyframes2, scope, selectorCache) { if (typeof subject === "string" && isDOMKeyframes(keyframes2)) { return resolveElements(subject, scope, selectorCache); } else if (subject instanceof NodeList) { return Array.from(subject); } else if (Array.isArray(subject)) { return subject; } else { return [subject]; } } function calculateRepeatDuration(duration, repeat, _repeatDelay) { return duration * (repeat + 1); } function calcNextTime(current, next, prev, labels) { var _a; if (typeof next === "number") { return next; } else if (next.startsWith("-") || next.startsWith("+")) { return Math.max(0, current + parseFloat(next)); } else if (next === "<") { return prev; } else { return (_a = labels.get(next)) !== null && _a !== void 0 ? _a : current; } } function eraseKeyframes(sequence, startTime, endTime) { for (let i = 0; i < sequence.length; i++) { const keyframe = sequence[i]; if (keyframe.at > startTime && keyframe.at < endTime) { removeItem(sequence, keyframe); i--; } } } function addKeyframes(sequence, keyframes2, easing, offset, startTime, endTime) { eraseKeyframes(sequence, startTime, endTime); for (let i = 0; i < keyframes2.length; i++) { sequence.push({ value: keyframes2[i], at: mixNumber$2(startTime, endTime, offset[i]), easing: getEasingForSegment(easing, i) }); } } function normalizeTimes(times, repeat) { for (let i = 0; i < times.length; i++) { times[i] = times[i] / (repeat + 1); } } function compareByTime(a, b) { if (a.at === b.at) { if (a.value === null) return 1; if (b.value === null) return -1; return 0; } else { return a.at - b.at; } } const defaultSegmentEasing = "easeInOut"; const MAX_REPEAT = 20; function createAnimationsFromSequence(sequence, { defaultTransition = {}, ...sequenceTransition } = {}, scope, generators2) { const defaultDuration = defaultTransition.duration || 0.3; const animationDefinitions = /* @__PURE__ */ new Map(); const sequences = /* @__PURE__ */ new Map(); const elementCache = {}; const timeLabels = /* @__PURE__ */ new Map(); let prevTime = 0; let currentTime = 0; let totalDuration = 0; for (let i = 0; i < sequence.length; i++) { const segment = sequence[i]; if (typeof segment === "string") { timeLabels.set(segment, currentTime); continue; } else if (!Array.isArray(segment)) { timeLabels.set(segment.name, calcNextTime(currentTime, segment.at, prevTime, timeLabels)); continue; } let [subject, keyframes2, transition = {}] = segment; if (transition.at !== void 0) { currentTime = calcNextTime(currentTime, transition.at, prevTime, timeLabels); } let maxDuration2 = 0; const resolveValueSequence = (valueKeyframes, valueTransition, valueSequence, elementIndex = 0, numSubjects = 0) => { const valueKeyframesAsList = keyframesAsList(valueKeyframes); const { delay: delay2 = 0, times = defaultOffset$1(valueKeyframesAsList), type = "keyframes", repeat, repeatType, repeatDelay = 0, ...remainingTransition } = valueTransition; let { ease: ease2 = defaultTransition.ease || "easeOut", duration } = valueTransition; const calculatedDelay = typeof delay2 === "function" ? delay2(elementIndex, numSubjects) : delay2; const numKeyframes = valueKeyframesAsList.length; const createGenerator = isGenerator(type) ? type : generators2 === null || generators2 === void 0 ? void 0 : generators2[type]; if (numKeyframes <= 2 && createGenerator) { let absoluteDelta = 100; if (numKeyframes === 2 && isNumberKeyframesArray(valueKeyframesAsList)) { const delta = valueKeyframesAsList[1] - valueKeyframesAsList[0]; absoluteDelta = Math.abs(delta); } const springTransition = { ...remainingTransition }; if (duration !== void 0) { springTransition.duration = /* @__PURE__ */ secondsToMilliseconds$1(duration); } const springEasing = createGeneratorEasing(springTransition, absoluteDelta, createGenerator); ease2 = springEasing.ease; duration = springEasing.duration; } duration !== null && duration !== void 0 ? duration : duration = defaultDuration; const startTime = currentTime + calculatedDelay; if (times.length === 1 && times[0] === 0) { times[1] = 1; } const remainder = times.length - valueKeyframesAsList.length; remainder > 0 && fillOffset(times, remainder); valueKeyframesAsList.length === 1 && valueKeyframesAsList.unshift(null); if (repeat) { exports.invariant(repeat < MAX_REPEAT, "Repeat count too high, must be less than 20"); duration = calculateRepeatDuration(duration, repeat); const originalKeyframes = [...valueKeyframesAsList]; const originalTimes = [...times]; ease2 = Array.isArray(ease2) ? [...ease2] : [ease2]; const originalEase = [...ease2]; for (let repeatIndex = 0; repeatIndex < repeat; repeatIndex++) { valueKeyframesAsList.push(...originalKeyframes); for (let keyframeIndex = 0; keyframeIndex < originalKeyframes.length; keyframeIndex++) { times.push(originalTimes[keyframeIndex] + (repeatIndex + 1)); ease2.push(keyframeIndex === 0 ? "linear" : getEasingForSegment(originalEase, keyframeIndex - 1)); } } normalizeTimes(times, repeat); } const targetTime = startTime + duration; addKeyframes(valueSequence, valueKeyframesAsList, ease2, times, startTime, targetTime); maxDuration2 = Math.max(calculatedDelay + duration, maxDuration2); totalDuration = Math.max(targetTime, totalDuration); }; if (isMotionValue$1(subject)) { const subjectSequence = getSubjectSequence(subject, sequences); resolveValueSequence(keyframes2, transition, getValueSequence("default", subjectSequence)); } else { const subjects = resolveSubjects(subject, keyframes2, scope, elementCache); const numSubjects = subjects.length; for (let subjectIndex = 0; subjectIndex < numSubjects; subjectIndex++) { keyframes2 = keyframes2; transition = transition; const thisSubject = subjects[subjectIndex]; const subjectSequence = getSubjectSequence(thisSubject, sequences); for (const key in keyframes2) { resolveValueSequence(keyframes2[key], getValueTransition(transition, key), getValueSequence(key, subjectSequence), subjectIndex, numSubjects); } } } prevTime = currentTime; currentTime += maxDuration2; } sequences.forEach((valueSequences, element) => { for (const key in valueSequences) { const valueSequence = valueSequences[key]; valueSequence.sort(compareByTime); const keyframes2 = []; const valueOffset = []; const valueEasing = []; for (let i = 0; i < valueSequence.length; i++) { const { at, value, easing } = valueSequence[i]; keyframes2.push(value); valueOffset.push(/* @__PURE__ */ progress$1(0, totalDuration, at)); valueEasing.push(easing || "easeOut"); } if (valueOffset[0] !== 0) { valueOffset.unshift(0); keyframes2.unshift(keyframes2[0]); valueEasing.unshift(defaultSegmentEasing); } if (valueOffset[valueOffset.length - 1] !== 1) { valueOffset.push(1); keyframes2.push(null); } if (!animationDefinitions.has(element)) { animationDefinitions.set(element, { keyframes: {}, transition: {} }); } const definition = animationDefinitions.get(element); definition.keyframes[key] = keyframes2; definition.transition[key] = { ...defaultTransition, duration: totalDuration, ease: valueEasing, times: valueOffset, ...sequenceTransition }; } }); return animationDefinitions; } function getSubjectSequence(subject, sequences) { !sequences.has(subject) && sequences.set(subject, {}); return sequences.get(subject); } function getValueSequence(name, sequences) { if (!sequences[name]) sequences[name] = []; return sequences[name]; } function keyframesAsList(keyframes2) { return Array.isArray(keyframes2) ? keyframes2 : [keyframes2]; } function getValueTransition(transition, key) { return transition && transition[key] ? { ...transition, ...transition[key] } : { ...transition }; } const isNumber$1 = (keyframe) => typeof keyframe === "number"; const isNumberKeyframesArray = (keyframes2) => keyframes2.every(isNumber$1); const visualElementStore = /* @__PURE__ */ new WeakMap(); const transformPropOrder = [ "transformPerspective", "x", "y", "z", "translateX", "translateY", "translateZ", "scale", "scaleX", "scaleY", "rotate", "rotateX", "rotateY", "rotateZ", "skew", "skewX", "skewY" ]; const transformProps = new Set(transformPropOrder); const positionalKeys = /* @__PURE__ */ new Set([ "width", "height", "top", "left", "right", "bottom", ...transformPropOrder ]); const isKeyframesTarget = (v) => { return Array.isArray(v); }; const isCustomValue = (v) => { return Boolean(v && typeof v === "object" && v.mix && v.toValue); }; const resolveFinalValueInKeyframes = (v) => { return isKeyframesTarget(v) ? v[v.length - 1] || 0 : v; }; function getValueState(visualElement) { const state2 = [{}, {}]; visualElement === null || visualElement === void 0 ? void 0 : visualElement.values.forEach((value, key) => { state2[0][key] = value.get(); state2[1][key] = value.getVelocity(); }); return state2; } function resolveVariantFromProps(props, definition, custom, visualElement) { if (typeof definition === "function") { const [current, velocity] = getValueState(visualElement); definition = definition(custom !== void 0 ? custom : props.custom, current, velocity); } if (typeof definition === "string") { definition = props.variants && props.variants[definition]; } if (typeof definition === "function") { const [current, velocity] = getValueState(visualElement); definition = definition(custom !== void 0 ? custom : props.custom, current, velocity); } return definition; } function resolveVariant$1(visualElement, definition, custom) { const props = visualElement.getProps(); return resolveVariantFromProps(props, definition, props.custom, visualElement); } function setMotionValue(visualElement, key, value) { if (visualElement.hasValue(key)) { visualElement.getValue(key).set(value); } else { visualElement.addValue(key, motionValue(value)); } } function setTarget(visualElement, definition) { const resolved = resolveVariant$1(visualElement, definition); let { transitionEnd = {}, transition = {}, ...target } = resolved || {}; target = { ...target, ...transitionEnd }; for (const key in target) { const value = resolveFinalValueInKeyframes(target[key]); setMotionValue(visualElement, key, value); } } function isWillChangeMotionValue$1(value) { return Boolean(isMotionValue$1(value) && value.add); } function addValueToWillChange$1(visualElement, key) { const willChange = visualElement.getValue("willChange"); if (isWillChangeMotionValue$1(willChange)) { return willChange.add(key); } } const camelToDash = (str) => str.replace(/([a-z])([A-Z])/gu, "$1-$2").toLowerCase(); const optimizedAppearDataId = "framerAppearId"; const optimizedAppearDataAttribute = "data-" + camelToDash(optimizedAppearDataId); function getOptimisedAppearId(visualElement) { return visualElement.props[optimizedAppearDataAttribute]; } const calcBezier = (t, a1, a2) => (((1 - 3 * a2 + 3 * a1) * t + (3 * a2 - 6 * a1)) * t + 3 * a1) * t; const subdivisionPrecision = 1e-7; const subdivisionMaxIterations = 12; function binarySubdivide(x, lowerBound, upperBound, mX1, mX2) { let currentX; let currentT; let i = 0; do { currentT = lowerBound + (upperBound - lowerBound) / 2; currentX = calcBezier(currentT, mX1, mX2) - x; if (currentX > 0) { upperBound = currentT; } else { lowerBound = currentT; } } while (Math.abs(currentX) > subdivisionPrecision && ++i < subdivisionMaxIterations); return currentT; } function cubicBezie