Menu: improve animation for top-right
All checks were successful
/ depoly (push) Successful in 1m18s

This commit is contained in:
thislight 2024-11-02 16:45:18 +08:00
parent bafa8f4e24
commit 10b517bceb
No known key found for this signature in database
GPG key ID: FCFE5192241CCD4E
3 changed files with 151 additions and 29 deletions

View file

@ -53,3 +53,145 @@ export function useHeroSignal(
return [() => undefined, () => undefined];
}
}
export function animateRollOutFromTop(
root: HTMLElement,
options?: Omit<KeyframeAnimationOptions, "duration">,
) {
const overflow = root.style.overflow;
root.style.overflow = "hidden";
const { height } = root.getBoundingClientRect();
const opts = Object.assign(
{
duration: Math.floor((height / 1600) * 1000),
},
options,
);
const animation = root.animate(
{
height: ["0px", `${height}px`],
},
opts,
);
const restore = () => (root.style.overflow = overflow);
animation.addEventListener("finish", restore);
animation.addEventListener("cancel", restore);
return animation;
}
export function animateRollInFromBottom(
root: HTMLElement,
options?: Omit<KeyframeAnimationOptions, "duration">,
) {
const overflow = root.style.overflow;
root.style.overflow = "hidden";
const { height } = root.getBoundingClientRect();
const opts = Object.assign(
{
duration: Math.floor((height / 1600) * 1000),
},
options,
);
const animation = root.animate(
{
height: [`${height}px`, "0px"],
},
opts,
);
const restore = () => (root.style.overflow = overflow);
animation.addEventListener("finish", restore);
animation.addEventListener("cancel", restore);
return animation;
}
export function animateGrowFromTopRight(
root: HTMLElement,
options?: KeyframeAnimationOptions,
) {
const transformOrigin = root.style.transformOrigin;
root.style.transformOrigin = "top right";
const { width, height } = root.getBoundingClientRect();
const durationX = Math.floor((height / 1600) * 1000);
const durationY = Math.floor((width / 1600) * 1000);
// finds the offset for the center frame,
// it will stops at the (minDuration / maxDuration)%
const minDuration = Math.min(durationX, durationY);
const maxDuration = Math.max(durationX, durationY);
const centerOffset = minDuration / maxDuration;
const keyframes = [
{ transform: "scaleX(0)", opacity: 0, height: "0px", offset: 0 },
{
transform: `scaleX(${minDuration === durationX ? "1" : centerOffset})`,
height: `${(minDuration === durationY ? 1 : centerOffset) * height}px`,
offset: centerOffset,
opacity: 1,
},
{ transform: "scaleX(1)", height: `${height}px`, offset: 1 },
];
const animation = root.animate(
keyframes,
{ ...options, duration: maxDuration },
);
const restore = () => {
root.style.transformOrigin = transformOrigin;
};
animation.addEventListener("cancel", restore);
animation.addEventListener("finish", restore);
return animation;
}
export function animateShrinkToTopRight(
root: HTMLElement,
options?: KeyframeAnimationOptions,
) {
const overflow = root.style.overflow;
root.style.overflow = "hidden";
const transformOrigin = root.style.transformOrigin;
root.style.transformOrigin = "top right";
const { width, height } = root.getBoundingClientRect();
const duration = Math.floor(
Math.max((width / 1600) * 1000, (height / 1600) * 1000),
);
const animation = root.animate(
{
transform: ["scale(1)", "scale(0.5)"],
opacity: [1, 0],
},
{ ...options, duration },
);
const restore = () => {
root.style.overflow = overflow;
root.style.transformOrigin = transformOrigin;
};
animation.addEventListener("cancel", restore);
animation.addEventListener("finish", restore);
return animation;
}