Skip to content

Commit 7ed7358

Browse files
committed
perf: optimize launch page animation performance
1 parent 11f8a78 commit 7ed7358

File tree

4 files changed

+105
-113
lines changed

4 files changed

+105
-113
lines changed

frontend/src/App.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@ function App() {
128128
const tHide = setTimeout(() => setSplashVisible(false), splashDurationMs);
129129
const tHeader = setTimeout(
130130
() => setRevealStarted(true),
131-
splashDurationMs + overlayFadeMs
131+
splashDurationMs - 200
132132
);
133133
return () => {
134134
clearTimeout(tHide);
@@ -355,7 +355,7 @@ function App() {
355355
height={24}
356356
className="rounded-md shadow-sm"
357357
/>
358-
<p className="font-bold text-[16px] sm:text-[18px] tracking-tight brand-text-gradient bg-clip-text text-transparent animate-text-gradient animate-fadeInMove">
358+
<p className="font-bold text-[16px] sm:text-[18px] tracking-tight text-emerald-600 dark:text-emerald-400 animate-fadeInMove">
359359
LeviLauncher
360360
</p>
361361
{isBeta && (

frontend/src/pages/DownloadPage.tsx

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -164,14 +164,6 @@ export const DownloadPage: React.FC = () => {
164164
}
165165
}, [testing, bestMirror, selectedUrl]);
166166

167-
const truncateMiddle = (s: string, max: number = 60): string => {
168-
if (!s) return "";
169-
if (s.length <= max) return s;
170-
const head = Math.ceil(max / 2) - 1;
171-
const tail = Math.floor(max / 2) - 1;
172-
return s.slice(0, head) + "…" + s.slice(s.length - tail);
173-
};
174-
175167
const isChinaUser = useMemo(() => {
176168
try {
177169
const tz = Intl.DateTimeFormat().resolvedOptions().timeZone || "";
Lines changed: 103 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,47 +1,119 @@
1-
import React from "react";
21
import { LeviIcon } from "../icons/LeviIcon";
32
import { useTranslation } from "react-i18next";
3+
import { motion } from "framer-motion";
44

55
export const SplashScreen = () => {
66
const { t } = useTranslation();
7+
78
return (
8-
<div className="relative h-[100dvh] w-full overflow-hidden">
9-
<div className="fixed inset-0 -z-10 pointer-events-none">
10-
<div className="absolute -top-36 -left-36 h-[28rem] w-[28rem] rounded-full bg-gradient-to-tr from-emerald-500/18 to-lime-400/18 blur-3xl animate-splash-bg" />
11-
<div className="absolute -bottom-36 -right-36 h-[30rem] w-[30rem] rounded-full bg-gradient-to-tr from-cyan-500/18 to-indigo-400/18 blur-3xl animate-splash-bg" />
9+
<div className="relative h-[100dvh] w-full overflow-hidden bg-background flex flex-col items-center justify-center">
10+
<div className="absolute inset-0 -z-10 overflow-hidden pointer-events-none">
11+
<motion.div
12+
className="absolute -top-[20%] -left-[10%] h-[60vh] w-[60vh] rounded-full bg-gradient-to-br from-emerald-500/20 to-cyan-500/20 blur-[100px]"
13+
animate={{
14+
x: [0, 50, 0],
15+
y: [0, 30, 0],
16+
scale: [1, 1.1, 1],
17+
}}
18+
transition={{
19+
duration: 10,
20+
repeat: Infinity,
21+
ease: "easeInOut",
22+
}}
23+
/>
24+
<motion.div
25+
className="absolute top-[40%] -right-[10%] h-[70vh] w-[70vh] rounded-full bg-gradient-to-bl from-indigo-500/20 to-purple-500/20 blur-[100px]"
26+
animate={{
27+
x: [0, -50, 0],
28+
y: [0, -40, 0],
29+
scale: [1, 1.2, 1],
30+
}}
31+
transition={{
32+
duration: 12,
33+
repeat: Infinity,
34+
ease: "easeInOut",
35+
}}
36+
/>
37+
<motion.div
38+
className="absolute -bottom-[20%] left-[20%] h-[50vh] w-[50vh] rounded-full bg-gradient-to-t from-lime-400/20 to-emerald-400/20 blur-[100px]"
39+
animate={{
40+
x: [0, 30, 0],
41+
y: [0, -20, 0],
42+
scale: [1, 1.15, 1],
43+
}}
44+
transition={{
45+
duration: 15,
46+
repeat: Infinity,
47+
ease: "easeInOut",
48+
}}
49+
/>
1250
</div>
1351

14-
<div className="flex h-full w-full flex-col items-center justify-center gap-6 px-4">
15-
<div className="relative h-[180px] w-[180px]">
16-
<div className="absolute inset-0 rounded-full border border-white/30 dark:border-white/10 opacity-50" />
17-
<div className="absolute inset-0 splash-orbit">
18-
<div className="absolute left-1/2 top-0 -translate-x-1/2 h-[10px] w-[10px] rounded-full bg-emerald-400 shadow-md" />
19-
</div>
20-
<div className="absolute inset-0 splash-ring-glow" />
21-
<div className="relative flex h-full w-full items-center justify-center splash-logo-float">
22-
<LeviIcon
23-
width={140}
24-
height={140}
25-
className="rounded-xl shadow-xl"
26-
/>
27-
</div>
52+
<div className="flex flex-col items-center justify-center gap-8 z-10 p-8">
53+
<div className="relative">
54+
<motion.div
55+
initial={{ scale: 0.8, opacity: 0 }}
56+
animate={{ scale: 1, opacity: 1 }}
57+
transition={{ duration: 0.8, ease: "easeOut" }}
58+
className="relative z-20"
59+
>
60+
<div className="relative flex items-center justify-center p-6 bg-[radial-gradient(circle_at_center,_var(--tw-gradient-stops))] from-emerald-200/30 to-white/80 dark:from-emerald-500/20 dark:to-white/5 backdrop-blur-2xl rounded-3xl border border-white/40 shadow-2xl ring-1 ring-white/30">
61+
<LeviIcon
62+
width={120}
63+
height={120}
64+
className="drop-shadow-2xl"
65+
/>
66+
</div>
67+
</motion.div>
2868
</div>
2969

30-
<h1 className="font-extrabold text-5xl tracking-tight brand-text-gradient bg-clip-text text-transparent animate-text-gradient splash-title">
31-
LeviLauncher
32-
</h1>
70+
<motion.div
71+
initial={{ y: 20, opacity: 0 }}
72+
animate={{ y: 0, opacity: 1 }}
73+
transition={{ delay: 0.3, duration: 0.6 }}
74+
className="text-center space-y-2"
75+
>
76+
<h1 className="font-extrabold text-5xl tracking-tight bg-gradient-to-r from-emerald-500 via-cyan-500 to-indigo-500 bg-clip-text text-transparent drop-shadow-sm">
77+
LeviLauncher
78+
</h1>
79+
</motion.div>
3380

34-
<div className="w-[260px] max-w-[70vw]">
35-
<div className="relative h-2 rounded-full bg-default-100/70 dark:bg-default-50/10 overflow-hidden border border-white/30">
36-
<div className="absolute top-0 bottom-0 rounded-full bg-default-400/60 indeterminate-bar1" />
37-
<div className="absolute top-0 bottom-0 rounded-full bg-default-400/40 indeterminate-bar2" />
81+
<motion.div
82+
initial={{ opacity: 0 }}
83+
animate={{ opacity: 1 }}
84+
transition={{ delay: 0.5, duration: 0.5 }}
85+
className="w-64 space-y-3"
86+
>
87+
<div className="relative h-1.5 w-full overflow-hidden rounded-full bg-default-200/50 dark:bg-default-100/10">
88+
<motion.div
89+
className="absolute top-0 bottom-0 left-0 h-full bg-gradient-to-r from-emerald-400 to-cyan-400 rounded-full"
90+
initial={{ width: "0%", x: "-100%" }}
91+
animate={{
92+
width: ["30%", "70%", "30%"],
93+
x: ["-100%", "150%", "-100%"]
94+
}}
95+
transition={{
96+
duration: 2,
97+
repeat: Infinity,
98+
ease: "easeInOut",
99+
}}
100+
/>
38101
</div>
39-
</div>
40-
41-
<p className="text-default-500 text-sm">
42-
{t("splash.preparing", { defaultValue: "正在准备启动..." })}
43-
</p>
102+
103+
<p className="text-center text-sm text-default-500 font-medium animate-pulse">
104+
{t("splash.preparing", { defaultValue: "正在准备启动..." })}
105+
</p>
106+
</motion.div>
44107
</div>
108+
109+
<motion.div
110+
initial={{ opacity: 0 }}
111+
animate={{ opacity: 1 }}
112+
transition={{ delay: 1, duration: 0.5 }}
113+
className="absolute bottom-8 text-xs text-default-400/60"
114+
>
115+
Designed for Minecraft Bedrock
116+
</motion.div>
45117
</div>
46118
);
47119
};

frontend/src/style.css

Lines changed: 0 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -305,75 +305,3 @@ body::-webkit-scrollbar,
305305
right: -90%;
306306
}
307307
}
308-
309-
/* ===== Splash Screen Animations ===== */
310-
/* Breathing background blobs for splash */
311-
@keyframes splashBgPulse {
312-
0% {
313-
transform: scale(1);
314-
filter: blur(60px);
315-
opacity: 0.9;
316-
}
317-
50% {
318-
transform: scale(1.06);
319-
filter: blur(72px);
320-
opacity: 1;
321-
}
322-
100% {
323-
transform: scale(1);
324-
filter: blur(60px);
325-
opacity: 0.9;
326-
}
327-
}
328-
.animate-splash-bg {
329-
animation: splashBgPulse 6s ease-in-out infinite;
330-
}
331-
332-
/* Gentle float for logo */
333-
@keyframes logoFloat {
334-
0% {
335-
transform: translateY(0);
336-
}
337-
50% {
338-
transform: translateY(-6px);
339-
}
340-
100% {
341-
transform: translateY(0);
342-
}
343-
}
344-
.splash-logo-float {
345-
animation: logoFloat 3.5s ease-in-out infinite;
346-
}
347-
348-
/* Orbiting dot around logo */
349-
.splash-orbit {
350-
animation: spin 6s linear infinite;
351-
transform-origin: center;
352-
}
353-
354-
/* Radial glow behind logo */
355-
.splash-ring-glow {
356-
position: absolute;
357-
inset: -20px;
358-
background: radial-gradient(
359-
closest-side,
360-
rgba(16, 185, 129, 0.25),
361-
transparent 70%
362-
);
363-
filter: blur(20px);
364-
}
365-
366-
/* Title entrance */
367-
@keyframes titleIn {
368-
0% {
369-
opacity: 0;
370-
transform: translateY(6px);
371-
}
372-
100% {
373-
opacity: 1;
374-
transform: translateY(0);
375-
}
376-
}
377-
.splash-title {
378-
animation: titleIn 0.5s ease-out 0.2s both;
379-
}

0 commit comments

Comments
 (0)