feat: 初始化多Agent协作系统项目并添加直播回放功能
- 添加导航栏组件及直播回放按钮 - 实现视频播放模态框 - 配置赛博朋克风格主题 - 添加课程首页和课程页面 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
32
src/App.tsx
Normal file
32
src/App.tsx
Normal file
@@ -0,0 +1,32 @@
|
||||
import React from 'react';
|
||||
import { BrowserRouter as Router, Routes, Route } from 'react-router-dom';
|
||||
import Navigation from './components/Navigation';
|
||||
import ScrollToTop from './components/ScrollToTop';
|
||||
import CyberpunkBackground from './components/CyberpunkBackground';
|
||||
import CyberpunkVignette from './components/CyberpunkVignette';
|
||||
import HomePage from './pages/HomePage';
|
||||
import CoursePage from './pages/CoursePage';
|
||||
|
||||
const App: React.FC = () => {
|
||||
return (
|
||||
<Router>
|
||||
<ScrollToTop />
|
||||
<div className="min-h-screen relative">
|
||||
<CyberpunkBackground />
|
||||
<CyberpunkVignette
|
||||
intensity="strong"
|
||||
variant="neon"
|
||||
/>
|
||||
<Navigation />
|
||||
<div className="container mx-auto px-4 pt-20 relative z-10">
|
||||
<Routes>
|
||||
<Route path="/" element={<HomePage />} />
|
||||
<Route path="/course" element={<CoursePage />} />
|
||||
</Routes>
|
||||
</div>
|
||||
</div>
|
||||
</Router>
|
||||
);
|
||||
};
|
||||
|
||||
export default App;
|
||||
43
src/AppOptimized.tsx
Normal file
43
src/AppOptimized.tsx
Normal file
@@ -0,0 +1,43 @@
|
||||
import React, { lazy, Suspense } from 'react';
|
||||
import { BrowserRouter as Router, Routes, Route } from 'react-router-dom';
|
||||
import Navigation from './components/Navigation';
|
||||
import ScrollToTop from './components/ScrollToTop';
|
||||
import CyberpunkBackgroundOptimized from './components/CyberpunkBackgroundOptimized';
|
||||
import CyberpunkVignette from './components/CyberpunkVignette';
|
||||
|
||||
// 懒加载页面组件
|
||||
const HomePage = lazy(() => import('./pages/HomePage'));
|
||||
|
||||
// 加载指示器组件
|
||||
const LoadingFallback: React.FC = () => (
|
||||
<div className="min-h-screen flex items-center justify-center">
|
||||
<div className="text-center">
|
||||
<div className="inline-flex items-center space-x-2">
|
||||
<div className="w-8 h-8 border-4 border-cyber-pink-500 border-t-transparent rounded-full animate-spin"></div>
|
||||
<span className="text-cyber-pink-400 font-medium">加载中...</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
const AppOptimized: React.FC = () => {
|
||||
return (
|
||||
<Router>
|
||||
<ScrollToTop />
|
||||
<div className="min-h-screen relative">
|
||||
<CyberpunkBackgroundOptimized />
|
||||
<CyberpunkVignette intensity="light" variant="minimal" />
|
||||
<Navigation />
|
||||
<div className="container mx-auto px-4 pt-20 relative z-10">
|
||||
<Suspense fallback={<LoadingFallback />}>
|
||||
<Routes>
|
||||
<Route path="/" element={<HomePage />} />
|
||||
</Routes>
|
||||
</Suspense>
|
||||
</div>
|
||||
</div>
|
||||
</Router>
|
||||
);
|
||||
};
|
||||
|
||||
export default AppOptimized;
|
||||
359
src/components/AgentNetworkBackground.tsx
Normal file
359
src/components/AgentNetworkBackground.tsx
Normal file
@@ -0,0 +1,359 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { motion, AnimatePresence } from 'framer-motion';
|
||||
|
||||
// 网络节点组件 - 代表Agent
|
||||
const NetworkNode: React.FC<{
|
||||
id: number;
|
||||
onConnect: (id: number) => void;
|
||||
connections: number[];
|
||||
}> = ({ id, onConnect, connections }) => {
|
||||
const [position] = useState({
|
||||
x: Math.random() * 90 + 5,
|
||||
y: Math.random() * 90 + 5,
|
||||
size: Math.random() * 8 + 4,
|
||||
});
|
||||
|
||||
const [isActive, setIsActive] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
const interval = setInterval(() => {
|
||||
if (Math.random() < 0.3) {
|
||||
setIsActive(true);
|
||||
onConnect(id);
|
||||
setTimeout(() => setIsActive(false), 2000);
|
||||
}
|
||||
}, Math.random() * 8000 + 5000);
|
||||
|
||||
return () => clearInterval(interval);
|
||||
}, [id, onConnect]);
|
||||
|
||||
return (
|
||||
<motion.div
|
||||
className="absolute"
|
||||
style={{
|
||||
left: `${position.x}%`,
|
||||
top: `${position.y}%`,
|
||||
width: `${position.size}px`,
|
||||
height: `${position.size}px`,
|
||||
}}
|
||||
initial={{ opacity: 0, scale: 0 }}
|
||||
animate={{
|
||||
opacity: isActive ? 0.9 : 0.4,
|
||||
scale: isActive ? 1.5 : 1,
|
||||
}}
|
||||
transition={{
|
||||
duration: 0.5,
|
||||
opacity: { duration: 0.3 }
|
||||
}}
|
||||
>
|
||||
{/* 节点核心 */}
|
||||
<div
|
||||
className={`w-full h-full rounded-full transition-all duration-500 ${
|
||||
isActive
|
||||
? 'bg-gradient-to-r from-cyber-pink-400 to-neon-purple-400 shadow-lg'
|
||||
: 'bg-gradient-to-r from-cyber-pink-500/50 to-neon-purple-500/50'
|
||||
}`}
|
||||
style={{
|
||||
boxShadow: isActive
|
||||
? `0 0 ${position.size * 2}px rgba(6, 182, 212, 0.6), 0 0 ${position.size * 4}px rgba(16, 185, 129, 0.3)`
|
||||
: `0 0 ${position.size}px rgba(6, 182, 212, 0.2)`
|
||||
}}
|
||||
/>
|
||||
|
||||
{/* 活跃时的脉冲效果 */}
|
||||
{isActive && (
|
||||
<motion.div
|
||||
className="absolute inset-0 rounded-full border-2 border-neon-cyan-300"
|
||||
initial={{ scale: 1, opacity: 0.6 }}
|
||||
animate={{ scale: 3, opacity: 0 }}
|
||||
transition={{ duration: 1.5, ease: "easeOut" }}
|
||||
/>
|
||||
)}
|
||||
</motion.div>
|
||||
);
|
||||
};
|
||||
|
||||
// 数据流粒子组件
|
||||
const DataParticle: React.FC<{ path: { start: {x: number, y: number}, end: {x: number, y: number} } }> = ({ path }) => {
|
||||
const [isVisible, setIsVisible] = useState(true);
|
||||
|
||||
useEffect(() => {
|
||||
const timer = setTimeout(() => setIsVisible(false), 3000);
|
||||
return () => clearTimeout(timer);
|
||||
}, []);
|
||||
|
||||
if (!isVisible) return null;
|
||||
|
||||
const distance = Math.sqrt(
|
||||
Math.pow(path.end.x - path.start.x, 2) + Math.pow(path.end.y - path.start.y, 2)
|
||||
);
|
||||
|
||||
return (
|
||||
<motion.div
|
||||
className="absolute w-2 h-2 bg-neon-purple-400 rounded-full"
|
||||
style={{
|
||||
left: `${path.start.x}%`,
|
||||
top: `${path.start.y}%`,
|
||||
boxShadow: '0 0 8px rgba(16, 185, 129, 0.8)'
|
||||
}}
|
||||
initial={{
|
||||
x: 0,
|
||||
y: 0,
|
||||
opacity: 0,
|
||||
scale: 0.5
|
||||
}}
|
||||
animate={{
|
||||
x: `${(path.end.x - path.start.x) * (100/100)}%`,
|
||||
y: `${(path.end.y - path.start.y) * (100/100)}%`,
|
||||
opacity: [0, 1, 1, 0],
|
||||
scale: [0.5, 1, 1, 0.5]
|
||||
}}
|
||||
transition={{
|
||||
duration: Math.max(distance / 20, 2),
|
||||
ease: "easeInOut"
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
// 连接线组件
|
||||
const ConnectionLine: React.FC<{
|
||||
start: {x: number, y: number};
|
||||
end: {x: number, y: number};
|
||||
isActive: boolean;
|
||||
}> = ({ start, end, isActive }) => {
|
||||
const length = Math.sqrt(Math.pow(end.x - start.x, 2) + Math.pow(end.y - start.y, 2));
|
||||
const angle = Math.atan2(end.y - start.y, end.x - start.x) * 180 / Math.PI;
|
||||
|
||||
return (
|
||||
<motion.div
|
||||
className="absolute origin-left"
|
||||
style={{
|
||||
left: `${start.x}%`,
|
||||
top: `${start.y}%`,
|
||||
width: `${length}%`,
|
||||
height: '1px',
|
||||
transform: `rotate(${angle}deg)`,
|
||||
background: isActive
|
||||
? 'linear-gradient(90deg, rgba(6, 182, 212, 0.8), rgba(16, 185, 129, 0.8))'
|
||||
: 'linear-gradient(90deg, rgba(6, 182, 212, 0.2), rgba(16, 185, 129, 0.2))',
|
||||
boxShadow: isActive ? '0 0 4px rgba(6, 182, 212, 0.5)' : 'none'
|
||||
}}
|
||||
initial={{ scaleX: 0, opacity: 0 }}
|
||||
animate={{
|
||||
scaleX: 1,
|
||||
opacity: isActive ? 0.8 : 0.3
|
||||
}}
|
||||
exit={{ scaleX: 0, opacity: 0 }}
|
||||
transition={{ duration: 0.5 }}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
// 主背景组件
|
||||
const AgentNetworkBackground: React.FC = () => {
|
||||
const [activeConnections, setActiveConnections] = useState<{
|
||||
start: {x: number, y: number};
|
||||
end: {x: number, y: number};
|
||||
id: string;
|
||||
}[]>([]);
|
||||
const [dataParticles, setDataParticles] = useState<{
|
||||
path: { start: {x: number, y: number}, end: {x: number, y: number} };
|
||||
id: string;
|
||||
}[]>([]);
|
||||
|
||||
const nodePositions = React.useRef<{[key: number]: {x: number, y: number}}>({});
|
||||
|
||||
// 初始化节点位置
|
||||
React.useEffect(() => {
|
||||
for (let i = 0; i < 12; i++) {
|
||||
nodePositions.current[i] = {
|
||||
x: Math.random() * 90 + 5,
|
||||
y: Math.random() * 90 + 5
|
||||
};
|
||||
}
|
||||
}, []);
|
||||
|
||||
const handleNodeConnect = (nodeId: number) => {
|
||||
// 随机选择其他节点连接
|
||||
const otherNodes = Object.keys(nodePositions.current)
|
||||
.map(Number)
|
||||
.filter(id => id !== nodeId);
|
||||
|
||||
if (otherNodes.length === 0) return;
|
||||
|
||||
const targetId = otherNodes[Math.floor(Math.random() * otherNodes.length)];
|
||||
const start = nodePositions.current[nodeId];
|
||||
const end = nodePositions.current[targetId];
|
||||
|
||||
if (!start || !end) return;
|
||||
|
||||
const connectionId = `${nodeId}-${targetId}-${Date.now()}`;
|
||||
|
||||
// 添加连接线
|
||||
const newConnection = { start, end, id: connectionId };
|
||||
setActiveConnections(prev => [...prev, newConnection]);
|
||||
|
||||
// 添加数据粒子
|
||||
const particleId = `particle-${Date.now()}`;
|
||||
setDataParticles(prev => [...prev, { path: { start, end }, id: particleId }]);
|
||||
|
||||
// 清除连接线
|
||||
setTimeout(() => {
|
||||
setActiveConnections(prev => prev.filter(conn => conn.id !== connectionId));
|
||||
}, 3000);
|
||||
|
||||
// 清除数据粒子
|
||||
setTimeout(() => {
|
||||
setDataParticles(prev => prev.filter(p => p.id !== particleId));
|
||||
}, 3000);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="fixed inset-0 pointer-events-none overflow-hidden z-0">
|
||||
{/* 基础深色技术背景 */}
|
||||
<div
|
||||
className="absolute inset-0"
|
||||
style={{
|
||||
background: `
|
||||
radial-gradient(circle at 25% 25%, rgba(15, 23, 42, 0.8) 0%, transparent 50%),
|
||||
radial-gradient(circle at 75% 75%, rgba(8, 145, 178, 0.1) 0%, transparent 50%),
|
||||
linear-gradient(135deg,
|
||||
#0f172a 0%,
|
||||
#1e293b 25%,
|
||||
#0f172a 50%,
|
||||
#164e63 75%,
|
||||
#0f172a 100%
|
||||
)
|
||||
`
|
||||
}}
|
||||
/>
|
||||
|
||||
{/* 网格背景 - 体现技术感 */}
|
||||
<div
|
||||
className="absolute inset-0 opacity-[0.08]"
|
||||
style={{
|
||||
backgroundImage: `
|
||||
linear-gradient(rgba(6, 182, 212, 0.3) 1px, transparent 1px),
|
||||
linear-gradient(90deg, rgba(6, 182, 212, 0.3) 1px, transparent 1px)
|
||||
`,
|
||||
backgroundSize: '50px 50px'
|
||||
}}
|
||||
/>
|
||||
|
||||
{/* 动态光晕效果 */}
|
||||
<motion.div
|
||||
className="absolute inset-0"
|
||||
style={{
|
||||
background: `
|
||||
radial-gradient(600px circle at var(--mouse-x, 50%) var(--mouse-y, 50%),
|
||||
rgba(6, 182, 212, 0.1) 0%,
|
||||
rgba(16, 185, 129, 0.05) 40%,
|
||||
transparent 100%
|
||||
)
|
||||
`
|
||||
}}
|
||||
animate={{
|
||||
'--mouse-x': ['30%', '70%', '30%'],
|
||||
'--mouse-y': ['40%', '60%', '40%']
|
||||
} as any}
|
||||
transition={{
|
||||
duration: 20,
|
||||
repeat: Infinity,
|
||||
ease: "easeInOut"
|
||||
}}
|
||||
/>
|
||||
|
||||
{/* 数据流背景效果 */}
|
||||
<motion.div
|
||||
className="absolute inset-0"
|
||||
style={{
|
||||
background: `
|
||||
linear-gradient(45deg,
|
||||
transparent 0%,
|
||||
rgba(6, 182, 212, 0.05) 25%,
|
||||
transparent 50%,
|
||||
rgba(16, 185, 129, 0.05) 75%,
|
||||
transparent 100%
|
||||
)
|
||||
`,
|
||||
transform: 'translateX(-100%)'
|
||||
}}
|
||||
animate={{
|
||||
transform: ['translateX(-100%)', 'translateX(100%)']
|
||||
}}
|
||||
transition={{
|
||||
duration: 15,
|
||||
repeat: Infinity,
|
||||
ease: "linear"
|
||||
}}
|
||||
/>
|
||||
|
||||
{/* 连接线层 */}
|
||||
<div className="absolute inset-0">
|
||||
<AnimatePresence>
|
||||
{activeConnections.map(connection => (
|
||||
<ConnectionLine
|
||||
key={connection.id}
|
||||
start={connection.start}
|
||||
end={connection.end}
|
||||
isActive={true}
|
||||
/>
|
||||
))}
|
||||
</AnimatePresence>
|
||||
</div>
|
||||
|
||||
{/* 网络节点 - Agent */}
|
||||
<div className="absolute inset-0">
|
||||
{Array.from({ length: 12 }, (_, i) => (
|
||||
<NetworkNode
|
||||
key={i}
|
||||
id={i}
|
||||
onConnect={handleNodeConnect}
|
||||
connections={[]}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* 数据粒子层 */}
|
||||
<div className="absolute inset-0">
|
||||
<AnimatePresence>
|
||||
{dataParticles.map(particle => (
|
||||
<DataParticle
|
||||
key={particle.id}
|
||||
path={particle.path}
|
||||
/>
|
||||
))}
|
||||
</AnimatePresence>
|
||||
</div>
|
||||
|
||||
{/* 顶部渐变遮罩 - 确保文字可读性 */}
|
||||
<div
|
||||
className="absolute inset-0 pointer-events-none"
|
||||
style={{
|
||||
background: `
|
||||
linear-gradient(180deg,
|
||||
rgba(15, 23, 42, 0.3) 0%,
|
||||
transparent 20%,
|
||||
transparent 80%,
|
||||
rgba(15, 23, 42, 0.2) 100%
|
||||
)
|
||||
`
|
||||
}}
|
||||
/>
|
||||
|
||||
{/* 性能优化 */}
|
||||
<style>{`
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
* {
|
||||
animation-duration: 0.01ms !important;
|
||||
animation-iteration-count: 1 !important;
|
||||
transition-duration: 0.01ms !important;
|
||||
}
|
||||
}
|
||||
`}</style>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default AgentNetworkBackground;
|
||||
64
src/components/CourseNavigation.tsx
Normal file
64
src/components/CourseNavigation.tsx
Normal file
@@ -0,0 +1,64 @@
|
||||
import React from 'react';
|
||||
import { Link, useLocation } from 'react-router-dom';
|
||||
import { ArrowLeft, ArrowRight } from './Icons';
|
||||
import { getChapterNavigation } from '../utils/courseNavigation';
|
||||
|
||||
interface CourseNavigationProps {
|
||||
className?: string;
|
||||
}
|
||||
|
||||
const CourseNavigation: React.FC<CourseNavigationProps> = ({ className = '' }) => {
|
||||
const location = useLocation();
|
||||
const { prev, next } = getChapterNavigation(location.pathname);
|
||||
|
||||
return (
|
||||
<div className={`flex justify-between items-center mt-12 ${className}`}>
|
||||
{prev ? (
|
||||
<Link
|
||||
to={prev.path}
|
||||
className="cyber-button flex items-center group"
|
||||
>
|
||||
<ArrowLeft className="w-5 h-5 mr-2 group-hover:-translate-x-1 transition-transform" />
|
||||
<div className="text-left">
|
||||
<div className="text-sm opacity-70">上一章</div>
|
||||
<div className="font-medium">{prev.title}</div>
|
||||
</div>
|
||||
</Link>
|
||||
) : (
|
||||
<Link
|
||||
to="/"
|
||||
className="cyber-button flex items-center group"
|
||||
>
|
||||
<ArrowLeft className="w-5 h-5 mr-2 group-hover:-translate-x-1 transition-transform" />
|
||||
返回首页
|
||||
</Link>
|
||||
)}
|
||||
|
||||
{next ? (
|
||||
<Link
|
||||
to={next.path}
|
||||
className="cyber-button flex items-center group"
|
||||
>
|
||||
<div className="text-right">
|
||||
<div className="text-sm opacity-70">下一章</div>
|
||||
<div className="font-medium">{next.title}</div>
|
||||
</div>
|
||||
<ArrowRight className="w-5 h-5 ml-2 group-hover:translate-x-1 transition-transform" />
|
||||
</Link>
|
||||
) : (
|
||||
<Link
|
||||
to="/course-summary"
|
||||
className="cyber-button flex items-center group"
|
||||
>
|
||||
<div className="text-right">
|
||||
<div className="text-sm opacity-70">课程完成</div>
|
||||
<div className="font-medium">课程总结</div>
|
||||
</div>
|
||||
<ArrowRight className="w-5 h-5 ml-2 group-hover:translate-x-1 transition-transform" />
|
||||
</Link>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default CourseNavigation;
|
||||
294
src/components/CyberpunkBackground.tsx
Normal file
294
src/components/CyberpunkBackground.tsx
Normal file
@@ -0,0 +1,294 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { motion } from 'framer-motion';
|
||||
|
||||
// 霓虹网格线
|
||||
const NeonGrid: React.FC = () => {
|
||||
return (
|
||||
<div className="absolute inset-0 overflow-hidden">
|
||||
<div
|
||||
className="absolute inset-0"
|
||||
style={{
|
||||
backgroundImage: `
|
||||
linear-gradient(rgba(244, 63, 94, 0.3) 1px, transparent 1px),
|
||||
linear-gradient(90deg, rgba(6, 182, 212, 0.3) 1px, transparent 1px)
|
||||
`,
|
||||
backgroundSize: '50px 50px',
|
||||
animation: 'grid-move 20s linear infinite',
|
||||
transform: 'perspective(500px) rotateX(60deg) translateZ(0)',
|
||||
transformOrigin: 'center center',
|
||||
filter: 'brightness(1.5)',
|
||||
mixBlendMode: 'screen',
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
// 日文字符数据雨效果
|
||||
const DataRain: React.FC = () => {
|
||||
const columns = 15; // 减少列数从30到15
|
||||
const [charColumns, setCharColumns] = useState<Array<{ chars: string[]; delay: number; duration: number; x: number }>>([]);
|
||||
|
||||
// 日文字符集合(片假名、平假名和汉字)
|
||||
const japaneseChars = [
|
||||
'ア', 'イ', 'ウ', 'エ', 'オ', 'カ', 'キ', 'ク', 'ケ', 'コ',
|
||||
'サ', 'シ', 'ス', 'セ', 'ソ', 'タ', 'チ', 'ツ', 'テ', 'ト',
|
||||
'ナ', 'ニ', 'ヌ', 'ネ', 'ノ', 'ハ', 'ヒ', 'フ', 'ヘ', 'ホ',
|
||||
'マ', 'ミ', 'ム', 'メ', 'モ', 'ヤ', 'ユ', 'ヨ', 'ラ', 'リ',
|
||||
'ル', 'レ', 'ロ', 'ワ', 'ヲ', 'ン', '日', '本', '語', '愛',
|
||||
'雨', '桜', '心', '風', '光', '影', '夢', '星', '月', '雲',
|
||||
'龍', '侍', '忍', '者', '武', '士', '道', '禅', '気', '力',
|
||||
'火', '水', '土', '空', '時', '無', '有', '生', '死', '闇',
|
||||
];
|
||||
|
||||
useEffect(() => {
|
||||
// 初始化列
|
||||
const drops = Array.from({ length: columns }, (_, i) => ({
|
||||
chars: Array.from({ length: 15 }, () => // 改为15个字符
|
||||
japaneseChars[Math.floor(Math.random() * japaneseChars.length)]
|
||||
),
|
||||
delay: Math.random() * 5,
|
||||
duration: 12 + Math.random() * 6,
|
||||
x: (i / columns) * 100,
|
||||
}));
|
||||
setCharColumns(drops);
|
||||
|
||||
// 定期更新字符 - 加快更新频率
|
||||
const interval = setInterval(() => {
|
||||
setCharColumns(prev => prev.map(column => ({
|
||||
...column,
|
||||
chars: column.chars.map(char =>
|
||||
Math.random() < 0.1 ? japaneseChars[Math.floor(Math.random() * japaneseChars.length)] : char // 从0.02增加到0.1
|
||||
)
|
||||
})));
|
||||
}, 50); // 从100ms减少到50ms
|
||||
|
||||
return () => clearInterval(interval);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className="absolute inset-0 overflow-hidden opacity-60">
|
||||
{charColumns.map((column, i) => (
|
||||
<motion.div
|
||||
key={i}
|
||||
className="absolute flex flex-col text-green-400"
|
||||
style={{
|
||||
left: `${column.x}%`,
|
||||
fontSize: '12px', // 从18px减小到12px
|
||||
fontFamily: "'Courier New', monospace",
|
||||
fontWeight: 'normal', // 从bold改为normal
|
||||
lineHeight: '1',
|
||||
letterSpacing: '0px',
|
||||
}}
|
||||
initial={{ y: -50 }} // 从更接近顶部的位置开始
|
||||
animate={{ y: window.innerHeight + 100 }}
|
||||
transition={{
|
||||
duration: column.duration,
|
||||
repeat: Infinity,
|
||||
delay: column.delay,
|
||||
ease: "linear",
|
||||
}}
|
||||
>
|
||||
{column.chars.map((char, idx) => (
|
||||
<div
|
||||
key={idx}
|
||||
style={{
|
||||
opacity: idx === 0 ? 1 : Math.max(0.05, 1 - (idx / column.chars.length) * 0.95),
|
||||
color: idx === 0 ? '#ffffff' : idx < 2 ? '#66ff66' : '#00ff00',
|
||||
textShadow: idx === 0
|
||||
? '0 0 8px #ffffff, 0 0 15px #00ff00'
|
||||
: idx < 2
|
||||
? '0 0 3px rgba(102, 255, 102, 0.8)'
|
||||
: '0 0 2px rgba(0, 255, 0, 0.3)',
|
||||
height: '14px', // 从20px减小到14px
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
}}
|
||||
>
|
||||
{char}
|
||||
</div>
|
||||
))}
|
||||
</motion.div>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
// 霓虹光束
|
||||
const NeonBeams: React.FC = () => {
|
||||
return (
|
||||
<div className="absolute inset-0 overflow-hidden">
|
||||
{[...Array(5)].map((_, i) => (
|
||||
<motion.div
|
||||
key={i}
|
||||
className="absolute h-px"
|
||||
style={{
|
||||
background: `linear-gradient(90deg,
|
||||
transparent,
|
||||
${i % 2 === 0 ? '#f43f5e' : '#06b6d4'},
|
||||
transparent
|
||||
)`,
|
||||
width: '200%',
|
||||
top: `${20 + i * 15}%`,
|
||||
filter: `blur(${i === 0 ? 2 : 1}px)`,
|
||||
boxShadow: `0 0 ${10 + i * 5}px ${i % 2 === 0 ? '#f43f5e' : '#06b6d4'}`,
|
||||
}}
|
||||
animate={{
|
||||
x: ['-100%', '0%'],
|
||||
}}
|
||||
transition={{
|
||||
duration: 10 + i * 2,
|
||||
repeat: Infinity,
|
||||
ease: "linear",
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
// 故障效果
|
||||
const GlitchOverlay: React.FC = () => {
|
||||
const [isGlitching, setIsGlitching] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
const interval = setInterval(() => {
|
||||
setIsGlitching(true);
|
||||
setTimeout(() => setIsGlitching(false), 200);
|
||||
}, 5000);
|
||||
|
||||
return () => clearInterval(interval);
|
||||
}, []);
|
||||
|
||||
if (!isGlitching) return null;
|
||||
|
||||
return (
|
||||
<motion.div
|
||||
className="absolute inset-0 pointer-events-none"
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 1 }}
|
||||
exit={{ opacity: 0 }}
|
||||
style={{
|
||||
background: `repeating-linear-gradient(
|
||||
0deg,
|
||||
transparent,
|
||||
transparent 2px,
|
||||
rgba(244, 63, 94, 0.03) 2px,
|
||||
rgba(244, 63, 94, 0.03) 4px
|
||||
)`,
|
||||
animation: 'glitch 0.3s infinite',
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
// 粒子效果
|
||||
const ParticleField: React.FC = () => {
|
||||
const [particles, setParticles] = useState<Array<{ x: number; y: number; size: number; delay: number }>>([]);
|
||||
|
||||
useEffect(() => {
|
||||
const newParticles = Array.from({ length: 50 }, () => ({
|
||||
x: Math.random() * 100,
|
||||
y: Math.random() * 100,
|
||||
size: Math.random() * 3 + 1,
|
||||
delay: Math.random() * 5,
|
||||
}));
|
||||
setParticles(newParticles);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className="absolute inset-0 overflow-hidden">
|
||||
{particles.map((particle, i) => (
|
||||
<motion.div
|
||||
key={i}
|
||||
className="absolute rounded-full"
|
||||
style={{
|
||||
left: `${particle.x}%`,
|
||||
top: `${particle.y}%`,
|
||||
width: particle.size,
|
||||
height: particle.size,
|
||||
background: i % 3 === 0 ? '#f43f5e' : i % 3 === 1 ? '#a855f7' : '#06b6d4',
|
||||
boxShadow: `0 0 ${particle.size * 2}px currentColor`,
|
||||
}}
|
||||
animate={{
|
||||
y: [0, -30, 0],
|
||||
opacity: [0.2, 1, 0.2],
|
||||
}}
|
||||
transition={{
|
||||
duration: 3 + particle.size,
|
||||
repeat: Infinity,
|
||||
delay: particle.delay,
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
// 主背景组件 - 原始版本
|
||||
const CyberpunkBackground: React.FC = () => {
|
||||
return (
|
||||
<div className="fixed inset-0 pointer-events-none overflow-hidden z-0">
|
||||
{/* 基础渐变背景 */}
|
||||
<div
|
||||
className="absolute inset-0"
|
||||
style={{
|
||||
background: `
|
||||
radial-gradient(circle at 20% 50%, rgba(244, 63, 94, 0.1) 0%, transparent 50%),
|
||||
radial-gradient(circle at 80% 50%, rgba(168, 85, 247, 0.1) 0%, transparent 50%),
|
||||
radial-gradient(circle at 50% 100%, rgba(6, 182, 212, 0.1) 0%, transparent 50%),
|
||||
linear-gradient(180deg, #0a0a0a 0%, #030712 100%)
|
||||
`,
|
||||
}}
|
||||
/>
|
||||
|
||||
{/* 动态元素 */}
|
||||
<NeonGrid />
|
||||
<NeonBeams />
|
||||
<DataRain />
|
||||
<ParticleField />
|
||||
<GlitchOverlay />
|
||||
|
||||
{/* 光晕效果 */}
|
||||
<div
|
||||
className="absolute inset-0"
|
||||
style={{
|
||||
background: `
|
||||
radial-gradient(ellipse at center,
|
||||
transparent 20%,
|
||||
rgba(0, 0, 0, 0.3) 40%,
|
||||
rgba(0, 0, 0, 0.6) 70%,
|
||||
rgba(0, 0, 0, 0.9) 100%
|
||||
)
|
||||
`,
|
||||
}}
|
||||
/>
|
||||
|
||||
{/* 边缘暗角 */}
|
||||
<div
|
||||
className="absolute inset-0"
|
||||
style={{
|
||||
boxShadow: 'inset 0 0 200px 50px rgba(0, 0, 0, 0.9)',
|
||||
}}
|
||||
/>
|
||||
|
||||
{/* CRT扫描线效果 */}
|
||||
<div
|
||||
className="absolute inset-0 opacity-30"
|
||||
style={{
|
||||
backgroundImage: `repeating-linear-gradient(
|
||||
0deg,
|
||||
transparent,
|
||||
transparent 2px,
|
||||
rgba(255, 255, 255, 0.03) 2px,
|
||||
rgba(255, 255, 255, 0.03) 4px
|
||||
)`,
|
||||
animation: 'scan 8s linear infinite',
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default CyberpunkBackground;
|
||||
151
src/components/CyberpunkBackgroundOptimized.tsx
Normal file
151
src/components/CyberpunkBackgroundOptimized.tsx
Normal file
@@ -0,0 +1,151 @@
|
||||
import React, { useState, useEffect, useMemo } from 'react';
|
||||
|
||||
// 优化后的霓虹网格线组件 - 增强颜色对比度
|
||||
const NeonGrid: React.FC = () => {
|
||||
return (
|
||||
<div className="absolute inset-0 overflow-hidden">
|
||||
<div
|
||||
className="absolute inset-0 cyberpunk-grid"
|
||||
style={{
|
||||
backgroundImage: `
|
||||
linear-gradient(rgba(244, 63, 94, 0.4) 1px, transparent 1px),
|
||||
linear-gradient(90deg, rgba(6, 182, 212, 0.4) 1px, transparent 1px)
|
||||
`,
|
||||
backgroundSize: '50px 50px',
|
||||
transform: 'perspective(500px) rotateX(60deg) scale(1.5)',
|
||||
transformOrigin: 'center center',
|
||||
filter: 'brightness(1.2)',
|
||||
mixBlendMode: 'screen',
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
// 优化的霓虹光束 - 减少数量,使用CSS动画
|
||||
const NeonBeams: React.FC = () => {
|
||||
return (
|
||||
<div className="absolute inset-0 overflow-hidden opacity-50">
|
||||
<div className="neon-beam neon-beam-1" />
|
||||
<div className="neon-beam neon-beam-2" />
|
||||
<div className="neon-beam neon-beam-3" />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
// 增强的数据雨效果 - 更多列,更好的可见性
|
||||
const DataRainSimplified: React.FC = () => {
|
||||
return (
|
||||
<div className="absolute inset-0 overflow-hidden opacity-50">
|
||||
<div className="data-rain-container">
|
||||
{Array.from({ length: 20 }, (_, i) => (
|
||||
<div
|
||||
key={i}
|
||||
className="data-rain-column"
|
||||
style={{
|
||||
left: `${i * 5}%`,
|
||||
animationDelay: `${i * 0.3}s`,
|
||||
opacity: 0.3 + Math.random() * 0.4,
|
||||
animationDuration: `${3 + Math.random() * 2}s`,
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
// 优化的故障效果 - 减少频率
|
||||
const GlitchOverlayOptimized: React.FC = () => {
|
||||
const [glitchActive, setGlitchActive] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
const interval = setInterval(() => {
|
||||
setGlitchActive(true);
|
||||
setTimeout(() => setGlitchActive(false), 100);
|
||||
}, 15000); // 降低频率
|
||||
|
||||
return () => clearInterval(interval);
|
||||
}, []);
|
||||
|
||||
if (!glitchActive) return null;
|
||||
|
||||
return (
|
||||
<div className="absolute inset-0 pointer-events-none glitch-overlay" />
|
||||
);
|
||||
};
|
||||
|
||||
// 优化后的主背景组件
|
||||
const CyberpunkBackgroundOptimized: React.FC = () => {
|
||||
// 使用useMemo缓存静态样式
|
||||
const backgroundStyle = useMemo(() => ({
|
||||
background: `
|
||||
radial-gradient(circle at 50% 50%,
|
||||
rgba(244, 63, 94, 0.05) 0%,
|
||||
transparent 50%
|
||||
),
|
||||
radial-gradient(circle at 80% 20%,
|
||||
rgba(168, 85, 247, 0.03) 0%,
|
||||
transparent 50%
|
||||
),
|
||||
radial-gradient(circle at 20% 80%,
|
||||
rgba(6, 182, 212, 0.03) 0%,
|
||||
transparent 50%
|
||||
),
|
||||
linear-gradient(180deg, #0a0a0a 0%, #030712 100%)
|
||||
`,
|
||||
}), []);
|
||||
|
||||
return (
|
||||
<div className="fixed inset-0 pointer-events-none overflow-hidden z-0">
|
||||
{/* 静态渐变背景 */}
|
||||
<div
|
||||
className="absolute inset-0"
|
||||
style={backgroundStyle}
|
||||
/>
|
||||
|
||||
{/* 简化的网格 */}
|
||||
<NeonGrid />
|
||||
|
||||
{/* CSS动画光束 */}
|
||||
<NeonBeams />
|
||||
|
||||
{/* 简化的数据雨 */}
|
||||
<DataRainSimplified />
|
||||
|
||||
{/* 降频的故障效果 */}
|
||||
<GlitchOverlayOptimized />
|
||||
|
||||
{/* 增强的静态光晕效果 */}
|
||||
<div
|
||||
className="absolute inset-0 pointer-events-none"
|
||||
style={{
|
||||
background: `
|
||||
radial-gradient(ellipse at center,
|
||||
transparent 20%,
|
||||
rgba(0, 0, 0, 0.3) 40%,
|
||||
rgba(0, 0, 0, 0.6) 70%,
|
||||
rgba(0, 0, 0, 0.9) 100%
|
||||
)
|
||||
`,
|
||||
mixBlendMode: 'multiply',
|
||||
}}
|
||||
/>
|
||||
|
||||
{/* 边缘光晕 */}
|
||||
<div
|
||||
className="absolute inset-0 pointer-events-none"
|
||||
style={{
|
||||
boxShadow: 'inset 0 0 150px 50px rgba(0, 0, 0, 0.8)',
|
||||
}}
|
||||
/>
|
||||
|
||||
{/* 简化的CRT效果 */}
|
||||
<div
|
||||
className="absolute inset-0 crt-effect"
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default CyberpunkBackgroundOptimized;
|
||||
293
src/components/CyberpunkIcons.tsx
Normal file
293
src/components/CyberpunkIcons.tsx
Normal file
@@ -0,0 +1,293 @@
|
||||
import React from 'react';
|
||||
|
||||
// 赛博朋克风格的图标组件
|
||||
interface IconProps {
|
||||
className?: string;
|
||||
size?: number;
|
||||
}
|
||||
|
||||
// CPU图标 - 带霓虹效果
|
||||
export const CyberCpu: React.FC<IconProps> = ({ className = "", size = 24 }) => (
|
||||
<svg
|
||||
width={size}
|
||||
height={size}
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
className={className}
|
||||
>
|
||||
<rect x="7" y="7" width="10" height="10" stroke="currentColor" strokeWidth="2" fill="none" />
|
||||
<rect x="9" y="9" width="6" height="6" fill="currentColor" opacity="0.3" />
|
||||
<path d="M10 2v5M14 2v5M10 17v5M14 17v5M2 10h5M17 10h5M2 14h5M17 14h5" stroke="currentColor" strokeWidth="2" strokeLinecap="round" />
|
||||
<circle cx="12" cy="12" r="1" fill="currentColor" className="animate-pulse" />
|
||||
</svg>
|
||||
);
|
||||
|
||||
// 网络图标 - 带连接动画
|
||||
export const CyberNetwork: React.FC<IconProps> = ({ className = "", size = 24 }) => (
|
||||
<svg
|
||||
width={size}
|
||||
height={size}
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
className={className}
|
||||
>
|
||||
<circle cx="12" cy="5" r="2" stroke="currentColor" strokeWidth="2" fill="currentColor" opacity="0.3" />
|
||||
<circle cx="5" cy="19" r="2" stroke="currentColor" strokeWidth="2" fill="currentColor" opacity="0.3" />
|
||||
<circle cx="19" cy="19" r="2" stroke="currentColor" strokeWidth="2" fill="currentColor" opacity="0.3" />
|
||||
<path d="M12 7v6M12 13l-5.5 4M12 13l5.5 4" stroke="currentColor" strokeWidth="2" strokeLinecap="round" className="animate-pulse" />
|
||||
<circle cx="12" cy="13" r="2" fill="currentColor" />
|
||||
</svg>
|
||||
);
|
||||
|
||||
// 代码图标 - 终端风格
|
||||
export const CyberCode: React.FC<IconProps> = ({ className = "", size = 24 }) => (
|
||||
<svg
|
||||
width={size}
|
||||
height={size}
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
className={className}
|
||||
>
|
||||
<rect x="3" y="3" width="18" height="18" stroke="currentColor" strokeWidth="2" rx="2" fill="none" />
|
||||
<path d="M8 8l-2 4l2 4M16 8l2 4l-2 4" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" />
|
||||
<path d="M12 16.5v.5" stroke="currentColor" strokeWidth="3" strokeLinecap="round" className="animate-pulse" />
|
||||
</svg>
|
||||
);
|
||||
|
||||
// 目标图标 - 瞄准镜风格
|
||||
export const CyberTarget: React.FC<IconProps> = ({ className = "", size = 24 }) => (
|
||||
<svg
|
||||
width={size}
|
||||
height={size}
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
className={className}
|
||||
>
|
||||
<circle cx="12" cy="12" r="9" stroke="currentColor" strokeWidth="2" fill="none" />
|
||||
<circle cx="12" cy="12" r="6" stroke="currentColor" strokeWidth="1.5" fill="none" opacity="0.6" />
|
||||
<circle cx="12" cy="12" r="3" stroke="currentColor" strokeWidth="1" fill="currentColor" opacity="0.3" />
|
||||
<path d="M12 3v3M12 18v3M3 12h3M18 12h3" stroke="currentColor" strokeWidth="2" strokeLinecap="round" />
|
||||
<circle cx="12" cy="12" r="1" fill="currentColor" className="animate-pulse" />
|
||||
</svg>
|
||||
);
|
||||
|
||||
// 火箭图标 - 发射风格
|
||||
export const CyberRocket: React.FC<IconProps> = ({ className = "", size = 24 }) => (
|
||||
<svg
|
||||
width={size}
|
||||
height={size}
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
className={className}
|
||||
>
|
||||
<path d="M12 2l3 7h7l-5.5 4 2 7L12 15l-6.5 5 2-7L2 9h7z" stroke="currentColor" strokeWidth="2" fill="currentColor" opacity="0.3" />
|
||||
<path d="M12 2v13" stroke="currentColor" strokeWidth="1" strokeDasharray="2 2" className="animate-pulse" />
|
||||
</svg>
|
||||
);
|
||||
|
||||
// 数据库图标 - 层级风格
|
||||
export const CyberDatabase: React.FC<IconProps> = ({ className = "", size = 24 }) => (
|
||||
<svg
|
||||
width={size}
|
||||
height={size}
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
className={className}
|
||||
>
|
||||
<ellipse cx="12" cy="6" rx="7" ry="3" stroke="currentColor" strokeWidth="2" fill="currentColor" opacity="0.3" />
|
||||
<path d="M5 6v6c0 1.66 3.13 3 7 3s7-1.34 7-3V6" stroke="currentColor" strokeWidth="2" />
|
||||
<path d="M5 12v6c0 1.66 3.13 3 7 3s7-1.34 7-3v-6" stroke="currentColor" strokeWidth="2" />
|
||||
<path d="M12 9v.01M12 15v.01" stroke="currentColor" strokeWidth="3" strokeLinecap="round" className="animate-pulse" />
|
||||
</svg>
|
||||
);
|
||||
|
||||
// 闪电图标 - 能量风格
|
||||
export const CyberZap: React.FC<IconProps> = ({ className = "", size = 24 }) => (
|
||||
<svg
|
||||
width={size}
|
||||
height={size}
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
className={className}
|
||||
>
|
||||
<path d="M13 2L3 14h9l-1 8 10-12h-9z" stroke="currentColor" strokeWidth="2" fill="currentColor" opacity="0.3" strokeLinejoin="round" />
|
||||
<path d="M13 2L3 14h9l-1 8 10-12h-9z" stroke="currentColor" strokeWidth="1" strokeLinejoin="round" className="animate-pulse" opacity="0.8" />
|
||||
</svg>
|
||||
);
|
||||
|
||||
// 盾牌图标 - 安全风格
|
||||
export const CyberShield: React.FC<IconProps> = ({ className = "", size = 24 }) => (
|
||||
<svg
|
||||
width={size}
|
||||
height={size}
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
className={className}
|
||||
>
|
||||
<path d="M12 2l8 3v7c0 5-3.5 8.5-8 10-4.5-1.5-8-5-8-10V5z" stroke="currentColor" strokeWidth="2" fill="currentColor" opacity="0.3" />
|
||||
<path d="M9 12l2 2 4-4" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" />
|
||||
<circle cx="12" cy="12" r="1" fill="currentColor" className="animate-pulse" />
|
||||
</svg>
|
||||
);
|
||||
|
||||
// 设置图标 - 齿轮风格
|
||||
export const CyberSettings: React.FC<IconProps> = ({ className = "", size = 24 }) => (
|
||||
<svg
|
||||
width={size}
|
||||
height={size}
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
className={className}
|
||||
>
|
||||
<path d="M12 15a3 3 0 100-6 3 3 0 000 6z" stroke="currentColor" strokeWidth="2" fill="currentColor" opacity="0.3" />
|
||||
<path d="M19.4 15a1.65 1.65 0 00.33 1.82l.06.06a2 2 0 010 2.83 2 2 0 01-2.83 0l-.06-.06a1.65 1.65 0 00-1.82-.33 1.65 1.65 0 00-1 1.51V21a2 2 0 01-2 2 2 2 0 01-2-2v-.09A1.65 1.65 0 009 19.4a1.65 1.65 0 00-1.82.33l-.06.06a2 2 0 01-2.83 0 2 2 0 010-2.83l.06-.06a1.65 1.65 0 00.33-1.82 1.65 1.65 0 00-1.51-1H3a2 2 0 01-2-2 2 2 0 012-2h.09A1.65 1.65 0 004.6 9a1.65 1.65 0 00-.33-1.82l-.06-.06a2 2 0 010-2.83 2 2 0 012.83 0l.06.06a1.65 1.65 0 001.82.33H9a1.65 1.65 0 001-1.51V3a2 2 0 012-2 2 2 0 012 2v.09a1.65 1.65 0 001 1.51 1.65 1.65 0 001.82-.33l.06-.06a2 2 0 012.83 0 2 2 0 010 2.83l-.06.06a1.65 1.65 0 00-.33 1.82V9a1.65 1.65 0 001.51 1H21a2 2 0 012 2 2 2 0 01-2 2h-.09a1.65 1.65 0 00-1.51 1z" stroke="currentColor" strokeWidth="2" />
|
||||
<circle cx="12" cy="12" r="1" fill="currentColor" className="animate-spin" style={{ animationDuration: '3s' }} />
|
||||
</svg>
|
||||
);
|
||||
|
||||
// 层级图标 - 架构风格
|
||||
export const CyberLayers: React.FC<IconProps> = ({ className = "", size = 24 }) => (
|
||||
<svg
|
||||
width={size}
|
||||
height={size}
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
className={className}
|
||||
>
|
||||
<path d="M12 2l10 5v10l-10 5-10-5V7z" stroke="currentColor" strokeWidth="2" fill="none" />
|
||||
<path d="M12 7l10 5-10 5-10-5z" stroke="currentColor" strokeWidth="2" fill="currentColor" opacity="0.3" />
|
||||
<path d="M2 12v5l10 5v-5M22 12v5l-10 5v-5" stroke="currentColor" strokeWidth="1" strokeDasharray="2 2" />
|
||||
<circle cx="12" cy="12" r="1" fill="currentColor" className="animate-pulse" />
|
||||
</svg>
|
||||
);
|
||||
|
||||
// 共享图标 - 连接风格
|
||||
export const CyberShare: React.FC<IconProps> = ({ className = "", size = 24 }) => (
|
||||
<svg
|
||||
width={size}
|
||||
height={size}
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
className={className}
|
||||
>
|
||||
<circle cx="6" cy="12" r="3" stroke="currentColor" strokeWidth="2" fill="currentColor" opacity="0.3" />
|
||||
<circle cx="18" cy="6" r="3" stroke="currentColor" strokeWidth="2" fill="currentColor" opacity="0.3" />
|
||||
<circle cx="18" cy="18" r="3" stroke="currentColor" strokeWidth="2" fill="currentColor" opacity="0.3" />
|
||||
<path d="M8.59 13.51l6.83 3.98M15.41 6.51l-6.82 3.98" stroke="currentColor" strokeWidth="2" className="animate-pulse" />
|
||||
<circle cx="12" cy="12" r="1" fill="currentColor" />
|
||||
</svg>
|
||||
);
|
||||
|
||||
// 用户图标 - 头像风格
|
||||
export const CyberUser: React.FC<IconProps> = ({ className = "", size = 24 }) => (
|
||||
<svg
|
||||
width={size}
|
||||
height={size}
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
className={className}
|
||||
>
|
||||
<path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2z" stroke="currentColor" strokeWidth="2" fill="currentColor" opacity="0.3" />
|
||||
<circle cx="12" cy="10" r="3" stroke="currentColor" strokeWidth="2" fill="none" />
|
||||
<path d="M12 13c-4 0-6 2-6 4v2h12v-2c0-2-2-4-6-4z" fill="currentColor" opacity="0.6" />
|
||||
<circle cx="9" cy="10" r="0.5" fill="currentColor" className="animate-pulse" />
|
||||
<circle cx="15" cy="10" r="0.5" fill="currentColor" className="animate-pulse" />
|
||||
</svg>
|
||||
);
|
||||
|
||||
// 勾选图标 - 确认风格
|
||||
export const CyberCheck: React.FC<IconProps> = ({ className = "", size = 24 }) => (
|
||||
<svg
|
||||
width={size}
|
||||
height={size}
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
className={className}
|
||||
>
|
||||
<rect x="3" y="3" width="18" height="18" stroke="currentColor" strokeWidth="2" rx="2" fill="currentColor" opacity="0.3" />
|
||||
<path d="M9 12l2 2 4-4" stroke="currentColor" strokeWidth="3" strokeLinecap="round" strokeLinejoin="round" />
|
||||
<path d="M9 12l2 2 4-4" stroke="currentColor" strokeWidth="1" strokeLinecap="round" strokeLinejoin="round" className="animate-pulse" opacity="0.8" />
|
||||
</svg>
|
||||
);
|
||||
|
||||
// 箭头图标 - 方向风格
|
||||
export const CyberArrow: React.FC<IconProps> = ({ className = "", size = 24 }) => (
|
||||
<svg
|
||||
width={size}
|
||||
height={size}
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
className={className}
|
||||
>
|
||||
<path d="M5 12h14M12 5l7 7-7 7" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" />
|
||||
<path d="M5 12h14" stroke="currentColor" strokeWidth="1" strokeLinecap="round" className="animate-pulse" opacity="0.6" />
|
||||
<circle cx="19" cy="12" r="1" fill="currentColor" className="animate-pulse" />
|
||||
</svg>
|
||||
);
|
||||
|
||||
// 图表图标 - 数据风格
|
||||
export const CyberChart: React.FC<IconProps> = ({ className = "", size = 24 }) => (
|
||||
<svg
|
||||
width={size}
|
||||
height={size}
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
className={className}
|
||||
>
|
||||
<rect x="3" y="3" width="18" height="18" stroke="currentColor" strokeWidth="2" rx="2" fill="none" />
|
||||
<path d="M8 16v-5M12 16v-8M16 16v-3" stroke="currentColor" strokeWidth="2" strokeLinecap="round" />
|
||||
<path d="M8 16v-5M12 16v-8M16 16v-3" stroke="currentColor" strokeWidth="1" strokeLinecap="round" className="animate-pulse" opacity="0.6" />
|
||||
<circle cx="12" cy="8" r="1" fill="currentColor" className="animate-pulse" />
|
||||
</svg>
|
||||
);
|
||||
|
||||
// 书籍图标 - 学习风格
|
||||
export const CyberBook: React.FC<IconProps> = ({ className = "", size = 24 }) => (
|
||||
<svg
|
||||
width={size}
|
||||
height={size}
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
className={className}
|
||||
>
|
||||
<path d="M4 19.5A2.5 2.5 0 016.5 17H20" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" />
|
||||
<path d="M6.5 2H20v20H6.5A2.5 2.5 0 014 19.5v-15A2.5 2.5 0 016.5 2z" stroke="currentColor" strokeWidth="2" fill="currentColor" opacity="0.3" />
|
||||
<path d="M8 7h8M8 11h8M8 15h4" stroke="currentColor" strokeWidth="1" strokeLinecap="round" className="animate-pulse" opacity="0.6" />
|
||||
</svg>
|
||||
);
|
||||
|
||||
// 时钟图标 - 时间风格
|
||||
export const CyberClock: React.FC<IconProps> = ({ className = "", size = 24 }) => (
|
||||
<svg
|
||||
width={size}
|
||||
height={size}
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
className={className}
|
||||
>
|
||||
<circle cx="12" cy="12" r="9" stroke="currentColor" strokeWidth="2" fill="currentColor" opacity="0.3" />
|
||||
<path d="M12 6v6l4 2" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" />
|
||||
<circle cx="12" cy="12" r="1" fill="currentColor" className="animate-pulse" />
|
||||
<path d="M12 3v1M12 20v1M3 12h1M20 12h1" stroke="currentColor" strokeWidth="1" strokeLinecap="round" opacity="0.6" />
|
||||
</svg>
|
||||
);
|
||||
|
||||
export default {
|
||||
CyberCpu,
|
||||
CyberNetwork,
|
||||
CyberCode,
|
||||
CyberTarget,
|
||||
CyberRocket,
|
||||
CyberDatabase,
|
||||
CyberZap,
|
||||
CyberShield,
|
||||
CyberSettings,
|
||||
CyberLayers,
|
||||
CyberShare,
|
||||
CyberUser,
|
||||
CyberCheck,
|
||||
CyberArrow,
|
||||
CyberChart,
|
||||
CyberBook,
|
||||
CyberClock
|
||||
};
|
||||
104
src/components/CyberpunkVignette.tsx
Normal file
104
src/components/CyberpunkVignette.tsx
Normal file
@@ -0,0 +1,104 @@
|
||||
import React from 'react';
|
||||
import { motion } from 'framer-motion';
|
||||
|
||||
interface CyberpunkVignetteProps {
|
||||
intensity?: 'light' | 'medium' | 'strong';
|
||||
variant?: 'default' | 'neon' | 'glitch' | 'minimal';
|
||||
}
|
||||
|
||||
const CyberpunkVignette: React.FC<CyberpunkVignetteProps> = ({
|
||||
intensity = 'medium',
|
||||
variant = 'neon'
|
||||
}) => {
|
||||
const intensityMap = {
|
||||
light: { opacity: 0.3, blur: 0.5 },
|
||||
medium: { opacity: 0.5, blur: 1 },
|
||||
strong: { opacity: 0.7, blur: 2 }
|
||||
};
|
||||
|
||||
const config = intensityMap[intensity];
|
||||
|
||||
const getBackground = () => {
|
||||
switch (variant) {
|
||||
case 'minimal':
|
||||
// 极简变体 - 性能优化版本
|
||||
return `
|
||||
linear-gradient(180deg,
|
||||
rgba(0, 0, 0, ${config.opacity * 0.2}) 0%,
|
||||
transparent 15%,
|
||||
transparent 85%,
|
||||
rgba(0, 0, 0, ${config.opacity * 0.2}) 100%
|
||||
)
|
||||
`;
|
||||
case 'glitch':
|
||||
return `
|
||||
radial-gradient(ellipse at center,
|
||||
transparent 30%,
|
||||
rgba(244, 63, 94, ${config.opacity * 0.3}) 60%,
|
||||
rgba(168, 85, 247, ${config.opacity * 0.5}) 80%,
|
||||
rgba(0, 0, 0, ${config.opacity}) 100%
|
||||
),
|
||||
conic-gradient(
|
||||
from 0deg at 50% 50%,
|
||||
rgba(244, 63, 94, ${config.opacity * 0.1}),
|
||||
rgba(168, 85, 247, ${config.opacity * 0.1}),
|
||||
rgba(6, 182, 212, ${config.opacity * 0.1}),
|
||||
rgba(244, 63, 94, ${config.opacity * 0.1})
|
||||
)
|
||||
`;
|
||||
case 'neon':
|
||||
return `
|
||||
radial-gradient(ellipse at center,
|
||||
transparent 40%,
|
||||
rgba(244, 63, 94, ${config.opacity * 0.2}) 70%,
|
||||
rgba(168, 85, 247, ${config.opacity * 0.3}) 85%,
|
||||
rgba(0, 0, 0, ${config.opacity * 0.8}) 100%
|
||||
),
|
||||
linear-gradient(180deg,
|
||||
rgba(0, 0, 0, ${config.opacity * 0.3}) 0%,
|
||||
transparent 10%,
|
||||
transparent 90%,
|
||||
rgba(0, 0, 0, ${config.opacity * 0.3}) 100%
|
||||
),
|
||||
linear-gradient(90deg,
|
||||
rgba(0, 0, 0, ${config.opacity * 0.3}) 0%,
|
||||
transparent 10%,
|
||||
transparent 90%,
|
||||
rgba(0, 0, 0, ${config.opacity * 0.3}) 100%
|
||||
)
|
||||
`;
|
||||
default:
|
||||
return `
|
||||
radial-gradient(ellipse at center,
|
||||
transparent 50%,
|
||||
rgba(0, 0, 0, ${config.opacity * 0.5}) 100%
|
||||
),
|
||||
linear-gradient(180deg,
|
||||
rgba(0, 0, 0, ${config.opacity * 0.4}) 0%,
|
||||
transparent 20%,
|
||||
transparent 80%,
|
||||
rgba(0, 0, 0, ${config.opacity * 0.4}) 100%
|
||||
)
|
||||
`;
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<motion.div
|
||||
className="fixed inset-0 pointer-events-none z-20"
|
||||
style={{
|
||||
background: getBackground(),
|
||||
filter: `blur(${config.blur}px)`,
|
||||
}}
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 1 }}
|
||||
transition={{ duration: 1, ease: "easeOut" }}
|
||||
/>
|
||||
|
||||
{/* 霓虹边框效果已移除 */}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default CyberpunkVignette;
|
||||
196
src/components/GlobalBackground.tsx
Normal file
196
src/components/GlobalBackground.tsx
Normal file
@@ -0,0 +1,196 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { motion, AnimatePresence } from 'framer-motion';
|
||||
|
||||
// 星点组件 - 实现消失后随机重生
|
||||
const Star: React.FC<{ index: number }> = ({ index }) => {
|
||||
const [position, setPosition] = useState({
|
||||
x: Math.random() * 100,
|
||||
y: Math.random() * 100,
|
||||
size: Math.random() * 4 + 2, // 增大尺寸范围 2-6px
|
||||
});
|
||||
const [isVisible, setIsVisible] = useState(true);
|
||||
|
||||
// 生成新的随机位置
|
||||
const regenerate = () => {
|
||||
setIsVisible(false);
|
||||
setTimeout(() => {
|
||||
setPosition({
|
||||
x: Math.random() * 100,
|
||||
y: Math.random() * 100,
|
||||
size: Math.random() * 4 + 2, // 增大尺寸范围 2-6px
|
||||
});
|
||||
setIsVisible(true);
|
||||
}, 500);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
// 随机生命周期(5-15秒)
|
||||
const lifetime = (Math.random() * 10 + 5) * 1000;
|
||||
const interval = setInterval(regenerate, lifetime);
|
||||
return () => clearInterval(interval);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<AnimatePresence>
|
||||
{isVisible && (
|
||||
<motion.div
|
||||
className="absolute bg-[#E5DFD7]"
|
||||
style={{
|
||||
width: `${position.size}px`,
|
||||
height: `${position.size}px`,
|
||||
borderRadius: '50%',
|
||||
left: `${position.x}%`,
|
||||
top: `${position.y}%`,
|
||||
boxShadow: `0 0 ${position.size * 3}px rgba(229, 223, 215, 0.9), 0 0 ${position.size * 6}px rgba(229, 223, 215, 0.4)`,
|
||||
}}
|
||||
initial={{ opacity: 0, scale: 0 }}
|
||||
animate={{
|
||||
opacity: [0, 1, 1, 0],
|
||||
scale: [0, 1, 1.2, 0]
|
||||
}}
|
||||
transition={{
|
||||
duration: Math.random() * 3 + 2,
|
||||
times: [0, 0.2, 0.8, 1],
|
||||
ease: "easeInOut"
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</AnimatePresence>
|
||||
);
|
||||
};
|
||||
|
||||
const GlobalBackground: React.FC = () => {
|
||||
return (
|
||||
<div className="fixed inset-0 pointer-events-none overflow-hidden">
|
||||
{/* 基础渐变层 - 调暗以突出效果 */}
|
||||
<div
|
||||
className="absolute inset-0"
|
||||
style={{
|
||||
background: 'linear-gradient(180deg, color-mix(in oklab, #7A9E9F 85%, #000 15%) 0%, color-mix(in oklab, #8A9B8F 75%, #000 25%) 65%, color-mix(in oklab, #8A9B8F 65%, #000 35%) 100%)'
|
||||
}}
|
||||
/>
|
||||
|
||||
{/* 液态流动层 - 增强金色光斑 */}
|
||||
<motion.div
|
||||
className="absolute inset-[-20%]"
|
||||
style={{
|
||||
background: `
|
||||
radial-gradient(45% 30% at 70% 20%, rgba(212, 180, 131, 0.20), transparent 60%),
|
||||
radial-gradient(40% 28% at 30% 80%, rgba(212, 180, 131, 0.15), transparent 60%)
|
||||
`,
|
||||
filter: 'blur(30px)'
|
||||
}}
|
||||
animate={{
|
||||
x: ['-4%', '6%'],
|
||||
y: ['-2%', '3%'],
|
||||
rotate: [0.6, -0.6]
|
||||
}}
|
||||
transition={{
|
||||
duration: 25,
|
||||
ease: "easeInOut",
|
||||
repeat: Infinity,
|
||||
repeatType: "reverse"
|
||||
}}
|
||||
/>
|
||||
|
||||
{/* 极光效果层1 - 增强可见度 */}
|
||||
<motion.div
|
||||
className="absolute w-full h-[200%] -top-1/2"
|
||||
style={{
|
||||
background: `linear-gradient(45deg,
|
||||
transparent 20%,
|
||||
rgba(212, 180, 131, 0.25) 30%,
|
||||
rgba(122, 158, 159, 0.20) 40%,
|
||||
rgba(212, 180, 131, 0.18) 50%,
|
||||
transparent 60%
|
||||
)`,
|
||||
filter: 'blur(60px)',
|
||||
transform: 'skewY(-15deg)',
|
||||
opacity: 0.6
|
||||
}}
|
||||
animate={{
|
||||
x: ['-100%', '100%'],
|
||||
opacity: [0.4, 0.7, 0.4]
|
||||
}}
|
||||
transition={{
|
||||
duration: 30,
|
||||
ease: "easeInOut",
|
||||
repeat: Infinity,
|
||||
repeatType: "reverse"
|
||||
}}
|
||||
/>
|
||||
|
||||
{/* 极光效果层2 */}
|
||||
<motion.div
|
||||
className="absolute w-full h-[200%] -top-1/2 opacity-20"
|
||||
style={{
|
||||
background: `linear-gradient(-30deg,
|
||||
transparent 10%,
|
||||
rgba(138, 155, 143, 0.15) 25%,
|
||||
rgba(212, 180, 131, 0.10) 35%,
|
||||
rgba(229, 223, 215, 0.08) 45%,
|
||||
transparent 60%
|
||||
)`,
|
||||
filter: 'blur(80px)',
|
||||
transform: 'skewY(10deg)'
|
||||
}}
|
||||
animate={{
|
||||
x: ['100%', '-100%'],
|
||||
opacity: [0.15, 0.3, 0.15]
|
||||
}}
|
||||
transition={{
|
||||
duration: 35,
|
||||
ease: "easeInOut",
|
||||
repeat: Infinity,
|
||||
repeatType: "reverse",
|
||||
delay: 10
|
||||
}}
|
||||
/>
|
||||
|
||||
{/* 额外的流动层 - 增强金色效果 */}
|
||||
<motion.div
|
||||
className="absolute inset-[-30%]"
|
||||
style={{
|
||||
background: `
|
||||
radial-gradient(50% 35% at 20% 50%, rgba(212, 180, 131, 0.12), transparent 70%),
|
||||
radial-gradient(35% 25% at 80% 70%, rgba(212, 180, 131, 0.10), transparent 60%)
|
||||
`,
|
||||
filter: 'blur(40px)',
|
||||
opacity: 0.7
|
||||
}}
|
||||
animate={{
|
||||
x: ['5%', '-5%'],
|
||||
y: ['3%', '-4%'],
|
||||
rotate: [-1, 1]
|
||||
}}
|
||||
transition={{
|
||||
duration: 40,
|
||||
ease: "easeInOut",
|
||||
repeat: Infinity,
|
||||
repeatType: "reverse"
|
||||
}}
|
||||
/>
|
||||
|
||||
{/* 星点效果 - 圆形,灭掉后随机重生 */}
|
||||
<div className="absolute inset-0">
|
||||
{Array.from({ length: 60 }, (_, i) => (
|
||||
<Star key={i} index={i} />
|
||||
))}
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
{/* 减少动效样式 */}
|
||||
<style>{`
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.motion-div {
|
||||
animation: none !important;
|
||||
transition: none !important;
|
||||
}
|
||||
}
|
||||
`}</style>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default GlobalBackground;
|
||||
575
src/components/Icons.tsx
Normal file
575
src/components/Icons.tsx
Normal file
@@ -0,0 +1,575 @@
|
||||
import React from 'react';
|
||||
|
||||
interface IconProps {
|
||||
className?: string;
|
||||
size?: number;
|
||||
}
|
||||
|
||||
// 书本图标 - 赛博朋克风格
|
||||
export const BookOpen: React.FC<IconProps> = ({ className = '', size = 24 }) => (
|
||||
<svg width={size} height={size} viewBox="0 0 24 24" fill="none" className={className}>
|
||||
<defs>
|
||||
<filter id="neon-glow">
|
||||
<feGaussianBlur stdDeviation="3" result="coloredBlur"/>
|
||||
<feMerge>
|
||||
<feMergeNode in="coloredBlur"/>
|
||||
<feMergeNode in="SourceGraphic"/>
|
||||
</feMerge>
|
||||
</filter>
|
||||
</defs>
|
||||
<path d="M4 19.5A2.5 2.5 0 016.5 17H20" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" filter="url(#neon-glow)" />
|
||||
<path d="M6.5 2H20v20H6.5A2.5 2.5 0 014 19.5v-15A2.5 2.5 0 016.5 2z" stroke="currentColor" strokeWidth="2" fill="currentColor" opacity="0.3" />
|
||||
<path d="M8 7h8M8 11h8M8 15h4" stroke="currentColor" strokeWidth="1" strokeLinecap="round" className="animate-pulse" opacity="0.8" />
|
||||
<circle cx="12" cy="11" r="0.5" fill="currentColor" className="animate-pulse" />
|
||||
</svg>
|
||||
);
|
||||
|
||||
// 设置图标 - 赛博朋克风格
|
||||
export const Settings: React.FC<IconProps> = ({ className = '', size = 24 }) => (
|
||||
<svg width={size} height={size} viewBox="0 0 24 24" fill="none" className={className}>
|
||||
<path d="M12 15a3 3 0 100-6 3 3 0 000 6z" stroke="currentColor" strokeWidth="2" fill="currentColor" opacity="0.3" />
|
||||
<path d="M19.4 15a1.65 1.65 0 00.33 1.82l.06.06a2 2 0 010 2.83 2 2 0 01-2.83 0l-.06-.06a1.65 1.65 0 00-1.82-.33 1.65 1.65 0 00-1 1.51V21a2 2 0 01-2 2 2 2 0 01-2-2v-.09A1.65 1.65 0 009 19.4a1.65 1.65 0 00-1.82.33l-.06.06a2 2 0 01-2.83 0 2 2 0 010-2.83l.06-.06a1.65 1.65 0 00.33-1.82 1.65 1.65 0 00-1.51-1H3a2 2 0 01-2-2 2 2 0 012-2h.09A1.65 1.65 0 004.6 9a1.65 1.65 0 00-.33-1.82l-.06-.06a2 2 0 010-2.83 2 2 0 012.83 0l.06.06a1.65 1.65 0 001.82.33H9a1.65 1.65 0 001-1.51V3a2 2 0 012-2 2 2 0 012 2v.09a1.65 1.65 0 001 1.51 1.65 1.65 0 001.82-.33l.06-.06a2 2 0 012.83 0 2 2 0 010 2.83l-.06.06a1.65 1.65 0 00-.33 1.82V9a1.65 1.65 0 001.51 1H21a2 2 0 012 2 2 2 0 01-2 2h-.09a1.65 1.65 0 00-1.51 1z" stroke="currentColor" strokeWidth="2" />
|
||||
<circle cx="12" cy="12" r="1" fill="currentColor" className="animate-spin" style={{ animationDuration: '3s' }} />
|
||||
</svg>
|
||||
);
|
||||
|
||||
// 闪电图标 - 赛博朋克风格
|
||||
export const Zap: React.FC<IconProps> = ({ className = '', size = 24 }) => (
|
||||
<svg width={size} height={size} viewBox="0 0 24 24" fill="none" className={className}>
|
||||
<path d="M13 2L3 14h9l-1 8 10-12h-9z" stroke="currentColor" strokeWidth="2" fill="currentColor" opacity="0.3" strokeLinejoin="round" />
|
||||
<path d="M13 2L3 14h9l-1 8 10-12h-9z" stroke="currentColor" strokeWidth="1" strokeLinejoin="round" className="animate-pulse" opacity="0.8" />
|
||||
<circle cx="12" cy="10" r="0.5" fill="currentColor" className="animate-pulse" />
|
||||
</svg>
|
||||
);
|
||||
|
||||
// CPU图标 - 赛博朋克风格
|
||||
export const Cpu: React.FC<IconProps> = ({ className = '', size = 24 }) => (
|
||||
<svg width={size} height={size} viewBox="0 0 24 24" fill="none" className={className}>
|
||||
<rect x="7" y="7" width="10" height="10" stroke="currentColor" strokeWidth="2" fill="none" />
|
||||
<rect x="9" y="9" width="6" height="6" fill="currentColor" opacity="0.3" />
|
||||
<path d="M10 2v5M14 2v5M10 17v5M14 17v5M2 10h5M17 10h5M2 14h5M17 14h5" stroke="currentColor" strokeWidth="2" strokeLinecap="round" />
|
||||
<circle cx="12" cy="12" r="1" fill="currentColor" className="animate-pulse" />
|
||||
<path d="M10 2v5M14 2v5M10 17v5M14 17v5" stroke="currentColor" strokeWidth="1" strokeLinecap="round" className="animate-pulse" opacity="0.5" />
|
||||
</svg>
|
||||
);
|
||||
|
||||
// 箭头右图标 - 赛博朋克风格
|
||||
export const ChevronRight: React.FC<IconProps> = ({ className = '', size = 24 }) => (
|
||||
<svg width={size} height={size} viewBox="0 0 24 24" fill="none" className={className}>
|
||||
<path d="M9 18l6-6-6-6" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" />
|
||||
<path d="M9 18l6-6-6-6" stroke="currentColor" strokeWidth="1" strokeLinecap="round" strokeLinejoin="round" className="animate-pulse" opacity="0.5" />
|
||||
</svg>
|
||||
);
|
||||
|
||||
// 箭头下图标 - 赛博朋克风格
|
||||
export const ChevronDown: React.FC<IconProps> = ({ className = '', size = 24 }) => (
|
||||
<svg width={size} height={size} viewBox="0 0 24 24" fill="none" className={className}>
|
||||
<path d="M6 9l6 6 6-6" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" />
|
||||
<path d="M6 9l6 6 6-6" stroke="currentColor" strokeWidth="1" strokeLinecap="round" strokeLinejoin="round" className="animate-pulse" opacity="0.5" />
|
||||
</svg>
|
||||
);
|
||||
|
||||
// 播放图标 - 赛博朋克风格
|
||||
export const PlayCircle: React.FC<IconProps> = ({ className = '', size = 24 }) => (
|
||||
<svg width={size} height={size} viewBox="0 0 24 24" fill="none" className={className}>
|
||||
<circle cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="2" fill="currentColor" opacity="0.3" />
|
||||
<polygon points="10,8 16,12 10,16 10,8" fill="currentColor" />
|
||||
<circle cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="1" className="animate-pulse" opacity="0.5" />
|
||||
<polygon points="10,8 16,12 10,16 10,8" fill="currentColor" className="animate-pulse" opacity="0.3" />
|
||||
</svg>
|
||||
);
|
||||
|
||||
// 时钟图标 - 赛博朋克风格
|
||||
export const Clock: React.FC<IconProps> = ({ className = '', size = 24 }) => (
|
||||
<svg width={size} height={size} viewBox="0 0 24 24" fill="none" className={className}>
|
||||
<circle cx="12" cy="12" r="9" stroke="currentColor" strokeWidth="2" fill="currentColor" opacity="0.3" />
|
||||
<path d="M12 6v6l4 2" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" />
|
||||
<circle cx="12" cy="12" r="1" fill="currentColor" className="animate-pulse" />
|
||||
<path d="M12 3v1M12 20v1M3 12h1M20 12h1" stroke="currentColor" strokeWidth="1" strokeLinecap="round" opacity="0.6" className="animate-pulse" />
|
||||
</svg>
|
||||
);
|
||||
|
||||
// 用户图标 - 赛博朋克风格
|
||||
export const Users: React.FC<IconProps> = ({ className = '', size = 24 }) => (
|
||||
<svg width={size} height={size} viewBox="0 0 24 24" fill="none" className={className}>
|
||||
<path d="M17 21v-2a4 4 0 00-4-4H5a4 4 0 00-4 4v2" stroke="currentColor" strokeWidth="2" fill="currentColor" opacity="0.3" />
|
||||
<circle cx="9" cy="7" r="4" stroke="currentColor" strokeWidth="2" fill="currentColor" opacity="0.3" />
|
||||
<path d="M23 21v-2a4 4 0 00-3-3.87M16 3.13a4 4 0 010 7.75" stroke="currentColor" strokeWidth="2" />
|
||||
<circle cx="9" cy="7" r="0.5" fill="currentColor" className="animate-pulse" />
|
||||
<circle cx="20" cy="7" r="0.5" fill="currentColor" className="animate-pulse" style={{ animationDelay: '0.5s' }} />
|
||||
</svg>
|
||||
);
|
||||
|
||||
// 目标图标 - 赛博朋克风格
|
||||
export const Target: React.FC<IconProps> = ({ className = '', size = 24 }) => (
|
||||
<svg width={size} height={size} viewBox="0 0 24 24" fill="none" className={className}>
|
||||
<circle cx="12" cy="12" r="9" stroke="currentColor" strokeWidth="2" fill="none" />
|
||||
<circle cx="12" cy="12" r="6" stroke="currentColor" strokeWidth="1.5" fill="none" opacity="0.6" className="animate-pulse" />
|
||||
<circle cx="12" cy="12" r="3" stroke="currentColor" strokeWidth="1" fill="currentColor" opacity="0.3" />
|
||||
<path d="M12 3v3M12 18v3M3 12h3M18 12h3" stroke="currentColor" strokeWidth="2" strokeLinecap="round" />
|
||||
<circle cx="12" cy="12" r="1" fill="currentColor" className="animate-pulse" />
|
||||
</svg>
|
||||
);
|
||||
|
||||
// 奖杯图标 - 赛博朋克风格
|
||||
export const Award: React.FC<IconProps> = ({ className = '', size = 24 }) => (
|
||||
<svg width={size} height={size} viewBox="0 0 24 24" fill="none" className={className}>
|
||||
<circle cx="12" cy="8" r="7" stroke="currentColor" strokeWidth="2" fill="currentColor" opacity="0.3" />
|
||||
<polyline points="8.21,13.89 7,23 12,20 17,23 15.79,13.88" stroke="currentColor" strokeWidth="2" fill="currentColor" opacity="0.3" />
|
||||
<circle cx="12" cy="8" r="3" fill="currentColor" opacity="0.5" />
|
||||
<circle cx="12" cy="8" r="1" fill="currentColor" className="animate-pulse" />
|
||||
<path d="M12 1v2M7 3l1 1M17 3l-1 1" stroke="currentColor" strokeWidth="1" strokeLinecap="round" className="animate-pulse" opacity="0.6" />
|
||||
</svg>
|
||||
);
|
||||
|
||||
// 菜单图标 - 赛博朋克风格
|
||||
export const Menu: React.FC<IconProps> = ({ className = '', size = 24 }) => (
|
||||
<svg width={size} height={size} viewBox="0 0 24 24" fill="none" className={className}>
|
||||
<line x1="3" y1="6" x2="21" y2="6" stroke="currentColor" strokeWidth="2" strokeLinecap="round" />
|
||||
<line x1="3" y1="12" x2="21" y2="12" stroke="currentColor" strokeWidth="2" strokeLinecap="round" />
|
||||
<line x1="3" y1="18" x2="21" y2="18" stroke="currentColor" strokeWidth="2" strokeLinecap="round" />
|
||||
<circle cx="3" cy="6" r="1" fill="currentColor" className="animate-pulse" />
|
||||
<circle cx="3" cy="12" r="1" fill="currentColor" className="animate-pulse" style={{ animationDelay: '0.2s' }} />
|
||||
<circle cx="3" cy="18" r="1" fill="currentColor" className="animate-pulse" style={{ animationDelay: '0.4s' }} />
|
||||
</svg>
|
||||
);
|
||||
|
||||
// 关闭图标 - 赛博朋克风格
|
||||
export const X: React.FC<IconProps> = ({ className = '', size = 24 }) => (
|
||||
<svg width={size} height={size} viewBox="0 0 24 24" fill="none" className={className}>
|
||||
<line x1="18" y1="6" x2="6" y2="18" stroke="currentColor" strokeWidth="2" strokeLinecap="round" />
|
||||
<line x1="6" y1="6" x2="18" y2="18" stroke="currentColor" strokeWidth="2" strokeLinecap="round" />
|
||||
<circle cx="12" cy="12" r="8" stroke="currentColor" strokeWidth="1" fill="none" className="animate-pulse" opacity="0.3" />
|
||||
<circle cx="12" cy="12" r="1" fill="currentColor" className="animate-pulse" />
|
||||
</svg>
|
||||
);
|
||||
|
||||
// 勾选图标 - 赛博朋克风格
|
||||
export const CheckCircle: React.FC<IconProps> = ({ className = '', size = 24 }) => (
|
||||
<svg width={size} height={size} viewBox="0 0 24 24" fill="none" className={className}>
|
||||
<circle cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="2" fill="currentColor" opacity="0.3" />
|
||||
<path d="M9 12l2 2 4-4" stroke="currentColor" strokeWidth="3" strokeLinecap="round" strokeLinejoin="round" />
|
||||
<path d="M9 12l2 2 4-4" stroke="currentColor" strokeWidth="1" strokeLinecap="round" strokeLinejoin="round" className="animate-pulse" opacity="0.8" />
|
||||
</svg>
|
||||
);
|
||||
|
||||
// 箭头右图标 - 赛博朋克风格
|
||||
export const ArrowRight: React.FC<IconProps> = ({ className = '', size = 24 }) => (
|
||||
<svg width={size} height={size} viewBox="0 0 24 24" fill="none" className={className}>
|
||||
<path d="M5 12h14M12 5l7 7-7 7" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" />
|
||||
<path d="M5 12h14" stroke="currentColor" strokeWidth="1" strokeLinecap="round" className="animate-pulse" opacity="0.6" />
|
||||
<circle cx="19" cy="12" r="1" fill="currentColor" className="animate-pulse" />
|
||||
</svg>
|
||||
);
|
||||
|
||||
// 显示器图标 - 赛博朋克风格
|
||||
export const Monitor: React.FC<IconProps> = ({ className = '', size = 24 }) => (
|
||||
<svg width={size} height={size} viewBox="0 0 24 24" fill="none" className={className}>
|
||||
<rect x="2" y="3" width="20" height="14" rx="2" stroke="currentColor" strokeWidth="2" fill="currentColor" opacity="0.3" />
|
||||
<line x1="8" y1="21" x2="16" y2="21" stroke="currentColor" strokeWidth="2" strokeLinecap="round" />
|
||||
<line x1="12" y1="17" x2="12" y2="21" stroke="currentColor" strokeWidth="2" strokeLinecap="round" />
|
||||
<rect x="4" y="5" width="16" height="10" fill="currentColor" opacity="0.2" />
|
||||
<circle cx="12" cy="10" r="1" fill="currentColor" className="animate-pulse" />
|
||||
<path d="M6 7h3M6 9h5" stroke="currentColor" strokeWidth="1" strokeLinecap="round" className="animate-pulse" opacity="0.5" />
|
||||
</svg>
|
||||
);
|
||||
|
||||
// 网络图标 - 赛博朋克风格
|
||||
export const Network: React.FC<IconProps> = ({ className = '', size = 24 }) => (
|
||||
<svg width={size} height={size} viewBox="0 0 24 24" fill="none" className={className}>
|
||||
<circle cx="12" cy="5" r="2" stroke="currentColor" strokeWidth="2" fill="currentColor" opacity="0.3" />
|
||||
<circle cx="5" cy="19" r="2" stroke="currentColor" strokeWidth="2" fill="currentColor" opacity="0.3" />
|
||||
<circle cx="19" cy="19" r="2" stroke="currentColor" strokeWidth="2" fill="currentColor" opacity="0.3" />
|
||||
<path d="M12 7v6M12 13l-5.5 4M12 13l5.5 4" stroke="currentColor" strokeWidth="2" strokeLinecap="round" className="animate-pulse" />
|
||||
<circle cx="12" cy="13" r="2" fill="currentColor" />
|
||||
<circle cx="12" cy="5" r="0.5" fill="currentColor" className="animate-pulse" />
|
||||
</svg>
|
||||
);
|
||||
|
||||
// 眼睛图标 - 赛博朋克风格
|
||||
export const Eye: React.FC<IconProps> = ({ className = '', size = 24 }) => (
|
||||
<svg width={size} height={size} viewBox="0 0 24 24" fill="none" className={className}>
|
||||
<path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z" stroke="currentColor" strokeWidth="2" fill="currentColor" opacity="0.3" />
|
||||
<circle cx="12" cy="12" r="3" stroke="currentColor" strokeWidth="2" fill="currentColor" opacity="0.5" />
|
||||
<circle cx="12" cy="12" r="1" fill="currentColor" className="animate-pulse" />
|
||||
<path d="M12 5v2M12 17v2" stroke="currentColor" strokeWidth="1" strokeLinecap="round" className="animate-pulse" opacity="0.4" />
|
||||
</svg>
|
||||
);
|
||||
|
||||
// 层级图标 - 赛博朋克风格
|
||||
export const Layers: React.FC<IconProps> = ({ className = '', size = 24 }) => (
|
||||
<svg width={size} height={size} viewBox="0 0 24 24" fill="none" className={className}>
|
||||
<path d="M12 2l10 5v10l-10 5-10-5V7z" stroke="currentColor" strokeWidth="2" fill="none" />
|
||||
<path d="M12 7l10 5-10 5-10-5z" stroke="currentColor" strokeWidth="2" fill="currentColor" opacity="0.3" />
|
||||
<path d="M2 12v5l10 5v-5M22 12v5l-10 5v-5" stroke="currentColor" strokeWidth="1" strokeDasharray="2 2" className="animate-pulse" opacity="0.6" />
|
||||
<circle cx="12" cy="12" r="1" fill="currentColor" className="animate-pulse" />
|
||||
</svg>
|
||||
);
|
||||
|
||||
// 箭头左图标 - 赛博朋克风格
|
||||
export const ArrowLeft: React.FC<IconProps> = ({ className = '', size = 24 }) => (
|
||||
<svg width={size} height={size} viewBox="0 0 24 24" fill="none" className={className}>
|
||||
<path d="M19 12H5M12 19l-7-7 7-7" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" />
|
||||
<path d="M19 12H5" stroke="currentColor" strokeWidth="1" strokeLinecap="round" className="animate-pulse" opacity="0.6" />
|
||||
<circle cx="5" cy="12" r="1" fill="currentColor" className="animate-pulse" />
|
||||
</svg>
|
||||
);
|
||||
|
||||
// 刷新图标 - 赛博朋克风格
|
||||
export const RefreshCw: React.FC<IconProps> = ({ className = '', size = 24 }) => (
|
||||
<svg width={size} height={size} viewBox="0 0 24 24" fill="none" className={className}>
|
||||
<polyline points="23,4 23,10 17,10" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" />
|
||||
<polyline points="1,20 1,14 7,14" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" />
|
||||
<path d="M20.49 9A9 9 0 005.64 5.64L1 10m22 4l-4.64 4.36A9 9 0 013.51 15" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" />
|
||||
<circle cx="12" cy="12" r="1" fill="currentColor" className="animate-spin" style={{ animationDuration: '2s' }} />
|
||||
<circle cx="12" cy="12" r="9" stroke="currentColor" strokeWidth="1" fill="none" className="animate-pulse" opacity="0.3" />
|
||||
</svg>
|
||||
);
|
||||
|
||||
// Hash图标 - 赛博朋克风格
|
||||
export const Hash = (props: IconProps) => (
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" {...props}>
|
||||
<line x1="4" y1="9" x2="20" y2="9" stroke="currentColor" strokeWidth="2" strokeLinecap="round" />
|
||||
<line x1="4" y1="15" x2="20" y2="15" stroke="currentColor" strokeWidth="2" strokeLinecap="round" />
|
||||
<line x1="10" y1="3" x2="8" y2="21" stroke="currentColor" strokeWidth="2" strokeLinecap="round" />
|
||||
<line x1="16" y1="3" x2="14" y2="21" stroke="currentColor" strokeWidth="2" strokeLinecap="round" />
|
||||
<circle cx="12" cy="12" r="1" fill="currentColor" className="animate-pulse" />
|
||||
<rect x="8" y="9" width="8" height="6" fill="currentColor" opacity="0.2" />
|
||||
</svg>
|
||||
);
|
||||
|
||||
// 计算器图标 - 赛博朋克风格
|
||||
export const Calculator = (props: IconProps) => (
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" {...props}>
|
||||
<rect x="4" y="2" width="16" height="20" rx="2" stroke="currentColor" strokeWidth="2" fill="currentColor" opacity="0.3" />
|
||||
<rect x="7" y="5" width="10" height="3" stroke="currentColor" strokeWidth="1" fill="currentColor" opacity="0.5" />
|
||||
<circle cx="8" cy="12" r="1" stroke="currentColor" strokeWidth="1" fill="currentColor" opacity="0.6" />
|
||||
<circle cx="12" cy="12" r="1" stroke="currentColor" strokeWidth="1" fill="currentColor" opacity="0.6" />
|
||||
<circle cx="16" cy="12" r="1" stroke="currentColor" strokeWidth="1" fill="currentColor" opacity="0.6" />
|
||||
<circle cx="8" cy="16" r="1" stroke="currentColor" strokeWidth="1" fill="currentColor" opacity="0.6" />
|
||||
<circle cx="12" cy="16" r="1" stroke="currentColor" strokeWidth="1" fill="currentColor" opacity="0.6" />
|
||||
<circle cx="16" cy="16" r="1" stroke="currentColor" strokeWidth="1" fill="currentColor" opacity="0.6" />
|
||||
<circle cx="12" cy="6.5" r="0.5" fill="currentColor" className="animate-pulse" />
|
||||
</svg>
|
||||
);
|
||||
|
||||
// 电源图标 - 赛博朋克风格
|
||||
export const Power = (props: IconProps) => (
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" {...props}>
|
||||
<path d="M18.36 6.64a9 9 0 11-12.73 0" stroke="currentColor" strokeWidth="2" strokeLinecap="round" />
|
||||
<line x1="12" y1="2" x2="12" y2="12" stroke="currentColor" strokeWidth="2" strokeLinecap="round" />
|
||||
<circle cx="12" cy="12" r="9" stroke="currentColor" strokeWidth="1" fill="currentColor" opacity="0.2" />
|
||||
<circle cx="12" cy="2" r="1" fill="currentColor" className="animate-pulse" />
|
||||
<path d="M12 2v10" stroke="currentColor" strokeWidth="1" strokeLinecap="round" className="animate-pulse" opacity="0.5" />
|
||||
</svg>
|
||||
);
|
||||
|
||||
// 代码图标 - 赛博朋克风格
|
||||
export const Code = (props: IconProps) => (
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" {...props}>
|
||||
<rect x="3" y="3" width="18" height="18" stroke="currentColor" strokeWidth="2" rx="2" fill="none" />
|
||||
<path d="M8 8l-2 4 2 4M16 8l2 4-2 4" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" />
|
||||
<path d="M12 16.5v.5" stroke="currentColor" strokeWidth="3" strokeLinecap="round" className="animate-pulse" />
|
||||
<rect x="3" y="3" width="18" height="18" stroke="currentColor" strokeWidth="1" rx="2" fill="none" className="animate-pulse" opacity="0.3" />
|
||||
</svg>
|
||||
);
|
||||
|
||||
// 盾牌图标 - 赛博朋克风格
|
||||
export const Shield = (props: IconProps) => (
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" {...props}>
|
||||
<path d="M12 2l8 3v7c0 5-3.5 8.5-8 10-4.5-1.5-8-5-8-10V5z" stroke="currentColor" strokeWidth="2" fill="currentColor" opacity="0.3" />
|
||||
<path d="M9 12l2 2 4-4" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" />
|
||||
<circle cx="12" cy="12" r="1" fill="currentColor" className="animate-pulse" />
|
||||
<path d="M12 2l8 3v7c0 5-3.5 8.5-8 10-4.5-1.5-8-5-8-10V5z" stroke="currentColor" strokeWidth="1" fill="none" className="animate-pulse" opacity="0.4" />
|
||||
</svg>
|
||||
);
|
||||
|
||||
// 箭头下图标 - 赛博朋克风格
|
||||
export const ArrowDown = (props: IconProps) => (
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" {...props}>
|
||||
<path d="M12 5v14M19 12l-7 7-7-7" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" />
|
||||
<path d="M12 5v14" stroke="currentColor" strokeWidth="1" strokeLinecap="round" className="animate-pulse" opacity="0.6" />
|
||||
<circle cx="12" cy="19" r="1" fill="currentColor" className="animate-pulse" />
|
||||
</svg>
|
||||
);
|
||||
|
||||
// 文件夹图标 - 赛博朋克风格
|
||||
export const Folder = (props: IconProps) => (
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" {...props}>
|
||||
<path d="M22 19a2 2 0 01-2 2H4a2 2 0 01-2-2V5a2 2 0 012-2h5l2 3h9a2 2 0 012 2z" stroke="currentColor" strokeWidth="2" fill="currentColor" opacity="0.3" />
|
||||
<path d="M22 19a2 2 0 01-2 2H4a2 2 0 01-2-2V5a2 2 0 012-2h5l2 3h9a2 2 0 012 2z" stroke="currentColor" strokeWidth="1" fill="none" className="animate-pulse" opacity="0.5" />
|
||||
<circle cx="12" cy="13" r="0.5" fill="currentColor" className="animate-pulse" />
|
||||
</svg>
|
||||
);
|
||||
|
||||
// 服务器图标 - 赛博朋克风格
|
||||
export const Server = (props: IconProps) => (
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" {...props}>
|
||||
<rect x="2" y="2" width="20" height="8" rx="2" stroke="currentColor" strokeWidth="2" fill="currentColor" opacity="0.3" />
|
||||
<rect x="2" y="14" width="20" height="8" rx="2" stroke="currentColor" strokeWidth="2" fill="currentColor" opacity="0.3" />
|
||||
<circle cx="6" cy="6" r="1" fill="currentColor" className="animate-pulse" />
|
||||
<circle cx="6" cy="18" r="1" fill="currentColor" className="animate-pulse" style={{ animationDelay: '0.5s' }} />
|
||||
<line x1="10" y1="6" x2="18" y2="6" stroke="currentColor" strokeWidth="1" strokeLinecap="round" opacity="0.5" />
|
||||
<line x1="10" y1="18" x2="18" y2="18" stroke="currentColor" strokeWidth="1" strokeLinecap="round" opacity="0.5" />
|
||||
</svg>
|
||||
);
|
||||
|
||||
// 数据库图标 - 赛博朋克风格
|
||||
export const Database = (props: IconProps) => (
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" {...props}>
|
||||
<ellipse cx="12" cy="5" rx="9" ry="3" stroke="currentColor" strokeWidth="2" fill="currentColor" opacity="0.3" />
|
||||
<path d="M21 12c0 1.66-4 3-9 3s-9-1.34-9-3M21 5v14c0 1.66-4 3-9 3s-9-1.34-9-3V5" stroke="currentColor" strokeWidth="2" />
|
||||
<path d="M21 12v7c0 1.66-4 3-9 3s-9-1.34-9-3v-7" stroke="currentColor" strokeWidth="1" strokeDasharray="2 2" className="animate-pulse" opacity="0.5" />
|
||||
<circle cx="12" cy="5" r="0.5" fill="currentColor" className="animate-pulse" />
|
||||
<circle cx="12" cy="12" r="0.5" fill="currentColor" className="animate-pulse" style={{ animationDelay: '0.3s' }} />
|
||||
<circle cx="12" cy="19" r="0.5" fill="currentColor" className="animate-pulse" style={{ animationDelay: '0.6s' }} />
|
||||
</svg>
|
||||
);
|
||||
|
||||
// 图表图标 - 赛博朋克风格
|
||||
export const BarChart = (props: IconProps) => (
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" {...props}>
|
||||
<rect x="3" y="3" width="18" height="18" stroke="currentColor" strokeWidth="2" rx="2" fill="none" />
|
||||
<rect x="7" y="8" width="3" height="13" fill="currentColor" opacity="0.6" />
|
||||
<rect x="14" y="5" width="3" height="16" fill="currentColor" opacity="0.6" />
|
||||
<rect x="7" y="8" width="3" height="13" fill="none" stroke="currentColor" strokeWidth="1" className="animate-pulse" opacity="0.8" />
|
||||
<rect x="14" y="5" width="3" height="16" fill="none" stroke="currentColor" strokeWidth="1" className="animate-pulse" opacity="0.8" />
|
||||
<circle cx="8.5" cy="8" r="0.5" fill="currentColor" className="animate-pulse" />
|
||||
<circle cx="15.5" cy="5" r="0.5" fill="currentColor" className="animate-pulse" style={{ animationDelay: '0.3s' }} />
|
||||
</svg>
|
||||
);
|
||||
|
||||
// 灯泡图标 (💡) - 赛博朋克风格
|
||||
export const Lightbulb = (props: IconProps) => (
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" {...props}>
|
||||
<path d="M12 2C8.13 2 5 5.13 5 9c0 2.38 1.19 4.47 3 5.74V17c0 .55.45 1 1 1h6c.55 0 1-.45 1-1v-2.26c1.81-1.27 3-3.36 3-5.74 0-3.87-3.13-7-7-7z" stroke="currentColor" strokeWidth="2" fill="currentColor" opacity="0.3" />
|
||||
<line x1="9" y1="21" x2="15" y2="21" stroke="currentColor" strokeWidth="2" strokeLinecap="round" />
|
||||
<line x1="12" y1="18" x2="12" y2="21" stroke="currentColor" strokeWidth="2" strokeLinecap="round" />
|
||||
<circle cx="12" cy="9" r="2" fill="currentColor" className="animate-pulse" />
|
||||
<path d="M12 2v3M18 9h3M6 9H3M17 14l2 2M7 14l-2 2" stroke="currentColor" strokeWidth="1" strokeLinecap="round" className="animate-pulse" opacity="0.5" />
|
||||
</svg>
|
||||
);
|
||||
|
||||
// 火箭图标 (🚀) - 赛博朋克风格
|
||||
export const Rocket = (props: IconProps) => (
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" {...props}>
|
||||
<path d="M12 2L8 8l-3 1v6l3 1 4 6 4-6 3-1V9l-3-1z" stroke="currentColor" strokeWidth="2" fill="currentColor" opacity="0.3" />
|
||||
<circle cx="12" cy="11" r="2" stroke="currentColor" strokeWidth="2" fill="currentColor" opacity="0.5" />
|
||||
<path d="M7 17l-2 4M17 17l2 4" stroke="currentColor" strokeWidth="2" strokeLinecap="round" />
|
||||
<circle cx="12" cy="11" r="0.5" fill="currentColor" className="animate-pulse" />
|
||||
<path d="M12 22v-2" stroke="currentColor" strokeWidth="3" strokeLinecap="round" className="animate-pulse" opacity="0.8" />
|
||||
</svg>
|
||||
);
|
||||
|
||||
// 工具图标 (🔧) - 赛博朋克风格
|
||||
export const Tool = (props: IconProps) => (
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" {...props}>
|
||||
<path d="M14.7 6.3a1 1 0 000 1.4l1.6 1.6a1 1 0 001.4 0l3.77-3.77a6 6 0 01-7.94 7.94l-6.91 6.91a2.12 2.12 0 01-3-3l6.91-6.91a6 6 0 017.94-7.94l-3.76 3.76z" stroke="currentColor" strokeWidth="2" fill="currentColor" opacity="0.3" />
|
||||
<circle cx="18" cy="6" r="1" fill="currentColor" className="animate-pulse" />
|
||||
<circle cx="6" cy="18" r="1" fill="currentColor" className="animate-pulse" style={{ animationDelay: '0.5s' }} />
|
||||
<path d="M15 9l-6 6" stroke="currentColor" strokeWidth="1" strokeLinecap="round" className="animate-pulse" opacity="0.5" />
|
||||
</svg>
|
||||
);
|
||||
|
||||
// 书籍图标 (📚) - 赛博朋克风格
|
||||
export const Books = (props: IconProps) => (
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" {...props}>
|
||||
<path d="M4 5h5v14H4zM9 5h5v14H9zM14 5h6v14h-6z" stroke="currentColor" strokeWidth="2" fill="currentColor" opacity="0.3" />
|
||||
<path d="M4 5h5v14H4z" stroke="currentColor" strokeWidth="2" />
|
||||
<path d="M9 5h5v14H9z" stroke="currentColor" strokeWidth="2" />
|
||||
<path d="M14 5h6v14h-6z" stroke="currentColor" strokeWidth="2" />
|
||||
<circle cx="6.5" cy="8" r="0.5" fill="currentColor" className="animate-pulse" />
|
||||
<circle cx="11.5" cy="8" r="0.5" fill="currentColor" className="animate-pulse" style={{ animationDelay: '0.2s' }} />
|
||||
<circle cx="17" cy="8" r="0.5" fill="currentColor" className="animate-pulse" style={{ animationDelay: '0.4s' }} />
|
||||
</svg>
|
||||
);
|
||||
|
||||
// 趋势图标 (📈) - 赛博朋克风格
|
||||
export const TrendingUp = (props: IconProps) => (
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" {...props}>
|
||||
<rect x="3" y="3" width="18" height="18" stroke="currentColor" strokeWidth="2" rx="2" fill="currentColor" opacity="0.2" />
|
||||
<polyline points="7,17 12,12 15,15 20,10" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" />
|
||||
<polyline points="15,10 20,10 20,15" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" />
|
||||
<circle cx="12" cy="12" r="1" fill="currentColor" className="animate-pulse" />
|
||||
<polyline points="7,17 12,12 15,15 20,10" stroke="currentColor" strokeWidth="1" strokeLinecap="round" strokeLinejoin="round" className="animate-pulse" opacity="0.5" />
|
||||
</svg>
|
||||
);
|
||||
|
||||
// 包裹图标 (📦) - 赛博朋克风格
|
||||
export const Package = (props: IconProps) => (
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" {...props}>
|
||||
<path d="M12 2l10 5v10l-10 5-10-5V7z" stroke="currentColor" strokeWidth="2" fill="currentColor" opacity="0.3" />
|
||||
<path d="M12 22V12M2 7l10 5 10-5M7 4.5L17 9.5" stroke="currentColor" strokeWidth="2" strokeLinecap="round" />
|
||||
<circle cx="12" cy="12" r="1" fill="currentColor" className="animate-pulse" />
|
||||
<path d="M12 2v5" stroke="currentColor" strokeWidth="1" strokeLinecap="round" className="animate-pulse" opacity="0.5" />
|
||||
</svg>
|
||||
);
|
||||
|
||||
// 闪光图标 (⚡) - 已存在 Zap,创建别名
|
||||
export const Lightning = Zap;
|
||||
|
||||
// 目标靶心图标 (🎯) - 已存在 Target,创建别名
|
||||
export const Bullseye = Target;
|
||||
|
||||
// 信号图标 (📡) - 赛博朋克风格
|
||||
export const Signal = (props: IconProps) => (
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" {...props}>
|
||||
<circle cx="12" cy="18" r="2" stroke="currentColor" strokeWidth="2" fill="currentColor" opacity="0.5" />
|
||||
<path d="M16.24 13.76a6 6 0 00-8.48 0" stroke="currentColor" strokeWidth="2" strokeLinecap="round" />
|
||||
<path d="M19.07 10.93a10 10 0 00-14.14 0" stroke="currentColor" strokeWidth="2" strokeLinecap="round" />
|
||||
<path d="M22 8a14 14 0 00-20 0" stroke="currentColor" strokeWidth="2" strokeLinecap="round" />
|
||||
<circle cx="12" cy="18" r="0.5" fill="currentColor" className="animate-pulse" />
|
||||
<path d="M16.24 13.76a6 6 0 00-8.48 0" stroke="currentColor" strokeWidth="1" strokeLinecap="round" className="animate-pulse" opacity="0.5" />
|
||||
</svg>
|
||||
);
|
||||
|
||||
// 消息图标 (💬) - 赛博朋克风格
|
||||
export const Message = (props: IconProps) => (
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" {...props}>
|
||||
<path d="M21 11.5a8.38 8.38 0 01-.9 3.8 8.5 8.5 0 01-7.6 4.7 8.38 8.38 0 01-3.8-.9L3 21l1.9-5.7a8.38 8.38 0 01-.9-3.8 8.5 8.5 0 014.7-7.6 8.38 8.38 0 013.8-.9h.5a8.48 8.48 0 018 8v.5z" stroke="currentColor" strokeWidth="2" fill="currentColor" opacity="0.3" />
|
||||
<circle cx="8" cy="11" r="0.5" fill="currentColor" className="animate-pulse" />
|
||||
<circle cx="12" cy="11" r="0.5" fill="currentColor" className="animate-pulse" style={{ animationDelay: '0.2s' }} />
|
||||
<circle cx="16" cy="11" r="0.5" fill="currentColor" className="animate-pulse" style={{ animationDelay: '0.4s' }} />
|
||||
</svg>
|
||||
);
|
||||
|
||||
// 标签图标 (🏷️) - 赛博朋克风格
|
||||
export const Tag = (props: IconProps) => (
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" {...props}>
|
||||
<path d="M20.59 13.41l-7.17 7.17a2 2 0 01-2.83 0L2 12V2h10l8.59 8.59a2 2 0 010 2.82z" stroke="currentColor" strokeWidth="2" fill="currentColor" opacity="0.3" />
|
||||
<circle cx="7" cy="7" r="1" stroke="currentColor" strokeWidth="2" fill="currentColor" opacity="0.6" />
|
||||
<circle cx="7" cy="7" r="0.5" fill="currentColor" className="animate-pulse" />
|
||||
<path d="M2 2l10 10" stroke="currentColor" strokeWidth="1" strokeLinecap="round" className="animate-pulse" opacity="0.5" />
|
||||
</svg>
|
||||
);
|
||||
|
||||
// 握手图标 (🤝) - 赛博朋克风格
|
||||
export const Handshake = (props: IconProps) => (
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" {...props}>
|
||||
<path d="M11 6l-3-3-6 6v8h7l3-3m10-8l-6-6-3 3m14 14v-8l-6-6m-5 5l5 5" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" fill="none" />
|
||||
<path d="M8 12h8" stroke="currentColor" strokeWidth="2" strokeLinecap="round" fill="none" />
|
||||
<circle cx="12" cy="12" r="2" fill="currentColor" opacity="0.5" />
|
||||
<circle cx="12" cy="12" r="0.5" fill="currentColor" className="animate-pulse" />
|
||||
<path d="M7 12h10" stroke="currentColor" strokeWidth="1" strokeLinecap="round" className="animate-pulse" opacity="0.5" />
|
||||
</svg>
|
||||
);
|
||||
|
||||
// 拼图图标 (🧩) - 赛博朋克风格
|
||||
export const Puzzle = (props: IconProps) => (
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" {...props}>
|
||||
<path d="M4 4h4c0-2 1-3 2-3s2 1 2 3h4v4c2 0 3 1 3 2s-1 2-3 2v4h-4c0 2-1 3-2 3s-2-1-2-3H4v-4c-2 0-3-1-3-2s1-2 3-2V4z" stroke="currentColor" strokeWidth="2" fill="currentColor" opacity="0.3" />
|
||||
<circle cx="12" cy="12" r="1" fill="currentColor" className="animate-pulse" />
|
||||
<path d="M8 12h8M12 8v8" stroke="currentColor" strokeWidth="1" strokeLinecap="round" className="animate-pulse" opacity="0.5" />
|
||||
</svg>
|
||||
);
|
||||
|
||||
// 刷新循环图标 (🔄) - 已有 RefreshCw,创建别名
|
||||
export const Refresh = RefreshCw;
|
||||
|
||||
// 图表图标 (📊) - 已有 BarChart,创建别名
|
||||
export const Chart = BarChart;
|
||||
|
||||
// 机器人图标 (🤖) - 赛博朋克风格
|
||||
export const Robot = (props: IconProps) => (
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" {...props}>
|
||||
<rect x="4" y="4" width="16" height="16" rx="4" stroke="currentColor" strokeWidth="2" fill="currentColor" opacity="0.3" />
|
||||
<rect x="6" y="2" width="12" height="2" rx="1" stroke="currentColor" strokeWidth="2" fill="currentColor" opacity="0.5" />
|
||||
<circle cx="9" cy="9" r="1" fill="currentColor" className="animate-pulse" />
|
||||
<circle cx="15" cy="9" r="1" fill="currentColor" className="animate-pulse" style={{ animationDelay: '0.2s' }} />
|
||||
<path d="M8 14h8" stroke="currentColor" strokeWidth="2" strokeLinecap="round" />
|
||||
<rect x="4" y="20" width="4" height="2" rx="1" stroke="currentColor" strokeWidth="1" fill="currentColor" opacity="0.5" />
|
||||
<rect x="16" y="20" width="4" height="2" rx="1" stroke="currentColor" strokeWidth="1" fill="currentColor" opacity="0.5" />
|
||||
<circle cx="12" cy="12" r="0.5" fill="currentColor" className="animate-pulse" style={{ animationDelay: '0.4s' }} />
|
||||
</svg>
|
||||
);
|
||||
|
||||
// 大脑图标 (🧠) - 赛博朋克风格
|
||||
export const Brain = (props: IconProps) => (
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" {...props}>
|
||||
<path d="M12 2C8 2 5 5 5 9c0 1.5.5 3 1.5 4C5.5 14 5 15.5 5 17c0 2.5 2 4.5 4.5 4.5h5c2.5 0 4.5-2 4.5-4.5 0-1.5-.5-3-1.5-4 1-.5 1.5-2.5 1.5-4 0-4-3-7-7-7z" stroke="currentColor" strokeWidth="2" fill="currentColor" opacity="0.3" />
|
||||
<path d="M8 10c0-1 .5-2 1.5-2S11 9 11 10M13 10c0-1 .5-2 1.5-2S16 9 16 10" stroke="currentColor" strokeWidth="2" strokeLinecap="round" />
|
||||
<circle cx="12" cy="12" r="1" fill="currentColor" className="animate-pulse" />
|
||||
<path d="M9 14c1 1 2 1 3 1s2 0 3-1" stroke="currentColor" strokeWidth="1" strokeLinecap="round" className="animate-pulse" opacity="0.6" />
|
||||
</svg>
|
||||
);
|
||||
|
||||
// 天平图标 (⚖️) - 赛博朋克风格
|
||||
export const Scale = (props: IconProps) => (
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" {...props}>
|
||||
<path d="M12 2v20" stroke="currentColor" strokeWidth="2" strokeLinecap="round" />
|
||||
<path d="M6 6l6 0 6 0" stroke="currentColor" strokeWidth="2" strokeLinecap="round" />
|
||||
<circle cx="6" cy="6" r="4" stroke="currentColor" strokeWidth="2" fill="currentColor" opacity="0.3" />
|
||||
<circle cx="18" cy="6" r="4" stroke="currentColor" strokeWidth="2" fill="currentColor" opacity="0.3" />
|
||||
<rect x="8" y="20" width="8" height="2" rx="1" stroke="currentColor" strokeWidth="2" fill="currentColor" opacity="0.5" />
|
||||
<circle cx="6" cy="6" r="0.5" fill="currentColor" className="animate-pulse" />
|
||||
<circle cx="18" cy="6" r="0.5" fill="currentColor" className="animate-pulse" style={{ animationDelay: '0.5s' }} />
|
||||
</svg>
|
||||
);
|
||||
|
||||
// 天平图标别名
|
||||
export const Balance = Scale;
|
||||
|
||||
// 添加更多赛博朋克风格图标
|
||||
export const Warning = (props: IconProps) => (
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" {...props}>
|
||||
<path d="M12 2L2 20h20L12 2z" stroke="currentColor" strokeWidth="2" fill="currentColor" opacity="0.3" />
|
||||
<path d="M12 9v4m0 4h.01" stroke="currentColor" strokeWidth="2" strokeLinecap="round" className="animate-pulse" />
|
||||
</svg>
|
||||
);
|
||||
|
||||
export const Cog = (props: IconProps) => (
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" {...props}>
|
||||
<circle cx="12" cy="12" r="3" stroke="currentColor" strokeWidth="2" fill="currentColor" opacity="0.3" />
|
||||
<path d="M12 1v6m0 6v6m6.36-15.36l-4.24 4.24m-4.24 4.24l-4.24 4.24M23 12h-6m-6 0H1m16.36 6.36l-4.24-4.24m-4.24-4.24L4.64 5.64" stroke="currentColor" strokeWidth="2" strokeLinecap="round" className="animate-spin-slow" />
|
||||
</svg>
|
||||
);
|
||||
|
||||
export const ClipboardCheck = (props: IconProps) => (
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" {...props}>
|
||||
<rect x="3" y="4" width="18" height="18" rx="2" stroke="currentColor" strokeWidth="2" fill="currentColor" opacity="0.3" />
|
||||
<path d="M9 12l2 2 4-4" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" className="animate-pulse" />
|
||||
<rect x="8" y="2" width="8" height="4" rx="1" stroke="currentColor" strokeWidth="2" />
|
||||
</svg>
|
||||
);
|
||||
|
||||
export const Globe = (props: IconProps) => (
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" {...props}>
|
||||
<circle cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="2" fill="currentColor" opacity="0.3" />
|
||||
<path d="M2 12h20M12 2a15.3 15.3 0 014 10 15.3 15.3 0 01-4 10 15.3 15.3 0 01-4-10 15.3 15.3 0 014-10z" stroke="currentColor" strokeWidth="2" className="animate-pulse" />
|
||||
</svg>
|
||||
);
|
||||
|
||||
export const Timer = (props: IconProps) => (
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" {...props}>
|
||||
<circle cx="12" cy="13" r="9" stroke="currentColor" strokeWidth="2" fill="currentColor" opacity="0.3" />
|
||||
<path d="M12 7v6l4 2" stroke="currentColor" strokeWidth="2" strokeLinecap="round" className="animate-pulse" />
|
||||
<path d="M9 2h6m-3 0v3" stroke="currentColor" strokeWidth="2" strokeLinecap="round" />
|
||||
</svg>
|
||||
);
|
||||
|
||||
export const Map = (props: IconProps) => (
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" {...props}>
|
||||
<path d="M1 6v16l7-4 8 4 7-4V2l-7 4-8-4-7 4z" stroke="currentColor" strokeWidth="2" fill="currentColor" opacity="0.3" />
|
||||
<path d="M8 2v16m8-12v16" stroke="currentColor" strokeWidth="2" className="animate-pulse" />
|
||||
</svg>
|
||||
);
|
||||
|
||||
export const Building = (props: IconProps) => (
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" {...props}>
|
||||
<rect x="4" y="2" width="16" height="20" stroke="currentColor" strokeWidth="2" fill="currentColor" opacity="0.3" />
|
||||
<rect x="8" y="6" width="3" height="3" stroke="currentColor" strokeWidth="1" className="animate-pulse" />
|
||||
<rect x="13" y="6" width="3" height="3" stroke="currentColor" strokeWidth="1" className="animate-pulse" />
|
||||
<rect x="8" y="12" width="3" height="3" stroke="currentColor" strokeWidth="1" className="animate-pulse" />
|
||||
<rect x="13" y="12" width="3" height="3" stroke="currentColor" strokeWidth="1" className="animate-pulse" />
|
||||
<rect x="10" y="18" width="4" height="4" stroke="currentColor" strokeWidth="2" />
|
||||
</svg>
|
||||
);
|
||||
|
||||
export const MessageSquare = (props: IconProps) => (
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" {...props}>
|
||||
<path d="M21 15a2 2 0 01-2 2H7l-4 4V5a2 2 0 012-2h14a2 2 0 012 2v10z" stroke="currentColor" strokeWidth="2" fill="currentColor" opacity="0.3" />
|
||||
<circle cx="9" cy="10" r="1" fill="currentColor" className="animate-pulse" />
|
||||
<circle cx="12" cy="10" r="1" fill="currentColor" className="animate-pulse" style={{ animationDelay: '0.2s' }} />
|
||||
<circle cx="15" cy="10" r="1" fill="currentColor" className="animate-pulse" style={{ animationDelay: '0.4s' }} />
|
||||
</svg>
|
||||
);
|
||||
102
src/components/LiquidGlass.tsx
Normal file
102
src/components/LiquidGlass.tsx
Normal file
@@ -0,0 +1,102 @@
|
||||
import React, { ReactNode, useRef, useEffect, useState } from 'react';
|
||||
|
||||
interface LiquidGlassProps {
|
||||
children: ReactNode;
|
||||
className?: string;
|
||||
style?: React.CSSProperties;
|
||||
onClick?: () => void;
|
||||
displacementScale?: number;
|
||||
blurAmount?: number;
|
||||
saturation?: number;
|
||||
elasticity?: number;
|
||||
cornerRadius?: number;
|
||||
mouseContainer?: React.RefObject<HTMLElement | null> | null;
|
||||
}
|
||||
|
||||
const LiquidGlass: React.FC<LiquidGlassProps> = ({
|
||||
children,
|
||||
className = '',
|
||||
style = {},
|
||||
onClick,
|
||||
displacementScale = 50,
|
||||
blurAmount = 0.08,
|
||||
saturation = 130,
|
||||
elasticity = 0.15,
|
||||
cornerRadius = 16,
|
||||
mouseContainer
|
||||
}) => {
|
||||
const glassRef = useRef<HTMLDivElement>(null);
|
||||
const [mousePosition, setMousePosition] = useState({ x: 0.5, y: 0.5 });
|
||||
const [isHovered, setIsHovered] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
const handleMouseMove = (e: Event) => {
|
||||
const mouseEvent = e as MouseEvent;
|
||||
const container = mouseContainer?.current || glassRef.current;
|
||||
if (!container) return;
|
||||
|
||||
const rect = container.getBoundingClientRect();
|
||||
const x = (mouseEvent.clientX - rect.left) / rect.width;
|
||||
const y = (mouseEvent.clientY - rect.top) / rect.height;
|
||||
|
||||
setMousePosition({ x: Math.max(0, Math.min(1, x)), y: Math.max(0, Math.min(1, y)) });
|
||||
};
|
||||
|
||||
const container = mouseContainer?.current || document;
|
||||
container.addEventListener('mousemove', handleMouseMove);
|
||||
|
||||
return () => {
|
||||
container.removeEventListener('mousemove', handleMouseMove);
|
||||
};
|
||||
}, [mouseContainer]);
|
||||
|
||||
const glassStyle: React.CSSProperties = {
|
||||
position: 'relative',
|
||||
overflow: 'hidden',
|
||||
backdropFilter: `blur(${blurAmount * 100}px) saturate(${saturation}%)`,
|
||||
background: 'rgba(255, 255, 255, 0.1)',
|
||||
border: '1px solid rgba(255, 255, 255, 0.2)',
|
||||
borderRadius: `${cornerRadius}px`,
|
||||
boxShadow: `
|
||||
0 8px 32px rgba(0, 0, 0, 0.1),
|
||||
inset 0 1px 0 rgba(255, 255, 255, 0.2)
|
||||
`,
|
||||
transition: `all ${elasticity}s cubic-bezier(0.4, 0, 0.2, 1)`,
|
||||
transform: isHovered
|
||||
? `perspective(1000px) rotateX(${(mousePosition.y - 0.5) * 10}deg) rotateY(${(mousePosition.x - 0.5) * 10}deg) scale(1.02)`
|
||||
: 'perspective(1000px) rotateX(0deg) rotateY(0deg) scale(1)',
|
||||
cursor: onClick ? 'pointer' : 'default',
|
||||
...style
|
||||
};
|
||||
|
||||
const overlayStyle: React.CSSProperties = {
|
||||
position: 'absolute',
|
||||
top: 0,
|
||||
left: 0,
|
||||
right: 0,
|
||||
bottom: 0,
|
||||
background: `radial-gradient(circle at ${mousePosition.x * 100}% ${mousePosition.y * 100}%, rgba(255, 255, 255, 0.1) 0%, transparent 50%)`,
|
||||
borderRadius: `${cornerRadius}px`,
|
||||
opacity: isHovered ? 1 : 0,
|
||||
transition: `opacity ${elasticity}s ease-out`,
|
||||
pointerEvents: 'none'
|
||||
};
|
||||
|
||||
return (
|
||||
<div
|
||||
ref={glassRef}
|
||||
className={className}
|
||||
style={glassStyle}
|
||||
onClick={onClick}
|
||||
onMouseEnter={() => setIsHovered(true)}
|
||||
onMouseLeave={() => setIsHovered(false)}
|
||||
>
|
||||
<div style={overlayStyle} />
|
||||
<div style={{ position: 'relative', zIndex: 1 }}>
|
||||
{children}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default LiquidGlass;
|
||||
138
src/components/Navigation.tsx
Normal file
138
src/components/Navigation.tsx
Normal file
@@ -0,0 +1,138 @@
|
||||
import React, { useState } from 'react';
|
||||
import { Link, useLocation } from 'react-router-dom';
|
||||
import { Cpu, PlayCircle, X } from './Icons';
|
||||
|
||||
const Navigation: React.FC = () => {
|
||||
const location = useLocation();
|
||||
const [isTrialModalOpen, setIsTrialModalOpen] = useState(false);
|
||||
|
||||
const navLinks = [
|
||||
{ path: '/', label: '课程首页' },
|
||||
{ path: '/course', label: '课程' }
|
||||
];
|
||||
|
||||
return (
|
||||
<>
|
||||
<nav className="fixed top-0 left-0 right-0 z-50 bg-cyber-dark-900/95 backdrop-blur-xl border-b border-cyber-pink-500/30">
|
||||
{/* Trial Button - Absolutely Positioned at Left */}
|
||||
<button
|
||||
onClick={() => setIsTrialModalOpen(true)}
|
||||
className="absolute left-4 top-1/2 -translate-y-1/2 flex items-center gap-2 px-4 py-2 bg-gradient-to-r from-purple-500 to-pink-500 hover:from-purple-600 hover:to-pink-600 text-white rounded-xl font-medium text-sm transition-all duration-300 hover:scale-105 shadow-lg hover:shadow-purple-500/30 z-10"
|
||||
>
|
||||
<PlayCircle className="w-4 h-4" />
|
||||
<span>直播回放</span>
|
||||
</button>
|
||||
|
||||
<div className="max-w-7xl mx-auto px-6">
|
||||
<div className="flex items-center justify-between h-16">
|
||||
{/* Logo */}
|
||||
<div className="flex items-center space-x-4 ml-32">
|
||||
<Link to="/" className="flex items-center space-x-3">
|
||||
<div className="w-10 h-10 bg-gradient-to-r from-cyber-pink-500 to-neon-purple-500 rounded-lg flex items-center justify-center shadow-neon-pink neon-glow-pink">
|
||||
<Cpu className="w-6 h-6 text-white" />
|
||||
</div>
|
||||
<span className="gradient-text font-bold text-lg uppercase tracking-wider">多Agent协作系统</span>
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
{/* Desktop Navigation */}
|
||||
<div className="hidden md:flex items-center space-x-8">
|
||||
{navLinks.map((link) => (
|
||||
<Link
|
||||
key={link.path}
|
||||
to={link.path}
|
||||
className={`relative px-4 py-2 font-semibold transition-all duration-300 ${
|
||||
location.pathname === link.path
|
||||
? 'text-neon-cyan-400 neon-text-cyan'
|
||||
: 'text-cyber-dark-200 hover:text-neon-cyan-400'
|
||||
}`}
|
||||
>
|
||||
{link.label}
|
||||
{location.pathname === link.path && (
|
||||
<span className="absolute bottom-0 left-0 right-0 h-0.5 bg-gradient-to-r from-neon-cyan-500 to-cyber-pink-500 shadow-neon-cyan"></span>
|
||||
)}
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* Mobile Navigation */}
|
||||
<div className="md:hidden flex items-center space-x-4">
|
||||
{navLinks.map((link) => (
|
||||
<Link
|
||||
key={link.path}
|
||||
to={link.path}
|
||||
className={`px-3 py-2 text-sm font-semibold transition-all duration-300 ${
|
||||
location.pathname === link.path
|
||||
? 'text-neon-cyan-400 neon-text-cyan'
|
||||
: 'text-cyber-dark-200 hover:text-neon-cyan-400'
|
||||
}`}
|
||||
>
|
||||
{link.label}
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
{/* Trial Video Modal */}
|
||||
{isTrialModalOpen && (
|
||||
<div className="fixed inset-0 z-[2000] flex items-center justify-center p-4">
|
||||
{/* Backdrop */}
|
||||
<div
|
||||
className="absolute inset-0 bg-black/80 backdrop-blur-sm"
|
||||
onClick={() => setIsTrialModalOpen(false)}
|
||||
/>
|
||||
|
||||
{/* Modal Content */}
|
||||
<div className="relative w-full max-w-5xl bg-gradient-to-br from-gray-900 to-black rounded-2xl shadow-2xl border border-purple-500/30 overflow-hidden">
|
||||
{/* Header */}
|
||||
<div className="flex items-center justify-between p-6 border-b border-purple-500/20">
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="w-10 h-10 bg-gradient-to-r from-purple-500 to-pink-500 rounded-lg flex items-center justify-center">
|
||||
<PlayCircle className="w-6 h-6 text-white" />
|
||||
</div>
|
||||
<h2 className="text-2xl font-bold text-white">直播回放</h2>
|
||||
</div>
|
||||
<button
|
||||
onClick={() => setIsTrialModalOpen(false)}
|
||||
className="w-10 h-10 rounded-lg bg-white/10 hover:bg-white/20 flex items-center justify-center transition-colors"
|
||||
>
|
||||
<X className="w-5 h-5 text-white" />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* Video Container */}
|
||||
<div className="relative bg-black" style={{ paddingBottom: '56.25%' }}>
|
||||
<video
|
||||
className="absolute inset-0 w-full h-full"
|
||||
controls
|
||||
autoPlay
|
||||
src="https://ddcz-1315997005.cos.ap-nanjing.myqcloud.com/static/video/web_teach/AIclass_playback.mov"
|
||||
>
|
||||
您的浏览器不支持视频播放
|
||||
</video>
|
||||
</div>
|
||||
|
||||
{/* Footer */}
|
||||
<div className="p-6 bg-gradient-to-r from-purple-900/20 to-pink-900/20 border-t border-purple-500/20">
|
||||
<div className="flex items-center justify-between">
|
||||
<p className="text-white/80">
|
||||
观看多Agent协作系统课程的完整直播回放,深入了解AI协作技术
|
||||
</p>
|
||||
<button
|
||||
onClick={() => setIsTrialModalOpen(false)}
|
||||
className="px-6 py-2 bg-gradient-to-r from-purple-500 to-pink-500 hover:from-purple-600 hover:to-pink-600 text-white rounded-lg font-medium transition-all hover:scale-105"
|
||||
>
|
||||
关闭
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default Navigation;
|
||||
27
src/components/PageVignette.tsx
Normal file
27
src/components/PageVignette.tsx
Normal file
@@ -0,0 +1,27 @@
|
||||
import React from 'react';
|
||||
|
||||
interface PageVignetteProps {
|
||||
intensity?: 'light' | 'medium' | 'strong';
|
||||
}
|
||||
|
||||
const PageVignette: React.FC<PageVignetteProps> = ({ intensity = 'medium' }) => {
|
||||
const opacityMap = {
|
||||
light: 0.1,
|
||||
medium: 0.15,
|
||||
strong: 0.25
|
||||
};
|
||||
|
||||
return (
|
||||
<div
|
||||
className="fixed inset-0 pointer-events-none z-40"
|
||||
style={{
|
||||
background: `
|
||||
radial-gradient(120% 90% at 50% 40%, transparent 35%, rgba(0,0,0,${opacityMap[intensity]}) 100%),
|
||||
radial-gradient(80% 60% at 50% 50%, transparent 60%, rgba(0,0,0,${opacityMap[intensity] * 0.5}) 100%)
|
||||
`
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default PageVignette;
|
||||
19
src/components/ScrollToTop.tsx
Normal file
19
src/components/ScrollToTop.tsx
Normal file
@@ -0,0 +1,19 @@
|
||||
import React, { useEffect } from 'react';
|
||||
import { useLocation } from 'react-router-dom';
|
||||
|
||||
const ScrollToTop: React.FC = () => {
|
||||
const { pathname } = useLocation();
|
||||
|
||||
useEffect(() => {
|
||||
// 页面切换时滚动到顶部
|
||||
window.scrollTo({
|
||||
top: 0,
|
||||
left: 0,
|
||||
behavior: 'smooth' // 平滑滚动效果
|
||||
});
|
||||
}, [pathname]);
|
||||
|
||||
return null; // 这个组件不渲染任何内容
|
||||
};
|
||||
|
||||
export default ScrollToTop;
|
||||
55
src/components/TechVignette.tsx
Normal file
55
src/components/TechVignette.tsx
Normal file
@@ -0,0 +1,55 @@
|
||||
import React from 'react';
|
||||
import { motion } from 'framer-motion';
|
||||
|
||||
interface TechVignetteProps {
|
||||
intensity?: 'light' | 'medium' | 'strong';
|
||||
variant?: 'default' | 'focused' | 'minimal';
|
||||
}
|
||||
|
||||
const TechVignette: React.FC<TechVignetteProps> = ({
|
||||
intensity = 'medium',
|
||||
variant = 'default'
|
||||
}) => {
|
||||
const intensityMap = {
|
||||
light: { opacity: 0.05, blur: 0.5 },
|
||||
medium: { opacity: 0.1, blur: 1 },
|
||||
strong: { opacity: 0.2, blur: 1.5 }
|
||||
};
|
||||
|
||||
const config = intensityMap[intensity];
|
||||
|
||||
const getBackground = () => {
|
||||
switch (variant) {
|
||||
case 'focused':
|
||||
return `
|
||||
radial-gradient(ellipse 80% 60% at 50% 40%, transparent 30%, rgba(15, 23, 42, ${config.opacity * 1.5}) 100%),
|
||||
radial-gradient(ellipse 60% 80% at 50% 50%, transparent 50%, rgba(8, 145, 178, ${config.opacity * 0.5}) 100%)
|
||||
`;
|
||||
case 'minimal':
|
||||
return `
|
||||
linear-gradient(180deg, rgba(15, 23, 42, ${config.opacity * 0.8}) 0%, transparent 30%, transparent 70%, rgba(15, 23, 42, ${config.opacity * 0.6}) 100%)
|
||||
`;
|
||||
default:
|
||||
return `
|
||||
radial-gradient(120% 90% at 50% 40%, transparent 35%, rgba(15, 23, 42, ${config.opacity}) 100%),
|
||||
radial-gradient(80% 60% at 50% 50%, transparent 60%, rgba(8, 145, 178, ${config.opacity * 0.3}) 100%),
|
||||
linear-gradient(180deg, rgba(15, 23, 42, ${config.opacity * 0.5}) 0%, transparent 20%, transparent 80%, rgba(15, 23, 42, ${config.opacity * 0.3}) 100%)
|
||||
`;
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<motion.div
|
||||
className="fixed inset-0 pointer-events-none z-20"
|
||||
style={{
|
||||
background: getBackground(),
|
||||
filter: `blur(${config.blur}px)`
|
||||
}}
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 1 }}
|
||||
transition={{ duration: 1, ease: "easeOut" }}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default TechVignette;
|
||||
142
src/hooks/usePerformanceMonitor.ts
Normal file
142
src/hooks/usePerformanceMonitor.ts
Normal file
@@ -0,0 +1,142 @@
|
||||
import { useEffect, useState, useCallback } from 'react';
|
||||
|
||||
interface PerformanceMetrics {
|
||||
fps: number;
|
||||
memoryUsage: number | null;
|
||||
renderTime: number;
|
||||
isLowPerformance: boolean;
|
||||
}
|
||||
|
||||
export const usePerformanceMonitor = () => {
|
||||
const [metrics, setMetrics] = useState<PerformanceMetrics>({
|
||||
fps: 60,
|
||||
memoryUsage: null,
|
||||
renderTime: 0,
|
||||
isLowPerformance: false
|
||||
});
|
||||
|
||||
const [shouldReduceEffects, setShouldReduceEffects] = useState(false);
|
||||
|
||||
// FPS 监控
|
||||
const measureFPS = useCallback(() => {
|
||||
let lastTime = performance.now();
|
||||
let frames = 0;
|
||||
let fps = 60;
|
||||
|
||||
const frame = () => {
|
||||
const currentTime = performance.now();
|
||||
frames++;
|
||||
|
||||
if (currentTime >= lastTime + 1000) {
|
||||
fps = Math.round((frames * 1000) / (currentTime - lastTime));
|
||||
frames = 0;
|
||||
lastTime = currentTime;
|
||||
|
||||
// 如果FPS低于30,启用性能模式
|
||||
if (fps < 30) {
|
||||
setShouldReduceEffects(true);
|
||||
}
|
||||
|
||||
setMetrics(prev => ({
|
||||
...prev,
|
||||
fps,
|
||||
isLowPerformance: fps < 30
|
||||
}));
|
||||
}
|
||||
|
||||
requestAnimationFrame(frame);
|
||||
};
|
||||
|
||||
requestAnimationFrame(frame);
|
||||
}, []);
|
||||
|
||||
// 内存使用监控
|
||||
const measureMemory = useCallback(() => {
|
||||
if ('memory' in performance) {
|
||||
const memory = (performance as any).memory;
|
||||
const usedMemory = memory.usedJSHeapSize / 1048576; // 转换为MB
|
||||
|
||||
setMetrics(prev => ({
|
||||
...prev,
|
||||
memoryUsage: Math.round(usedMemory)
|
||||
}));
|
||||
}
|
||||
}, []);
|
||||
|
||||
// 渲染时间监控
|
||||
const measureRenderTime = useCallback(() => {
|
||||
const observer = new PerformanceObserver((list) => {
|
||||
const entries = list.getEntries();
|
||||
entries.forEach((entry) => {
|
||||
if (entry.entryType === 'measure' && entry.name.includes('render')) {
|
||||
setMetrics(prev => ({
|
||||
...prev,
|
||||
renderTime: Math.round(entry.duration)
|
||||
}));
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
observer.observe({ entryTypes: ['measure'] });
|
||||
|
||||
return () => observer.disconnect();
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
// 启动性能监控
|
||||
measureFPS();
|
||||
const memoryInterval = setInterval(measureMemory, 5000);
|
||||
const cleanupRender = measureRenderTime();
|
||||
|
||||
// 检测用户偏好
|
||||
const mediaQuery = window.matchMedia('(prefers-reduced-motion: reduce)');
|
||||
if (mediaQuery.matches) {
|
||||
setShouldReduceEffects(true);
|
||||
}
|
||||
|
||||
// 检测设备性能
|
||||
if (navigator.hardwareConcurrency && navigator.hardwareConcurrency < 4) {
|
||||
setShouldReduceEffects(true);
|
||||
}
|
||||
|
||||
// 检测电池状态
|
||||
if ('getBattery' in navigator) {
|
||||
(navigator as any).getBattery().then((battery: any) => {
|
||||
if (battery.level < 0.2 || !battery.charging) {
|
||||
setShouldReduceEffects(true);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return () => {
|
||||
clearInterval(memoryInterval);
|
||||
cleanupRender();
|
||||
};
|
||||
}, [measureFPS, measureMemory, measureRenderTime]);
|
||||
|
||||
return {
|
||||
metrics,
|
||||
shouldReduceEffects,
|
||||
enablePerformanceMode: () => setShouldReduceEffects(true),
|
||||
disablePerformanceMode: () => setShouldReduceEffects(false)
|
||||
};
|
||||
};
|
||||
|
||||
// 性能优化建议
|
||||
export const getPerformanceRecommendations = (metrics: PerformanceMetrics): string[] => {
|
||||
const recommendations: string[] = [];
|
||||
|
||||
if (metrics.fps < 30) {
|
||||
recommendations.push('FPS较低,建议减少动画效果');
|
||||
}
|
||||
|
||||
if (metrics.memoryUsage && metrics.memoryUsage > 500) {
|
||||
recommendations.push('内存使用较高,建议清理缓存');
|
||||
}
|
||||
|
||||
if (metrics.renderTime > 16) {
|
||||
recommendations.push('渲染时间过长,建议简化组件');
|
||||
}
|
||||
|
||||
return recommendations;
|
||||
};
|
||||
179
src/hooks/useSectionScroll.ts
Normal file
179
src/hooks/useSectionScroll.ts
Normal file
@@ -0,0 +1,179 @@
|
||||
import { useEffect, useRef, useState } from 'react';
|
||||
|
||||
interface UseSectionScrollOptions {
|
||||
duration?: number; // 滚动动画持续时间(毫秒)
|
||||
easing?: string; // 缓动函数
|
||||
}
|
||||
|
||||
export const useSectionScroll = (options: UseSectionScrollOptions = {}) => {
|
||||
const {
|
||||
duration = 1800, // 默认1.8秒
|
||||
easing = 'cubic-bezier(0.4, 0, 0.2, 1)' // Apple风格缓动
|
||||
} = options;
|
||||
|
||||
const [currentSection, setCurrentSection] = useState(0);
|
||||
const [isScrolling, setIsScrolling] = useState(false);
|
||||
const sectionsRef = useRef<HTMLElement[]>([]);
|
||||
const touchStartY = useRef(0);
|
||||
|
||||
// 平滑滚动到指定section
|
||||
const scrollToSection = (index: number) => {
|
||||
if (index < 0 || index >= sectionsRef.current.length || isScrolling) {
|
||||
return;
|
||||
}
|
||||
|
||||
setIsScrolling(true);
|
||||
const targetSection = sectionsRef.current[index];
|
||||
|
||||
if (targetSection) {
|
||||
// 使用scrollIntoView实现平滑滚动
|
||||
targetSection.scrollIntoView({
|
||||
behavior: 'smooth',
|
||||
block: 'start'
|
||||
});
|
||||
|
||||
setCurrentSection(index);
|
||||
|
||||
// 滚动完成后重置状态
|
||||
setTimeout(() => {
|
||||
setIsScrolling(false);
|
||||
}, duration);
|
||||
}
|
||||
};
|
||||
|
||||
// 处理滚轮事件
|
||||
const handleWheel = (e: WheelEvent) => {
|
||||
if (isScrolling) {
|
||||
e.preventDefault();
|
||||
return;
|
||||
}
|
||||
|
||||
const currentSectionElement = sectionsRef.current[currentSection];
|
||||
if (!currentSectionElement) return;
|
||||
|
||||
// 检查滚动事件是否发生在当前section内
|
||||
const target = e.target as HTMLElement;
|
||||
if (!currentSectionElement.contains(target)) return;
|
||||
|
||||
const { scrollTop, scrollHeight, clientHeight } = currentSectionElement;
|
||||
const isAtTop = scrollTop === 0;
|
||||
const isAtBottom = Math.abs(scrollHeight - clientHeight - scrollTop) < 1;
|
||||
|
||||
// 向下滚动
|
||||
if (e.deltaY > 0) {
|
||||
// 只有在section底部时才阻止默认行为并切换
|
||||
if (isAtBottom && currentSection < sectionsRef.current.length - 1) {
|
||||
e.preventDefault();
|
||||
scrollToSection(currentSection + 1);
|
||||
}
|
||||
// 否则允许section内部正常滚动
|
||||
}
|
||||
// 向上滚动
|
||||
else if (e.deltaY < 0) {
|
||||
// 只有在section顶部时才阻止默认行为并切换
|
||||
if (isAtTop && currentSection > 0) {
|
||||
e.preventDefault();
|
||||
scrollToSection(currentSection - 1);
|
||||
}
|
||||
// 否则允许section内部正常滚动
|
||||
}
|
||||
};
|
||||
|
||||
// 处理触摸事件(移动端)
|
||||
const handleTouchStart = (e: TouchEvent) => {
|
||||
touchStartY.current = e.touches[0].clientY;
|
||||
};
|
||||
|
||||
const handleTouchMove = (e: TouchEvent) => {
|
||||
if (isScrolling) {
|
||||
e.preventDefault();
|
||||
return;
|
||||
}
|
||||
|
||||
const currentSectionElement = sectionsRef.current[currentSection];
|
||||
if (!currentSectionElement) return;
|
||||
|
||||
const touchEndY = e.touches[0].clientY;
|
||||
const deltaY = touchStartY.current - touchEndY;
|
||||
|
||||
const { scrollTop, scrollHeight, clientHeight } = currentSectionElement;
|
||||
const isAtTop = scrollTop === 0;
|
||||
const isAtBottom = Math.abs(scrollHeight - clientHeight - scrollTop) < 1;
|
||||
|
||||
// 向上滑动(手指向上移动)
|
||||
if (deltaY > 50) {
|
||||
if (isAtBottom && currentSection < sectionsRef.current.length - 1) {
|
||||
e.preventDefault();
|
||||
scrollToSection(currentSection + 1);
|
||||
touchStartY.current = touchEndY;
|
||||
}
|
||||
}
|
||||
// 向下滑动(手指向下移动)
|
||||
else if (deltaY < -50) {
|
||||
if (isAtTop && currentSection > 0) {
|
||||
e.preventDefault();
|
||||
scrollToSection(currentSection - 1);
|
||||
touchStartY.current = touchEndY;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 处理键盘事件
|
||||
const handleKeyDown = (e: KeyboardEvent) => {
|
||||
if (isScrolling) return;
|
||||
|
||||
switch (e.key) {
|
||||
case 'ArrowDown':
|
||||
case 'PageDown':
|
||||
if (currentSection < sectionsRef.current.length - 1) {
|
||||
e.preventDefault();
|
||||
scrollToSection(currentSection + 1);
|
||||
}
|
||||
break;
|
||||
case 'ArrowUp':
|
||||
case 'PageUp':
|
||||
if (currentSection > 0) {
|
||||
e.preventDefault();
|
||||
scrollToSection(currentSection - 1);
|
||||
}
|
||||
break;
|
||||
case 'Home':
|
||||
e.preventDefault();
|
||||
scrollToSection(0);
|
||||
break;
|
||||
case 'End':
|
||||
e.preventDefault();
|
||||
scrollToSection(sectionsRef.current.length - 1);
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
// 绑定事件监听
|
||||
window.addEventListener('wheel', handleWheel, { passive: false });
|
||||
window.addEventListener('touchstart', handleTouchStart, { passive: false });
|
||||
window.addEventListener('touchmove', handleTouchMove, { passive: false });
|
||||
window.addEventListener('keydown', handleKeyDown);
|
||||
|
||||
return () => {
|
||||
window.removeEventListener('wheel', handleWheel);
|
||||
window.removeEventListener('touchstart', handleTouchStart);
|
||||
window.removeEventListener('touchmove', handleTouchMove);
|
||||
window.removeEventListener('keydown', handleKeyDown);
|
||||
};
|
||||
}, [currentSection, isScrolling]);
|
||||
|
||||
// 注册section元素
|
||||
const registerSection = (element: HTMLElement | null, index: number) => {
|
||||
if (element) {
|
||||
sectionsRef.current[index] = element;
|
||||
}
|
||||
};
|
||||
|
||||
return {
|
||||
currentSection,
|
||||
scrollToSection,
|
||||
registerSection,
|
||||
isScrolling
|
||||
};
|
||||
};
|
||||
624
src/index.backup_20251101_102520.css
Normal file
624
src/index.backup_20251101_102520.css
Normal file
@@ -0,0 +1,624 @@
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
@layer base {
|
||||
@import url('https://fonts.googleapis.com/css2?family=Orbitron:wght@400;500;600;700;800;900&family=Rajdhani:wght@300;400;500;600;700&display=swap');
|
||||
|
||||
body {
|
||||
@apply bg-cyber-dark-900 text-cyber-dark-100;
|
||||
font-family: 'Orbitron', 'Rajdhani', -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', sans-serif;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
/* 全局隐藏滚动条 */
|
||||
html {
|
||||
scrollbar-width: none; /* Firefox */
|
||||
-ms-overflow-style: none; /* IE and Edge */
|
||||
}
|
||||
|
||||
html::-webkit-scrollbar {
|
||||
display: none; /* Chrome, Safari and Opera */
|
||||
}
|
||||
}
|
||||
|
||||
@layer components {
|
||||
/* 赛博朋克玻璃态效果 */
|
||||
.glass-effect {
|
||||
@apply backdrop-blur-xl backdrop-saturate-150;
|
||||
background: linear-gradient(135deg,
|
||||
rgba(244, 63, 94, 0.1) 0%,
|
||||
rgba(168, 85, 247, 0.05) 50%,
|
||||
rgba(6, 182, 212, 0.1) 100%
|
||||
);
|
||||
border: 1px solid rgba(244, 63, 94, 0.3);
|
||||
box-shadow:
|
||||
0 8px 32px rgba(244, 63, 94, 0.2),
|
||||
0 0 80px rgba(168, 85, 247, 0.1),
|
||||
inset 0 0 20px rgba(244, 63, 94, 0.05);
|
||||
}
|
||||
|
||||
.glass-effect-hover {
|
||||
@apply transition-all duration-300;
|
||||
}
|
||||
|
||||
.glass-effect-hover:hover {
|
||||
background: linear-gradient(135deg,
|
||||
rgba(244, 63, 94, 0.15) 0%,
|
||||
rgba(168, 85, 247, 0.08) 50%,
|
||||
rgba(6, 182, 212, 0.15) 100%
|
||||
);
|
||||
border-color: rgba(244, 63, 94, 0.5);
|
||||
box-shadow:
|
||||
0 12px 48px rgba(244, 63, 94, 0.3),
|
||||
0 0 120px rgba(168, 85, 247, 0.2),
|
||||
inset 0 0 30px rgba(244, 63, 94, 0.08);
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
/* 霓虹文字效果 */
|
||||
.neon-text {
|
||||
text-shadow:
|
||||
0 0 10px currentColor,
|
||||
0 0 20px currentColor,
|
||||
0 0 40px currentColor,
|
||||
0 0 80px currentColor;
|
||||
animation: neon-pulse 2s ease-in-out infinite alternate;
|
||||
}
|
||||
|
||||
.neon-text-pink {
|
||||
@apply text-cyber-pink-400;
|
||||
text-shadow:
|
||||
0 0 10px #f43f5e,
|
||||
0 0 20px #f43f5e,
|
||||
0 0 40px #f43f5e,
|
||||
0 0 80px #f43f5e;
|
||||
}
|
||||
|
||||
.neon-text-purple {
|
||||
@apply text-neon-purple-400;
|
||||
text-shadow:
|
||||
0 0 10px #a855f7,
|
||||
0 0 20px #a855f7,
|
||||
0 0 40px #a855f7,
|
||||
0 0 80px #a855f7;
|
||||
}
|
||||
|
||||
.neon-text-cyan {
|
||||
@apply text-neon-cyan-400;
|
||||
text-shadow:
|
||||
0 0 10px #06b6d4,
|
||||
0 0 20px #06b6d4,
|
||||
0 0 40px #06b6d4,
|
||||
0 0 80px #06b6d4;
|
||||
}
|
||||
|
||||
/* 霓虹边框 */
|
||||
.neon-border {
|
||||
position: relative;
|
||||
border: 2px solid transparent;
|
||||
background: linear-gradient(#0a0a0a, #0a0a0a) padding-box,
|
||||
linear-gradient(45deg, #f43f5e, #a855f7, #06b6d4) border-box;
|
||||
}
|
||||
|
||||
.neon-border::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
inset: -2px;
|
||||
background: linear-gradient(45deg, #f43f5e, #a855f7, #06b6d4);
|
||||
border-radius: inherit;
|
||||
opacity: 0.5;
|
||||
filter: blur(10px);
|
||||
z-index: -1;
|
||||
animation: neon-border-pulse 3s ease-in-out infinite;
|
||||
}
|
||||
|
||||
/* 赛博朋克按钮 */
|
||||
.cyber-button {
|
||||
@apply relative px-6 py-3 font-bold text-cyber-dark-100 uppercase tracking-wider;
|
||||
background: linear-gradient(45deg, rgba(244, 63, 94, 0.2), rgba(168, 85, 247, 0.2));
|
||||
border: 1px solid rgba(244, 63, 94, 0.5);
|
||||
clip-path: polygon(0 0, calc(100% - 10px) 0, 100% 30%, 100% 100%, 10px 100%, 0 70%);
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.cyber-button:hover {
|
||||
background: linear-gradient(45deg, rgba(244, 63, 94, 0.4), rgba(168, 85, 247, 0.4));
|
||||
border-color: rgba(244, 63, 94, 0.8);
|
||||
box-shadow:
|
||||
0 0 20px rgba(244, 63, 94, 0.5),
|
||||
0 0 40px rgba(168, 85, 247, 0.3),
|
||||
inset 0 0 20px rgba(244, 63, 94, 0.2);
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
.cyber-button::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 2px;
|
||||
background: linear-gradient(90deg, transparent, #f43f5e, transparent);
|
||||
animation: scan 2s linear infinite;
|
||||
}
|
||||
|
||||
/* 故障文字效果 */
|
||||
.glitch-text {
|
||||
position: relative;
|
||||
color: #fff;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.glitch-text::before,
|
||||
.glitch-text::after {
|
||||
content: attr(data-text);
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.glitch-text::before {
|
||||
animation: glitch-1 0.5s infinite;
|
||||
color: #f43f5e;
|
||||
z-index: -1;
|
||||
}
|
||||
|
||||
.glitch-text::after {
|
||||
animation: glitch-2 0.5s infinite;
|
||||
color: #06b6d4;
|
||||
z-index: -2;
|
||||
}
|
||||
|
||||
/* 赛博朋克卡片 */
|
||||
.cyber-card {
|
||||
@apply relative overflow-hidden;
|
||||
background: linear-gradient(135deg,
|
||||
rgba(10, 10, 10, 0.9) 0%,
|
||||
rgba(23, 23, 23, 0.8) 100%
|
||||
);
|
||||
border: 1px solid rgba(244, 63, 94, 0.3);
|
||||
box-shadow:
|
||||
0 0 30px rgba(244, 63, 94, 0.2),
|
||||
inset 0 0 20px rgba(168, 85, 247, 0.05);
|
||||
}
|
||||
|
||||
.cyber-card::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: -2px;
|
||||
left: -2px;
|
||||
right: -2px;
|
||||
bottom: -2px;
|
||||
background: linear-gradient(45deg, #f43f5e, #a855f7, #06b6d4, #3b82f6);
|
||||
opacity: 0;
|
||||
transition: opacity 0.3s ease;
|
||||
z-index: -1;
|
||||
filter: blur(10px);
|
||||
}
|
||||
|
||||
.cyber-card:hover::before {
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
/* 扫描线效果 */
|
||||
.scan-lines::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: repeating-linear-gradient(
|
||||
0deg,
|
||||
rgba(0, 0, 0, 0.15),
|
||||
rgba(0, 0, 0, 0.15) 1px,
|
||||
transparent 1px,
|
||||
transparent 2px
|
||||
);
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
/* 数据流动画 */
|
||||
.data-stream {
|
||||
background: linear-gradient(90deg,
|
||||
transparent,
|
||||
rgba(244, 63, 94, 0.5),
|
||||
rgba(168, 85, 247, 0.5),
|
||||
rgba(6, 182, 212, 0.5),
|
||||
transparent
|
||||
);
|
||||
background-size: 200% 100%;
|
||||
animation: data-flow 3s linear infinite;
|
||||
}
|
||||
}
|
||||
|
||||
@layer utilities {
|
||||
/* 霓虹发光工具类 */
|
||||
.neon-glow-pink {
|
||||
filter: drop-shadow(0 0 10px #f43f5e) drop-shadow(0 0 20px #f43f5e);
|
||||
}
|
||||
|
||||
.neon-glow-purple {
|
||||
filter: drop-shadow(0 0 10px #a855f7) drop-shadow(0 0 20px #a855f7);
|
||||
}
|
||||
|
||||
.neon-glow-cyan {
|
||||
filter: drop-shadow(0 0 10px #06b6d4) drop-shadow(0 0 20px #06b6d4);
|
||||
}
|
||||
|
||||
.neon-glow-blue {
|
||||
filter: drop-shadow(0 0 10px #3b82f6) drop-shadow(0 0 20px #3b82f6);
|
||||
}
|
||||
|
||||
/* 文字渐变 */
|
||||
.gradient-text {
|
||||
@apply bg-gradient-to-r from-cyber-pink-400 via-neon-purple-400 to-neon-cyan-400 bg-clip-text text-transparent;
|
||||
}
|
||||
|
||||
.gradient-text-reverse {
|
||||
@apply bg-gradient-to-l from-cyber-pink-400 via-neon-purple-400 to-neon-cyan-400 bg-clip-text text-transparent;
|
||||
}
|
||||
}
|
||||
|
||||
/* 自定义动画 */
|
||||
@keyframes neon-pulse {
|
||||
0%, 100% {
|
||||
opacity: 1;
|
||||
filter: brightness(1);
|
||||
}
|
||||
50% {
|
||||
opacity: 0.8;
|
||||
filter: brightness(1.2);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes neon-border-pulse {
|
||||
0%, 100% {
|
||||
opacity: 0.5;
|
||||
transform: scale(1);
|
||||
}
|
||||
50% {
|
||||
opacity: 0.8;
|
||||
transform: scale(1.02);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes glitch-1 {
|
||||
0%, 100% {
|
||||
clip-path: inset(0 0 0 0);
|
||||
transform: translate(0);
|
||||
}
|
||||
20% {
|
||||
clip-path: inset(20% 0 60% 0);
|
||||
transform: translate(-2px, 2px);
|
||||
}
|
||||
40% {
|
||||
clip-path: inset(40% 0 40% 0);
|
||||
transform: translate(2px, -2px);
|
||||
}
|
||||
60% {
|
||||
clip-path: inset(60% 0 20% 0);
|
||||
transform: translate(-2px, -2px);
|
||||
}
|
||||
80% {
|
||||
clip-path: inset(80% 0 10% 0);
|
||||
transform: translate(2px, 2px);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes glitch-2 {
|
||||
0%, 100% {
|
||||
clip-path: inset(0 0 0 0);
|
||||
transform: translate(0);
|
||||
}
|
||||
20% {
|
||||
clip-path: inset(80% 0 10% 0);
|
||||
transform: translate(2px, -2px);
|
||||
}
|
||||
40% {
|
||||
clip-path: inset(60% 0 20% 0);
|
||||
transform: translate(-2px, 2px);
|
||||
}
|
||||
60% {
|
||||
clip-path: inset(40% 0 40% 0);
|
||||
transform: translate(2px, 2px);
|
||||
}
|
||||
80% {
|
||||
clip-path: inset(20% 0 60% 0);
|
||||
transform: translate(-2px, -2px);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes scan {
|
||||
0% {
|
||||
transform: translateX(-100%);
|
||||
}
|
||||
100% {
|
||||
transform: translateX(100%);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes data-flow {
|
||||
0% {
|
||||
background-position: -200% 0;
|
||||
}
|
||||
100% {
|
||||
background-position: 200% 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* 增强的网格样式 - 更好的可见性 */
|
||||
.cyberpunk-grid {
|
||||
will-change: auto;
|
||||
animation: grid-move 30s linear infinite;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.cyberpunk-grid::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background: linear-gradient(180deg,
|
||||
transparent 0%,
|
||||
rgba(6, 182, 212, 0.1) 50%,
|
||||
transparent 100%
|
||||
);
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
@keyframes grid-move {
|
||||
0% {
|
||||
background-position: 0 0;
|
||||
}
|
||||
100% {
|
||||
background-position: 50px 50px;
|
||||
}
|
||||
}
|
||||
|
||||
/* 优化的霓虹光束 */
|
||||
.neon-beam {
|
||||
position: absolute;
|
||||
height: 1px;
|
||||
width: 200%;
|
||||
left: -50%;
|
||||
background: linear-gradient(90deg, transparent, #f43f5e, transparent);
|
||||
box-shadow: 0 0 10px #f43f5e;
|
||||
transform: translateX(-100%);
|
||||
animation: beam-move 8s linear infinite;
|
||||
}
|
||||
|
||||
.neon-beam-1 {
|
||||
top: 20%;
|
||||
animation-delay: 0s;
|
||||
}
|
||||
|
||||
.neon-beam-2 {
|
||||
top: 50%;
|
||||
background: linear-gradient(90deg, transparent, #a855f7, transparent);
|
||||
box-shadow: 0 0 10px #a855f7;
|
||||
animation-delay: 2.5s;
|
||||
animation-duration: 10s;
|
||||
}
|
||||
|
||||
.neon-beam-3 {
|
||||
top: 80%;
|
||||
background: linear-gradient(90deg, transparent, #06b6d4, transparent);
|
||||
box-shadow: 0 0 10px #06b6d4;
|
||||
animation-delay: 5s;
|
||||
animation-duration: 12s;
|
||||
}
|
||||
|
||||
@keyframes beam-move {
|
||||
0% {
|
||||
transform: translateX(-100%);
|
||||
}
|
||||
100% {
|
||||
transform: translateX(100%);
|
||||
}
|
||||
}
|
||||
|
||||
/* 优化的数据雨 */
|
||||
.data-rain-container {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
}
|
||||
|
||||
.data-rain-column {
|
||||
position: absolute;
|
||||
width: 2px;
|
||||
height: 150px;
|
||||
background: linear-gradient(180deg,
|
||||
transparent 0%,
|
||||
rgba(6, 182, 212, 0.4) 20%,
|
||||
rgba(244, 63, 94, 0.8) 40%,
|
||||
rgba(168, 85, 247, 1) 50%,
|
||||
rgba(244, 63, 94, 0.8) 60%,
|
||||
rgba(6, 182, 212, 0.4) 80%,
|
||||
transparent 100%
|
||||
);
|
||||
filter: blur(0.8px);
|
||||
box-shadow:
|
||||
0 0 10px rgba(244, 63, 94, 0.6),
|
||||
0 0 20px rgba(168, 85, 247, 0.4);
|
||||
animation: rain-fall 10s linear infinite;
|
||||
will-change: transform;
|
||||
}
|
||||
|
||||
@keyframes rain-fall {
|
||||
0% {
|
||||
transform: translateY(-100px);
|
||||
}
|
||||
100% {
|
||||
transform: translateY(100vh);
|
||||
}
|
||||
}
|
||||
|
||||
/* 日文字符数据雨样式 - 黑客帝国风格 */
|
||||
.data-rain-column-text {
|
||||
position: absolute;
|
||||
writing-mode: vertical-rl;
|
||||
text-orientation: upright;
|
||||
line-height: 1.1;
|
||||
letter-spacing: 0;
|
||||
animation: matrix-fall linear infinite;
|
||||
transform: translateZ(0);
|
||||
}
|
||||
|
||||
.data-rain-column-text span {
|
||||
color: #00ff41;
|
||||
text-shadow:
|
||||
0 0 3px #00ff41,
|
||||
0 0 8px rgba(0, 255, 65, 0.8);
|
||||
transition: all 0.5s ease;
|
||||
}
|
||||
|
||||
/* 最亮的字符(头部) */
|
||||
.data-rain-column-text span:first-child {
|
||||
color: #ffffff;
|
||||
text-shadow:
|
||||
0 0 10px #ffffff,
|
||||
0 0 20px #00ff41,
|
||||
0 0 30px #00ff41;
|
||||
}
|
||||
|
||||
/* 渐变效果 */
|
||||
.data-rain-column-text span:nth-child(2) {
|
||||
color: #b3ffb3;
|
||||
text-shadow:
|
||||
0 0 5px #b3ffb3,
|
||||
0 0 10px rgba(0, 255, 65, 0.6);
|
||||
}
|
||||
|
||||
.data-rain-column-text span:nth-child(3) {
|
||||
color: #66ff66;
|
||||
text-shadow:
|
||||
0 0 4px #66ff66,
|
||||
0 0 8px rgba(0, 255, 65, 0.5);
|
||||
}
|
||||
|
||||
.data-rain-column-text span:nth-child(n+10) {
|
||||
color: #00cc33;
|
||||
opacity: 0.8;
|
||||
text-shadow: 0 0 2px rgba(0, 255, 65, 0.4);
|
||||
}
|
||||
|
||||
.data-rain-column-text span:nth-child(n+20) {
|
||||
color: #009926;
|
||||
opacity: 0.4;
|
||||
text-shadow: 0 0 1px rgba(0, 255, 65, 0.2);
|
||||
}
|
||||
|
||||
.data-rain-column-text span:nth-child(n+30) {
|
||||
color: #006619;
|
||||
opacity: 0.2;
|
||||
text-shadow: none;
|
||||
}
|
||||
|
||||
@keyframes matrix-fall {
|
||||
0% {
|
||||
transform: translateY(-100%);
|
||||
opacity: 0;
|
||||
}
|
||||
10% {
|
||||
opacity: 1;
|
||||
}
|
||||
90% {
|
||||
opacity: 1;
|
||||
}
|
||||
100% {
|
||||
transform: translateY(100vh);
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* 优化的故障效果 */
|
||||
.glitch-overlay {
|
||||
background: repeating-linear-gradient(
|
||||
0deg,
|
||||
rgba(244, 63, 94, 0.03),
|
||||
rgba(244, 63, 94, 0.03) 2px,
|
||||
transparent 2px,
|
||||
transparent 4px
|
||||
);
|
||||
animation: glitch-simple 0.1s 2;
|
||||
}
|
||||
|
||||
@keyframes glitch-simple {
|
||||
0%, 100% {
|
||||
transform: translate(0);
|
||||
}
|
||||
50% {
|
||||
transform: translate(1px, -1px);
|
||||
}
|
||||
}
|
||||
|
||||
/* CRT效果优化 */
|
||||
.crt-effect {
|
||||
background: repeating-linear-gradient(
|
||||
0deg,
|
||||
rgba(0, 0, 0, 0.15),
|
||||
rgba(0, 0, 0, 0.15) 1px,
|
||||
transparent 1px,
|
||||
transparent 2px
|
||||
);
|
||||
opacity: 0.3;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
/* 性能优化:启用GPU加速 */
|
||||
.gpu-accelerated {
|
||||
transform: translateZ(0);
|
||||
will-change: auto;
|
||||
backface-visibility: hidden;
|
||||
-webkit-backface-visibility: hidden;
|
||||
}
|
||||
|
||||
/* 减少动画开销 */
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
* {
|
||||
animation-duration: 0.01ms !important;
|
||||
animation-iteration-count: 1 !important;
|
||||
transition-duration: 0.01ms !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* 优化悬浮效果 */
|
||||
.optimized-hover {
|
||||
transition: transform 0.2s ease, box-shadow 0.2s ease;
|
||||
will-change: auto;
|
||||
}
|
||||
|
||||
.optimized-hover:hover {
|
||||
will-change: transform, box-shadow;
|
||||
}
|
||||
|
||||
/*/* 隐藏滚动条 */
|
||||
::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* 对于Firefox */
|
||||
* {
|
||||
scrollbar-width: none;
|
||||
}
|
||||
|
||||
/* 对于IE和Edge */
|
||||
body {
|
||||
-ms-overflow-style: none;
|
||||
}
|
||||
|
||||
/* 选中文本样式 */
|
||||
::selection {
|
||||
background: rgba(244, 63, 94, 0.5);
|
||||
color: #fff;
|
||||
text-shadow: 0 0 10px rgba(244, 63, 94, 0.8);
|
||||
}
|
||||
|
||||
::-moz-selection {
|
||||
background: rgba(244, 63, 94, 0.5);
|
||||
color: #fff;
|
||||
text-shadow: 0 0 10px rgba(244, 63, 94, 0.8);
|
||||
}
|
||||
667
src/index.css
Normal file
667
src/index.css
Normal file
@@ -0,0 +1,667 @@
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
@layer base {
|
||||
@import url('https://fonts.googleapis.com/css2?family=Orbitron:wght@400;500;600;700;800;900&family=Rajdhani:wght@300;400;500;600;700&display=swap');
|
||||
|
||||
body {
|
||||
@apply bg-cyber-dark-900 text-cyber-dark-100;
|
||||
font-family: 'Orbitron', 'Rajdhani', -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', sans-serif;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
/* 全局隐藏滚动条 */
|
||||
html {
|
||||
scrollbar-width: none; /* Firefox */
|
||||
-ms-overflow-style: none; /* IE and Edge */
|
||||
}
|
||||
|
||||
html::-webkit-scrollbar {
|
||||
display: none; /* Chrome, Safari and Opera */
|
||||
}
|
||||
}
|
||||
|
||||
@layer components {
|
||||
/* 赛博朋克玻璃态效果 */
|
||||
.glass-effect {
|
||||
@apply backdrop-blur-xl backdrop-saturate-150;
|
||||
background: linear-gradient(135deg,
|
||||
rgba(244, 63, 94, 0.1) 0%,
|
||||
rgba(168, 85, 247, 0.05) 50%,
|
||||
rgba(6, 182, 212, 0.1) 100%
|
||||
);
|
||||
border: 1px solid rgba(244, 63, 94, 0.3);
|
||||
box-shadow:
|
||||
0 8px 32px rgba(244, 63, 94, 0.2),
|
||||
0 0 80px rgba(168, 85, 247, 0.1),
|
||||
inset 0 0 20px rgba(244, 63, 94, 0.05);
|
||||
}
|
||||
|
||||
.glass-effect-hover {
|
||||
@apply transition-all duration-300;
|
||||
}
|
||||
|
||||
.glass-effect-hover:hover {
|
||||
background: linear-gradient(135deg,
|
||||
rgba(244, 63, 94, 0.15) 0%,
|
||||
rgba(168, 85, 247, 0.08) 50%,
|
||||
rgba(6, 182, 212, 0.15) 100%
|
||||
);
|
||||
border-color: rgba(244, 63, 94, 0.5);
|
||||
box-shadow:
|
||||
0 12px 48px rgba(244, 63, 94, 0.3),
|
||||
0 0 120px rgba(168, 85, 247, 0.2),
|
||||
inset 0 0 30px rgba(244, 63, 94, 0.08);
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
/* 霓虹文字效果 */
|
||||
.neon-text {
|
||||
text-shadow:
|
||||
0 0 10px currentColor,
|
||||
0 0 20px currentColor,
|
||||
0 0 40px currentColor,
|
||||
0 0 80px currentColor;
|
||||
animation: neon-pulse 2s ease-in-out infinite alternate;
|
||||
}
|
||||
|
||||
.neon-text-pink {
|
||||
@apply text-cyber-pink-400;
|
||||
text-shadow:
|
||||
0 0 10px #f43f5e,
|
||||
0 0 20px #f43f5e,
|
||||
0 0 40px #f43f5e,
|
||||
0 0 80px #f43f5e;
|
||||
}
|
||||
|
||||
.neon-text-purple {
|
||||
@apply text-neon-purple-400;
|
||||
text-shadow:
|
||||
0 0 10px #a855f7,
|
||||
0 0 20px #a855f7,
|
||||
0 0 40px #a855f7,
|
||||
0 0 80px #a855f7;
|
||||
}
|
||||
|
||||
.neon-text-cyan {
|
||||
@apply text-neon-cyan-400;
|
||||
text-shadow:
|
||||
0 0 10px #06b6d4,
|
||||
0 0 20px #06b6d4,
|
||||
0 0 40px #06b6d4,
|
||||
0 0 80px #06b6d4;
|
||||
}
|
||||
|
||||
/* 霓虹边框 */
|
||||
.neon-border {
|
||||
position: relative;
|
||||
border: 2px solid transparent;
|
||||
background: linear-gradient(#0a0a0a, #0a0a0a) padding-box,
|
||||
linear-gradient(45deg, #f43f5e, #a855f7, #06b6d4) border-box;
|
||||
}
|
||||
|
||||
.neon-border::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
inset: -2px;
|
||||
background: linear-gradient(45deg, #f43f5e, #a855f7, #06b6d4);
|
||||
border-radius: inherit;
|
||||
opacity: 0.5;
|
||||
filter: blur(10px);
|
||||
z-index: -1;
|
||||
animation: neon-border-pulse 3s ease-in-out infinite;
|
||||
}
|
||||
|
||||
/* 赛博朋克按钮 */
|
||||
.cyber-button {
|
||||
@apply relative px-6 py-3 font-bold text-cyber-dark-100 uppercase tracking-wider;
|
||||
background: linear-gradient(45deg, rgba(244, 63, 94, 0.2), rgba(168, 85, 247, 0.2));
|
||||
border: 1px solid rgba(244, 63, 94, 0.5);
|
||||
clip-path: polygon(0 0, calc(100% - 10px) 0, 100% 30%, 100% 100%, 10px 100%, 0 70%);
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.cyber-button:hover {
|
||||
background: linear-gradient(45deg, rgba(244, 63, 94, 0.4), rgba(168, 85, 247, 0.4));
|
||||
border-color: rgba(244, 63, 94, 0.8);
|
||||
box-shadow:
|
||||
0 0 20px rgba(244, 63, 94, 0.5),
|
||||
0 0 40px rgba(168, 85, 247, 0.3),
|
||||
inset 0 0 20px rgba(244, 63, 94, 0.2);
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
.cyber-button::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 2px;
|
||||
background: linear-gradient(90deg, transparent, #f43f5e, transparent);
|
||||
animation: scan 2s linear infinite;
|
||||
}
|
||||
|
||||
/* 故障文字效果 */
|
||||
.glitch-text {
|
||||
position: relative;
|
||||
color: #fff;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.glitch-text::before,
|
||||
.glitch-text::after {
|
||||
content: attr(data-text);
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.glitch-text::before {
|
||||
animation: glitch-1 0.5s infinite;
|
||||
color: #f43f5e;
|
||||
z-index: -1;
|
||||
}
|
||||
|
||||
.glitch-text::after {
|
||||
animation: glitch-2 0.5s infinite;
|
||||
color: #06b6d4;
|
||||
z-index: -2;
|
||||
}
|
||||
|
||||
/* 赛博朋克卡片 */
|
||||
.cyber-card {
|
||||
@apply relative overflow-hidden;
|
||||
background: linear-gradient(135deg,
|
||||
rgba(10, 10, 10, 0.9) 0%,
|
||||
rgba(23, 23, 23, 0.8) 100%
|
||||
);
|
||||
border: 1px solid rgba(244, 63, 94, 0.3);
|
||||
box-shadow:
|
||||
0 0 30px rgba(244, 63, 94, 0.2),
|
||||
inset 0 0 20px rgba(168, 85, 247, 0.05);
|
||||
}
|
||||
|
||||
.cyber-card::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: -2px;
|
||||
left: -2px;
|
||||
right: -2px;
|
||||
bottom: -2px;
|
||||
background: linear-gradient(45deg, #f43f5e, #a855f7, #06b6d4, #3b82f6);
|
||||
opacity: 0;
|
||||
transition: opacity 0.3s ease;
|
||||
z-index: -1;
|
||||
filter: blur(10px);
|
||||
}
|
||||
|
||||
.cyber-card:hover::before {
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
/* 扫描线效果 */
|
||||
.scan-lines::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: repeating-linear-gradient(
|
||||
0deg,
|
||||
rgba(0, 0, 0, 0.15),
|
||||
rgba(0, 0, 0, 0.15) 1px,
|
||||
transparent 1px,
|
||||
transparent 2px
|
||||
);
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
/* 数据流动画 */
|
||||
.data-stream {
|
||||
background: linear-gradient(90deg,
|
||||
transparent,
|
||||
rgba(244, 63, 94, 0.5),
|
||||
rgba(168, 85, 247, 0.5),
|
||||
rgba(6, 182, 212, 0.5),
|
||||
transparent
|
||||
);
|
||||
background-size: 200% 100%;
|
||||
animation: data-flow 3s linear infinite;
|
||||
}
|
||||
}
|
||||
|
||||
@layer utilities {
|
||||
/* 霓虹发光工具类 */
|
||||
.neon-glow-pink {
|
||||
filter: drop-shadow(0 0 10px #f43f5e) drop-shadow(0 0 20px #f43f5e);
|
||||
}
|
||||
|
||||
.neon-glow-purple {
|
||||
filter: drop-shadow(0 0 10px #a855f7) drop-shadow(0 0 20px #a855f7);
|
||||
}
|
||||
|
||||
.neon-glow-cyan {
|
||||
filter: drop-shadow(0 0 10px #06b6d4) drop-shadow(0 0 20px #06b6d4);
|
||||
}
|
||||
|
||||
.neon-glow-blue {
|
||||
filter: drop-shadow(0 0 10px #3b82f6) drop-shadow(0 0 20px #3b82f6);
|
||||
}
|
||||
|
||||
/* 文字渐变 */
|
||||
.gradient-text {
|
||||
@apply bg-gradient-to-r from-cyber-pink-400 via-neon-purple-400 to-neon-cyan-400 bg-clip-text text-transparent;
|
||||
}
|
||||
|
||||
.gradient-text-reverse {
|
||||
@apply bg-gradient-to-l from-cyber-pink-400 via-neon-purple-400 to-neon-cyan-400 bg-clip-text text-transparent;
|
||||
}
|
||||
}
|
||||
|
||||
/* 自定义动画 */
|
||||
@keyframes neon-pulse {
|
||||
0%, 100% {
|
||||
opacity: 1;
|
||||
filter: brightness(1);
|
||||
}
|
||||
50% {
|
||||
opacity: 0.8;
|
||||
filter: brightness(1.2);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes neon-border-pulse {
|
||||
0%, 100% {
|
||||
opacity: 0.5;
|
||||
transform: scale(1);
|
||||
}
|
||||
50% {
|
||||
opacity: 0.8;
|
||||
transform: scale(1.02);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes glitch-1 {
|
||||
0%, 100% {
|
||||
clip-path: inset(0 0 0 0);
|
||||
transform: translate(0);
|
||||
}
|
||||
20% {
|
||||
clip-path: inset(20% 0 60% 0);
|
||||
transform: translate(-2px, 2px);
|
||||
}
|
||||
40% {
|
||||
clip-path: inset(40% 0 40% 0);
|
||||
transform: translate(2px, -2px);
|
||||
}
|
||||
60% {
|
||||
clip-path: inset(60% 0 20% 0);
|
||||
transform: translate(-2px, -2px);
|
||||
}
|
||||
80% {
|
||||
clip-path: inset(80% 0 10% 0);
|
||||
transform: translate(2px, 2px);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes glitch-2 {
|
||||
0%, 100% {
|
||||
clip-path: inset(0 0 0 0);
|
||||
transform: translate(0);
|
||||
}
|
||||
20% {
|
||||
clip-path: inset(80% 0 10% 0);
|
||||
transform: translate(2px, -2px);
|
||||
}
|
||||
40% {
|
||||
clip-path: inset(60% 0 20% 0);
|
||||
transform: translate(-2px, 2px);
|
||||
}
|
||||
60% {
|
||||
clip-path: inset(40% 0 40% 0);
|
||||
transform: translate(2px, 2px);
|
||||
}
|
||||
80% {
|
||||
clip-path: inset(20% 0 60% 0);
|
||||
transform: translate(-2px, -2px);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes scan {
|
||||
0% {
|
||||
transform: translateX(-100%);
|
||||
}
|
||||
100% {
|
||||
transform: translateX(100%);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes data-flow {
|
||||
0% {
|
||||
background-position: -200% 0;
|
||||
}
|
||||
100% {
|
||||
background-position: 200% 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* 增强的网格样式 - 更好的可见性 */
|
||||
.cyberpunk-grid {
|
||||
will-change: auto;
|
||||
animation: grid-move 30s linear infinite;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.cyberpunk-grid::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background: linear-gradient(180deg,
|
||||
transparent 0%,
|
||||
rgba(6, 182, 212, 0.1) 50%,
|
||||
transparent 100%
|
||||
);
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
@keyframes grid-move {
|
||||
0% {
|
||||
background-position: 0 0;
|
||||
}
|
||||
100% {
|
||||
background-position: 50px 50px;
|
||||
}
|
||||
}
|
||||
|
||||
/* 优化的霓虹光束 */
|
||||
.neon-beam {
|
||||
position: absolute;
|
||||
height: 1px;
|
||||
width: 200%;
|
||||
left: -50%;
|
||||
background: linear-gradient(90deg, transparent, #f43f5e, transparent);
|
||||
box-shadow: 0 0 10px #f43f5e;
|
||||
transform: translateX(-100%);
|
||||
animation: beam-move 8s linear infinite;
|
||||
}
|
||||
|
||||
.neon-beam-1 {
|
||||
top: 20%;
|
||||
animation-delay: 0s;
|
||||
}
|
||||
|
||||
.neon-beam-2 {
|
||||
top: 50%;
|
||||
background: linear-gradient(90deg, transparent, #a855f7, transparent);
|
||||
box-shadow: 0 0 10px #a855f7;
|
||||
animation-delay: 2.5s;
|
||||
animation-duration: 10s;
|
||||
}
|
||||
|
||||
.neon-beam-3 {
|
||||
top: 80%;
|
||||
background: linear-gradient(90deg, transparent, #06b6d4, transparent);
|
||||
box-shadow: 0 0 10px #06b6d4;
|
||||
animation-delay: 5s;
|
||||
animation-duration: 12s;
|
||||
}
|
||||
|
||||
@keyframes beam-move {
|
||||
0% {
|
||||
transform: translateX(-100%);
|
||||
}
|
||||
100% {
|
||||
transform: translateX(100%);
|
||||
}
|
||||
}
|
||||
|
||||
/* 优化的数据雨 */
|
||||
.data-rain-container {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
}
|
||||
|
||||
.data-rain-column {
|
||||
position: absolute;
|
||||
width: 2px;
|
||||
height: 150px;
|
||||
background: linear-gradient(180deg,
|
||||
transparent 0%,
|
||||
rgba(6, 182, 212, 0.4) 20%,
|
||||
rgba(244, 63, 94, 0.8) 40%,
|
||||
rgba(168, 85, 247, 1) 50%,
|
||||
rgba(244, 63, 94, 0.8) 60%,
|
||||
rgba(6, 182, 212, 0.4) 80%,
|
||||
transparent 100%
|
||||
);
|
||||
filter: blur(0.8px);
|
||||
box-shadow:
|
||||
0 0 10px rgba(244, 63, 94, 0.6),
|
||||
0 0 20px rgba(168, 85, 247, 0.4);
|
||||
animation: rain-fall 10s linear infinite;
|
||||
will-change: transform;
|
||||
}
|
||||
|
||||
@keyframes rain-fall {
|
||||
0% {
|
||||
transform: translateY(-100px);
|
||||
}
|
||||
100% {
|
||||
transform: translateY(100vh);
|
||||
}
|
||||
}
|
||||
|
||||
/* 日文字符数据雨样式 - 黑客帝国风格 */
|
||||
.data-rain-column-text {
|
||||
position: absolute;
|
||||
writing-mode: vertical-rl;
|
||||
text-orientation: upright;
|
||||
line-height: 1.1;
|
||||
letter-spacing: 0;
|
||||
animation: matrix-fall linear infinite;
|
||||
transform: translateZ(0);
|
||||
}
|
||||
|
||||
.data-rain-column-text span {
|
||||
color: #00ff41;
|
||||
text-shadow:
|
||||
0 0 3px #00ff41,
|
||||
0 0 8px rgba(0, 255, 65, 0.8);
|
||||
transition: all 0.5s ease;
|
||||
}
|
||||
|
||||
/* 最亮的字符(头部) */
|
||||
.data-rain-column-text span:first-child {
|
||||
color: #ffffff;
|
||||
text-shadow:
|
||||
0 0 10px #ffffff,
|
||||
0 0 20px #00ff41,
|
||||
0 0 30px #00ff41;
|
||||
}
|
||||
|
||||
/* 渐变效果 */
|
||||
.data-rain-column-text span:nth-child(2) {
|
||||
color: #b3ffb3;
|
||||
text-shadow:
|
||||
0 0 5px #b3ffb3,
|
||||
0 0 10px rgba(0, 255, 65, 0.6);
|
||||
}
|
||||
|
||||
.data-rain-column-text span:nth-child(3) {
|
||||
color: #66ff66;
|
||||
text-shadow:
|
||||
0 0 4px #66ff66,
|
||||
0 0 8px rgba(0, 255, 65, 0.5);
|
||||
}
|
||||
|
||||
.data-rain-column-text span:nth-child(n+10) {
|
||||
color: #00cc33;
|
||||
opacity: 0.8;
|
||||
text-shadow: 0 0 2px rgba(0, 255, 65, 0.4);
|
||||
}
|
||||
|
||||
.data-rain-column-text span:nth-child(n+20) {
|
||||
color: #009926;
|
||||
opacity: 0.4;
|
||||
text-shadow: 0 0 1px rgba(0, 255, 65, 0.2);
|
||||
}
|
||||
|
||||
.data-rain-column-text span:nth-child(n+30) {
|
||||
color: #006619;
|
||||
opacity: 0.2;
|
||||
text-shadow: none;
|
||||
}
|
||||
|
||||
@keyframes matrix-fall {
|
||||
0% {
|
||||
transform: translateY(-100%);
|
||||
opacity: 0;
|
||||
}
|
||||
10% {
|
||||
opacity: 1;
|
||||
}
|
||||
90% {
|
||||
opacity: 1;
|
||||
}
|
||||
100% {
|
||||
transform: translateY(100vh);
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* 优化的故障效果 */
|
||||
.glitch-overlay {
|
||||
background: repeating-linear-gradient(
|
||||
0deg,
|
||||
rgba(244, 63, 94, 0.03),
|
||||
rgba(244, 63, 94, 0.03) 2px,
|
||||
transparent 2px,
|
||||
transparent 4px
|
||||
);
|
||||
animation: glitch-simple 0.1s 2;
|
||||
}
|
||||
|
||||
@keyframes glitch-simple {
|
||||
0%, 100% {
|
||||
transform: translate(0);
|
||||
}
|
||||
50% {
|
||||
transform: translate(1px, -1px);
|
||||
}
|
||||
}
|
||||
|
||||
/* CRT效果优化 */
|
||||
.crt-effect {
|
||||
background: repeating-linear-gradient(
|
||||
0deg,
|
||||
rgba(0, 0, 0, 0.15),
|
||||
rgba(0, 0, 0, 0.15) 1px,
|
||||
transparent 1px,
|
||||
transparent 2px
|
||||
);
|
||||
opacity: 0.3;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
/* 性能优化:启用GPU加速 */
|
||||
.gpu-accelerated {
|
||||
transform: translateZ(0);
|
||||
will-change: auto;
|
||||
backface-visibility: hidden;
|
||||
-webkit-backface-visibility: hidden;
|
||||
}
|
||||
|
||||
/* 减少动画开销 */
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
* {
|
||||
animation-duration: 0.01ms !important;
|
||||
animation-iteration-count: 1 !important;
|
||||
transition-duration: 0.01ms !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* 优化悬浮效果 */
|
||||
.optimized-hover {
|
||||
transition: transform 0.2s ease, box-shadow 0.2s ease;
|
||||
will-change: auto;
|
||||
}
|
||||
|
||||
.optimized-hover:hover {
|
||||
will-change: transform, box-shadow;
|
||||
}
|
||||
|
||||
/*/* 隐藏滚动条 */
|
||||
::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* 对于Firefox */
|
||||
* {
|
||||
scrollbar-width: none;
|
||||
}
|
||||
|
||||
/* 对于IE和Edge */
|
||||
body {
|
||||
-ms-overflow-style: none;
|
||||
}
|
||||
|
||||
/* 选中文本样式 */
|
||||
::selection {
|
||||
background: rgba(244, 63, 94, 0.5);
|
||||
color: #fff;
|
||||
text-shadow: 0 0 10px rgba(244, 63, 94, 0.8);
|
||||
}
|
||||
|
||||
::-moz-selection {
|
||||
background: rgba(244, 63, 94, 0.5);
|
||||
color: #fff;
|
||||
text-shadow: 0 0 10px rgba(244, 63, 94, 0.8);
|
||||
}
|
||||
|
||||
/* Section Scroll 平滑全屏滚动效果 */
|
||||
.section-scroll-container {
|
||||
height: 100vh;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.section-scroll-item {
|
||||
min-height: 100vh;
|
||||
max-height: 100vh;
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
position: relative;
|
||||
scroll-behavior: smooth;
|
||||
-webkit-overflow-scrolling: touch; /* iOS平滑滚动 */
|
||||
}
|
||||
|
||||
/* 隐藏section内部滚动条 */
|
||||
.section-scroll-item::-webkit-scrollbar {
|
||||
display: none; /* Chrome, Safari, Edge */
|
||||
}
|
||||
|
||||
/* Firefox隐藏滚动条 */
|
||||
.section-scroll-item {
|
||||
scrollbar-width: none; /* Firefox */
|
||||
-ms-overflow-style: none; /* IE and Edge */
|
||||
}
|
||||
|
||||
/* 隐藏横向滚动条 */
|
||||
.scrollbar-hide {
|
||||
scrollbar-width: none; /* Firefox */
|
||||
-ms-overflow-style: none; /* IE and Edge */
|
||||
}
|
||||
|
||||
.scrollbar-hide::-webkit-scrollbar {
|
||||
display: none; /* Chrome, Safari, Edge */
|
||||
}
|
||||
|
||||
/* 模糊效果 */
|
||||
.blur-sm {
|
||||
filter: blur(2px);
|
||||
}
|
||||
13
src/index.tsx
Normal file
13
src/index.tsx
Normal file
@@ -0,0 +1,13 @@
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom/client';
|
||||
import './index.css';
|
||||
import App from './App';
|
||||
|
||||
const root = ReactDOM.createRoot(
|
||||
document.getElementById('root') as HTMLElement
|
||||
);
|
||||
root.render(
|
||||
<React.StrictMode>
|
||||
<App />
|
||||
</React.StrictMode>
|
||||
);
|
||||
1142
src/pages/CoursePage.tsx
Normal file
1142
src/pages/CoursePage.tsx
Normal file
File diff suppressed because it is too large
Load Diff
1084
src/pages/HomePage.backup_20251101_102331.tsx
Normal file
1084
src/pages/HomePage.backup_20251101_102331.tsx
Normal file
File diff suppressed because it is too large
Load Diff
1089
src/pages/HomePage.backup_20251101_153155.tsx
Normal file
1089
src/pages/HomePage.backup_20251101_153155.tsx
Normal file
File diff suppressed because it is too large
Load Diff
1084
src/pages/HomePage.fullpage_backup_20251101_104226.tsx
Normal file
1084
src/pages/HomePage.fullpage_backup_20251101_104226.tsx
Normal file
File diff suppressed because it is too large
Load Diff
1156
src/pages/HomePage.tsx
Normal file
1156
src/pages/HomePage.tsx
Normal file
File diff suppressed because it is too large
Load Diff
175
src/styles/optimized.css
Normal file
175
src/styles/optimized.css
Normal file
@@ -0,0 +1,175 @@
|
||||
/* 优化后的赛博朋克样式 - 减少性能开销 */
|
||||
|
||||
/* 禁用过度的文字阴影效果 */
|
||||
.neon-text-optimized {
|
||||
color: #f43f5e;
|
||||
text-shadow: 0 0 10px rgba(244, 63, 94, 0.5);
|
||||
}
|
||||
|
||||
.neon-text-purple-optimized {
|
||||
color: #a855f7;
|
||||
text-shadow: 0 0 10px rgba(168, 85, 247, 0.5);
|
||||
}
|
||||
|
||||
.neon-text-cyan-optimized {
|
||||
color: #06b6d4;
|
||||
text-shadow: 0 0 10px rgba(6, 182, 212, 0.5);
|
||||
}
|
||||
|
||||
/* 简化的玻璃效果 */
|
||||
.glass-effect-optimized {
|
||||
background: rgba(10, 10, 10, 0.8);
|
||||
backdrop-filter: blur(10px);
|
||||
-webkit-backdrop-filter: blur(10px);
|
||||
border: 1px solid rgba(244, 63, 94, 0.2);
|
||||
}
|
||||
|
||||
/* 优化的卡片悬浮效果 */
|
||||
.cyber-card-optimized {
|
||||
background: linear-gradient(135deg,
|
||||
rgba(10, 10, 10, 0.9) 0%,
|
||||
rgba(23, 23, 23, 0.8) 100%
|
||||
);
|
||||
border: 1px solid rgba(244, 63, 94, 0.2);
|
||||
transition: transform 0.2s ease, border-color 0.2s ease;
|
||||
}
|
||||
|
||||
.cyber-card-optimized:hover {
|
||||
transform: translateY(-2px);
|
||||
border-color: rgba(244, 63, 94, 0.4);
|
||||
}
|
||||
|
||||
/* 简化的按钮样式 */
|
||||
.cyber-button-optimized {
|
||||
background: linear-gradient(45deg, rgba(244, 63, 94, 0.8), rgba(168, 85, 247, 0.8));
|
||||
border: none;
|
||||
color: white;
|
||||
padding: 12px 24px;
|
||||
border-radius: 8px;
|
||||
font-weight: 600;
|
||||
transition: transform 0.2s ease, box-shadow 0.2s ease;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 1px;
|
||||
}
|
||||
|
||||
.cyber-button-optimized:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 8px 20px rgba(244, 63, 94, 0.3);
|
||||
}
|
||||
|
||||
/* 限制动画范围 */
|
||||
@media screen and (max-width: 768px) {
|
||||
/* 移动端禁用复杂动画 */
|
||||
.neon-beam,
|
||||
.data-rain-column,
|
||||
.glitch-overlay {
|
||||
animation: none !important;
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* 简化背景 */
|
||||
.cyberpunk-grid {
|
||||
animation: none;
|
||||
opacity: 0.2;
|
||||
}
|
||||
|
||||
/* 减少模糊效果 */
|
||||
.glass-effect-optimized {
|
||||
backdrop-filter: blur(5px);
|
||||
-webkit-backdrop-filter: blur(5px);
|
||||
}
|
||||
}
|
||||
|
||||
/* 性能模式 - 自动检测低端设备 */
|
||||
@media (prefers-reduced-motion: reduce),
|
||||
(max-device-memory: 2GB),
|
||||
(max-device-width: 640px) {
|
||||
/* 禁用所有动画 */
|
||||
*,
|
||||
*::before,
|
||||
*::after {
|
||||
animation-play-state: paused !important;
|
||||
transition: none !important;
|
||||
}
|
||||
|
||||
/* 禁用模糊效果 */
|
||||
.glass-effect-optimized,
|
||||
.cyber-card-optimized {
|
||||
backdrop-filter: none;
|
||||
-webkit-backdrop-filter: none;
|
||||
background: rgba(10, 10, 10, 0.95);
|
||||
}
|
||||
|
||||
/* 禁用阴影 */
|
||||
* {
|
||||
box-shadow: none !important;
|
||||
text-shadow: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* 懒加载占位符 */
|
||||
.lazy-load-placeholder {
|
||||
background: linear-gradient(90deg,
|
||||
rgba(244, 63, 94, 0.1) 0%,
|
||||
rgba(168, 85, 247, 0.1) 50%,
|
||||
rgba(244, 63, 94, 0.1) 100%
|
||||
);
|
||||
animation: lazy-pulse 2s ease-in-out infinite;
|
||||
}
|
||||
|
||||
@keyframes lazy-pulse {
|
||||
0%, 100% { opacity: 0.5; }
|
||||
50% { opacity: 0.8; }
|
||||
}
|
||||
|
||||
/* 优化滚动性能 */
|
||||
.scroll-optimized {
|
||||
will-change: auto;
|
||||
contain: layout style paint;
|
||||
}
|
||||
|
||||
.scroll-optimized:hover {
|
||||
will-change: transform;
|
||||
}
|
||||
|
||||
/* 使用CSS Grid替代复杂布局 */
|
||||
.grid-optimized {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
|
||||
gap: 1.5rem;
|
||||
contain: layout;
|
||||
}
|
||||
|
||||
/* 图片优化 */
|
||||
img {
|
||||
content-visibility: auto;
|
||||
contain-intrinsic-size: 400px 300px;
|
||||
}
|
||||
|
||||
/* 字体优化 */
|
||||
@font-face {
|
||||
font-family: 'Orbitron';
|
||||
font-display: swap; /* 防止字体加载阻塞 */
|
||||
src: local('Orbitron'), url('https://fonts.googleapis.com/css2?family=Orbitron:wght@400;600;700&display=swap');
|
||||
}
|
||||
|
||||
/* 优化的渐变文字 */
|
||||
.gradient-text-optimized {
|
||||
background: linear-gradient(90deg, #f43f5e, #a855f7);
|
||||
-webkit-background-clip: text;
|
||||
background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
/* 移除动画避免重绘 */
|
||||
}
|
||||
|
||||
/* 硬件加速优化类 */
|
||||
.hw-accelerated {
|
||||
transform: translate3d(0, 0, 0);
|
||||
will-change: auto;
|
||||
}
|
||||
|
||||
/* 减少重排的布局 */
|
||||
.layout-optimized {
|
||||
contain: layout;
|
||||
content-visibility: auto;
|
||||
}
|
||||
92
src/utils/courseNavigation.ts
Normal file
92
src/utils/courseNavigation.ts
Normal file
@@ -0,0 +1,92 @@
|
||||
export interface CourseChapter {
|
||||
id: string;
|
||||
path: string;
|
||||
title: string;
|
||||
description: string;
|
||||
}
|
||||
|
||||
export const courseChapters: CourseChapter[] = [
|
||||
{
|
||||
id: 'automation-industry',
|
||||
path: '/course/automation-industry',
|
||||
title: '多Agent系统基础理论',
|
||||
description: '了解多Agent系统的基本概念与发展历程'
|
||||
},
|
||||
{
|
||||
id: 'plc-basics',
|
||||
path: '/course/plc-basics',
|
||||
title: '多Agent系统架构模式',
|
||||
description: '掌握四种主要架构模式的特点与应用'
|
||||
},
|
||||
{
|
||||
id: 'multi-agent-basics',
|
||||
path: '/course/multi-agent-basics',
|
||||
title: '多Agent系统基础架构',
|
||||
description: '深入理解多Agent系统的核心架构'
|
||||
},
|
||||
{
|
||||
id: 'agent-role-design',
|
||||
path: '/course/agent-role-design',
|
||||
title: 'Agent角色设计',
|
||||
description: '学习Agent角色的专业化分工策略'
|
||||
},
|
||||
{
|
||||
id: 'communication-mechanism',
|
||||
path: '/course/communication-mechanism',
|
||||
title: 'Agent间通信机制',
|
||||
description: '掌握Agent间的通信协议与信息交换'
|
||||
},
|
||||
{
|
||||
id: 'collaboration-management',
|
||||
path: '/course/collaboration-management',
|
||||
title: '协作管理与任务分配',
|
||||
description: '学习协作策略和任务分配机制'
|
||||
},
|
||||
{
|
||||
id: 'central-coordination',
|
||||
path: '/course/central-coordination',
|
||||
title: '中央协调与管理',
|
||||
description: '理解中央协调器的设计与实现'
|
||||
},
|
||||
{
|
||||
id: 'application-architecture',
|
||||
path: '/course/application-architecture',
|
||||
title: '实际应用架构',
|
||||
description: '掌握三大典型应用架构设计'
|
||||
},
|
||||
{
|
||||
id: 'a2a-protocol',
|
||||
path: '/course/a2a-protocol',
|
||||
title: 'A2A协议与标准化实践',
|
||||
description: '学习谷歌A2A协议的前沿实践'
|
||||
},
|
||||
{
|
||||
id: 'program-development',
|
||||
path: '/course/program-development',
|
||||
title: '关键角色职责图谱',
|
||||
description: '理解系统中各角色的职责分工'
|
||||
}
|
||||
];
|
||||
|
||||
export const getChapterNavigation = (currentPath: string) => {
|
||||
const currentIndex = courseChapters.findIndex(chapter => chapter.path === currentPath);
|
||||
|
||||
if (currentIndex === -1) {
|
||||
return { prev: null, next: null };
|
||||
}
|
||||
|
||||
const prev = currentIndex > 0 ? courseChapters[currentIndex - 1] : null;
|
||||
const next = currentIndex < courseChapters.length - 1 ? courseChapters[currentIndex + 1] : null;
|
||||
|
||||
return { prev, next };
|
||||
};
|
||||
|
||||
export const getNextChapter = (currentPath: string): CourseChapter | null => {
|
||||
const { next } = getChapterNavigation(currentPath);
|
||||
return next;
|
||||
};
|
||||
|
||||
export const getPrevChapter = (currentPath: string): CourseChapter | null => {
|
||||
const { prev } = getChapterNavigation(currentPath);
|
||||
return prev;
|
||||
};
|
||||
86
src/utils/performanceConfig.ts
Normal file
86
src/utils/performanceConfig.ts
Normal file
@@ -0,0 +1,86 @@
|
||||
// 性能配置文件
|
||||
export const performanceConfig = {
|
||||
// 动画配置
|
||||
animation: {
|
||||
// 减少动画持续时间
|
||||
duration: {
|
||||
fast: 0.2,
|
||||
normal: 0.3,
|
||||
slow: 0.5
|
||||
},
|
||||
// 简化的弹簧配置
|
||||
spring: {
|
||||
light: {
|
||||
type: "spring" as const,
|
||||
stiffness: 300,
|
||||
damping: 30
|
||||
},
|
||||
normal: {
|
||||
type: "spring" as const,
|
||||
stiffness: 200,
|
||||
damping: 25
|
||||
},
|
||||
heavy: {
|
||||
type: "spring" as const,
|
||||
stiffness: 100,
|
||||
damping: 20
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// 延迟加载配置
|
||||
lazyLoad: {
|
||||
threshold: 0.1,
|
||||
rootMargin: "50px"
|
||||
},
|
||||
|
||||
// 减少复杂效果
|
||||
effects: {
|
||||
enableGlitch: false, // 默认关闭故障效果
|
||||
enableParticles: false, // 关闭粒子效果
|
||||
enableComplexShadows: false, // 关闭复杂阴影
|
||||
reducedMotion: false // 减少动效模式
|
||||
},
|
||||
|
||||
// 防抖和节流配置
|
||||
optimization: {
|
||||
debounceDelay: 300,
|
||||
throttleDelay: 100,
|
||||
maxAnimationElements: 20 // 限制同时动画的元素数量
|
||||
}
|
||||
};
|
||||
|
||||
// 检测用户偏好
|
||||
export const detectPerformanceMode = () => {
|
||||
// 检查是否偏好减少动效
|
||||
const prefersReducedMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
|
||||
|
||||
// 检查设备性能
|
||||
const isLowEndDevice = navigator.hardwareConcurrency ? navigator.hardwareConcurrency < 4 : false;
|
||||
|
||||
// 检查连接速度
|
||||
const connection = (navigator as any).connection;
|
||||
const isSlowConnection = connection &&
|
||||
(connection.effectiveType === 'slow-2g' ||
|
||||
connection.effectiveType === '2g' ||
|
||||
connection.saveData);
|
||||
|
||||
return {
|
||||
prefersReducedMotion,
|
||||
isLowEndDevice,
|
||||
isSlowConnection,
|
||||
shouldReduceEffects: prefersReducedMotion || isLowEndDevice || isSlowConnection
|
||||
};
|
||||
};
|
||||
|
||||
// 动画优化hook
|
||||
export const useOptimizedAnimation = () => {
|
||||
const { shouldReduceEffects } = detectPerformanceMode();
|
||||
|
||||
return {
|
||||
transition: shouldReduceEffects
|
||||
? { duration: 0.1 }
|
||||
: performanceConfig.animation.spring.normal,
|
||||
animate: shouldReduceEffects ? false : true
|
||||
};
|
||||
};
|
||||
Reference in New Issue
Block a user