114 lines
4.4 KiB
JavaScript
114 lines
4.4 KiB
JavaScript
import { secondsToMilliseconds } from '../../utils/time-conversion.mjs';
|
|
import { getDefaultTransition } from '../utils/default-transitions.mjs';
|
|
import { getValueTransition } from '../utils/get-value-transition.mjs';
|
|
import { MotionGlobalConfig } from '../../utils/GlobalConfig.mjs';
|
|
import { instantAnimationState } from '../../utils/use-instant-transition-state.mjs';
|
|
import { getFinalKeyframe } from '../animators/waapi/utils/get-final-keyframe.mjs';
|
|
import { frame } from '../../frameloop/frame.mjs';
|
|
import { AcceleratedAnimation } from '../animators/AcceleratedAnimation.mjs';
|
|
import { MainThreadAnimation } from '../animators/MainThreadAnimation.mjs';
|
|
import { GroupPlaybackControls } from '../GroupPlaybackControls.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);
|
|
let 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
|
|
* unqiue transition settings for this value.
|
|
*/
|
|
if (!isTransitionDefined(valueTransition)) {
|
|
options = {
|
|
...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.
|
|
*/
|
|
if (options.duration) {
|
|
options.duration = secondsToMilliseconds(options.duration);
|
|
}
|
|
if (options.repeatDelay) {
|
|
options.repeatDelay = secondsToMilliseconds(options.repeatDelay);
|
|
}
|
|
if (options.from !== undefined) {
|
|
options.keyframes[0] = options.from;
|
|
}
|
|
let shouldSkip = false;
|
|
if (options.type === false ||
|
|
(options.duration === 0 && !options.repeatDelay)) {
|
|
options.duration = 0;
|
|
if (options.delay === 0) {
|
|
shouldSkip = true;
|
|
}
|
|
}
|
|
if (instantAnimationState.current ||
|
|
MotionGlobalConfig.skipAnimations) {
|
|
shouldSkip = true;
|
|
options.duration = 0;
|
|
options.delay = 0;
|
|
}
|
|
/**
|
|
* 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();
|
|
});
|
|
// We still want to return some animation controls here rather
|
|
// than returning undefined
|
|
return new GroupPlaybackControls([]);
|
|
}
|
|
}
|
|
/**
|
|
* Animate via WAAPI if possible. If this is a handoff animation, the optimised animation will be running via
|
|
* WAAPI. Therefore, this animation must be JS to ensure it runs "under" the
|
|
* optimised animation.
|
|
*/
|
|
if (!isHandoff && AcceleratedAnimation.supports(options)) {
|
|
return new AcceleratedAnimation(options);
|
|
}
|
|
else {
|
|
return new MainThreadAnimation(options);
|
|
}
|
|
};
|
|
|
|
export { animateMotionValue };
|