feat: 初始化多Agent协作系统项目并添加直播回放功能
- 添加导航栏组件及直播回放按钮 - 实现视频播放模态框 - 配置赛博朋克风格主题 - 添加课程首页和课程页面 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
45
.claude/settings.local.json
Normal file
@@ -0,0 +1,45 @@
|
||||
{
|
||||
"permissions": {
|
||||
"allow": [
|
||||
"mcp__serena__read_file",
|
||||
"mcp__serena__activate_project",
|
||||
"mcp__serena__check_onboarding_performed",
|
||||
"Bash(npm start)",
|
||||
"mcp__serena__search_for_pattern",
|
||||
"mcp__serena__replace_regex",
|
||||
"mcp__serena__list_dir",
|
||||
"mcp__serena__create_text_file",
|
||||
"Bash(rm:*)",
|
||||
"Bash(npm run build:*)",
|
||||
"mcp__serena__execute_shell_command",
|
||||
"Bash(pkill:*)",
|
||||
"Bash(mkdir:*)",
|
||||
"Bash(cp:*)",
|
||||
"Bash(git checkout:*)",
|
||||
"mcp__serena__replace_symbol_body",
|
||||
"mcp__serena__think_about_task_adherence",
|
||||
"mcp__serena__onboarding",
|
||||
"mcp__serena__write_memory",
|
||||
"Bash(lsof:*)",
|
||||
"Bash(PORT=3001 npm start)",
|
||||
"Bash(kill:*)",
|
||||
"Bash(python3:*)",
|
||||
"mcp__serena__find_file",
|
||||
"mcp__serena__think_about_whether_you_are_done",
|
||||
"mcp__serena__read_memory",
|
||||
"Bash(npx tsc:*)",
|
||||
"mcp__serena__get_symbols_overview",
|
||||
"Bash(grep:*)",
|
||||
"Bash(find:*)",
|
||||
"Bash(timeout 10 npm start)",
|
||||
"Bash(npm run dev:*)",
|
||||
"Bash(npm install:*)",
|
||||
"Bash(npm uninstall:*)",
|
||||
"Bash(git init:*)",
|
||||
"Bash(git remote add:*)",
|
||||
"Bash(git add:*)",
|
||||
"Bash(git commit -m \"$(cat <<''EOF''\nfeat: 初始化多Agent协作系统项目并添加直播回放功能\n\n- 添加导航栏组件及直播回放按钮\n- 实现视频播放模态框\n- 配置赛博朋克风格主题\n- 添加课程首页和课程页面\n\n🤖 Generated with [Claude Code](https://claude.com/claude-code)\n\nCo-Authored-By: Claude <noreply@anthropic.com>\nEOF\n)\")"
|
||||
],
|
||||
"deny": []
|
||||
}
|
||||
}
|
||||
35
.gitignore
vendored
Normal file
@@ -0,0 +1,35 @@
|
||||
# Dependencies
|
||||
node_modules/
|
||||
/.pnp
|
||||
.pnp.js
|
||||
|
||||
# Testing
|
||||
/coverage
|
||||
|
||||
# Production
|
||||
/build
|
||||
/dist
|
||||
|
||||
# Misc
|
||||
.DS_Store
|
||||
.env.local
|
||||
.env.development.local
|
||||
.env.test.local
|
||||
.env.production.local
|
||||
|
||||
# Logs
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
lerna-debug.log*
|
||||
|
||||
# IDE
|
||||
.vscode/
|
||||
.idea/
|
||||
*.swp
|
||||
*.swo
|
||||
*~
|
||||
|
||||
# Temporary files
|
||||
*.tmp
|
||||
.cache/
|
||||
1
.serena/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
/cache
|
||||
60
.serena/memories/code_style_conventions.md
Normal file
@@ -0,0 +1,60 @@
|
||||
# 代码风格与约定
|
||||
|
||||
## TypeScript风格
|
||||
- 使用TypeScript严格模式
|
||||
- 接口和类型定义使用PascalCase
|
||||
- 变量和函数使用camelCase
|
||||
- 常量使用UPPER_SNAKE_CASE
|
||||
|
||||
## React约定
|
||||
- 组件使用PascalCase命名
|
||||
- Props接口以Props结尾 (如: HomePageProps)
|
||||
- 使用函数式组件和Hooks
|
||||
- 导出组件使用export default
|
||||
|
||||
## 文件命名
|
||||
- 组件文件: PascalCase.tsx (如: HomePage.tsx)
|
||||
- 工具文件: camelCase.ts
|
||||
- 样式文件: kebab-case.css
|
||||
|
||||
## CSS/Tailwind约定
|
||||
- 优先使用Tailwind CSS类
|
||||
- 自定义CSS使用CSS变量
|
||||
- 颜色变量遵循命名模式: --bg-*, --surf-*, --accent-*
|
||||
- 响应式设计: mobile-first
|
||||
|
||||
## 项目特定约定
|
||||
- 课程相关页面放在 pages/course/ 目录
|
||||
- 图片资源放在 src/assets/images/
|
||||
- 组件props解构使用
|
||||
- 动画使用Framer Motion库
|
||||
|
||||
## 导入顺序
|
||||
1. React相关导入
|
||||
2. 第三方库导入
|
||||
3. 本地组件导入
|
||||
4. 类型导入
|
||||
5. 样式导入
|
||||
|
||||
## 组件结构
|
||||
```tsx
|
||||
import React from 'react';
|
||||
import { motion } from 'framer-motion';
|
||||
|
||||
interface ComponentProps {
|
||||
// props定义
|
||||
}
|
||||
|
||||
const Component: React.FC<ComponentProps> = ({ prop1, prop2 }) => {
|
||||
// hooks
|
||||
// 事件处理函数
|
||||
// 渲染逻辑
|
||||
return (
|
||||
<motion.div>
|
||||
{/* JSX */}
|
||||
</motion.div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Component;
|
||||
```
|
||||
40
.serena/memories/project_overview.md
Normal file
@@ -0,0 +1,40 @@
|
||||
# 项目概述
|
||||
|
||||
## 项目目的
|
||||
这是一个多Agent协作系统的教学网站项目,原本是PLC教学网站,现已转换为专注于IT运维风格的多Agent系统课程教学平台。
|
||||
|
||||
## 技术栈
|
||||
- **前端框架**: React 18.2.0 + TypeScript 4.9.5
|
||||
- **路由**: React Router DOM 6.20.1
|
||||
- **动画库**: Framer Motion 12.19.2
|
||||
- **拖拽库**: @dnd-kit/core 6.3.1, react-beautiful-dnd 13.1.1
|
||||
- **样式**: Tailwind CSS 3.3.6 + 自定义CSS
|
||||
- **构建工具**: React Scripts 5.0.1
|
||||
|
||||
## 项目结构
|
||||
- `src/`
|
||||
- `components/` - 可复用组件
|
||||
- `pages/` - 页面组件
|
||||
- `course/` - 课程相关页面
|
||||
- `assets/` - 静态资源
|
||||
- `public/` - 公共静态资源
|
||||
- `tailwind.config.js` - Tailwind配置
|
||||
- `tsconfig.json` - TypeScript配置
|
||||
|
||||
## 课程内容
|
||||
基于课程讲义.md,包含7大模块:
|
||||
1. 多Agent系统基础
|
||||
2. Agent角色设计
|
||||
3. Agent间通信机制
|
||||
4. 协作与任务管理
|
||||
5. 中央协调与管理
|
||||
6. 实际应用架构
|
||||
7. A2A协议案例讲解
|
||||
|
||||
## 设计风格
|
||||
- IT运维风格(IT风)
|
||||
- 深蓝主色调 (#0f172a)
|
||||
- 青色辅助色 (#0891b2)
|
||||
- 绿色强调色 (#10b981)
|
||||
- 玻璃拟态效果
|
||||
- 流畅动画交互
|
||||
57
.serena/memories/suggested_commands.md
Normal file
@@ -0,0 +1,57 @@
|
||||
# 建议命令
|
||||
|
||||
## 开发相关命令
|
||||
|
||||
### 启动开发服务器
|
||||
```bash
|
||||
npm start
|
||||
```
|
||||
|
||||
### 构建生产版本
|
||||
```bash
|
||||
npm run build
|
||||
```
|
||||
|
||||
### 运行测试
|
||||
```bash
|
||||
npm run test
|
||||
```
|
||||
|
||||
### 安装依赖
|
||||
```bash
|
||||
npm install
|
||||
```
|
||||
|
||||
## Git操作
|
||||
```bash
|
||||
git status
|
||||
git add .
|
||||
git commit -m "提交信息"
|
||||
git push
|
||||
```
|
||||
|
||||
## 文件查看
|
||||
```bash
|
||||
ls -la # 查看目录内容
|
||||
find . -name "*.tsx" # 查找TypeScript文件
|
||||
grep -r "搜索内容" src/ # 在src目录搜索内容
|
||||
```
|
||||
|
||||
## 系统工具 (Darwin)
|
||||
```bash
|
||||
open . # 在Finder中打开当前目录
|
||||
which node # 查看node路径
|
||||
ps aux | grep node # 查看node进程
|
||||
kill -9 <PID> # 结束进程
|
||||
```
|
||||
|
||||
## 开发工具
|
||||
- VS Code: 推荐的IDE
|
||||
- Chrome DevTools: 调试工具
|
||||
- React DevTools: React调试扩展
|
||||
|
||||
## 注意事项
|
||||
- 系统为Darwin (macOS)
|
||||
- 使用npm作为包管理器
|
||||
- TypeScript配置严格模式
|
||||
- 支持ES6+语法
|
||||
49
.serena/memories/task_completion_checklist.md
Normal file
@@ -0,0 +1,49 @@
|
||||
# 任务完成检查清单
|
||||
|
||||
## 编码任务完成后必须执行
|
||||
|
||||
### 1. 代码质量检查
|
||||
- [ ] 检查TypeScript类型错误
|
||||
- [ ] 确保所有组件正确导入导出
|
||||
- [ ] 检查控制台错误和警告
|
||||
- [ ] 验证响应式设计在不同设备上的表现
|
||||
|
||||
### 2. 功能测试
|
||||
- [ ] 测试新功能的基本操作
|
||||
- [ ] 验证路由跳转正常
|
||||
- [ ] 测试动画效果流畅
|
||||
- [ ] 检查交互元素响应性
|
||||
|
||||
### 3. 样式检查
|
||||
- [ ] 验证IT运维风格主题一致性
|
||||
- [ ] 检查颜色变量使用正确
|
||||
- [ ] 确保玻璃拟态效果正常
|
||||
- [ ] 验证Tailwind类名正确应用
|
||||
|
||||
### 4. 性能检查
|
||||
- [ ] 检查页面加载速度
|
||||
- [ ] 验证图片资源优化
|
||||
- [ ] 确保不存在内存泄漏
|
||||
|
||||
### 5. 浏览器兼容性
|
||||
- [ ] Chrome测试
|
||||
- [ ] Safari测试 (Darwin系统)
|
||||
- [ ] 移动端Safari测试
|
||||
|
||||
## 课程内容任务特定检查
|
||||
|
||||
### 课程页面创建
|
||||
- [ ] 页面结构符合课程讲义.md内容
|
||||
- [ ] 包含所有必要的学习目标
|
||||
- [ ] 交互式元素功能正常
|
||||
- [ ] 导航链接正确配置
|
||||
|
||||
### 样式更新任务
|
||||
- [ ] 色彩搭配符合IT运维风格
|
||||
- [ ] 动画缓动效果自然
|
||||
- [ ] 响应式布局适配各设备
|
||||
|
||||
## 提交前最终检查
|
||||
- [ ] 运行 npm start 确保应用启动正常
|
||||
- [ ] 快速浏览所有页面确保无明显问题
|
||||
- [ ] 检查git状态,确保没有遗漏文件
|
||||
68
.serena/project.yml
Normal file
@@ -0,0 +1,68 @@
|
||||
# language of the project (csharp, python, rust, java, typescript, go, cpp, or ruby)
|
||||
# * For C, use cpp
|
||||
# * For JavaScript, use typescript
|
||||
# Special requirements:
|
||||
# * csharp: Requires the presence of a .sln file in the project folder.
|
||||
language: typescript
|
||||
|
||||
# whether to use the project's gitignore file to ignore files
|
||||
# Added on 2025-04-07
|
||||
ignore_all_files_in_gitignore: true
|
||||
# list of additional paths to ignore
|
||||
# same syntax as gitignore, so you can use * and **
|
||||
# Was previously called `ignored_dirs`, please update your config if you are using that.
|
||||
# Added (renamed)on 2025-04-07
|
||||
ignored_paths: []
|
||||
|
||||
# whether the project is in read-only mode
|
||||
# If set to true, all editing tools will be disabled and attempts to use them will result in an error
|
||||
# Added on 2025-04-18
|
||||
read_only: false
|
||||
|
||||
|
||||
# list of tool names to exclude. We recommend not excluding any tools, see the readme for more details.
|
||||
# Below is the complete list of tools for convenience.
|
||||
# To make sure you have the latest list of tools, and to view their descriptions,
|
||||
# execute `uv run scripts/print_tool_overview.py`.
|
||||
#
|
||||
# * `activate_project`: Activates a project by name.
|
||||
# * `check_onboarding_performed`: Checks whether project onboarding was already performed.
|
||||
# * `create_text_file`: Creates/overwrites a file in the project directory.
|
||||
# * `delete_lines`: Deletes a range of lines within a file.
|
||||
# * `delete_memory`: Deletes a memory from Serena's project-specific memory store.
|
||||
# * `execute_shell_command`: Executes a shell command.
|
||||
# * `find_referencing_code_snippets`: Finds code snippets in which the symbol at the given location is referenced.
|
||||
# * `find_referencing_symbols`: Finds symbols that reference the symbol at the given location (optionally filtered by type).
|
||||
# * `find_symbol`: Performs a global (or local) search for symbols with/containing a given name/substring (optionally filtered by type).
|
||||
# * `get_current_config`: Prints the current configuration of the agent, including the active and available projects, tools, contexts, and modes.
|
||||
# * `get_symbols_overview`: Gets an overview of the top-level symbols defined in a given file or directory.
|
||||
# * `initial_instructions`: Gets the initial instructions for the current project.
|
||||
# Should only be used in settings where the system prompt cannot be set,
|
||||
# e.g. in clients you have no control over, like Claude Desktop.
|
||||
# * `insert_after_symbol`: Inserts content after the end of the definition of a given symbol.
|
||||
# * `insert_at_line`: Inserts content at a given line in a file.
|
||||
# * `insert_before_symbol`: Inserts content before the beginning of the definition of a given symbol.
|
||||
# * `list_dir`: Lists files and directories in the given directory (optionally with recursion).
|
||||
# * `list_memories`: Lists memories in Serena's project-specific memory store.
|
||||
# * `onboarding`: Performs onboarding (identifying the project structure and essential tasks, e.g. for testing or building).
|
||||
# * `prepare_for_new_conversation`: Provides instructions for preparing for a new conversation (in order to continue with the necessary context).
|
||||
# * `read_file`: Reads a file within the project directory.
|
||||
# * `read_memory`: Reads the memory with the given name from Serena's project-specific memory store.
|
||||
# * `remove_project`: Removes a project from the Serena configuration.
|
||||
# * `replace_lines`: Replaces a range of lines within a file with new content.
|
||||
# * `replace_symbol_body`: Replaces the full definition of a symbol.
|
||||
# * `restart_language_server`: Restarts the language server, may be necessary when edits not through Serena happen.
|
||||
# * `search_for_pattern`: Performs a search for a pattern in the project.
|
||||
# * `summarize_changes`: Provides instructions for summarizing the changes made to the codebase.
|
||||
# * `switch_modes`: Activates modes by providing a list of their names
|
||||
# * `think_about_collected_information`: Thinking tool for pondering the completeness of collected information.
|
||||
# * `think_about_task_adherence`: Thinking tool for determining whether the agent is still on track with the current task.
|
||||
# * `think_about_whether_you_are_done`: Thinking tool for determining whether the task is truly completed.
|
||||
# * `write_memory`: Writes a named memory (for future reference) to Serena's project-specific memory store.
|
||||
excluded_tools: []
|
||||
|
||||
# initial prompt for the project. It will always be given to the LLM upon activating the project
|
||||
# (contrary to the memories, which are loaded on demand).
|
||||
initial_prompt: ""
|
||||
|
||||
project_name: "education_web_外贸业务的核心流程及关键角色"
|
||||
BIN
.yoyo/db/snapshot_embeddings.sqlitejs
Normal file
1
.yoyo/snapshot
Submodule
100
README-教学网页.md
Normal file
@@ -0,0 +1,100 @@
|
||||
# 外贸业务核心流程及关键角色教学网页
|
||||
|
||||
## 📖 项目简介
|
||||
|
||||
这是一个基于React和TypeScript开发的外贸业务基础课程教学网页,采用现代化的玻璃风格UI设计,为大专院校国际贸易专业学生提供优质的学习体验。
|
||||
|
||||
## 🎯 课程内容
|
||||
|
||||
### 核心模块
|
||||
- **外贸业务全景图**:跨境交易定义、目标、发展历程及面临的关键挑战
|
||||
- **外贸业务框架**:政策支持、产业链协作与服务支撑体系
|
||||
- **外贸核心流程**:客户开发、风险筛查、合同签订、生产备货、物流通关、货款回收
|
||||
- **关键角色职责图谱**:出口商、进口商、外贸经理、货代、银行、海关等角色定位
|
||||
|
||||
### 学生能力培养目标
|
||||
- 掌握外贸业务全流程操作能力
|
||||
- 具备客户开发与商务谈判技能
|
||||
- 理解国际物流与报关操作
|
||||
- 掌握外汇管理与风险控制
|
||||
|
||||
### 职业发展方向
|
||||
- **外贸业务主管**:需求量大,发展前景好
|
||||
- **外贸业务员**:入门岗位,成长空间广阔
|
||||
- **物流单证员**:专业性强,薪资稳定
|
||||
- **外贸跟单员**:综合能力要求高
|
||||
- **国际采购助理**:供应链管理核心岗位
|
||||
|
||||
## 🎨 设计特色
|
||||
|
||||
- **现代玻璃风格UI**:采用最新的玻璃拟态设计,视觉效果现代时尚
|
||||
- **响应式布局**:完美适配各种设备尺寸
|
||||
- **流畅交互动画**:使用Framer Motion提供丝滑的用户体验
|
||||
- **渐变色彩搭配**:精心设计的色彩方案,营造专业学习氛围
|
||||
|
||||
## 🚀 技术栈
|
||||
|
||||
- **前端框架**: React 18 + TypeScript
|
||||
- **样式系统**: Tailwind CSS + 自定义玻璃效果
|
||||
- **动画库**: Framer Motion
|
||||
- **路由管理**: React Router DOM
|
||||
- **图标系统**: Lucide React
|
||||
- **构建工具**: Create React App
|
||||
|
||||
## 📱 页面结构
|
||||
|
||||
### 主要页面
|
||||
- **首页**: 课程概览与模块导航
|
||||
- **学习目标**: 详细的能力培养目标
|
||||
- **职业发展**: 外贸相关职业路径介绍
|
||||
- **课程总结**: 知识点梳理与总结
|
||||
- **课堂测试**: 多种题型的在线测试
|
||||
|
||||
### 课程章节
|
||||
- **外贸业务全景图**: 跨境交易基础认知与模式分类
|
||||
- **外贸业务框架**: 生态系统、政策支持与产业协作
|
||||
- **外贸核心流程**: 六大核心环节详细解析
|
||||
- **关键角色职责图谱**: 各角色定位与协作关系
|
||||
|
||||
## 🎯 教学特色
|
||||
|
||||
### 实践案例教学
|
||||
- **智能手表出口案例**: 完整业务流程演示
|
||||
- **文具出口实操**: 订单执行全流程跟踪
|
||||
- **茶叶出口案例**: 国际物流与报关实务
|
||||
|
||||
### 互动学习体验
|
||||
- **课堂测试系统**: 选择题、填空题、连线题、排序题
|
||||
- **作业练习平台**: 拖拽式业务流程构建
|
||||
- **进度跟踪功能**: 学习进度可视化展示
|
||||
|
||||
### 知识点覆盖
|
||||
- B2B/B2C/C2C贸易模式对比
|
||||
- 国际贸易术语与结算方式
|
||||
- 跨境物流与报关实务
|
||||
- 外汇管理与风险防范
|
||||
- 客户开发与商务谈判
|
||||
|
||||
## 🔧 本地开发
|
||||
|
||||
```bash
|
||||
# 克隆项目
|
||||
git clone [repository-url]
|
||||
|
||||
# 安装依赖
|
||||
npm install
|
||||
|
||||
# 启动开发服务器
|
||||
npm start
|
||||
|
||||
# 构建生产版本
|
||||
npm run build
|
||||
```
|
||||
|
||||
## 📄 许可证
|
||||
|
||||
本项目采用 MIT 许可证 - 查看 [LICENSE](LICENSE) 文件了解详情
|
||||
|
||||
## 🙏 致谢
|
||||
|
||||
**致谢**:感谢所有为外贸教育事业贡献力量的教育工作者和行业专家!
|
||||
114
README.md
Normal file
@@ -0,0 +1,114 @@
|
||||
# Liquid Glass React
|
||||
|
||||
Apple's Liquid Glass effect for React.
|
||||
|
||||
Card Example | Button Example
|
||||
:-------------------------:|:-------------------------:
|
||||
 | 
|
||||
|
||||
## 🎬 Demo
|
||||
|
||||
[Click here](https://liquid-glass.maxrovensky.com) to see it in action!
|
||||
|
||||

|
||||
|
||||
## ✨ Features
|
||||
|
||||
- Proper edgy bending and refraction
|
||||
- Multiple refraction modes
|
||||
- Configurable frosty level
|
||||
- Supports arbitrary child elements
|
||||
- Configurable paddings
|
||||
- Correct hover and click effects
|
||||
- Edges and highlights take on the underlying light like Apple's does
|
||||
- Configurable chromatic aberration
|
||||
- Configurable elasticity, to mimic Apple's "liquid" feel
|
||||
|
||||
> **⚠️ NOTE:** Safari and Firefox only partially support the effect (displacement will not be visible)
|
||||
|
||||
## 🚀 Usage
|
||||
|
||||
### Installation
|
||||
|
||||
```bash
|
||||
npm install liquid-glass-react
|
||||
```
|
||||
|
||||
### Basic Usage
|
||||
|
||||
```tsx
|
||||
import LiquidGlass from 'liquid-glass-react'
|
||||
|
||||
function App() {
|
||||
return (
|
||||
<LiquidGlass>
|
||||
<div className="p-6">
|
||||
<h2>Your content here</h2>
|
||||
<p>This will have the liquid glass effect</p>
|
||||
</div>
|
||||
</LiquidGlass>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
### Button Example
|
||||
|
||||
```tsx
|
||||
<LiquidGlass
|
||||
displacementScale={64}
|
||||
blurAmount={0.1}
|
||||
saturation={130}
|
||||
aberrationIntensity={2}
|
||||
elasticity={0.35}
|
||||
cornerRadius={100}
|
||||
padding="8px 16px"
|
||||
onClick={() => console.log('Button clicked!')}
|
||||
>
|
||||
<span className="text-white font-medium">Click Me</span>
|
||||
</LiquidGlass>
|
||||
```
|
||||
|
||||
### Mouse Container Example
|
||||
|
||||
When you want the glass effect to respond to mouse movement over a larger area (like a parent container), use the `mouseContainer` prop:
|
||||
|
||||
```tsx
|
||||
function App() {
|
||||
const containerRef = useRef<HTMLDivElement>(null)
|
||||
|
||||
return (
|
||||
<div ref={containerRef} className="w-full h-screen bg-image">
|
||||
<LiquidGlass
|
||||
mouseContainer={containerRef}
|
||||
elasticity={0.3}
|
||||
style={{ position: 'fixed', top: '50%', left: '50%' }}
|
||||
>
|
||||
<div className="p-6">
|
||||
<h2>Glass responds to mouse anywhere in the container</h2>
|
||||
</div>
|
||||
</LiquidGlass>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
## Props
|
||||
|
||||
| Prop | Type | Default | Description |
|
||||
|------|------|---------|-------------|
|
||||
| `children` | `React.ReactNode` | - | The content to render inside the glass container |
|
||||
| `displacementScale` | `number` | `70` | Controls the intensity of the displacement effect |
|
||||
| `blurAmount` | `number` | `0.0625` | Controls the blur/frosting level |
|
||||
| `saturation` | `number` | `140` | Controls color saturation of the glass effect |
|
||||
| `aberrationIntensity` | `number` | `2` | Controls chromatic aberration intensity |
|
||||
| `elasticity` | `number` | `0.15` | Controls the "liquid" elastic feel (0 = rigid, higher = more elastic) |
|
||||
| `cornerRadius` | `number` | `999` | Border radius in pixels |
|
||||
| `className` | `string` | `""` | Additional CSS classes |
|
||||
| `padding` | `string` | - | CSS padding value |
|
||||
| `style` | `React.CSSProperties` | - | Additional inline styles |
|
||||
| `overLight` | `boolean` | `false` | Whether the glass is over a light background |
|
||||
| `onClick` | `() => void` | - | Click handler |
|
||||
| `mouseContainer` | `React.RefObject<HTMLElement \| null> \| null` | `null` | Container element to track mouse movement on (defaults to the glass component itself) |
|
||||
| `mode` | `"standard" \| "polar" \| "prominent" \| "shader"` | `"standard"` | Refraction mode for different visual effects. `shader` is the most accurate but not the most stable. |
|
||||
| `globalMousePos` | `{ x: number; y: number }` | - | Global mouse position coordinates for manual control |
|
||||
| `mouseOffset` | `{ x: number; y: number }` | - | Mouse position offset for fine-tuning positioning |
|
||||
1307
install.sh
Normal file
17687
package-lock.json
generated
Normal file
52
package.json
Normal file
@@ -0,0 +1,52 @@
|
||||
{
|
||||
"name": "foreign-trade-education-web",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@dnd-kit/core": "^6.3.1",
|
||||
"@dnd-kit/sortable": "^10.0.0",
|
||||
"@dnd-kit/utilities": "^3.2.2",
|
||||
"@types/node": "^16.18.68",
|
||||
"@types/react": "^18.2.43",
|
||||
"@types/react-dom": "^18.2.17",
|
||||
"framer-motion": "^12.19.2",
|
||||
"react": "^18.2.0",
|
||||
"react-beautiful-dnd": "^13.1.1",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-router-dom": "^6.20.1",
|
||||
"react-scripts": "5.0.1",
|
||||
"typescript": "^4.9.5",
|
||||
"web-vitals": "^2.1.4"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "react-scripts start",
|
||||
"build": "react-scripts build",
|
||||
"test": "react-scripts test",
|
||||
"eject": "react-scripts eject"
|
||||
},
|
||||
"eslintConfig": {
|
||||
"extends": [
|
||||
"react-app",
|
||||
"react-app/jest"
|
||||
]
|
||||
},
|
||||
"browserslist": {
|
||||
"production": [
|
||||
">0.2%",
|
||||
"not dead",
|
||||
"not op_mini all"
|
||||
],
|
||||
"development": [
|
||||
"last 1 chrome version",
|
||||
"last 1 firefox version",
|
||||
"last 1 safari version"
|
||||
]
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/jest": "^27.5.2",
|
||||
"@types/react-beautiful-dnd": "^13.1.8",
|
||||
"autoprefixer": "^10.4.16",
|
||||
"postcss": "^8.4.32",
|
||||
"tailwindcss": "^3.3.6"
|
||||
}
|
||||
}
|
||||
6
postcss.config.js
Normal file
@@ -0,0 +1,6 @@
|
||||
module.exports = {
|
||||
plugins: {
|
||||
tailwindcss: {},
|
||||
autoprefixer: {},
|
||||
},
|
||||
}
|
||||
BIN
public/images/一、为什么你觉得就业越来越难?/AI关键词的招聘信息增长.jpeg
Normal file
|
After Width: | Height: | Size: 214 KiB |
|
After Width: | Height: | Size: 128 KiB |
BIN
public/images/三、企业需要AI的原因/三、企业需要AI的原因_AI人才成稀缺物种.png
Normal file
|
After Width: | Height: | Size: 2.0 MiB |
BIN
public/images/三、企业需要AI的原因/三、企业需要AI的原因_勒紧裤腰带.png
Normal file
|
After Width: | Height: | Size: 670 KiB |
BIN
public/images/三、企业需要AI的原因/三、企业需要AI的原因_提升工作效率.png
Normal file
|
After Width: | Height: | Size: 1.7 MiB |
BIN
public/images/三、企业需要AI的原因/三、企业需要AI的原因_节省人力成本.png
Normal file
|
After Width: | Height: | Size: 933 KiB |
BIN
public/images/二、和AI相关的岗位薪资那么高?/严峻现实.png
Normal file
|
After Width: | Height: | Size: 806 KiB |
BIN
public/images/二、和AI相关的岗位薪资那么高?/严峻现实2.png
Normal file
|
After Width: | Height: | Size: 2.3 MiB |
BIN
public/images/二、和AI相关的岗位薪资那么高?/薪资提升-1.png
Normal file
|
After Width: | Height: | Size: 116 KiB |
BIN
public/images/二、和AI相关的岗位薪资那么高?/薪资提升-2.png
Normal file
|
After Width: | Height: | Size: 87 KiB |
BIN
public/images/二、和AI相关的岗位薪资那么高?/薪资提升-3.png
Normal file
|
After Width: | Height: | Size: 109 KiB |
BIN
public/images/五、大专生学会用AI的最大问题/五、大专生学会用AI的最大问题_写作问答类AI工具logo.jpg
Normal file
|
After Width: | Height: | Size: 360 KiB |
BIN
public/images/五、大专生学会用AI的最大问题/五、大专生学会用AI的最大问题_图像创作类AI工具logo.jpg
Normal file
|
After Width: | Height: | Size: 429 KiB |
BIN
public/images/五、大专生学会用AI的最大问题/五、大专生学会用AI的最大问题_编程开发类AI工具logo.jpg
Normal file
|
After Width: | Height: | Size: 360 KiB |
BIN
public/images/五、大专生学会用AI的最大问题/五、大专生学会用AI的最大问题_视频生成类AI工具logo.jpg
Normal file
|
After Width: | Height: | Size: 666 KiB |
BIN
public/images/四、AI正在渗透所有行业/四、AI正在渗透所有行业_各行各业_AI生成图片的界面.png
Normal file
|
After Width: | Height: | Size: 1.6 MiB |
BIN
public/images/四、AI正在渗透所有行业/四、AI正在渗透所有行业_各行各业_Claude编程界面.png
Normal file
|
After Width: | Height: | Size: 610 KiB |
BIN
public/images/四、AI正在渗透所有行业/四、AI正在渗透所有行业_各行各业_N8N工作流节点.png
Normal file
|
After Width: | Height: | Size: 854 KiB |
BIN
public/images/四、AI正在渗透所有行业/四、AI正在渗透所有行业_各行各业_软件中AI分析界面.png
Normal file
|
After Width: | Height: | Size: 2.2 MiB |
BIN
public/images/四、AI正在渗透所有行业/四、AI正在渗透所有行业_各行各业_达芬奇视频剪辑界面.png
Normal file
|
After Width: | Height: | Size: 1.8 MiB |
BIN
public/images/四、AI正在渗透所有行业/四、日常生活_AI客服.png
Normal file
|
After Width: | Height: | Size: 753 KiB |
BIN
public/images/四、AI正在渗透所有行业/四、日常生活_AI绘图.png
Normal file
|
After Width: | Height: | Size: 1.8 MiB |
BIN
public/images/四、AI正在渗透所有行业/四、日常生活_数字人.png
Normal file
|
After Width: | Height: | Size: 658 KiB |
BIN
public/images/课程页面/AI基础课/Agent自动化工作流.png
Normal file
|
After Width: | Height: | Size: 138 KiB |
BIN
public/images/课程页面/AI基础课/上下文设计与表达逻辑.png
Normal file
|
After Width: | Height: | Size: 57 KiB |
BIN
public/images/课程页面/AI基础课/各种类型的AI工具.png
Normal file
|
After Width: | Height: | Size: 411 KiB |
BIN
public/images/课程页面/AI基础课/提示词工程.png
Normal file
|
After Width: | Height: | Size: 224 KiB |
BIN
public/images/课程页面/图片效果/1.生成页面.jpeg
Normal file
|
After Width: | Height: | Size: 596 KiB |
BIN
public/images/课程页面/图片效果/2.主页.jpeg
Normal file
|
After Width: | Height: | Size: 454 KiB |
BIN
public/images/课程页面/图片效果/3.产品页.jpeg
Normal file
|
After Width: | Height: | Size: 307 KiB |
BIN
public/images/课程页面/图片效果/4.产品详情页.jpeg
Normal file
|
After Width: | Height: | Size: 244 KiB |
BIN
public/images/课程页面/图片效果/5.联系我们.jpeg
Normal file
|
After Width: | Height: | Size: 335 KiB |
BIN
public/images/课程页面/图片效果/七、我们的课程设置_第二阶段_案例展示二AI编程开发小型商品展示网站-1.png
Normal file
|
After Width: | Height: | Size: 276 KiB |
BIN
public/images/课程页面/图片效果/七、我们的课程设置_第二阶段_案例展示二AI编程开发小型商品展示网站-2.png
Normal file
|
After Width: | Height: | Size: 377 KiB |
BIN
public/images/课程页面/图片效果/七、我们的课程设置_第二阶段_案例展示二AI编程开发小型商品展示网站-3.png
Normal file
|
After Width: | Height: | Size: 37 KiB |
BIN
public/images/课程页面/图片效果/七、我们的课程设置_第二阶段_案例展示二AI编程开发小型商品展示网站-4.png
Normal file
|
After Width: | Height: | Size: 487 KiB |
BIN
public/images/课程页面/图片效果/奶茶海报1.png
Normal file
|
After Width: | Height: | Size: 4.0 MiB |
BIN
public/images/课程页面/图片效果/奶茶海报2.png
Normal file
|
After Width: | Height: | Size: 3.8 MiB |
BIN
public/images/课程页面/图片效果/奶茶海报3.png
Normal file
|
After Width: | Height: | Size: 3.9 MiB |
BIN
public/images/课程页面/图片效果/奶茶海报4.png
Normal file
|
After Width: | Height: | Size: 4.1 MiB |
BIN
public/images/课程页面/第一阶段轮播图/七、我们的课程设置_AI工具logo轮播图.png
Normal file
|
After Width: | Height: | Size: 534 KiB |
BIN
public/images/课程页面/第一阶段轮播图/七、构建属于自己的AI工作流系统.png
Normal file
|
After Width: | Height: | Size: 236 KiB |
BIN
public/images/课程页面/第一阶段轮播图/七、网页作品集-1.png
Normal file
|
After Width: | Height: | Size: 370 KiB |
BIN
public/images/课程页面/第一阶段轮播图/七、网页作品集-2.png
Normal file
|
After Width: | Height: | Size: 893 KiB |
BIN
public/images/课程页面/第三阶段/七、我们的课程设置_第三阶段_案例展示AI财经商贸智能财务助理.png
Normal file
|
After Width: | Height: | Size: 203 KiB |
BIN
public/images/课程页面/第三阶段/第三阶段ai赋能流程n8n图片1.png
Normal file
|
After Width: | Height: | Size: 113 KiB |
BIN
public/images/课程页面/第三阶段/第三阶段ai赋能流程n8n图片2.png
Normal file
|
After Width: | Height: | Size: 92 KiB |
BIN
public/images/课程页面/订单班图片/七、我们的课程设置_第二阶段_涵盖方向_大健康.jpg
Normal file
|
After Width: | Height: | Size: 136 KiB |
BIN
public/images/课程页面/订单班图片/七、我们的课程设置_第二阶段_涵盖方向_文旅.jpg
Normal file
|
After Width: | Height: | Size: 341 KiB |
BIN
public/images/课程页面/订单班图片/七、我们的课程设置_第二阶段_涵盖方向_智能制造.png
Normal file
|
After Width: | Height: | Size: 402 KiB |
BIN
public/images/课程页面/订单班图片/七、我们的课程设置_第二阶段_涵盖方向_智能开发.jpg
Normal file
|
After Width: | Height: | Size: 206 KiB |
BIN
public/images/课程页面/订单班图片/七、我们的课程设置_第二阶段_涵盖方向_视觉设计.jpg
Normal file
|
After Width: | Height: | Size: 179 KiB |
BIN
public/images/课程页面/订单班图片/七、我们的课程设置_第二阶段_涵盖方向_财经商贸.jpg
Normal file
|
After Width: | Height: | Size: 212 KiB |
32
public/index.html
Normal file
@@ -0,0 +1,32 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<meta name="theme-color" content="#000000" />
|
||||
<meta
|
||||
name="description"
|
||||
content="多Agent协作系统教学网页 - 现代化玻璃风格界面"
|
||||
/>
|
||||
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
|
||||
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
|
||||
<title>多Agent协作系统</title>
|
||||
<style>
|
||||
body {
|
||||
margin: 0;
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
|
||||
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
|
||||
sans-serif;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
min-height: 100vh;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<noscript>您需要启用JavaScript来运行此应用程序。</noscript>
|
||||
<div id="root"></div>
|
||||
</body>
|
||||
</html>
|
||||
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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
1084
src/pages/HomePage.backup_20251101_102331.tsx
Normal file
1089
src/pages/HomePage.backup_20251101_153155.tsx
Normal file
1084
src/pages/HomePage.fullpage_backup_20251101_104226.tsx
Normal file
1156
src/pages/HomePage.tsx
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
@@ -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
@@ -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
|
||||
};
|
||||
};
|
||||
174
tailwind.config.js
Normal file
@@ -0,0 +1,174 @@
|
||||
/** @type {import('tailwindcss').Config} */
|
||||
module.exports = {
|
||||
content: [
|
||||
"./src/**/*.{js,jsx,ts,tsx}",
|
||||
],
|
||||
theme: {
|
||||
extend: {
|
||||
colors: {
|
||||
// 赛博朋克主色调 - 霓虹粉紫
|
||||
'cyber-pink': {
|
||||
900: '#831843',
|
||||
800: '#9f1239',
|
||||
700: '#be123c',
|
||||
600: '#e11d48',
|
||||
500: '#f43f5e',
|
||||
400: '#fb7185',
|
||||
300: '#fda4af',
|
||||
200: '#fecdd3',
|
||||
100: '#ffe4e6',
|
||||
50: '#fff1f2',
|
||||
},
|
||||
// 赛博朋克霓虹蓝
|
||||
'neon-blue': {
|
||||
900: '#1e3a8a',
|
||||
800: '#1e40af',
|
||||
700: '#1d4ed8',
|
||||
600: '#2563eb',
|
||||
500: '#3b82f6',
|
||||
400: '#60a5fa',
|
||||
300: '#93c5fd',
|
||||
200: '#bfdbfe',
|
||||
100: '#dbeafe',
|
||||
50: '#eff6ff',
|
||||
},
|
||||
// 赛博朋克霓虹紫
|
||||
'neon-purple': {
|
||||
900: '#581c87',
|
||||
800: '#6b21a8',
|
||||
700: '#7c3aed',
|
||||
600: '#9333ea',
|
||||
500: '#a855f7',
|
||||
400: '#c084fc',
|
||||
300: '#d8b4fe',
|
||||
200: '#e9d5ff',
|
||||
100: '#f3e8ff',
|
||||
50: '#faf5ff',
|
||||
},
|
||||
// 赛博朋克霓虹青
|
||||
'neon-cyan': {
|
||||
900: '#164e63',
|
||||
800: '#155e75',
|
||||
700: '#0e7490',
|
||||
600: '#0891b2',
|
||||
500: '#06b6d4',
|
||||
400: '#22d3ee',
|
||||
300: '#67e8f9',
|
||||
200: '#a5f3fc',
|
||||
100: '#cffafe',
|
||||
50: '#ecfeff',
|
||||
},
|
||||
// 赛博朋克暗黑背景
|
||||
'cyber-dark': {
|
||||
950: '#030712',
|
||||
900: '#0a0a0a',
|
||||
800: '#171717',
|
||||
700: '#262626',
|
||||
600: '#404040',
|
||||
500: '#525252',
|
||||
400: '#737373',
|
||||
300: '#a3a3a3',
|
||||
200: '#d4d4d4',
|
||||
100: '#f5f5f5',
|
||||
50: '#fafafa',
|
||||
},
|
||||
// 赛博朋克霓虹黄
|
||||
'neon-yellow': {
|
||||
900: '#713f12',
|
||||
800: '#854d0e',
|
||||
700: '#a16207',
|
||||
600: '#ca8a04',
|
||||
500: '#eab308',
|
||||
400: '#facc15',
|
||||
300: '#fde047',
|
||||
200: '#fef08a',
|
||||
100: '#fef3c7',
|
||||
50: '#fefce8',
|
||||
},
|
||||
// 保留旧色系用于快速迁移
|
||||
base: {
|
||||
50: '#E5DFD7',
|
||||
800: '#8A9B8F',
|
||||
900: 'color-mix(in oklab, #7A9E9F 86%, #000 14%)'
|
||||
},
|
||||
accent: {
|
||||
500: '#D4B483'
|
||||
},
|
||||
'surf-tundra': {
|
||||
300: '#67e8f9',
|
||||
400: '#22d3ee',
|
||||
500: '#06b6d4',
|
||||
600: '#0891b2',
|
||||
},
|
||||
'accent-sand': {
|
||||
400: '#4ade80',
|
||||
500: '#22c55e',
|
||||
600: '#16a34a',
|
||||
}
|
||||
},
|
||||
backdropBlur: {
|
||||
xs: '2px',
|
||||
sm: '8px',
|
||||
md: '12px',
|
||||
lg: '16px',
|
||||
xl: '24px',
|
||||
'2xl': '40px',
|
||||
},
|
||||
backdropSaturate: {
|
||||
150: '150%',
|
||||
175: '175%',
|
||||
200: '200%',
|
||||
},
|
||||
boxShadow: {
|
||||
'neon-pink': '0 0 20px rgba(244, 63, 94, 0.5), 0 0 40px rgba(244, 63, 94, 0.3), 0 0 60px rgba(244, 63, 94, 0.1)',
|
||||
'neon-blue': '0 0 20px rgba(59, 130, 246, 0.5), 0 0 40px rgba(59, 130, 246, 0.3), 0 0 60px rgba(59, 130, 246, 0.1)',
|
||||
'neon-purple': '0 0 20px rgba(168, 85, 247, 0.5), 0 0 40px rgba(168, 85, 247, 0.3), 0 0 60px rgba(168, 85, 247, 0.1)',
|
||||
'neon-cyan': '0 0 20px rgba(6, 182, 212, 0.5), 0 0 40px rgba(6, 182, 212, 0.3), 0 0 60px rgba(6, 182, 212, 0.1)',
|
||||
'glass': '0 8px 32px rgba(0, 0, 0, 0.4), 0 0 0 0.5px rgba(255, 255, 255, 0.1) inset',
|
||||
'glass-hover': '0 12px 48px rgba(0, 0, 0, 0.5), 0 0 0 0.5px rgba(255, 255, 255, 0.2) inset',
|
||||
'glass-sm': '0 4px 24px rgba(0, 0, 0, 0.3), 0 0 0 0.5px rgba(255, 255, 255, 0.08) inset',
|
||||
'glass-lg': '0 20px 60px rgba(0, 0, 0, 0.4), 0 0 0 0.5px rgba(255, 255, 255, 0.15) inset',
|
||||
'inner-light': '0 0 0 0.5px rgba(255, 255, 255, 0.1) inset',
|
||||
},
|
||||
animation: {
|
||||
'fade-in': 'fadeIn 0.5s ease-in-out',
|
||||
'slide-up': 'slideUp 0.5s ease-out',
|
||||
'pulse-slow': 'pulse 3s ease-in-out infinite',
|
||||
'float': 'float 6s ease-in-out infinite',
|
||||
'neon-flicker': 'neonFlicker 2s infinite alternate',
|
||||
'glitch': 'glitch 1s infinite',
|
||||
'scan-line': 'scanLine 8s linear infinite',
|
||||
},
|
||||
keyframes: {
|
||||
fadeIn: {
|
||||
'0%': { opacity: '0' },
|
||||
'100%': { opacity: '1' },
|
||||
},
|
||||
slideUp: {
|
||||
'0%': { transform: 'translateY(20px)', opacity: '0' },
|
||||
'100%': { transform: 'translateY(0)', opacity: '1' },
|
||||
},
|
||||
float: {
|
||||
'0%, 100%': { transform: 'translateY(0px)' },
|
||||
'50%': { transform: 'translateY(-10px)' },
|
||||
},
|
||||
neonFlicker: {
|
||||
'0%, 100%': { opacity: '1' },
|
||||
'50%': { opacity: '0.8' },
|
||||
},
|
||||
glitch: {
|
||||
'0%, 100%': { transform: 'translate(0)' },
|
||||
'20%': { transform: 'translate(-1px, 1px)' },
|
||||
'40%': { transform: 'translate(-1px, -1px)' },
|
||||
'60%': { transform: 'translate(1px, 1px)' },
|
||||
'80%': { transform: 'translate(1px, -1px)' },
|
||||
},
|
||||
scanLine: {
|
||||
'0%': { transform: 'translateY(-100%)' },
|
||||
'100%': { transform: 'translateY(100%)' },
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
plugins: [],
|
||||
}
|
||||
26
tsconfig.json
Normal file
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "es5",
|
||||
"lib": [
|
||||
"dom",
|
||||
"dom.iterable",
|
||||
"esnext"
|
||||
],
|
||||
"allowJs": true,
|
||||
"skipLibCheck": true,
|
||||
"esModuleInterop": true,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"strict": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"module": "esnext",
|
||||
"moduleResolution": "node",
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"noEmit": true,
|
||||
"jsx": "react-jsx"
|
||||
},
|
||||
"include": [
|
||||
"src"
|
||||
]
|
||||
}
|
||||