2435 lines
85 KiB
HTML
2435 lines
85 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="zh-CN">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<title>赛博朋克2078 - 神经网络城市可视化平台</title>
|
||
|
||
<!-- 外部CDN资源 -->
|
||
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
|
||
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.2/gsap.min.js"></script>
|
||
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/7.8.5/d3.min.js"></script>
|
||
<script src="https://cdnjs.cloudflare.com/ajax/libs/particles.js/2.0.0/particles.min.js"></script>
|
||
<script src="https://cdnjs.cloudflare.com/ajax/libs/tween.js/18.6.4/tween.umd.js"></script>
|
||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||
<link href="https://fonts.googleapis.com/css2?family=Orbitron:wght@400;700;900&family=Rajdhani:wght@300;500;700&display=swap" rel="stylesheet">
|
||
|
||
<style>
|
||
:root {
|
||
--neon-pink: #ff00ff;
|
||
--electric-blue: #00ffff;
|
||
--laser-purple: #8800ff;
|
||
--deep-black: #0a0a0a;
|
||
--metal-gray: #2a2a2a;
|
||
--cyber-green: #00ff88;
|
||
--danger-red: #ff0055;
|
||
--glass-bg: rgba(20, 20, 30, 0.6);
|
||
--border-glow: 0 0 10px;
|
||
}
|
||
|
||
* {
|
||
margin: 0;
|
||
padding: 0;
|
||
box-sizing: border-box;
|
||
}
|
||
|
||
body {
|
||
font-family: 'Rajdhani', sans-serif;
|
||
background: var(--deep-black);
|
||
color: #fff;
|
||
overflow-x: hidden;
|
||
line-height: 1.6;
|
||
}
|
||
|
||
h1, h2, h3, h4, h5 {
|
||
font-family: 'Orbitron', sans-serif;
|
||
text-transform: uppercase;
|
||
letter-spacing: 2px;
|
||
}
|
||
|
||
/* 开场动画 */
|
||
#intro-animation {
|
||
position: fixed;
|
||
top: 0;
|
||
left: 0;
|
||
width: 100%;
|
||
height: 100%;
|
||
background: var(--deep-black);
|
||
z-index: 9999;
|
||
display: flex;
|
||
justify-content: center;
|
||
align-items: center;
|
||
overflow: hidden;
|
||
}
|
||
|
||
.neural-network-viz {
|
||
width: 800px;
|
||
height: 600px;
|
||
position: relative;
|
||
}
|
||
|
||
.node {
|
||
position: absolute;
|
||
width: 8px;
|
||
height: 8px;
|
||
background: var(--neon-pink);
|
||
border-radius: 50%;
|
||
box-shadow: 0 0 20px var(--neon-pink);
|
||
opacity: 0;
|
||
}
|
||
|
||
.connection {
|
||
position: absolute;
|
||
height: 2px;
|
||
background: linear-gradient(90deg, transparent, var(--electric-blue), transparent);
|
||
opacity: 0;
|
||
transform-origin: left center;
|
||
}
|
||
|
||
/* 主容器 */
|
||
.main-container {
|
||
position: relative;
|
||
min-height: 100vh;
|
||
opacity: 0;
|
||
transition: opacity 0.5s ease;
|
||
}
|
||
|
||
/* 背景粒子 */
|
||
#particles-js {
|
||
position: fixed;
|
||
width: 100%;
|
||
height: 100%;
|
||
top: 0;
|
||
left: 0;
|
||
z-index: 1;
|
||
pointer-events: none;
|
||
}
|
||
|
||
/* 模块基础样式 */
|
||
section {
|
||
position: relative;
|
||
padding: 80px 20px;
|
||
margin: 0 auto;
|
||
max-width: 1400px;
|
||
z-index: 2;
|
||
}
|
||
|
||
.section-title {
|
||
font-size: 2.5rem;
|
||
text-align: center;
|
||
margin-bottom: 60px;
|
||
background: linear-gradient(45deg, var(--electric-blue), var(--laser-purple));
|
||
-webkit-background-clip: text;
|
||
-webkit-text-fill-color: transparent;
|
||
text-shadow: 0 0 30px rgba(0, 255, 255, 0.5);
|
||
position: relative;
|
||
animation: glow-text 2s ease-in-out infinite alternate;
|
||
}
|
||
|
||
@keyframes glow-text {
|
||
from { filter: drop-shadow(0 0 10px var(--electric-blue)); }
|
||
to { filter: drop-shadow(0 0 20px var(--laser-purple)); }
|
||
}
|
||
|
||
/* 模块二:3D城市视图 */
|
||
#city-view {
|
||
background: linear-gradient(180deg, rgba(10,10,10,0.9), rgba(0,0,0,0.95));
|
||
border: 1px solid rgba(0, 255, 255, 0.2);
|
||
backdrop-filter: blur(10px);
|
||
border-radius: 15px;
|
||
overflow: hidden;
|
||
position: relative;
|
||
min-height: 600px;
|
||
}
|
||
|
||
#city-canvas {
|
||
width: 100%;
|
||
height: 600px;
|
||
display: block;
|
||
}
|
||
|
||
.city-stats {
|
||
position: absolute;
|
||
top: 20px;
|
||
right: 20px;
|
||
background: var(--glass-bg);
|
||
border: 1px solid var(--electric-blue);
|
||
padding: 15px;
|
||
border-radius: 8px;
|
||
font-family: 'Orbitron', monospace;
|
||
font-size: 0.9rem;
|
||
min-width: 200px;
|
||
backdrop-filter: blur(5px);
|
||
}
|
||
|
||
.stat-item {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
margin: 5px 0;
|
||
color: var(--electric-blue);
|
||
}
|
||
|
||
.stat-value {
|
||
color: var(--neon-pink);
|
||
text-shadow: 0 0 5px var(--neon-pink);
|
||
}
|
||
|
||
/* 模块三:神经网络数据流 */
|
||
#network-viz {
|
||
background: var(--glass-bg);
|
||
border: 1px solid var(--laser-purple);
|
||
border-radius: 15px;
|
||
padding: 20px;
|
||
min-height: 500px;
|
||
position: relative;
|
||
overflow: hidden;
|
||
}
|
||
|
||
#network-canvas {
|
||
width: 100%;
|
||
height: 400px;
|
||
border-radius: 10px;
|
||
background: rgba(0, 0, 0, 0.3);
|
||
}
|
||
|
||
.network-metrics {
|
||
display: grid;
|
||
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
||
gap: 15px;
|
||
margin-top: 20px;
|
||
}
|
||
|
||
.metric-card {
|
||
background: rgba(136, 0, 255, 0.1);
|
||
border: 1px solid var(--laser-purple);
|
||
padding: 15px;
|
||
border-radius: 8px;
|
||
text-align: center;
|
||
transition: all 0.3s ease;
|
||
cursor: pointer;
|
||
}
|
||
|
||
.metric-card:hover {
|
||
background: rgba(136, 0, 255, 0.2);
|
||
transform: translateY(-3px);
|
||
box-shadow: 0 5px 15px rgba(136, 0, 255, 0.4);
|
||
}
|
||
|
||
.metric-value {
|
||
font-size: 1.8rem;
|
||
font-family: 'Orbitron', monospace;
|
||
color: var(--electric-blue);
|
||
text-shadow: 0 0 10px var(--electric-blue);
|
||
}
|
||
|
||
/* 模块四:监控仪表板 */
|
||
#dashboard {
|
||
display: grid;
|
||
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
|
||
gap: 20px;
|
||
background: var(--glass-bg);
|
||
border: 1px solid rgba(0, 255, 255, 0.3);
|
||
border-radius: 15px;
|
||
padding: 30px;
|
||
backdrop-filter: blur(10px);
|
||
}
|
||
|
||
.dashboard-panel {
|
||
background: rgba(0, 0, 0, 0.5);
|
||
border: 1px solid rgba(255, 255, 255, 0.1);
|
||
border-radius: 10px;
|
||
padding: 20px;
|
||
position: relative;
|
||
overflow: hidden;
|
||
}
|
||
|
||
.dashboard-panel::before {
|
||
content: '';
|
||
position: absolute;
|
||
top: 0;
|
||
left: 0;
|
||
right: 0;
|
||
height: 2px;
|
||
background: linear-gradient(90deg, transparent, var(--neon-pink), transparent);
|
||
opacity: 0.7;
|
||
}
|
||
|
||
.panel-title {
|
||
font-size: 1rem;
|
||
color: var(--electric-blue);
|
||
margin-bottom: 15px;
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 10px;
|
||
}
|
||
|
||
/* 仪表盘 */
|
||
.gauge-container {
|
||
width: 150px;
|
||
height: 150px;
|
||
margin: 0 auto;
|
||
position: relative;
|
||
}
|
||
|
||
.gauge-bg {
|
||
width: 100%;
|
||
height: 100%;
|
||
border-radius: 50%;
|
||
border: 10px solid rgba(255, 255, 255, 0.1);
|
||
position: relative;
|
||
}
|
||
|
||
.gauge-fill {
|
||
position: absolute;
|
||
width: 100%;
|
||
height: 100%;
|
||
border-radius: 50%;
|
||
border: 10px solid transparent;
|
||
border-top-color: var(--cyber-green);
|
||
border-right-color: var(--neon-pink);
|
||
transform: rotate(0deg);
|
||
transition: transform 0.5s ease;
|
||
filter: drop-shadow(0 0 5px var(--neon-pink));
|
||
}
|
||
|
||
.gauge-value {
|
||
position: absolute;
|
||
top: 50%;
|
||
left: 50%;
|
||
transform: translate(-50%, -50%);
|
||
font-size: 1.5rem;
|
||
font-family: 'Orbitron', monospace;
|
||
color: var(--electric-blue);
|
||
}
|
||
|
||
/* 波形图 */
|
||
.waveform {
|
||
width: 100%;
|
||
height: 100px;
|
||
background: rgba(0, 0, 0, 0.3);
|
||
border-radius: 5px;
|
||
position: relative;
|
||
overflow: hidden;
|
||
}
|
||
|
||
.waveform-line {
|
||
stroke: var(--electric-blue);
|
||
stroke-width: 2;
|
||
fill: none;
|
||
filter: drop-shadow(0 0 3px var(--electric-blue));
|
||
}
|
||
|
||
/* 服务器网格 */
|
||
.server-grid {
|
||
display: grid;
|
||
grid-template-columns: repeat(5, 1fr);
|
||
gap: 5px;
|
||
margin-top: 10px;
|
||
}
|
||
|
||
.server-node {
|
||
width: 20px;
|
||
height: 20px;
|
||
background: rgba(0, 255, 136, 0.3);
|
||
border: 1px solid var(--cyber-green);
|
||
border-radius: 2px;
|
||
transition: all 0.3s ease;
|
||
cursor: pointer;
|
||
}
|
||
|
||
.server-node.error {
|
||
background: rgba(255, 0, 85, 0.5);
|
||
border-color: var(--danger-red);
|
||
animation: blink 0.5s infinite;
|
||
}
|
||
|
||
@keyframes blink {
|
||
0%, 100% { opacity: 1; }
|
||
50% { opacity: 0.3; }
|
||
}
|
||
|
||
/* 日志窗口 */
|
||
.log-window {
|
||
height: 150px;
|
||
background: rgba(0, 0, 0, 0.7);
|
||
border: 1px solid var(--metal-gray);
|
||
border-radius: 5px;
|
||
padding: 10px;
|
||
font-family: 'Courier New', monospace;
|
||
font-size: 0.8rem;
|
||
overflow-y: auto;
|
||
position: relative;
|
||
color: var(--cyber-green);
|
||
}
|
||
|
||
.log-window::before {
|
||
content: '';
|
||
position: absolute;
|
||
top: 0;
|
||
left: 0;
|
||
width: 100%;
|
||
height: 100%;
|
||
background: repeating-linear-gradient(
|
||
0deg,
|
||
transparent,
|
||
transparent 2px,
|
||
rgba(0, 255, 136, 0.03) 2px,
|
||
rgba(0, 255, 136, 0.03) 4px
|
||
);
|
||
pointer-events: none;
|
||
}
|
||
|
||
.log-entry {
|
||
margin-bottom: 5px;
|
||
opacity: 0;
|
||
animation: log-fade-in 0.3s forwards;
|
||
}
|
||
|
||
@keyframes log-fade-in {
|
||
to { opacity: 1; }
|
||
}
|
||
|
||
.log-timestamp {
|
||
color: var(--electric-blue);
|
||
margin-right: 10px;
|
||
}
|
||
|
||
/* 模块五:时间线导航 */
|
||
#timeline-nav {
|
||
background: var(--glass-bg);
|
||
border: 1px solid var(--laser-purple);
|
||
border-radius: 15px;
|
||
padding: 30px;
|
||
overflow-x: auto;
|
||
overflow-y: hidden;
|
||
white-space: nowrap;
|
||
backdrop-filter: blur(10px);
|
||
}
|
||
|
||
.timeline-track {
|
||
display: inline-flex;
|
||
gap: 20px;
|
||
min-width: 100%;
|
||
align-items: center;
|
||
}
|
||
|
||
.timeline-node {
|
||
background: rgba(0, 0, 0, 0.5);
|
||
border: 2px solid var(--metal-gray);
|
||
padding: 15px 30px;
|
||
border-radius: 25px;
|
||
cursor: pointer;
|
||
transition: all 0.3s ease;
|
||
position: relative;
|
||
min-width: 120px;
|
||
text-align: center;
|
||
white-space: nowrap;
|
||
font-family: 'Orbitron', monospace;
|
||
}
|
||
|
||
.timeline-node:hover {
|
||
background: rgba(136, 0, 255, 0.2);
|
||
border-color: var(--laser-purple);
|
||
transform: translateY(-5px) scale(1.05);
|
||
box-shadow: 0 5px 20px rgba(136, 0, 255, 0.6);
|
||
}
|
||
|
||
.timeline-node.active {
|
||
border-color: var(--neon-pink);
|
||
background: rgba(255, 0, 255, 0.2);
|
||
box-shadow: 0 0 20px var(--neon-pink), inset 0 0 10px var(--neon-pink);
|
||
color: var(--neon-pink);
|
||
text-shadow: 0 0 10px var(--neon-pink);
|
||
}
|
||
|
||
.timeline-node .year {
|
||
font-size: 1.2rem;
|
||
font-weight: 700;
|
||
display: block;
|
||
}
|
||
|
||
.timeline-node .desc {
|
||
font-size: 0.8rem;
|
||
opacity: 0.8;
|
||
margin-top: 5px;
|
||
white-space: normal;
|
||
}
|
||
|
||
/* 模块六:数据卡片 */
|
||
#data-cards {
|
||
display: grid;
|
||
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
|
||
gap: 30px;
|
||
perspective: 1000px;
|
||
}
|
||
|
||
.holo-card {
|
||
background: linear-gradient(135deg,
|
||
rgba(0, 255, 255, 0.1),
|
||
rgba(136, 0, 255, 0.1));
|
||
border: 1px solid rgba(255, 255, 255, 0.2);
|
||
border-radius: 15px;
|
||
padding: 25px;
|
||
transform: rotateX(10deg) rotateY(-5deg);
|
||
transform-style: preserve-3d;
|
||
transition: all 0.4s cubic-bezier(0.175, 0.885, 0.32, 1.275);
|
||
cursor: pointer;
|
||
position: relative;
|
||
overflow: hidden;
|
||
backdrop-filter: blur(5px);
|
||
}
|
||
|
||
.holo-card::before {
|
||
content: '';
|
||
position: absolute;
|
||
top: -50%;
|
||
left: -50%;
|
||
width: 200%;
|
||
height: 200%;
|
||
background: linear-gradient(45deg,
|
||
transparent,
|
||
rgba(255, 255, 255, 0.1),
|
||
transparent);
|
||
transform: rotate(45deg);
|
||
transition: all 0.5s ease;
|
||
opacity: 0;
|
||
}
|
||
|
||
.holo-card:hover {
|
||
transform: rotateX(0deg) rotateY(0deg) translateY(-10px) scale(1.05);
|
||
border-color: var(--electric-blue);
|
||
box-shadow: 0 10px 30px rgba(0, 255, 255, 0.4),
|
||
inset 0 0 20px rgba(0, 255, 255, 0.2);
|
||
}
|
||
|
||
.holo-card:hover::before {
|
||
opacity: 1;
|
||
animation: shine 0.5s ease;
|
||
}
|
||
|
||
@keyframes shine {
|
||
0% { transform: translateX(-100%) rotate(45deg); }
|
||
100% { transform: translateX(100%) rotate(45deg); }
|
||
}
|
||
|
||
.card-header {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 10px;
|
||
margin-bottom: 15px;
|
||
font-size: 1.1rem;
|
||
font-weight: 700;
|
||
color: var(--electric-blue);
|
||
}
|
||
|
||
.card-icon {
|
||
font-size: 1.5rem;
|
||
filter: drop-shadow(0 0 5px var(--electric-blue));
|
||
}
|
||
|
||
.card-stat {
|
||
font-size: 2rem;
|
||
font-family: 'Orbitron', monospace;
|
||
color: var(--neon-pink);
|
||
text-shadow: 0 0 10px var(--neon-pink);
|
||
margin: 10px 0;
|
||
}
|
||
|
||
.card-details {
|
||
max-height: 0;
|
||
overflow: hidden;
|
||
transition: max-height 0.3s ease;
|
||
opacity: 0;
|
||
font-size: 0.9rem;
|
||
color: #ccc;
|
||
}
|
||
|
||
.holo-card:hover .card-details {
|
||
max-height: 100px;
|
||
opacity: 1;
|
||
margin-top: 10px;
|
||
}
|
||
|
||
/* 模块七:命令控制台 */
|
||
#command-console {
|
||
position: fixed;
|
||
bottom: 0;
|
||
left: 0;
|
||
right: 0;
|
||
background: rgba(10, 10, 15, 0.95);
|
||
border-top: 2px solid var(--electric-blue);
|
||
padding: 20px;
|
||
z-index: 1000;
|
||
transform: translateY(0);
|
||
transition: transform 0.3s ease;
|
||
backdrop-filter: blur(10px);
|
||
}
|
||
|
||
.console-wrapper {
|
||
max-width: 1400px;
|
||
margin: 0 auto;
|
||
position: relative;
|
||
}
|
||
|
||
.console-input-container {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 10px;
|
||
background: rgba(0, 0, 0, 0.5);
|
||
border: 1px solid var(--metal-gray);
|
||
border-radius: 8px;
|
||
padding: 12px 15px;
|
||
transition: all 0.3s ease;
|
||
}
|
||
|
||
.console-input-container:focus-within {
|
||
border-color: var(--neon-pink);
|
||
box-shadow: 0 0 15px rgba(255, 0, 255, 0.3);
|
||
}
|
||
|
||
.console-prompt {
|
||
color: var(--cyber-green);
|
||
font-family: 'Orbitron', monospace;
|
||
font-weight: 700;
|
||
font-size: 1.2rem;
|
||
}
|
||
|
||
#command-input {
|
||
flex: 1;
|
||
background: transparent;
|
||
border: none;
|
||
color: #fff;
|
||
font-family: 'Courier New', monospace;
|
||
font-size: 1rem;
|
||
outline: none;
|
||
caret-color: var(--neon-pink);
|
||
}
|
||
|
||
.cursor-blink {
|
||
animation: blink 1s infinite;
|
||
color: var(--neon-pink);
|
||
}
|
||
|
||
.command-feedback {
|
||
position: absolute;
|
||
bottom: 100%;
|
||
left: 0;
|
||
right: 0;
|
||
background: rgba(0, 0, 0, 0.9);
|
||
border: 1px solid var(--electric-blue);
|
||
border-radius: 8px 8px 0 0;
|
||
padding: 15px;
|
||
margin-bottom: 10px;
|
||
max-height: 200px;
|
||
overflow-y: auto;
|
||
display: none;
|
||
font-family: 'Courier New', monospace;
|
||
font-size: 0.9rem;
|
||
}
|
||
|
||
.command-feedback.active {
|
||
display: block;
|
||
animation: slide-up 0.3s ease;
|
||
}
|
||
|
||
@keyframes slide-up {
|
||
from { transform: translateY(10px); opacity: 0; }
|
||
to { transform: translateY(0); opacity: 1; }
|
||
}
|
||
|
||
.feedback-success { color: var(--cyber-green); }
|
||
.feedback-error { color: var(--danger-red); }
|
||
.feedback-info { color: var(--electric-blue); }
|
||
|
||
/* Glitch效果 */
|
||
.glitch {
|
||
position: relative;
|
||
animation: glitch-skew 2s infinite;
|
||
}
|
||
|
||
.glitch::before,
|
||
.glitch::after {
|
||
content: attr(data-text);
|
||
position: absolute;
|
||
top: 0;
|
||
left: 0;
|
||
width: 100%;
|
||
height: 100%;
|
||
}
|
||
|
||
.glitch::before {
|
||
left: 2px;
|
||
text-shadow: -2px 0 var(--neon-pink);
|
||
clip: rect(44px, 450px, 56px, 0);
|
||
animation: glitch-anim 5s infinite linear alternate-reverse;
|
||
}
|
||
|
||
.glitch::after {
|
||
left: -2px;
|
||
text-shadow: -2px 0 var(--electric-blue);
|
||
clip: rect(44px, 450px, 56px, 0);
|
||
animation: glitch-anim2 5s infinite linear alternate-reverse;
|
||
}
|
||
|
||
@keyframes glitch-anim {
|
||
0% { clip: rect(31px, 9999px, 94px, 0); }
|
||
20% { clip: rect(85px, 9999px, 22px, 0); }
|
||
40% { clip: rect(21px, 9999px, 67px, 0); }
|
||
60% { clip: rect(56px, 9999px, 83px, 0); }
|
||
80% { clip: rect(12px, 9999px, 45px, 0); }
|
||
100% { clip: rect(76px, 9999px, 12px, 0); }
|
||
}
|
||
|
||
@keyframes glitch-anim2 {
|
||
0% { clip: rect(65px, 9999px, 100px, 0); }
|
||
20% { clip: rect(12px, 9999px, 67px, 0); }
|
||
40% { clip: rect(89px, 9999px, 23px, 0); }
|
||
60% { clip: rect(45px, 9999px, 78px, 0); }
|
||
80% { clip: rect(23px, 9999px, 56px, 0); }
|
||
100% { clip: rect(12px, 9999px, 89px, 0); }
|
||
}
|
||
|
||
@keyframes glitch-skew {
|
||
0% { transform: skew(0.5deg); }
|
||
10% { transform: skew(-0.5deg); }
|
||
20% { transform: skew(0.5deg); }
|
||
30% { transform: skew(-0.3deg); }
|
||
40% { transform: skew(0.3deg); }
|
||
50% { transform: skew(-0.2deg); }
|
||
60% { transform: skew(0.2deg); }
|
||
70% { transform: skew(-0.1deg); }
|
||
80% { transform: skew(0.1deg); }
|
||
90% { transform: skew(-0.2deg); }
|
||
100% { transform: skew(0.2deg); }
|
||
}
|
||
|
||
/* 响应式设计 */
|
||
@media (max-width: 768px) {
|
||
.section-title {
|
||
font-size: 1.8rem;
|
||
}
|
||
|
||
section {
|
||
padding: 40px 15px;
|
||
}
|
||
|
||
#dashboard {
|
||
grid-template-columns: 1fr;
|
||
}
|
||
|
||
#data-cards {
|
||
grid-template-columns: 1fr;
|
||
}
|
||
|
||
#city-canvas {
|
||
height: 400px;
|
||
}
|
||
|
||
.timeline-node {
|
||
min-width: 100px;
|
||
padding: 10px 20px;
|
||
}
|
||
|
||
.city-stats {
|
||
font-size: 0.8rem;
|
||
padding: 10px;
|
||
top: 10px;
|
||
right: 10px;
|
||
}
|
||
|
||
#command-console {
|
||
padding: 15px 10px;
|
||
}
|
||
}
|
||
|
||
/* 加载动画 */
|
||
.loader {
|
||
position: fixed;
|
||
top: 50%;
|
||
left: 50%;
|
||
transform: translate(-50%, -50%);
|
||
width: 50px;
|
||
height: 50px;
|
||
border: 3px solid rgba(0, 255, 255, 0.2);
|
||
border-top-color: var(--electric-blue);
|
||
border-radius: 50%;
|
||
animation: spin 1s linear infinite;
|
||
z-index: 10000;
|
||
display: none;
|
||
box-shadow: 0 0 20px var(--electric-blue);
|
||
}
|
||
|
||
@keyframes spin {
|
||
to { transform: translate(-50%, -50%) rotate(360deg); }
|
||
}
|
||
|
||
/* 滚动条样式 */
|
||
::-webkit-scrollbar {
|
||
width: 8px;
|
||
height: 8px;
|
||
}
|
||
|
||
::-webkit-scrollbar-track {
|
||
background: rgba(0, 0, 0, 0.3);
|
||
}
|
||
|
||
::-webkit-scrollbar-thumb {
|
||
background: linear-gradient(var(--neon-pink), var(--laser-purple));
|
||
border-radius: 4px;
|
||
}
|
||
|
||
::-webkit-scrollbar-thumb:hover {
|
||
background: var(--electric-blue);
|
||
}
|
||
|
||
/* 视差层 */
|
||
.parallax-layer {
|
||
position: absolute;
|
||
top: 0;
|
||
left: 0;
|
||
width: 100%;
|
||
height: 100%;
|
||
pointer-events: none;
|
||
opacity: 0.3;
|
||
}
|
||
|
||
.scan-line {
|
||
position: fixed;
|
||
top: 0;
|
||
left: 0;
|
||
width: 100%;
|
||
height: 2px;
|
||
background: linear-gradient(90deg, transparent, var(--electric-blue), transparent);
|
||
opacity: 0;
|
||
pointer-events: none;
|
||
z-index: 9998;
|
||
box-shadow: 0 0 10px var(--electric-blue);
|
||
}
|
||
|
||
.scan-line.active {
|
||
animation: scan 1s ease-in-out;
|
||
}
|
||
|
||
@keyframes scan {
|
||
0% { top: 0; opacity: 0; }
|
||
10% { opacity: 1; }
|
||
90% { opacity: 1; }
|
||
100% { top: 100%; opacity: 0; }
|
||
}
|
||
|
||
/* 工具提示 */
|
||
.tooltip {
|
||
position: fixed;
|
||
background: rgba(0, 0, 0, 0.9);
|
||
border: 1px solid var(--electric-blue);
|
||
padding: 8px 12px;
|
||
border-radius: 5px;
|
||
font-size: 0.8rem;
|
||
pointer-events: none;
|
||
opacity: 0;
|
||
transition: opacity 0.2s ease;
|
||
z-index: 2000;
|
||
font-family: 'Orbitron', monospace;
|
||
}
|
||
|
||
.tooltip.show {
|
||
opacity: 1;
|
||
}
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<!-- 开场动画 -->
|
||
<div id="intro-animation">
|
||
<div class="neural-network-viz" id="network-viz-container"></div>
|
||
</div>
|
||
|
||
<!-- 背景粒子 -->
|
||
<div id="particles-js"></div>
|
||
|
||
<!-- 扫描线效果 -->
|
||
<div class="scan-line" id="scan-line"></div>
|
||
|
||
<!-- 工具提示 -->
|
||
<div class="tooltip" id="tooltip"></div>
|
||
|
||
<!-- 主容器 -->
|
||
<div class="main-container" id="main-container">
|
||
|
||
<!-- 模块一:3D城市视图 -->
|
||
<section id="city-view-section">
|
||
<h2 class="section-title" data-text="城市三维概览">城市三维概览</h2>
|
||
<div id="city-view">
|
||
<div id="city-canvas"></div>
|
||
<div class="city-stats">
|
||
<div class="stat-item">
|
||
<span>时间</span>
|
||
<span class="stat-value" id="city-time">2078-01-01 00:00:00</span>
|
||
</div>
|
||
<div class="stat-item">
|
||
<span>城市状态</span>
|
||
<span class="stat-value" id="city-status">稳定</span>
|
||
</div>
|
||
<div class="stat-item">
|
||
<span>人口密度</span>
|
||
<span class="stat-value" id="population-density">0.87</span>
|
||
</div>
|
||
<div class="stat-item">
|
||
<span>能耗指数</span>
|
||
<span class="stat-value" id="energy-index">2.34</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<!-- 模块二:神经网络数据流 -->
|
||
<section id="network-section">
|
||
<h2 class="section-title" data-text="神经网络数据流">神经网络数据流</h2>
|
||
<div id="network-viz">
|
||
<canvas id="network-canvas"></canvas>
|
||
<div class="network-metrics">
|
||
<div class="metric-card">
|
||
<div class="metric-value" id="neuron-activity">87%</div>
|
||
<div>神经元活跃度</div>
|
||
</div>
|
||
<div class="metric-card">
|
||
<div class="metric-value" id="data-throughput">5.2TB/s</div>
|
||
<div>数据吞吐量</div>
|
||
</div>
|
||
<div class="metric-card">
|
||
<div class="metric-value" id="latency">0.8ms</div>
|
||
<div>延迟指标</div>
|
||
</div>
|
||
<div class="metric-card">
|
||
<div class="metric-value" id="synapses">1.2M</div>
|
||
<div>突触连接</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<!-- 模块三:智能监控仪表板 -->
|
||
<section id="dashboard-section">
|
||
<h2 class="section-title" data-text="智能系统监控">智能系统监控</h2>
|
||
<div id="dashboard">
|
||
<!-- CPU/内存仪表 -->
|
||
<div class="dashboard-panel">
|
||
<div class="panel-title">
|
||
<i class="fas fa-microchip"></i>
|
||
CPU/内存负载
|
||
</div>
|
||
<div class="gauge-container">
|
||
<div class="gauge-bg"></div>
|
||
<div class="gauge-fill" id="cpu-gauge"></div>
|
||
<div class="gauge-value" id="cpu-value">45%</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 网络流量波形 -->
|
||
<div class="dashboard-panel">
|
||
<div class="panel-title">
|
||
<i class="fas fa-wave-square"></i>
|
||
网络流量
|
||
</div>
|
||
<div class="waveform">
|
||
<svg width="100%" height="100%" viewBox="0 0 300 100" id="waveform-svg">
|
||
<path class="waveform-line" d="" />
|
||
</svg>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 服务器状态网格 -->
|
||
<div class="dashboard-panel">
|
||
<div class="panel-title">
|
||
<i class="fas fa-server"></i>
|
||
服务器状态
|
||
</div>
|
||
<div class="server-grid" id="server-grid"></div>
|
||
</div>
|
||
|
||
<!-- 系统日志 -->
|
||
<div class="dashboard-panel">
|
||
<div class="panel-title">
|
||
<i class="fas fa-terminal"></i>
|
||
系统日志
|
||
</div>
|
||
<div class="log-window" id="log-window"></div>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<!-- 模块四:时间线导航 -->
|
||
<section id="timeline-section">
|
||
<h2 class="section-title" data-text="历史时间线">历史时间线</h2>
|
||
<div id="timeline-nav">
|
||
<div class="timeline-track" id="timeline-track">
|
||
<!-- 时间节点将通过JS动态生成 -->
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<!-- 模块五:数据卡片 -->
|
||
<section id="cards-section">
|
||
<h2 class="section-title" data-text="全息数据投影">全息数据投影</h2>
|
||
<div id="data-cards">
|
||
<div class="holo-card" data-tooltip="查看详细人口统计">
|
||
<div class="card-header">
|
||
<i class="fas fa-users card-icon"></i>
|
||
人口统计
|
||
</div>
|
||
<div class="card-stat">2-Sector</div>
|
||
<div class="card-details">
|
||
总人口:2,847,392<br>
|
||
增长率:+2.3%<br>
|
||
平均年龄:34.7岁
|
||
</div>
|
||
</div>
|
||
|
||
<div class="holo-card" data-tooltip="能源消耗分析">
|
||
<div class="card-header">
|
||
<i class="fas fa-bolt card-icon"></i>
|
||
能量消耗
|
||
</div>
|
||
<div class="card-stat">1.8GW</div>
|
||
<div class="card-details">
|
||
核聚变:78%<br>
|
||
太阳能:15%<br>
|
||
生物质能:7%
|
||
</div>
|
||
</div>
|
||
|
||
<div class="holo-card" data-tooltip="网络节点分布">
|
||
<div class="card-header">
|
||
<i class="fas fa-network-wired card-icon"></i>
|
||
节点分布
|
||
</div>
|
||
<div class="card-stat">8.9M</div>
|
||
<div class="card-details">
|
||
活跃节点:87%<br>
|
||
骨干节点:1,024<br>
|
||
边缘节点:8.8M
|
||
</div>
|
||
</div>
|
||
|
||
<div class="holo-card" data-tooltip="AI意识同步率分析">
|
||
<div class="card-header">
|
||
<i class="fas fa-brain card-icon"></i>
|
||
AI同步率
|
||
</div>
|
||
<div class="card-stat">94.7%</div>
|
||
<div class="card-details">
|
||
集体意识:<br>
|
||
- 网络融合:98%<br>
|
||
- 独立性:89%<br>
|
||
- 学习能力:92%
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<!-- 空白区域用于滚动 -->
|
||
<div style="height: 100px;"></div>
|
||
</div>
|
||
|
||
<!-- 模块七:命令控制台 -->
|
||
<div id="command-console">
|
||
<div class="console-wrapper">
|
||
<div class="command-feedback" id="command-feedback"></div>
|
||
<div class="console-input-container">
|
||
<span class="console-prompt">></span>
|
||
<input type="text" id="command-input" placeholder="输入命令... (尝试: 显示能源分布, 开始系统诊断, 切换到东区视图)" autocomplete="off">
|
||
<span class="cursor-blink">_</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 加载指示器 -->
|
||
<div class="loader" id="loader"></div>
|
||
|
||
<script>
|
||
// 全局配置
|
||
const CONFIG = {
|
||
colors: {
|
||
neonPink: '#ff00ff',
|
||
electricBlue: '#00ffff',
|
||
laserPurple: '#8800ff',
|
||
deepBlack: '#0a0a0a',
|
||
cyberGreen: '#00ff88',
|
||
dangerRed: '#ff0055'
|
||
},
|
||
animation: {
|
||
introDuration: 4000,
|
||
particleCount: 80
|
||
}
|
||
};
|
||
|
||
// 应用状态
|
||
const appState = {
|
||
isIntroComplete: false,
|
||
currentTimeline: 2078,
|
||
city3D: null,
|
||
networkViz: null,
|
||
metrics: {
|
||
cpu: 45,
|
||
memory: 60,
|
||
latency: 0.8,
|
||
throughput: 5.2
|
||
}
|
||
};
|
||
|
||
// 工具函数
|
||
const utils = {
|
||
random: (min, max) => Math.random() * (max - min) + min,
|
||
randomInt: (min, max) => Math.floor(Math.random() * (max - min + 1)) + min,
|
||
clamp: (value, min, max) => Math.min(Math.max(value, min), max),
|
||
|
||
// 创建元素
|
||
createElement: (tag, className, parent) => {
|
||
const el = document.createElement(tag);
|
||
if (className) el.className = className;
|
||
if (parent) parent.appendChild(el);
|
||
return el;
|
||
},
|
||
|
||
// 显示提示
|
||
showTooltip: (text, x, y) => {
|
||
const tooltip = document.getElementById('tooltip');
|
||
tooltip.textContent = text;
|
||
tooltip.style.left = x + 10 + 'px';
|
||
tooltip.style.top = y + 10 + 'px';
|
||
tooltip.classList.add('show');
|
||
},
|
||
|
||
hideTooltip: () => {
|
||
document.getElementById('tooltip').classList.remove('show');
|
||
},
|
||
|
||
// 扫描线效果
|
||
scanEffect: () => {
|
||
const scanLine = document.getElementById('scan-line');
|
||
scanLine.classList.remove('active');
|
||
void scanLine.offsetWidth; // 触发重排
|
||
scanLine.classList.add('active');
|
||
// 播放音效(视觉反馈)
|
||
console.log('[系统] 扫描完成');
|
||
},
|
||
|
||
// 显示反馈信息
|
||
showFeedback: (message, type = 'info') => {
|
||
const feedback = document.getElementById('command-feedback');
|
||
const timestamp = new Date().toLocaleTimeString();
|
||
const entry = document.createElement('div');
|
||
entry.className = `feedback-${type}`;
|
||
entry.innerHTML = `<span class="log-timestamp">[${timestamp}]</span> ${message}`;
|
||
|
||
feedback.appendChild(entry);
|
||
feedback.classList.add('active');
|
||
feedback.scrollTop = feedback.scrollHeight;
|
||
|
||
// 3秒后自动隐藏
|
||
setTimeout(() => {
|
||
if (feedback.children.length > 5) {
|
||
feedback.removeChild(feedback.firstChild);
|
||
}
|
||
}, 3000);
|
||
}
|
||
};
|
||
|
||
// 开场动画模块
|
||
class IntroAnimation {
|
||
constructor() {
|
||
this.container = document.getElementById('network-viz-container');
|
||
this.width = 800;
|
||
this.height = 600;
|
||
this.nodes = [];
|
||
this.connections = [];
|
||
this.animationTimeline = null;
|
||
}
|
||
|
||
init() {
|
||
// 创建节点
|
||
for (let i = 0; i < 15; i++) {
|
||
const node = utils.createElement('div', 'node', this.container);
|
||
node.style.left = utils.random(50, this.width - 50) + 'px';
|
||
node.style.top = utils.random(50, this.height - 50) + 'px';
|
||
this.nodes.push(node);
|
||
}
|
||
|
||
// 创建连线
|
||
for (let i = 0; i < 6; i++) {
|
||
const conn = utils.createElement('div', 'connection', this.container);
|
||
this.connections.push(conn);
|
||
|
||
// 随机连接两个节点
|
||
const node1 = this.nodes[utils.randomInt(0, this.nodes.length - 1)];
|
||
const node2 = this.nodes[utils.randomInt(0, this.nodes.length - 1)];
|
||
|
||
if (node1 !== node2) {
|
||
this.positionConnection(conn, node1, node2);
|
||
}
|
||
}
|
||
|
||
this.animate();
|
||
}
|
||
|
||
positionConnection(conn, node1, node2) {
|
||
const x1 = parseFloat(node1.style.left) + 4;
|
||
const y1 = parseFloat(node1.style.top) + 4;
|
||
const x2 = parseFloat(node2.style.left) + 4;
|
||
const y2 = parseFloat(node2.style.top) + 4;
|
||
|
||
const length = Math.sqrt(Math.pow(x2 - x1, 2) + Math.pow(y2 - y1, 2));
|
||
const angle = Math.atan2(y2 - y1, x2 - x1) * 180 / Math.PI;
|
||
|
||
conn.style.width = length + 'px';
|
||
conn.style.left = x1 + 'px';
|
||
conn.style.top = y1 + 'px';
|
||
conn.style.transform = `rotate(${angle}deg)`;
|
||
}
|
||
|
||
animate() {
|
||
const tl = gsap.timeline({
|
||
onComplete: () => this.finalize()
|
||
});
|
||
|
||
// 节点出现
|
||
tl.to(this.nodes, {
|
||
opacity: 1,
|
||
duration: 0.3,
|
||
stagger: 0.05,
|
||
ease: "back.out(1.7)"
|
||
});
|
||
|
||
// 连线出现
|
||
tl.to(this.connections, {
|
||
opacity: 1,
|
||
duration: 0.4,
|
||
stagger: 0.1,
|
||
ease: "power2.out"
|
||
}, "-=0.2");
|
||
|
||
// 脉冲效果
|
||
tl.to(this.nodes, {
|
||
scale: 1.5,
|
||
duration: 0.3,
|
||
yoyo: true,
|
||
repeat: 1,
|
||
stagger: {
|
||
each: 0.1,
|
||
from: "center"
|
||
},
|
||
ease: "sine.inOut"
|
||
});
|
||
|
||
// 整体扩散
|
||
tl.to(this.container, {
|
||
scale: 1.2,
|
||
duration: 0.5,
|
||
ease: "power2.inOut"
|
||
});
|
||
|
||
// 淡出
|
||
tl.to(this.container, {
|
||
opacity: 0,
|
||
duration: 0.5,
|
||
ease: "power2.out"
|
||
});
|
||
}
|
||
|
||
finalize() {
|
||
const intro = document.getElementById('intro-animation');
|
||
const main = document.getElementById('main-container');
|
||
|
||
gsap.to(intro, {
|
||
opacity: 0,
|
||
duration: 0.5,
|
||
onComplete: () => {
|
||
intro.style.display = 'none';
|
||
appState.isIntroComplete = true;
|
||
main.style.opacity = 1;
|
||
this.onComplete();
|
||
}
|
||
});
|
||
}
|
||
|
||
onComplete() {
|
||
// 初始化其他模块
|
||
cityView3D.init();
|
||
networkViz.init();
|
||
dashboard.init();
|
||
timeline.init();
|
||
commandConsole.init();
|
||
particles.init();
|
||
utils.scanEffect();
|
||
utils.showFeedback('系统启动完成 - 欢迎访问神经网络城市平台', 'success');
|
||
}
|
||
}
|
||
|
||
// 3D城市视图模块
|
||
class CityView3D {
|
||
constructor() {
|
||
this.scene = null;
|
||
this.camera = null;
|
||
this.renderer = null;
|
||
this.controls = null;
|
||
this.buildings = [];
|
||
this.particles = [];
|
||
this.container = null;
|
||
this.mouseX = 0;
|
||
this.mouseY = 0;
|
||
this.targetRotation = { x: 0, y: 0 };
|
||
this.currentRotation = { x: 0, y: 0 };
|
||
}
|
||
|
||
init() {
|
||
this.container = document.getElementById('city-canvas');
|
||
if (!this.container) return;
|
||
|
||
// 场景设置
|
||
this.scene = new THREE.Scene();
|
||
this.scene.background = new THREE.Color(0x0a0a0a);
|
||
this.scene.fog = new THREE.Fog(0x0a0a0a, 50, 200);
|
||
|
||
// 相机设置
|
||
this.camera = new THREE.PerspectiveCamera(
|
||
60,
|
||
this.container.clientWidth / this.container.clientHeight,
|
||
0.1,
|
||
1000
|
||
);
|
||
this.camera.position.set(0, 40, 60);
|
||
this.camera.lookAt(0, 0, 0);
|
||
|
||
// 渲染器
|
||
this.renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true });
|
||
this.renderer.setSize(this.container.clientWidth, this.container.clientHeight);
|
||
this.renderer.setPixelRatio(window.devicePixelRatio);
|
||
this.container.appendChild(this.renderer.domElement);
|
||
|
||
// 灯光
|
||
const ambientLight = new THREE.AmbientLight(0x404040, 2);
|
||
this.scene.add(ambientLight);
|
||
|
||
const directionalLight = new THREE.DirectionalLight(0x00ffff, 1);
|
||
directionalLight.position.set(10, 50, 20);
|
||
this.scene.add(directionalLight);
|
||
|
||
const pointLight = new THREE.PointLight(0xff00ff, 1, 100);
|
||
pointLight.position.set(0, 20, 0);
|
||
this.scene.add(pointLight);
|
||
|
||
// 创建城市
|
||
this.createCity();
|
||
|
||
// 创建粒子系统
|
||
this.createParticles();
|
||
|
||
// 鼠标交互
|
||
this.setupInteractions();
|
||
|
||
// 开始动画循环
|
||
this.animate();
|
||
|
||
// 响应窗口大小变化
|
||
window.addEventListener('resize', () => this.onResize());
|
||
}
|
||
|
||
createCity() {
|
||
const gridSize = 8;
|
||
const spacing = 15;
|
||
const geometry = new THREE.BoxGeometry(1, 1, 1);
|
||
|
||
for (let i = 0; i < gridSize; i++) {
|
||
for (let j = 0; j < gridSize; j++) {
|
||
if (Math.random() > 0.7) continue; // 创建空白区域
|
||
|
||
const height = Math.random() * 20 + 5;
|
||
|
||
// 正确的材质:使用 MeshStandardMaterial
|
||
const material = new THREE.MeshStandardMaterial({
|
||
color: new THREE.Color().setHSL(Math.random() * 0.1 + 0.5, 0.8, 0.3),
|
||
emissive: new THREE.Color().setHSL(Math.random() * 0.1 + 0.5, 1, 0.2),
|
||
emissiveIntensity: 0.5,
|
||
metalness: 0.8,
|
||
roughness: 0.2
|
||
});
|
||
|
||
const building = new THREE.Mesh(geometry, material);
|
||
building.position.set(
|
||
(i - gridSize / 2) * spacing + (Math.random() - 0.5) * 5,
|
||
height / 2,
|
||
(j - gridSize / 2) * spacing + (Math.random() - 0.5) * 5
|
||
);
|
||
building.scale.set(3 + Math.random() * 2, height, 3 + Math.random() * 2);
|
||
|
||
this.scene.add(building);
|
||
this.buildings.push(building);
|
||
|
||
// 添加窗口灯光
|
||
if (Math.random() > 0.5) {
|
||
const windowLight = new THREE.PointLight(0x00ffff, 0.5, 5);
|
||
windowLight.position.set(
|
||
building.position.x + (Math.random() - 0.5) * 2,
|
||
building.position.y + (Math.random() - 0.5) * height,
|
||
building.position.z
|
||
);
|
||
this.scene.add(windowLight);
|
||
}
|
||
}
|
||
}
|
||
|
||
// 地面网格
|
||
const gridHelper = new THREE.GridHelper(200, 40, 0x00ffff, 0x222222);
|
||
gridHelper.position.y = -0.1;
|
||
this.scene.add(gridHelper);
|
||
}
|
||
|
||
createParticles() {
|
||
const particleCount = 50;
|
||
const geometry = new THREE.BufferGeometry();
|
||
const positions = [];
|
||
const velocities = [];
|
||
|
||
for (let i = 0; i < particleCount; i++) {
|
||
positions.push(
|
||
(Math.random() - 0.5) * 100,
|
||
Math.random() * 30 + 10,
|
||
(Math.random() - 0.5) * 100
|
||
);
|
||
velocities.push(
|
||
(Math.random() - 0.5) * 0.1,
|
||
(Math.random() - 0.5) * 0.05,
|
||
(Math.random() - 0.5) * 0.1
|
||
);
|
||
}
|
||
|
||
geometry.setAttribute('position', new THREE.Float32BufferAttribute(positions, 3));
|
||
|
||
const material = new THREE.PointsMaterial({
|
||
color: 0xff00ff,
|
||
size: 0.5,
|
||
transparent: true,
|
||
opacity: 0.8,
|
||
blending: THREE.AdditiveBlending
|
||
});
|
||
|
||
const particles = new THREE.Points(geometry, material);
|
||
this.scene.add(particles);
|
||
this.particles.push({ mesh: particles, velocities: velocities });
|
||
}
|
||
|
||
setupInteractions() {
|
||
this.container.addEventListener('mousemove', (e) => {
|
||
const rect = this.container.getBoundingClientRect();
|
||
this.mouseX = ((e.clientX - rect.left) / rect.width) * 2 - 1;
|
||
this.mouseY = ((e.clientY - rect.top) / rect.height) * 2 - 1;
|
||
|
||
this.targetRotation.y = this.mouseX * 0.5;
|
||
this.targetRotation.x = -this.mouseY * 0.3;
|
||
});
|
||
|
||
// 滚轮缩放
|
||
this.container.addEventListener('wheel', (e) => {
|
||
e.preventDefault();
|
||
this.camera.position.z += e.deltaY * 0.05;
|
||
this.camera.position.z = Math.max(20, Math.min(100, this.camera.position.z));
|
||
});
|
||
}
|
||
|
||
animate() {
|
||
if (!this.renderer) return;
|
||
|
||
requestAnimationFrame(() => this.animate());
|
||
|
||
// 平滑相机旋转
|
||
this.currentRotation.x += (this.targetRotation.x - this.currentRotation.x) * 0.05;
|
||
this.currentRotation.y += (this.targetRotation.y - this.currentRotation.y) * 0.05;
|
||
|
||
const radius = this.camera.position.z;
|
||
const lookAt = new THREE.Vector3(0, 5, 0);
|
||
|
||
this.camera.position.x = radius * Math.sin(this.currentRotation.y) * Math.cos(this.currentRotation.x);
|
||
this.camera.position.y = 40 + this.currentRotation.x * 20;
|
||
this.camera.position.z = radius * Math.cos(this.currentRotation.y) * Math.cos(this.currentRotation.x);
|
||
this.camera.lookAt(lookAt);
|
||
|
||
// 更新粒子
|
||
this.particles.forEach(particle => {
|
||
const positions = particle.mesh.geometry.attributes.position.array;
|
||
for (let i = 0; i < positions.length; i += 3) {
|
||
positions[i] += particle.velocities[i] * 0.1;
|
||
positions[i + 1] += particle.velocities[i + 1] * 0.1;
|
||
positions[i + 2] += particle.velocities[i + 2] * 0.1;
|
||
|
||
// 边界检查
|
||
if (Math.abs(positions[i]) > 50) particle.velocities[i] *= -1;
|
||
if (positions[i + 1] > 40 || positions[i + 1] < 5) particle.velocities[i + 1] *= -1;
|
||
if (Math.abs(positions[i + 2]) > 50) particle.velocities[i + 2] *= -1;
|
||
}
|
||
particle.mesh.geometry.attributes.position.needsUpdate = true;
|
||
});
|
||
|
||
// 动态发光
|
||
this.buildings.forEach((building, index) => {
|
||
if (Math.random() > 0.95) {
|
||
building.material.emissiveIntensity = 0.8;
|
||
setTimeout(() => {
|
||
building.material.emissiveIntensity = 0.2;
|
||
}, 100);
|
||
}
|
||
});
|
||
|
||
this.renderer.render(this.scene, this.camera);
|
||
}
|
||
|
||
onResize() {
|
||
if (!this.container || !this.camera || !this.renderer) return;
|
||
|
||
this.camera.aspect = this.container.clientWidth / this.container.clientHeight;
|
||
this.camera.updateProjectionMatrix();
|
||
this.renderer.setSize(this.container.clientWidth, this.container.clientHeight);
|
||
}
|
||
|
||
// 更新城市状态显示
|
||
updateStats() {
|
||
const now = new Date();
|
||
const futureDate = new Date(2078, 0, 1);
|
||
futureDate.setMilliseconds(now.getMilliseconds());
|
||
futureDate.setSeconds(now.getSeconds());
|
||
futureDate.setMinutes(now.getMinutes());
|
||
futureDate.setHours(now.getHours());
|
||
|
||
document.getElementById('city-time').textContent = futureDate.toLocaleString('zh-CN');
|
||
|
||
// 动态数据
|
||
const status = ['稳定', '优化中', '高效', '自适应'][Math.floor(Math.random() * 4)];
|
||
document.getElementById('city-status').textContent = status;
|
||
|
||
document.getElementById('population-density').textContent =
|
||
(0.8 + Math.random() * 0.2).toFixed(2);
|
||
document.getElementById('energy-index').textContent =
|
||
(2.2 + Math.random() * 0.3).toFixed(2);
|
||
}
|
||
}
|
||
|
||
// 神经网络数据流模块
|
||
class NetworkViz {
|
||
constructor() {
|
||
this.canvas = null;
|
||
this.ctx = null;
|
||
this.nodes = [];
|
||
this.pulses = [];
|
||
this.animationId = null;
|
||
}
|
||
|
||
init() {
|
||
this.canvas = document.getElementById('network-canvas');
|
||
if (!this.canvas) return;
|
||
|
||
this.ctx = this.canvas.getContext('2d');
|
||
this.resize();
|
||
this.createNodes();
|
||
this.animate();
|
||
|
||
window.addEventListener('resize', () => this.resize());
|
||
}
|
||
|
||
resize() {
|
||
if (!this.canvas) return;
|
||
this.canvas.width = this.canvas.offsetWidth;
|
||
this.canvas.height = this.canvas.offsetHeight;
|
||
}
|
||
|
||
createNodes() {
|
||
const nodeCount = 12;
|
||
for (let i = 0; i < nodeCount; i++) {
|
||
this.nodes.push({
|
||
x: Math.random() * this.canvas.width,
|
||
y: Math.random() * this.canvas.height,
|
||
vx: (Math.random() - 0.5) * 0.5,
|
||
vy: (Math.random() - 0.5) * 0.5,
|
||
radius: Math.random() * 3 + 2,
|
||
id: i,
|
||
active: false,
|
||
pulseTimer: 0
|
||
});
|
||
}
|
||
}
|
||
|
||
createPulse(fromNode, toNode) {
|
||
this.pulses.push({
|
||
from: fromNode,
|
||
to: toNode,
|
||
progress: 0,
|
||
speed: 0.02 + Math.random() * 0.03
|
||
});
|
||
}
|
||
|
||
draw() {
|
||
if (!this.ctx) return;
|
||
|
||
this.ctx.fillStyle = 'rgba(0, 0, 0, 0.1)';
|
||
this.ctx.fillRect(0, 0, this.canvas.width, this.canvas.height);
|
||
|
||
// 绘制连接
|
||
for (let i = 0; i < this.nodes.length; i++) {
|
||
for (let j = i + 1; j < this.nodes.length; j++) {
|
||
const dx = this.nodes[i].x - this.nodes[j].x;
|
||
const dy = this.nodes[i].y - this.nodes[j].y;
|
||
const dist = Math.sqrt(dx * dx + dy * dy);
|
||
|
||
if (dist < 120) {
|
||
const opacity = 1 - (dist / 120);
|
||
this.ctx.strokeStyle = `rgba(0, 255, 255, ${opacity * 0.3})`;
|
||
this.ctx.lineWidth = 1;
|
||
this.ctx.beginPath();
|
||
this.ctx.moveTo(this.nodes[i].x, this.nodes[i].y);
|
||
this.ctx.lineTo(this.nodes[j].x, this.nodes[j].y);
|
||
this.ctx.stroke();
|
||
}
|
||
}
|
||
}
|
||
|
||
// 更新和绘制脉冲
|
||
this.pulses = this.pulses.filter(pulse => {
|
||
if (pulse.progress >= 1) {
|
||
// 脉冲到达,激活目标节点
|
||
pulse.to.active = true;
|
||
pulse.to.pulseTimer = 10;
|
||
return false;
|
||
}
|
||
|
||
pulse.progress += pulse.speed;
|
||
|
||
const x = pulse.from.x + (pulse.to.x - pulse.from.x) * pulse.progress;
|
||
const y = pulse.from.y + (pulse.to.y - pulse.from.y) * pulse.progress;
|
||
|
||
this.ctx.fillStyle = '#ff00ff';
|
||
this.ctx.shadowBlur = 10;
|
||
this.ctx.shadowColor = '#ff00ff';
|
||
this.ctx.beginPath();
|
||
this.ctx.arc(x, y, 2, 0, Math.PI * 2);
|
||
this.ctx.fill();
|
||
this.ctx.shadowBlur = 0;
|
||
|
||
return true;
|
||
});
|
||
|
||
// 更新和绘制节点
|
||
this.nodes.forEach(node => {
|
||
// 更新位置
|
||
node.x += node.vx;
|
||
node.y += node.vy;
|
||
|
||
// 边界检查
|
||
if (node.x < 0 || node.x > this.canvas.width) node.vx *= -1;
|
||
if (node.y < 0 || node.y > this.canvas.height) node.vy *= -1;
|
||
|
||
// 节点脉冲
|
||
if (node.pulseTimer > 0) {
|
||
node.pulseTimer--;
|
||
}
|
||
|
||
// 绘制节点
|
||
const radius = node.radius + (node.pulseTimer > 0 ? 2 : 0);
|
||
const opacity = node.active ? 1 : 0.5;
|
||
|
||
this.ctx.fillStyle = `rgba(255, 0, 255, ${opacity})`;
|
||
this.ctx.beginPath();
|
||
this.ctx.arc(node.x, node.y, radius, 0, Math.PI * 2);
|
||
this.ctx.fill();
|
||
|
||
// 如果活跃,绘制发光
|
||
if (node.pulseTimer > 0) {
|
||
this.ctx.strokeStyle = 'rgba(0, 255, 255, 0.5)';
|
||
this.ctx.lineWidth = 1;
|
||
this.ctx.beginPath();
|
||
this.ctx.arc(node.x, node.y, radius + 3, 0, Math.PI * 2);
|
||
this.ctx.stroke();
|
||
}
|
||
|
||
// 重置活跃状态
|
||
if (Math.random() > 0.98) {
|
||
node.active = false;
|
||
}
|
||
|
||
// 随机创建新脉冲
|
||
if (Math.random() > 0.95 && this.pulses.length < 8) {
|
||
const target = this.nodes[utils.randomInt(0, this.nodes.length - 1)];
|
||
if (target !== node) {
|
||
this.createPulse(node, target);
|
||
}
|
||
}
|
||
});
|
||
}
|
||
|
||
animate() {
|
||
this.draw();
|
||
this.animationId = requestAnimationFrame(() => this.animate());
|
||
}
|
||
|
||
updateMetrics() {
|
||
// 更新显示的指标
|
||
const activity = utils.randomInt(80, 95);
|
||
const throughput = (4.5 + Math.random() * 2).toFixed(1);
|
||
const latency = (0.5 + Math.random() * 0.5).toFixed(1);
|
||
const synapses = utils.randomInt(1000000, 1500000);
|
||
|
||
document.getElementById('neuron-activity').textContent = activity + '%';
|
||
document.getElementById('data-throughput').textContent = throughput + 'TB/s';
|
||
document.getElementById('latency').textContent = latency + 'ms';
|
||
document.getElementById('synapses').textContent = (synapses / 1000000).toFixed(1) + 'M';
|
||
}
|
||
}
|
||
|
||
// 监控仪表板模块
|
||
class Dashboard {
|
||
constructor() {
|
||
this.cpuGauge = null;
|
||
this.cpuValue = null;
|
||
this.waveformSvg = null;
|
||
this.waveformPath = null;
|
||
this.serverGrid = null;
|
||
this.logWindow = null;
|
||
this.waveData = [];
|
||
}
|
||
|
||
init() {
|
||
this.cpuGauge = document.getElementById('cpu-gauge');
|
||
this.cpuValue = document.getElementById('cpu-value');
|
||
this.waveformSvg = document.getElementById('waveform-svg');
|
||
this.waveformPath = this.waveformSvg.querySelector('.waveform-line');
|
||
this.serverGrid = document.getElementById('server-grid');
|
||
this.logWindow = document.getElementById('log-window');
|
||
|
||
if (!this.cpuGauge || !this.waveformSvg) return;
|
||
|
||
// 创建服务器网格
|
||
this.createServerGrid();
|
||
|
||
// 开始数据更新循环
|
||
this.startDataUpdates();
|
||
|
||
// 开始波形动画
|
||
this.animateWaveform();
|
||
}
|
||
|
||
createServerGrid() {
|
||
for (let i = 0; i < 25; i++) {
|
||
const node = utils.createElement('div', 'server-node', this.serverGrid);
|
||
|
||
// 随机设置错误状态
|
||
if (Math.random() > 0.85) {
|
||
node.classList.add('error');
|
||
}
|
||
|
||
// 点击交互
|
||
node.addEventListener('click', () => {
|
||
utils.scanEffect();
|
||
const status = node.classList.contains('error') ? '异常' : '正常';
|
||
utils.showFeedback(`服务器节点 ${i + 1} 状态: ${status}`,
|
||
node.classList.contains('error') ? 'error' : 'success');
|
||
});
|
||
}
|
||
}
|
||
|
||
startDataUpdates() {
|
||
// CPU/内存数据更新
|
||
setInterval(() => {
|
||
const cpu = utils.randomInt(30, 80);
|
||
const memory = utils.randomInt(45, 75);
|
||
|
||
// 更新CPU仪表
|
||
const rotation = (cpu / 100) * 180 - 90; // -90到90度
|
||
this.cpuGauge.style.transform = `rotate(${rotation}deg)`;
|
||
this.cpuValue.textContent = cpu + '%';
|
||
this.cpuValue.style.color = cpu > 70 ? CONFIG.colors.dangerRed : CONFIG.colors.electricBlue;
|
||
|
||
// 更新服务器状态(随机变化)
|
||
const nodes = this.serverGrid.querySelectorAll('.server-node');
|
||
nodes.forEach(node => {
|
||
if (Math.random() > 0.95) {
|
||
node.classList.toggle('error');
|
||
}
|
||
});
|
||
|
||
// 添加日志
|
||
this.addLogEntry();
|
||
|
||
}, 2000);
|
||
|
||
// 网络流量指标更新(已经在Dashboard中处理)
|
||
setInterval(() => {
|
||
appState.metrics.throughput = (4 + Math.random() * 2).toFixed(1);
|
||
appState.metrics.latency = (0.5 + Math.random() * 0.5).toFixed(1);
|
||
}, 3000);
|
||
}
|
||
|
||
addLogEntry() {
|
||
const logs = [
|
||
'系统诊断完成 - 所有子系统正常',
|
||
'数据包传输优化: 提升效率 12%',
|
||
'神经网络节点连接良好',
|
||
'能源分配系统稳定运行',
|
||
'安全协议 v4.2 已激活',
|
||
'原子钟同步完成',
|
||
'数据完整性检查通过',
|
||
'AI协议栈更新完成',
|
||
'冗余系统待命',
|
||
'通讯链路加密通道建立'
|
||
];
|
||
|
||
const entry = document.createElement('div');
|
||
entry.className = 'log-entry';
|
||
|
||
const timestamp = new Date().toLocaleTimeString();
|
||
const message = logs[utils.randomInt(0, logs.length - 1)];
|
||
|
||
entry.innerHTML = `<span class="log-timestamp">${timestamp}</span>${message}`;
|
||
this.logWindow.appendChild(entry);
|
||
|
||
// 滚动到底部
|
||
this.logWindow.scrollTop = this.logWindow.scrollHeight;
|
||
|
||
// 保持最多10条记录
|
||
while (this.logWindow.children.length > 10) {
|
||
this.logWindow.removeChild(this.logWindow.firstChild);
|
||
}
|
||
}
|
||
|
||
animateWaveform() {
|
||
if (!this.waveformPath) return;
|
||
|
||
const width = 300;
|
||
const height = 100;
|
||
const points = 20;
|
||
|
||
const generatePath = () => {
|
||
let path = `M 0 ${height / 2}`;
|
||
|
||
for (let i = 1; i <= points; i++) {
|
||
const x = (i / points) * width;
|
||
const y = height / 2 + Math.sin((i * 0.5) + Date.now() * 0.002) * 20 * Math.sin(Date.now() * 0.001);
|
||
path += ` L ${x} ${y}`;
|
||
}
|
||
|
||
return path;
|
||
};
|
||
|
||
const update = () => {
|
||
if (this.waveformPath) {
|
||
this.waveformPath.setAttribute('d', generatePath());
|
||
}
|
||
requestAnimationFrame(update);
|
||
};
|
||
|
||
update();
|
||
}
|
||
}
|
||
|
||
// 时间线导航模块
|
||
class Timeline {
|
||
constructor() {
|
||
this.track = null;
|
||
this.data = [
|
||
{ year: 2078, desc: '当前时间', active: true },
|
||
{ year: 2065, desc: 'AI觉醒' },
|
||
{ year: 2050, desc: '神经网络融合' },
|
||
{ year: 2035, desc: '量子计算突破' },
|
||
{ year: 2020, desc: '数字文明开始' }
|
||
];
|
||
}
|
||
|
||
init() {
|
||
this.track = document.getElementById('timeline-track');
|
||
if (!this.track) return;
|
||
|
||
// 创建时间线节点
|
||
this.data.forEach((item, index) => {
|
||
const node = utils.createElement('div', 'timeline-node', this.track);
|
||
node.innerHTML = `
|
||
<span class="year">${item.year}</span>
|
||
<div class="desc">${item.desc}</div>
|
||
`;
|
||
|
||
if (item.active) {
|
||
node.classList.add('active');
|
||
}
|
||
|
||
// 交互
|
||
node.addEventListener('click', () => this.selectNode(node, item));
|
||
node.addEventListener('mouseenter', (e) => {
|
||
if (item.year !== 2078) {
|
||
utils.showTooltip(`切换到 ${item.year} 年视图`, e.clientX, e.clientY);
|
||
}
|
||
});
|
||
node.addEventListener('mouseleave', () => utils.hideTooltip());
|
||
});
|
||
}
|
||
|
||
selectNode(node, item) {
|
||
// 移除所有active
|
||
this.track.querySelectorAll('.timeline-node').forEach(n => n.classList.remove('active'));
|
||
node.classList.add('active');
|
||
|
||
// 切换视图(模拟)
|
||
utils.scanEffect();
|
||
utils.showFeedback(`切换时间线到 ${item.year} - 建筑风格更新`, 'success');
|
||
|
||
// 视觉反馈 - 城市视图变化(模拟)
|
||
if (cityView3D.scene && item.year !== 2078) {
|
||
cityView3D.scene.fog.density = 0.02 + (2078 - item.year) * 0.001;
|
||
}
|
||
|
||
appState.currentTimeline = item.year;
|
||
}
|
||
}
|
||
|
||
// 命令控制台模块
|
||
class CommandConsole {
|
||
constructor() {
|
||
this.input = null;
|
||
this.feedback = null;
|
||
this.commands = {
|
||
'显示能源分布': this.showEnergyDistribution,
|
||
'切换到东区视图': this.switchToEastDistrict,
|
||
'开始系统诊断': this.startSystemDiagnostics,
|
||
'显示网络拓扑': this.showNetworkTopology,
|
||
'检查AI状态': this.checkAIStatus,
|
||
'更新系统': this.updateSystem,
|
||
'重启系统': this.restartSystem,
|
||
'显示帮助': this.showHelp
|
||
};
|
||
}
|
||
|
||
init() {
|
||
this.input = document.getElementById('command-input');
|
||
this.feedback = document.getElementById('command-feedback');
|
||
|
||
if (!this.input) return;
|
||
|
||
this.input.addEventListener('keydown', (e) => {
|
||
if (e.key === 'Enter') {
|
||
this.executeCommand(this.input.value.trim());
|
||
this.input.value = '';
|
||
}
|
||
});
|
||
|
||
// 自动聚焦
|
||
setTimeout(() => this.input.focus(), 1000);
|
||
}
|
||
|
||
executeCommand(command) {
|
||
if (!command) return;
|
||
|
||
utils.scanEffect();
|
||
|
||
// 查找匹配命令
|
||
let matchedKey = null;
|
||
for (const key in this.commands) {
|
||
if (command.includes(key) || key.includes(command)) {
|
||
matchedKey = key;
|
||
break;
|
||
}
|
||
}
|
||
|
||
if (matchedKey) {
|
||
utils.showFeedback(`执行命令: ${matchedKey}`, 'info');
|
||
this.commands[matchedKey].call(this);
|
||
} else {
|
||
utils.showFeedback(`未知命令: ${command}。输入"显示帮助"查看可用命令。`, 'error');
|
||
}
|
||
}
|
||
|
||
showEnergyDistribution() {
|
||
setTimeout(() => {
|
||
utils.showFeedback('正在生成能源分布图...', 'info');
|
||
setTimeout(() => {
|
||
utils.showFeedback('城市核心: 78% | 太阳能阵列: 15% | 生物能: 7%', 'success');
|
||
}, 1000);
|
||
}, 500);
|
||
}
|
||
|
||
switchToEastDistrict() {
|
||
setTimeout(() => {
|
||
utils.showFeedback('加载东城区全息投影...', 'info');
|
||
setTimeout(() => {
|
||
utils.showFeedback('东城区视图已激活 - 高密度商业区', 'success');
|
||
// 触发视觉效果
|
||
if (cityView3D.camera) {
|
||
gsap.to(cityView3D.camera.position, {
|
||
x: 30,
|
||
z: 40,
|
||
duration: 1,
|
||
ease: "power2.inOut"
|
||
});
|
||
}
|
||
}, 1200);
|
||
}, 500);
|
||
}
|
||
|
||
startSystemDiagnostics() {
|
||
const steps = [
|
||
'检查硬件状态...',
|
||
'扫描网络连接...',
|
||
'验证数据完整性...',
|
||
'测试AI核心...',
|
||
'分析系统日志...'
|
||
];
|
||
|
||
let delay = 0;
|
||
steps.forEach((step, index) => {
|
||
setTimeout(() => {
|
||
utils.showFeedback(step, 'info');
|
||
if (index === steps.length - 1) {
|
||
setTimeout(() => {
|
||
utils.showFeedback('系统诊断完成 - 所有指标正常', 'success');
|
||
// 激活所有服务器节点
|
||
const nodes = document.querySelectorAll('.server-node');
|
||
nodes.forEach(node => node.classList.remove('error'));
|
||
}, 1000);
|
||
}
|
||
}, delay);
|
||
delay += 800;
|
||
});
|
||
}
|
||
|
||
showNetworkTopology() {
|
||
setTimeout(() => {
|
||
utils.showFeedback('生成网络拓扑图...', 'info');
|
||
setTimeout(() => {
|
||
utils.showFeedback('主节点: 1024 | 边缘节点: 8.8M | 连接质量: 优秀', 'success');
|
||
}, 800);
|
||
}, 300);
|
||
}
|
||
|
||
checkAIStatus() {
|
||
setTimeout(() => {
|
||
utils.showFeedback('评估AI意识状态...', 'info');
|
||
setTimeout(() => {
|
||
const sync = (92 + Math.random() * 6).toFixed(1);
|
||
utils.showFeedback(`AI同步率: ${sync}% - 运行正常`, 'success');
|
||
// 更新数据卡片
|
||
const aiCard = document.querySelector('.holo-card:nth-child(4) .card-stat');
|
||
if (aiCard) aiCard.textContent = sync + '%';
|
||
}, 1200);
|
||
}, 300);
|
||
}
|
||
|
||
updateSystem() {
|
||
setTimeout(() => {
|
||
utils.showFeedback('连接更新服务器...', 'info');
|
||
setTimeout(() => {
|
||
utils.showFeedback('下载核心包 (24.5GB)...', 'info');
|
||
let progress = 0;
|
||
const interval = setInterval(() => {
|
||
progress += 10;
|
||
if (progress >= 100) {
|
||
clearInterval(interval);
|
||
utils.showFeedback('安装完成 - 系统已更新到 v2078.4', 'success');
|
||
} else {
|
||
utils.showFeedback(`进度: ${progress}%`, 'info');
|
||
}
|
||
}, 500);
|
||
}, 1000);
|
||
}, 500);
|
||
}
|
||
|
||
restartSystem() {
|
||
setTimeout(() => {
|
||
utils.showFeedback('初始化系统重启序列...', 'warning');
|
||
setTimeout(() => {
|
||
utils.showFeedback('正在关闭所有服务...', 'warning');
|
||
setTimeout(() => {
|
||
utils.showFeedback('执行冷启动...', 'warning');
|
||
setTimeout(() => {
|
||
utils.showFeedback('系统重启完成 - 所有服务已恢复', 'success');
|
||
utils.scanEffect();
|
||
}, 2000);
|
||
}, 1000);
|
||
}, 1000);
|
||
}, 500);
|
||
}
|
||
|
||
showHelp() {
|
||
const helpText = `
|
||
可用命令:
|
||
• 显示能源分布 - 查看能源分配情况
|
||
• 切换到东区视图 - 加载东城区投影
|
||
• 开始系统诊断 - 运行全面系统检查
|
||
• 显示网络拓扑 - 查看网络结构
|
||
• 检查AI状态 - 评估AI意识同步率
|
||
• 更新系统 - 下载并安装更新
|
||
• 重启系统 - 执行系统重启
|
||
• 显示帮助 - 显示此帮助信息
|
||
`;
|
||
|
||
setTimeout(() => {
|
||
utils.showFeedback(helpText, 'info');
|
||
}, 300);
|
||
}
|
||
}
|
||
|
||
// 粒子背景模块
|
||
class ParticlesBackground {
|
||
init() {
|
||
const container = document.getElementById('particles-js');
|
||
if (!container || typeof particlesJS === 'undefined') return;
|
||
|
||
particlesJS('particles-js', {
|
||
particles: {
|
||
number: {
|
||
value: CONFIG.animation.particleCount,
|
||
density: {
|
||
enable: true,
|
||
value_area: 800
|
||
}
|
||
},
|
||
color: {
|
||
value: '#00ffff'
|
||
},
|
||
shape: {
|
||
type: 'circle'
|
||
},
|
||
opacity: {
|
||
value: 0.5,
|
||
random: true,
|
||
anim: {
|
||
enable: true,
|
||
speed: 1,
|
||
opacity_min: 0.1,
|
||
sync: false
|
||
}
|
||
},
|
||
size: {
|
||
value: 3,
|
||
random: true,
|
||
anim: {
|
||
enable: true,
|
||
speed: 2,
|
||
size_min: 0.1,
|
||
sync: false
|
||
}
|
||
},
|
||
line_linked: {
|
||
enable: true,
|
||
distance: 150,
|
||
color: '#ff00ff',
|
||
opacity: 0.4,
|
||
width: 1
|
||
},
|
||
move: {
|
||
enable: true,
|
||
speed: 1,
|
||
direction: 'none',
|
||
random: true,
|
||
straight: false,
|
||
out_mode: 'out',
|
||
bounce: false
|
||
}
|
||
},
|
||
interactivity: {
|
||
detect_on: 'canvas',
|
||
events: {
|
||
onhover: {
|
||
enable: true,
|
||
mode: 'grab'
|
||
},
|
||
onclick: {
|
||
enable: true,
|
||
mode: 'push'
|
||
},
|
||
resize: true
|
||
},
|
||
modes: {
|
||
grab: {
|
||
distance: 140,
|
||
line_linked: {
|
||
opacity: 1
|
||
}
|
||
},
|
||
push: {
|
||
particles_nb: 4
|
||
}
|
||
}
|
||
},
|
||
retina_detect: true
|
||
});
|
||
}
|
||
}
|
||
|
||
// 工具提示管理
|
||
class TooltipManager {
|
||
constructor() {
|
||
this.tooltip = document.getElementById('tooltip');
|
||
this.init();
|
||
}
|
||
|
||
init() {
|
||
// 为所有带有data-tooltip属性的元素添加事件
|
||
document.addEventListener('mouseover', (e) => {
|
||
const element = e.target.closest('[data-tooltip]');
|
||
if (element) {
|
||
const text = element.getAttribute('data-tooltip');
|
||
if (text) {
|
||
utils.showTooltip(text, e.clientX, e.clientY);
|
||
}
|
||
}
|
||
});
|
||
|
||
document.addEventListener('mouseout', (e) => {
|
||
if (e.target.closest('[data-tooltip]')) {
|
||
utils.hideTooltip();
|
||
}
|
||
});
|
||
|
||
document.addEventListener('mousemove', (e) => {
|
||
if (this.tooltip.classList.contains('show')) {
|
||
this.tooltip.style.left = e.clientX + 10 + 'px';
|
||
this.tooltip.style.top = e.clientY + 10 + 'px';
|
||
}
|
||
});
|
||
}
|
||
}
|
||
|
||
// 数据更新管理器
|
||
class DataUpdateManager {
|
||
constructor() {
|
||
this.interval = null;
|
||
}
|
||
|
||
start() {
|
||
// 主要数据更新循环(视觉数据动态化)
|
||
this.interval = setInterval(() => {
|
||
// 只在主界面可见时更新
|
||
if (!appState.isIntroComplete) return;
|
||
|
||
// 更新3D城市统计
|
||
if (cityView3D) cityView3D.updateStats();
|
||
|
||
// 更新网络指标
|
||
if (networkViz) networkViz.updateMetrics();
|
||
|
||
}, 2000);
|
||
}
|
||
|
||
stop() {
|
||
if (this.interval) {
|
||
clearInterval(this.interval);
|
||
this.interval = null;
|
||
}
|
||
}
|
||
}
|
||
|
||
// 视差效果管理器
|
||
class ParallaxManager {
|
||
constructor() {
|
||
this.layers = [];
|
||
this.init();
|
||
}
|
||
|
||
init() {
|
||
// 创建视差层
|
||
const parallaxContainer = document.createElement('div');
|
||
parallaxContainer.className = 'parallax-layer';
|
||
parallaxContainer.style.zIndex = '0';
|
||
document.body.appendChild(parallaxContainer);
|
||
|
||
// 创建一些视觉元素作为视差层
|
||
for (let i = 0; i < 3; i++) {
|
||
const element = document.createElement('div');
|
||
element.style.position = 'absolute';
|
||
element.style.width = '100%';
|
||
element.style.height = '100%';
|
||
element.style.background = `radial-gradient(circle at ${Math.random() * 100}% ${Math.random() * 100}%,
|
||
transparent, ${i === 0 ? 'rgba(255,0,255,0.05)' : i === 1 ? 'rgba(0,255,255,0.05)' : 'rgba(136,0,255,0.05)'})`;
|
||
element.style.opacity = '0.3';
|
||
parallaxContainer.appendChild(element);
|
||
this.layers.push({ element, speed: (i + 1) * 0.1 });
|
||
}
|
||
|
||
// 滚动事件
|
||
window.addEventListener('scroll', () => this.onScroll());
|
||
}
|
||
|
||
onScroll() {
|
||
const scrollY = window.scrollY;
|
||
this.layers.forEach(layer => {
|
||
layer.element.style.transform = `translateY(${scrollY * layer.speed}px)`;
|
||
});
|
||
}
|
||
}
|
||
|
||
// 滚动入场动画管理器
|
||
class ScrollAnimationManager {
|
||
constructor() {
|
||
this.sections = [];
|
||
this.observer = null;
|
||
this.init();
|
||
}
|
||
|
||
init() {
|
||
// 使用Intersection Observer监听滚动入场
|
||
this.observer = new IntersectionObserver((entries) => {
|
||
entries.forEach(entry => {
|
||
if (entry.isIntersecting) {
|
||
entry.target.style.animation = 'fade-in-up 0.6s ease forwards';
|
||
}
|
||
});
|
||
}, {
|
||
threshold: 0.1
|
||
});
|
||
|
||
// 添加CSS动画
|
||
const style = document.createElement('style');
|
||
style.textContent = `
|
||
@keyframes fade-in-up {
|
||
from {
|
||
opacity: 0;
|
||
transform: translateY(30px);
|
||
}
|
||
to {
|
||
opacity: 1;
|
||
transform: translateY(0);
|
||
}
|
||
}
|
||
`;
|
||
document.head.appendChild(style);
|
||
|
||
// 监听所有section
|
||
setTimeout(() => {
|
||
document.querySelectorAll('section').forEach(section => {
|
||
section.style.opacity = '0'; // 初始隐藏
|
||
this.observer.observe(section);
|
||
});
|
||
}, 100);
|
||
}
|
||
}
|
||
|
||
// 性能优化:使用 requestAnimationFrame 进行节流
|
||
class RAFScheduler {
|
||
constructor() {
|
||
this.callbacks = new Set();
|
||
this.isRunning = false;
|
||
}
|
||
|
||
add(callback) {
|
||
this.callbacks.add(callback);
|
||
if (!this.isRunning) {
|
||
this.run();
|
||
}
|
||
}
|
||
|
||
remove(callback) {
|
||
this.callbacks.delete(callback);
|
||
}
|
||
|
||
run() {
|
||
this.isRunning = true;
|
||
const loop = () => {
|
||
if (this.callbacks.size > 0) {
|
||
this.callbacks.forEach(cb => cb());
|
||
requestAnimationFrame(loop);
|
||
} else {
|
||
this.isRunning = false;
|
||
}
|
||
};
|
||
requestAnimationFrame(loop);
|
||
}
|
||
}
|
||
|
||
// 初始化所有模块
|
||
const introAnimation = new IntroAnimation();
|
||
const cityView3D = new CityView3D();
|
||
const networkViz = new NetworkViz();
|
||
const dashboard = new Dashboard();
|
||
const timeline = new Timeline();
|
||
const commandConsole = new CommandConsole();
|
||
const particles = new ParticlesBackground();
|
||
const dataUpdateManager = new DataUpdateManager();
|
||
const parallaxManager = new ParallaxManager();
|
||
const scrollAnimationManager = new ScrollAnimationManager();
|
||
const tooltipManager = new TooltipManager();
|
||
|
||
// 页面加载完成后开始
|
||
document.addEventListener('DOMContentLoaded', () => {
|
||
// 显示加载器(如果需要)
|
||
const loader = document.getElementById('loader');
|
||
|
||
// 确保所有外部库加载完成
|
||
Promise.all([
|
||
new Promise(resolve => {
|
||
// 检查Three.js
|
||
if (typeof THREE !== 'undefined') resolve();
|
||
else setTimeout(resolve, 500);
|
||
}),
|
||
new Promise(resolve => {
|
||
// 检查GSAP
|
||
if (typeof gsap !== 'undefined') resolve();
|
||
else setTimeout(resolve, 500);
|
||
}),
|
||
new Promise(resolve => {
|
||
// 检查Particles.js
|
||
if (typeof particlesJS !== 'undefined') resolve();
|
||
else setTimeout(resolve, 500);
|
||
})
|
||
]).then(() => {
|
||
// 开始开场动画
|
||
introAnimation.init();
|
||
|
||
// 启动数据更新
|
||
dataUpdateManager.start();
|
||
|
||
// 模拟一些初始的系统日志
|
||
setTimeout(() => {
|
||
if (dashboard.logWindow) {
|
||
const logs = [
|
||
'系统启动完成 - 神经网络城市平台 v2078',
|
||
'所有子系统初始化完成',
|
||
'3D渲染引擎就绪',
|
||
'数据可视化模块加载',
|
||
'等待用户命令...'
|
||
];
|
||
logs.forEach((log, index) => {
|
||
setTimeout(() => {
|
||
if (dashboard.addLogEntry) {
|
||
// 手动添加初始日志
|
||
const entry = document.createElement('div');
|
||
entry.className = 'log-entry';
|
||
entry.innerHTML = `<span class="log-timestamp">${new Date().toLocaleTimeString()}</span>${log}`;
|
||
dashboard.logWindow.appendChild(entry);
|
||
dashboard.logWindow.scrollTop = dashboard.logWindow.scrollHeight;
|
||
}
|
||
}, index * 200);
|
||
});
|
||
}
|
||
}, 1000);
|
||
|
||
utils.showFeedback('系统初始化开始...', 'info');
|
||
});
|
||
|
||
// 模拟CDN加载失败的情况
|
||
window.addEventListener('error', (e) => {
|
||
if (e.target.tagName === 'SCRIPT') {
|
||
utils.showFeedback('外部库加载失败,使用简化模式', 'error');
|
||
// 这里可以添加降级处理
|
||
}
|
||
}, true);
|
||
});
|
||
|
||
// 错误处理
|
||
window.addEventListener('error', (e) => {
|
||
console.error('系统错误:', e.error);
|
||
utils.showFeedback(`错误: ${e.message}`, 'error');
|
||
});
|
||
|
||
// 防止右键菜单(全屏体验)
|
||
document.addEventListener('contextmenu', (e) => {
|
||
e.preventDefault();
|
||
return false;
|
||
});
|
||
</script>
|
||
</body>
|
||
</html>
|