motion-v
Version:
<h1 align="center"> <img width="35" height="35" alt="Motion logo" src="https://github.com/user-attachments/assets/00d6d1c3-72c4-4c2f-a664-69da13182ffc" /><br />Motion for Vue</h1>
182 lines (181 loc) • 7.12 kB
JavaScript
import { shallowCompare } from "./utils.mjs";
import { getVariantContext } from "./utils/get-variant-context.mjs";
import { animateVisualElement, calcChildStagger, isAnimationControls, isVariantLabel, resolveVariant } from "motion-dom";
var variantPriorityOrder = [
"animate",
"whileInView",
"whileFocus",
"whileHover",
"whilePress",
"whileDrag",
"exit"
];
var reversePriorityOrder = [...variantPriorityOrder].reverse();
var numAnimationTypes = variantPriorityOrder.length;
function createTypeState(isActive = false) {
return {
isActive,
protectedKeys: {},
needsAnimating: {},
prevResolvedValues: {}
};
}
function createState() {
return {
animate: createTypeState(true),
whileInView: createTypeState(),
whileHover: createTypeState(),
whilePress: createTypeState(),
whileDrag: createTypeState(),
whileFocus: createTypeState(),
exit: createTypeState()
};
}
function checkVariantsDidChange(prev, next) {
if (typeof next === "string") return next !== prev;
else if (Array.isArray(next)) return !shallowCompare(next, prev);
return false;
}
function isKeyframesTarget(v) {
return Array.isArray(v);
}
function createAnimateFunction(visualElement) {
return (animations) => {
return Promise.all(animations.map(({ animation, options }) => animateVisualElement(visualElement, animation, options)));
};
}
function createAnimationState(visualElement) {
let animate = createAnimateFunction(visualElement);
let state = createState();
let isInitialRender = true;
const buildResolvedTypeValues = (type) => (acc, definition) => {
const resolved = resolveVariant(visualElement, definition, type === "exit" ? visualElement.presenceContext?.custom : void 0);
if (resolved) {
const { transition, transitionEnd, ...target } = resolved;
acc = {
...acc,
...target,
...transitionEnd
};
}
return acc;
};
function setAnimateFunction(makeAnimator) {
animate = makeAnimator(visualElement);
}
function animateChanges(changedActiveType) {
const { props } = visualElement;
const context = getVariantContext(visualElement.parent) || {};
const animations = [];
const removedKeys = /* @__PURE__ */ new Set();
let encounteredKeys = {};
let removedVariantIndex = Infinity;
for (let i = 0; i < numAnimationTypes; i++) {
const type = reversePriorityOrder[i];
const typeState = state[type];
const prop = props[type] !== void 0 ? props[type] : context[type];
const propIsVariant = isVariantLabel(prop);
const activeDelta = type === changedActiveType ? typeState.isActive : null;
if (activeDelta === false) removedVariantIndex = i;
let isInherited = prop === context[type] && prop !== props[type] && propIsVariant;
if (isInherited && isInitialRender && visualElement.manuallyAnimateOnMount) isInherited = false;
typeState.protectedKeys = { ...encounteredKeys };
if (!typeState.isActive && activeDelta === null || !prop && !typeState.prevProp || isAnimationControls(prop) || typeof prop === "boolean") continue;
const variantDidChange = checkVariantsDidChange(typeState.prevProp, prop);
let shouldAnimateType = variantDidChange || type === changedActiveType && typeState.isActive && !isInherited && propIsVariant || i > removedVariantIndex && propIsVariant;
let handledRemovedValues = false;
const definitionList = Array.isArray(prop) ? prop : [prop];
let resolvedValues = definitionList.reduce(buildResolvedTypeValues(type), {});
if (activeDelta === false) resolvedValues = {};
const { prevResolvedValues = {} } = typeState;
const allKeys = {
...prevResolvedValues,
...resolvedValues
};
const markToAnimate = (key) => {
shouldAnimateType = true;
if (removedKeys.has(key)) {
handledRemovedValues = true;
removedKeys.delete(key);
}
typeState.needsAnimating[key] = true;
const motionValue$1 = visualElement.getValue(key);
if (motionValue$1) motionValue$1.liveStyle = false;
};
for (const key in allKeys) {
const next = resolvedValues[key];
const prev = prevResolvedValues[key];
if (Object.hasOwnProperty.call(encounteredKeys, key)) continue;
let valueHasChanged = false;
if (isKeyframesTarget(next) && isKeyframesTarget(prev)) valueHasChanged = !shallowCompare(next, prev);
else valueHasChanged = next !== prev;
if (valueHasChanged) if (next !== void 0 && next !== null) markToAnimate(key);
else removedKeys.add(key);
else if (next !== void 0 && removedKeys.has(key)) markToAnimate(key);
else typeState.protectedKeys[key] = true;
}
typeState.prevProp = prop;
typeState.prevResolvedValues = resolvedValues;
if (typeState.isActive) encounteredKeys = {
...encounteredKeys,
...resolvedValues
};
if (isInitialRender && visualElement.blockInitialAnimation) shouldAnimateType = false;
const willAnimateViaParent = isInherited && variantDidChange;
if (shouldAnimateType && (!willAnimateViaParent || handledRemovedValues)) animations.push(...definitionList.map((animation) => {
const options = { type };
if (typeof animation === "string" && isInitialRender && !willAnimateViaParent && visualElement.manuallyAnimateOnMount && visualElement.parent) {
const { parent } = visualElement;
const parentVariant = resolveVariant(parent, animation);
if (parent.enteringChildren && parentVariant) {
const { delayChildren } = parentVariant.transition || {};
options.delay = calcChildStagger(parent.enteringChildren, visualElement, delayChildren);
}
}
return {
animation,
options
};
}));
}
if (removedKeys.size) {
const fallbackAnimation = {};
if (typeof props.initial !== "boolean") {
const initialTransition = resolveVariant(visualElement, Array.isArray(props.initial) ? props.initial[0] : props.initial);
if (initialTransition && initialTransition.transition) fallbackAnimation.transition = initialTransition.transition;
}
removedKeys.forEach((key) => {
const fallbackTarget = visualElement.getBaseTarget(key);
const motionValue$1 = visualElement.getValue(key);
if (motionValue$1) motionValue$1.liveStyle = true;
fallbackAnimation[key] = fallbackTarget ?? null;
});
animations.push({ animation: fallbackAnimation });
}
let shouldAnimate = Boolean(animations.length);
if (isInitialRender && (props.initial === false || props.initial === props.animate) && !visualElement.manuallyAnimateOnMount) shouldAnimate = false;
isInitialRender = false;
return shouldAnimate ? animate(animations) : Promise.resolve();
}
function setActive(type, isActive) {
if (state[type].isActive === isActive) return Promise.resolve();
visualElement.variantChildren?.forEach((child) => {
child.animationState?.setActive(type, isActive);
});
state[type].isActive = isActive;
const animations = animateChanges(type);
for (const key in state) state[key].protectedKeys = {};
return animations;
}
return {
animateChanges,
setActive,
setAnimateFunction,
getState: () => state,
reset: () => {
state = createState();
isInitialRender = true;
}
};
}
export { createAnimationState };