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
JavaScript
"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