112 lines
4.1 KiB
TypeScript
112 lines
4.1 KiB
TypeScript
/**
|
|
* Loading component with green theme
|
|
*/
|
|
import { useEffect, useState } from 'react';
|
|
|
|
interface LoadingProps {
|
|
message?: string;
|
|
showProgress?: boolean;
|
|
}
|
|
|
|
export function Loading({ message = "正在加载深空探索器...", showProgress = true }: LoadingProps) {
|
|
const [dots, setDots] = useState('');
|
|
const [progress, setProgress] = useState(0);
|
|
|
|
// Animate dots
|
|
useEffect(() => {
|
|
const interval = setInterval(() => {
|
|
setDots(prev => prev.length >= 3 ? '' : prev + '.');
|
|
}, 500);
|
|
return () => clearInterval(interval);
|
|
}, []);
|
|
|
|
// Simulate progress
|
|
useEffect(() => {
|
|
if (!showProgress) return;
|
|
|
|
const interval = setInterval(() => {
|
|
setProgress(prev => {
|
|
if (prev >= 90) return prev; // Stop at 90% until real data loads
|
|
return prev + Math.random() * 10;
|
|
});
|
|
}, 300);
|
|
|
|
return () => clearInterval(interval);
|
|
}, [showProgress]);
|
|
|
|
return (
|
|
<div className="fixed inset-0 w-full h-full flex items-center justify-center bg-black z-50">
|
|
{/* Starfield background effect */}
|
|
<div className="absolute inset-0 overflow-hidden">
|
|
{[...Array(50)].map((_, i) => (
|
|
<div
|
|
key={i}
|
|
className="absolute w-1 h-1 bg-white rounded-full opacity-50"
|
|
style={{
|
|
left: `${Math.random() * 100}%`,
|
|
top: `${Math.random() * 100}%`,
|
|
animation: `twinkle ${2 + Math.random() * 3}s infinite`,
|
|
animationDelay: `${Math.random() * 2}s`
|
|
}}
|
|
/>
|
|
))}
|
|
</div>
|
|
|
|
{/* Loading content */}
|
|
<div className="relative z-10 text-center">
|
|
{/* Spinning loader with green theme */}
|
|
<div className="mb-6 flex justify-center">
|
|
<div className="relative">
|
|
{/* Outer ring */}
|
|
<div className="w-24 h-24 rounded-full border-4 border-[#238636]/20"></div>
|
|
{/* Spinning ring */}
|
|
<div className="absolute inset-0 w-24 h-24 rounded-full border-4 border-transparent border-t-[#238636] border-r-[#238636] animate-spin"></div>
|
|
{/* Inner glow */}
|
|
<div className="absolute inset-0 w-24 h-24 rounded-full shadow-[0_0_30px_rgba(35,134,54,0.3)]"></div>
|
|
|
|
{/* Center icon */}
|
|
<div className="absolute inset-0 flex items-center justify-center">
|
|
<svg className="w-10 h-10 text-[#238636]" fill="currentColor" viewBox="0 0 20 20">
|
|
<path d="M10 2a.75.75 0 01.75.75v1.5a.75.75 0 01-1.5 0v-1.5A.75.75 0 0110 2zM10 15a.75.75 0 01.75.75v1.5a.75.75 0 01-1.5 0v-1.5A.75.75 0 0110 15zM10 7a3 3 0 100 6 3 3 0 000-6zM15.657 5.404a.75.75 0 10-1.06-1.06l-1.061 1.06a.75.75 0 001.06 1.06l1.06-1.06zM6.464 14.596a.75.75 0 10-1.06-1.06l-1.06 1.06a.75.75 0 001.06 1.06l1.06-1.06zM18 10a.75.75 0 01-.75.75h-1.5a.75.75 0 010-1.5h1.5A.75.75 0 0118 10zM5 10a.75.75 0 01-.75.75h-1.5a.75.75 0 010-1.5h1.5A.75.75 0 015 10zM14.596 15.657a.75.75 0 001.06-1.06l-1.06-1.061a.75.75 0 10-1.06 1.06l1.06 1.06zM5.404 6.464a.75.75 0 001.06-1.06l-1.06-1.06a.75.75 0 10-1.061 1.06l1.06 1.06z" />
|
|
</svg>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Loading text with green theme */}
|
|
<div className="text-[#238636] text-xl font-medium mb-2">
|
|
{message}{dots}
|
|
</div>
|
|
|
|
{/* Subtitle */}
|
|
<div className="text-gray-400 text-sm mb-6">
|
|
加载天体模型与轨道数据
|
|
</div>
|
|
|
|
{/* Progress bar */}
|
|
{showProgress && (
|
|
<div className="w-64 mx-auto">
|
|
<div className="h-1 bg-white/10 rounded-full overflow-hidden">
|
|
<div
|
|
className="h-full bg-gradient-to-r from-[#238636] to-[#2ea043] rounded-full transition-all duration-300 shadow-[0_0_10px_rgba(35,134,54,0.5)]"
|
|
style={{ width: `${progress}%` }}
|
|
/>
|
|
</div>
|
|
<div className="text-[#238636] text-xs mt-2">
|
|
{Math.round(progress)}%
|
|
</div>
|
|
</div>
|
|
)}
|
|
</div>
|
|
|
|
{/* Add twinkle animation */}
|
|
<style>{`
|
|
@keyframes twinkle {
|
|
0%, 100% { opacity: 0.2; }
|
|
50% { opacity: 0.8; }
|
|
}
|
|
`}</style>
|
|
</div>
|
|
);
|
|
}
|