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>
229 lines (228 loc) • 10.2 kB
JavaScript
import { isAnimationControls } from "../../animation/utils.mjs";
import { Feature } from "../feature.mjs";
import { mountedStates } from "../../state/motion-state.mjs";
import { visualElementStore } from "../../external/.pnpm/framer-motion@12.23.12/external/framer-motion/dist/es/render/store.mjs";
import { motionEvent } from "../../state/event.mjs";
import { style } from "../../state/style.mjs";
import { transformResetValue } from "../../state/transform.mjs";
import { hasChanged, resolveVariant } from "../../state/utils.mjs";
import { isDef } from "@vueuse/core";
import { createVisualElement } from "../../state/create-visual-element.mjs";
import { prefersReducedMotion } from "../../external/.pnpm/framer-motion@12.23.12/external/framer-motion/dist/es/utils/reduced-motion/state.mjs";
import { calcChildStagger } from "./calc-child-stagger.mjs";
import { animate } from "../../external/.pnpm/framer-motion@12.23.12/external/framer-motion/dist/es/animation/animate/index.mjs";
import { noop } from "../../external/.pnpm/motion-utils@12.23.6/external/motion-utils/dist/es/noop.mjs";
const STATE_TYPES = ["initial", "animate", "whileInView", "whileHover", "whilePress", "whileDrag", "whileFocus", "exit"];
class AnimationFeature extends Feature {
constructor(state) {
var _a, _b;
super(state);
this.animateUpdates = ({
controlActiveState,
directAnimate,
directTransition,
controlDelay = 0,
isExit
} = {}) => {
const { reducedMotion } = this.state.options.motionConfig;
this.state.visualElement.shouldReduceMotion = reducedMotion === "always" || reducedMotion === "user" && !!prefersReducedMotion.current;
const prevTarget = this.state.target;
this.state.target = { ...this.state.baseTarget };
let animationOptions = {};
animationOptions = this.resolveStateAnimation({
controlActiveState,
directAnimate,
directTransition
});
this.state.finalTransition = animationOptions;
const factories = this.createAnimationFactories(prevTarget, animationOptions, controlDelay);
const { getChildAnimations } = this.setupChildAnimations(animationOptions, this.state.activeStates);
return this.executeAnimations({
factories,
getChildAnimations,
transition: animationOptions,
controlActiveState,
isExit
});
};
this.state.visualElement = createVisualElement(this.state.options.as, {
presenceContext: null,
parent: (_a = this.state.parent) == null ? void 0 : _a.visualElement,
props: {
...this.state.options,
whileTap: this.state.options.whilePress
},
visualState: {
renderState: {
transform: {},
transformOrigin: {},
style: {},
vars: {},
attrs: {}
},
latestValues: {
...this.state.baseTarget
}
},
reducedMotionConfig: this.state.options.motionConfig.reducedMotion
});
(_b = this.state.visualElement.parent) == null ? void 0 : _b.addChild(this.state.visualElement);
this.state.animateUpdates = this.animateUpdates;
if (this.state.isMounted())
this.state.startAnimation();
}
updateAnimationControlsSubscription() {
const { animate: animate2 } = this.state.options;
if (isAnimationControls(animate2)) {
this.unmountControls = animate2.subscribe(this.state);
}
}
executeAnimations({
factories,
getChildAnimations,
transition,
controlActiveState,
isExit = false
}) {
const getAnimation = () => Promise.all(factories.map((factory) => factory()).filter(Boolean));
const animationTarget = { ...this.state.target };
const element = this.state.element;
const finishAnimation = (animationPromise) => {
var _a, _b;
element.dispatchEvent(motionEvent("motionstart", animationTarget));
(_b = (_a = this.state.options).onAnimationStart) == null ? void 0 : _b.call(_a, animationTarget);
animationPromise.then(() => {
var _a2, _b2;
element.dispatchEvent(motionEvent("motioncomplete", animationTarget, isExit));
(_b2 = (_a2 = this.state.options).onAnimationComplete) == null ? void 0 : _b2.call(_a2, animationTarget);
}).catch(noop);
};
const getAnimationPromise = () => {
const animationPromise = (transition == null ? void 0 : transition.when) ? (transition.when === "beforeChildren" ? getAnimation() : getChildAnimations()).then(() => transition.when === "beforeChildren" ? getChildAnimations() : getAnimation()) : Promise.all([getAnimation(), getChildAnimations()]);
finishAnimation(animationPromise);
return animationPromise;
};
return controlActiveState ? getAnimationPromise : getAnimationPromise();
}
/**
* Setup child animations
*/
setupChildAnimations(transition, controlActiveState) {
var _a;
const visualElement = this.state.visualElement;
if (!((_a = visualElement.variantChildren) == null ? void 0 : _a.size) || !controlActiveState)
return { getChildAnimations: () => Promise.resolve() };
const { staggerChildren = 0, staggerDirection = 1, delayChildren = 0 } = transition || {};
const numChildren = visualElement.variantChildren.size;
const maxStaggerDuration = (numChildren - 1) * staggerChildren;
const delayIsFunction = typeof delayChildren === "function";
const generateStaggerDuration = delayIsFunction ? (i) => delayChildren(i, numChildren) : staggerDirection === 1 ? (i = 0) => i * staggerChildren : (i = 0) => maxStaggerDuration - i * staggerChildren;
const childAnimations = Array.from(visualElement.variantChildren).map((child, index) => {
return child.state.animateUpdates({
controlActiveState,
controlDelay: (delayIsFunction ? 0 : delayChildren) + generateStaggerDuration(index)
});
});
return {
getChildAnimations: () => Promise.all(childAnimations.map((animation) => {
return animation();
}))
};
}
createAnimationFactories(prevTarget, animationOptions, controlDelay) {
const factories = [];
Object.keys(this.state.target).forEach((key) => {
var _a;
if (!hasChanged(prevTarget[key], this.state.target[key]))
return;
(_a = this.state.baseTarget)[key] ?? (_a[key] = style.get(this.state.element, key));
const keyValue = this.state.target[key] === "none" && isDef(transformResetValue[key]) ? transformResetValue[key] : this.state.target[key];
factories.push(() => {
var _a2;
return animate(
this.state.element,
{ [key]: keyValue },
{
...(animationOptions == null ? void 0 : animationOptions[key]) || animationOptions,
delay: (((_a2 = animationOptions == null ? void 0 : animationOptions[key]) == null ? void 0 : _a2.delay) || (animationOptions == null ? void 0 : animationOptions.delay) || 0) + controlDelay
}
);
});
});
return factories;
}
resolveStateAnimation({
controlActiveState,
directAnimate,
directTransition
}) {
let variantTransition = this.state.options.transition;
let variant = {};
const { variants, custom, transition, animatePresenceContext } = this.state.options;
const customValue = custom ?? (animatePresenceContext == null ? void 0 : animatePresenceContext.custom);
this.state.activeStates = { ...this.state.activeStates, ...controlActiveState };
STATE_TYPES.forEach((name) => {
if (!this.state.activeStates[name] || isAnimationControls(this.state.options[name]))
return;
const definition = this.state.options[name];
let resolvedVariant = isDef(definition) ? resolveVariant(definition, variants, customValue) : void 0;
if (this.state.visualElement.isVariantNode) {
const controlVariant = resolveVariant(this.state.context[name], variants, customValue);
resolvedVariant = controlVariant ? Object.assign(controlVariant || {}, resolvedVariant) : variant;
}
if (!resolvedVariant)
return;
if (name !== "initial")
variantTransition = resolvedVariant.transition || this.state.options.transition || {};
variant = Object.assign(variant, resolvedVariant);
});
if (directAnimate) {
variant = resolveVariant(directAnimate, variants, customValue);
variantTransition = variant.transition || directTransition || transition;
}
Object.entries(variant).forEach(([key, value]) => {
if (key === "transition")
return;
this.state.target[key] = value;
});
return variantTransition;
}
/**
* Subscribe any provided AnimationControls to the component's VisualElement
*/
mount() {
var _a, _b;
const { element } = this.state;
mountedStates.set(element, this.state);
if (!visualElementStore.get(element)) {
this.state.visualElement.mount(element);
visualElementStore.set(element, this.state.visualElement);
}
this.state.visualElement.state = this.state;
this.updateAnimationControlsSubscription();
const visualElement = this.state.visualElement;
const parentVisualElement = visualElement.parent;
visualElement.enteringChildren = void 0;
if (((_a = this.state.parent) == null ? void 0 : _a.isMounted()) && !visualElement.isControllingVariants && ((_b = parentVisualElement == null ? void 0 : parentVisualElement.enteringChildren) == null ? void 0 : _b.has(visualElement))) {
const { delayChildren } = this.state.parent.finalTransition || {};
this.animateUpdates({
controlActiveState: this.state.parent.activeStates,
controlDelay: calcChildStagger(parentVisualElement.enteringChildren, visualElement, delayChildren)
})();
}
}
update() {
const { animate: animate2 } = this.state.options;
const { animate: prevAnimate } = this.state.visualElement.prevProps || {};
if (animate2 !== prevAnimate) {
this.updateAnimationControlsSubscription();
}
}
unmount() {
var _a;
(_a = this.unmountControls) == null ? void 0 : _a.call(this);
}
}
export {
AnimationFeature
};