Files
ttstd-infinite-canvas/web/src/components/ui/aceternity/full-screen-loader.tsx

75 lines
4.3 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { motion, useReducedMotion } from "motion/react";
import { aceternityMotion } from "@/lib/aceternity-motion";
import { cn } from "@/lib/utils";
type FullScreenLoaderProps = {
label?: string;
detail?: string;
className?: string;
};
export function FullScreenLoader({ label = "正在恢复创作空间", detail = "同步账号、模型和项目数据", className }: FullScreenLoaderProps) {
const reducedMotion = useReducedMotion();
return (
<motion.div
role="status"
aria-live="polite"
aria-label={`${label},${detail}`}
initial={reducedMotion ? false : { opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ duration: aceternityMotion.duration.state, ease: aceternityMotion.easing.enter }}
className={cn("fixed inset-0 z-[1200] grid min-h-dvh place-items-center overflow-hidden bg-[#f5f6f8] px-6 text-stone-950 dark:bg-[#0b0c0f] dark:text-white", className)}
>
<div aria-hidden className="absolute inset-0 bg-[linear-gradient(rgba(15,23,42,.045)_1px,transparent_1px),linear-gradient(90deg,rgba(15,23,42,.045)_1px,transparent_1px)] bg-[size:44px_44px] dark:bg-[linear-gradient(rgba(255,255,255,.035)_1px,transparent_1px),linear-gradient(90deg,rgba(255,255,255,.035)_1px,transparent_1px)]" />
<div aria-hidden className="absolute inset-0 overflow-hidden opacity-70">
{[0, 1, 2].map((index) => (
<motion.span
key={index}
className="absolute left-[-20%] h-px w-[140%] origin-center bg-current opacity-[0.08] dark:opacity-[0.12]"
style={{ top: `${30 + index * 20}%`, rotate: index % 2 ? -7 : 7 }}
animate={reducedMotion ? undefined : { x: ["-8%", "8%", "-8%"] }}
transition={{ duration: 4.8 + index * 0.8, repeat: Infinity, ease: "easeInOut", delay: index * -1.1 }}
/>
))}
</div>
<div className="relative flex w-full max-w-sm flex-col items-center text-center">
<div className="relative grid size-24 place-items-center">
<motion.span
aria-hidden
className="absolute inset-1 rounded-[28px] border border-cyan-500/28 dark:border-cyan-300/22"
animate={reducedMotion ? undefined : { rotate: 360 }}
transition={{ duration: 3.2, repeat: Infinity, ease: "linear" }}
style={{ clipPath: "polygon(0 0, 68% 0, 68% 2px, 0 2px, 0 100%)" }}
/>
<motion.span
aria-hidden
className="absolute inset-3 rounded-[22px] border border-amber-500/30 dark:border-amber-300/22"
animate={reducedMotion ? undefined : { rotate: -360 }}
transition={{ duration: 4.1, repeat: Infinity, ease: "linear" }}
style={{ clipPath: "polygon(32% 0, 100% 0, 100% 100%, 98% 100%, 98% 2px, 32% 2px)" }}
/>
<motion.span
className="size-10 bg-current"
style={{ mask: "url(/logo.svg) center / contain no-repeat", WebkitMask: "url(/logo.svg) center / contain no-repeat" }}
animate={reducedMotion ? undefined : { opacity: [0.58, 1, 0.58], scale: [0.96, 1, 0.96] }}
transition={{ duration: 1.8, repeat: Infinity, ease: "easeInOut" }}
/>
</div>
<p className="mt-6 text-base font-semibold tracking-normal">{label}</p>
<p className="mt-2 text-sm text-stone-500 dark:text-white/42">{detail}</p>
<div aria-hidden className="relative mt-6 h-px w-44 overflow-hidden bg-stone-900/10 dark:bg-white/10">
<motion.span
className="absolute inset-y-0 left-0 w-16 bg-cyan-500 dark:bg-cyan-300"
animate={reducedMotion ? { x: 56 } : { x: [-64, 176] }}
transition={reducedMotion ? undefined : { duration: 1.15, repeat: Infinity, ease: aceternityMotion.easing.enter }}
/>
</div>
</div>
</motion.div>
);
}