99 lines
3.9 KiB
JavaScript
99 lines
3.9 KiB
JavaScript
import { getValueTransition, makeAnimationInstant, frame, JSAnimation, AsyncMotionValueAnimation } from 'motion-dom';
|
|
import { secondsToMilliseconds, MotionGlobalConfig } from 'motion-utils';
|
|
import { getFinalKeyframe } from '../animators/waapi/utils/get-final-keyframe.mjs';
|
|
import { getDefaultTransition } from '../utils/default-transitions.mjs';
|
|
import { isTransitionDefined } from '../utils/is-transition-defined.mjs';
|
|
|
|
const animateMotionValue = (name, value, target, transition = {}, element, isHandoff) => (onComplete) => {
|
|
const valueTransition = getValueTransition(transition, name) || {};
|
|
/**
|
|
* Most transition values are currently completely overwritten by value-specific
|
|
* transitions. In the future it'd be nicer to blend these transitions. But for now
|
|
* delay actually does inherit from the root transition if not value-specific.
|
|
*/
|
|
const delay = valueTransition.delay || transition.delay || 0;
|
|
/**
|
|
* Elapsed isn't a public transition option but can be passed through from
|
|
* optimized appear effects in milliseconds.
|
|
*/
|
|
let { elapsed = 0 } = transition;
|
|
elapsed = elapsed - secondsToMilliseconds(delay);
|
|
const options = {
|
|
keyframes: Array.isArray(target) ? target : [null, target],
|
|
ease: "easeOut",
|
|
velocity: value.getVelocity(),
|
|
...valueTransition,
|
|
delay: -elapsed,
|
|
onUpdate: (v) => {
|
|
value.set(v);
|
|
valueTransition.onUpdate && valueTransition.onUpdate(v);
|
|
},
|
|
onComplete: () => {
|
|
onComplete();
|
|
valueTransition.onComplete && valueTransition.onComplete();
|
|
},
|
|
name,
|
|
motionValue: value,
|
|
element: isHandoff ? undefined : element,
|
|
};
|
|
/**
|
|
* If there's no transition defined for this value, we can generate
|
|
* unique transition settings for this value.
|
|
*/
|
|
if (!isTransitionDefined(valueTransition)) {
|
|
Object.assign(options, getDefaultTransition(name, options));
|
|
}
|
|
/**
|
|
* Both WAAPI and our internal animation functions use durations
|
|
* as defined by milliseconds, while our external API defines them
|
|
* as seconds.
|
|
*/
|
|
options.duration && (options.duration = secondsToMilliseconds(options.duration));
|
|
options.repeatDelay && (options.repeatDelay = secondsToMilliseconds(options.repeatDelay));
|
|
/**
|
|
* Support deprecated way to set initial value. Prefer keyframe syntax.
|
|
*/
|
|
if (options.from !== undefined) {
|
|
options.keyframes[0] = options.from;
|
|
}
|
|
let shouldSkip = false;
|
|
if (options.type === false ||
|
|
(options.duration === 0 && !options.repeatDelay)) {
|
|
makeAnimationInstant(options);
|
|
if (options.delay === 0) {
|
|
shouldSkip = true;
|
|
}
|
|
}
|
|
if (MotionGlobalConfig.instantAnimations ||
|
|
MotionGlobalConfig.skipAnimations) {
|
|
shouldSkip = true;
|
|
makeAnimationInstant(options);
|
|
options.delay = 0;
|
|
}
|
|
/**
|
|
* If the transition type or easing has been explicitly set by the user
|
|
* then we don't want to allow flattening the animation.
|
|
*/
|
|
options.allowFlatten = !valueTransition.type && !valueTransition.ease;
|
|
/**
|
|
* If we can or must skip creating the animation, and apply only
|
|
* the final keyframe, do so. We also check once keyframes are resolved but
|
|
* this early check prevents the need to create an animation at all.
|
|
*/
|
|
if (shouldSkip && !isHandoff && value.get() !== undefined) {
|
|
const finalKeyframe = getFinalKeyframe(options.keyframes, valueTransition);
|
|
if (finalKeyframe !== undefined) {
|
|
frame.update(() => {
|
|
options.onUpdate(finalKeyframe);
|
|
options.onComplete();
|
|
});
|
|
return;
|
|
}
|
|
}
|
|
return valueTransition.isSync
|
|
? new JSAnimation(options)
|
|
: new AsyncMotionValueAnimation(options);
|
|
};
|
|
|
|
export { animateMotionValue };
|