Files
lcbp3.np-dms.work/frontend/node_modules/motion-dom/dist/es/view/start.mjs
2025-09-21 20:29:15 +07:00

147 lines
6.7 KiB
JavaScript

import { secondsToMilliseconds } from 'motion-utils';
import { BaseGroupPlaybackControls } from '../animation/controls/BaseGroup.mjs';
import { getValueTransition } from '../animation/utils/get-value-transition.mjs';
import { NativeAnimationControls } from '../animation/waapi/NativeAnimationControls.mjs';
import { PseudoAnimation } from '../animation/waapi/PseudoAnimation.mjs';
import { applyGeneratorOptions } from '../animation/waapi/utils/convert-options.mjs';
import { mapEasingToNativeEasing } from '../animation/waapi/utils/easing.mjs';
import { chooseLayerType } from './utils/choose-layer-type.mjs';
import { css } from './utils/css.mjs';
import { getLayerName } from './utils/get-layer-name.mjs';
import { getViewAnimations } from './utils/get-view-animations.mjs';
import { hasTarget } from './utils/has-target.mjs';
const definitionNames = ["layout", "enter", "exit", "new", "old"];
function startViewAnimation(update, defaultOptions, targets) {
if (!document.startViewTransition) {
return new Promise(async (resolve) => {
await update();
resolve(new BaseGroupPlaybackControls([]));
});
}
// TODO: Go over existing targets and ensure they all have ids
/**
* If we don't have any animations defined for the root target,
* remove it from being captured.
*/
if (!hasTarget("root", targets)) {
css.set(":root", {
"view-transition-name": "none",
});
}
/**
* Set the timing curve to linear for all view transition layers.
* This gets baked into the keyframes, which can't be changed
* without breaking the generated animation.
*
* This allows us to set easing via updateTiming - which can be changed.
*/
css.set("::view-transition-group(*), ::view-transition-old(*), ::view-transition-new(*)", { "animation-timing-function": "linear !important" });
css.commit(); // Write
const transition = document.startViewTransition(async () => {
await update();
// TODO: Go over new targets and ensure they all have ids
});
transition.finished.finally(() => {
css.remove(); // Write
});
return new Promise((resolve) => {
transition.ready.then(() => {
var _a;
const generatedViewAnimations = getViewAnimations();
const animations = [];
/**
* Create animations for our definitions
*/
targets.forEach((definition, target) => {
// TODO: If target is not "root", resolve elements
// and iterate over each
for (const key of definitionNames) {
if (!definition[key])
continue;
const { keyframes, options } = definition[key];
for (let [valueName, valueKeyframes] of Object.entries(keyframes)) {
if (!valueKeyframes)
continue;
const valueOptions = {
...getValueTransition(defaultOptions, valueName),
...getValueTransition(options, valueName),
};
const type = chooseLayerType(key);
/**
* If this is an opacity animation, and keyframes are not an array,
* we need to convert them into an array and set an initial value.
*/
if (valueName === "opacity" &&
!Array.isArray(valueKeyframes)) {
const initialValue = type === "new" ? 0 : 1;
valueKeyframes = [initialValue, valueKeyframes];
}
/**
* Resolve stagger function if provided.
*/
if (typeof valueOptions.delay === "function") {
valueOptions.delay = valueOptions.delay(0, 1);
}
const animation = new PseudoAnimation(document.documentElement, `::view-transition-${type}(${target})`, valueName, valueKeyframes, valueOptions);
animations.push(animation);
}
}
});
/**
* Handle browser generated animations
*/
for (const animation of generatedViewAnimations) {
if (animation.playState === "finished")
continue;
const { effect } = animation;
if (!effect || !(effect instanceof KeyframeEffect))
continue;
const { pseudoElement } = effect;
if (!pseudoElement)
continue;
const name = getLayerName(pseudoElement);
if (!name)
continue;
const targetDefinition = targets.get(name.layer);
if (!targetDefinition) {
/**
* If transition name is group then update the timing of the animation
* whereas if it's old or new then we could possibly replace it using
* the above method.
*/
const transitionName = name.type === "group" ? "layout" : "";
const animationTransition = {
...getValueTransition(defaultOptions, transitionName),
};
applyGeneratorOptions(animationTransition);
const easing = mapEasingToNativeEasing(animationTransition.ease, animationTransition.duration);
effect.updateTiming({
delay: secondsToMilliseconds((_a = animationTransition.delay) !== null && _a !== void 0 ? _a : 0),
duration: animationTransition.duration,
easing,
});
animations.push(new NativeAnimationControls(animation));
}
else if (hasOpacity(targetDefinition, "enter") &&
hasOpacity(targetDefinition, "exit") &&
effect
.getKeyframes()
.some((keyframe) => keyframe.mixBlendMode)) {
animations.push(new NativeAnimationControls(animation));
}
else {
animation.cancel();
}
}
resolve(new BaseGroupPlaybackControls(animations));
});
});
}
function hasOpacity(target, key) {
var _a;
return (_a = target === null || target === void 0 ? void 0 : target[key]) === null || _a === void 0 ? void 0 : _a.keyframes.opacity;
}
export { startViewAnimation };