2192 lines
76 KiB
HTML
2192 lines
76 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>
|
||
|
||
<!-- Google Fonts - 科技感字体 -->
|
||
<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&family=Share+Tech+Mono&display=swap" rel="stylesheet">
|
||
|
||
<!-- Font Awesome 图标库 -->
|
||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
||
|
||
<!-- Three.js 核心库 -->
|
||
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
|
||
<!-- Three.js 扩展库 -->
|
||
<script src="https://cdn.jsdelivr.net/npm/three@0.128.0/examples/js/controls/OrbitControls.js"></script>
|
||
<script src="https://cdn.jsdelivr.net/npm/three@0.128.0/examples/js/postprocessing/EffectComposer.js"></script>
|
||
<script src="https://cdn.jsdelivr.net/npm/three@0.128.0/examples/js/postprocessing/RenderPass.js"></script>
|
||
<script src="https://cdn.jsdelivr.net/npm/three@0.128.0/examples/js/postprocessing/UnrealBloomPass.js"></script>
|
||
|
||
<!-- GSAP 动画库 -->
|
||
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.2/gsap.min.js"></script>
|
||
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.2/ScrollTrigger.min.js"></script>
|
||
|
||
<!-- D3.js 数据可视化库 -->
|
||
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/7.8.5/d3.min.js"></script>
|
||
|
||
<!-- Particles.js 粒子效果库 -->
|
||
<script src="https://cdnjs.cloudflare.com/ajax/libs/particles.js/2.0.0/particles.min.js"></script>
|
||
|
||
<!-- Tween.js 过渡动画库 -->
|
||
<script src="https://cdnjs.cloudflare.com/ajax/libs/tween.js/18.6.4/tween.umd.js"></script>
|
||
|
||
<style>
|
||
/* ==================== 基础样式与CSS变量 ==================== */
|
||
:root {
|
||
--neon-pink: #ff00ff;
|
||
--cyber-blue: #00ffff;
|
||
--laser-purple: #8800ff;
|
||
--deep-space: #0a0a0a;
|
||
--metal-gray: #1a1a2e;
|
||
--text-glow: 0 0 10px var(--cyber-blue), 0 0 20px var(--cyber-blue);
|
||
--pink-glow: 0 0 10px var(--neon-pink), 0 0 20px var(--neon-pink);
|
||
--purple-glow: 0 0 10px var(--laser-purple), 0 0 20px var(--laser-purple);
|
||
}
|
||
|
||
* {
|
||
margin: 0;
|
||
padding: 0;
|
||
box-sizing: border-box;
|
||
}
|
||
|
||
html {
|
||
scroll-behavior: smooth;
|
||
}
|
||
|
||
body {
|
||
font-family: 'Rajdhani', 'Microsoft YaHei', sans-serif;
|
||
background: var(--deep-space);
|
||
color: var(--cyber-blue);
|
||
overflow-x: hidden;
|
||
min-height: 100vh;
|
||
}
|
||
|
||
/* 滚动条样式 */
|
||
::-webkit-scrollbar {
|
||
width: 8px;
|
||
}
|
||
|
||
::-webkit-scrollbar-track {
|
||
background: var(--deep-space);
|
||
}
|
||
|
||
::-webkit-scrollbar-thumb {
|
||
background: linear-gradient(180deg, var(--cyber-blue), var(--laser-purple));
|
||
border-radius: 4px;
|
||
}
|
||
|
||
/* ==================== CRT扫描线效果 ==================== */
|
||
.crt-overlay {
|
||
position: fixed;
|
||
top: 0;
|
||
left: 0;
|
||
width: 100%;
|
||
height: 100%;
|
||
pointer-events: none;
|
||
z-index: 10000;
|
||
background: repeating-linear-gradient(
|
||
0deg,
|
||
rgba(0, 0, 0, 0.1) 0px,
|
||
rgba(0, 0, 0, 0.1) 1px,
|
||
transparent 1px,
|
||
transparent 2px
|
||
);
|
||
opacity: 0.3;
|
||
}
|
||
|
||
.vignette {
|
||
position: fixed;
|
||
top: 0;
|
||
left: 0;
|
||
width: 100%;
|
||
height: 100%;
|
||
pointer-events: none;
|
||
z-index: 9999;
|
||
background: radial-gradient(ellipse at center, transparent 0%, rgba(0,0,0,0.8) 100%);
|
||
}
|
||
|
||
/* ==================== 加载界面 ==================== */
|
||
.loading-screen {
|
||
position: fixed;
|
||
top: 0;
|
||
left: 0;
|
||
width: 100%;
|
||
height: 100%;
|
||
background: #000;
|
||
z-index: 10001;
|
||
display: flex;
|
||
flex-direction: column;
|
||
justify-content: center;
|
||
align-items: center;
|
||
transition: opacity 1s ease-out;
|
||
}
|
||
|
||
.loading-screen.hidden {
|
||
opacity: 0;
|
||
pointer-events: none;
|
||
}
|
||
|
||
.loading-title {
|
||
font-family: 'Orbitron', sans-serif;
|
||
font-size: 2.5rem;
|
||
font-weight: 900;
|
||
background: linear-gradient(90deg, var(--neon-pink), var(--cyber-blue), var(--laser-purple));
|
||
-webkit-background-clip: text;
|
||
-webkit-text-fill-color: transparent;
|
||
background-clip: text;
|
||
text-shadow: 0 0 30px rgba(0, 255, 255, 0.5);
|
||
margin-bottom: 2rem;
|
||
animation: glitch-text 3s infinite;
|
||
}
|
||
|
||
.loading-ring {
|
||
width: 150px;
|
||
height: 150px;
|
||
border: 3px solid transparent;
|
||
border-top-color: var(--neon-pink);
|
||
border-radius: 50%;
|
||
animation: spin 1s linear infinite;
|
||
position: relative;
|
||
}
|
||
|
||
.loading-ring::before {
|
||
content: '';
|
||
position: absolute;
|
||
top: -3px;
|
||
left: -3px;
|
||
right: -3px;
|
||
bottom: -3px;
|
||
border: 3px solid transparent;
|
||
border-top-color: var(--cyber-blue);
|
||
border-radius: 50%;
|
||
animation: spin 2s linear infinite reverse;
|
||
}
|
||
|
||
.loading-text {
|
||
margin-top: 2rem;
|
||
font-family: 'Share Tech Mono', monospace;
|
||
font-size: 1rem;
|
||
color: var(--cyber-blue);
|
||
animation: blink 1s infinite;
|
||
}
|
||
|
||
@keyframes spin {
|
||
to { transform: rotate(360deg); }
|
||
}
|
||
|
||
@keyframes blink {
|
||
0%, 50% { opacity: 1; }
|
||
51%, 100% { opacity: 0.3; }
|
||
}
|
||
|
||
@keyframes glitch-text {
|
||
0%, 90%, 100% {
|
||
transform: translate(0);
|
||
}
|
||
92% {
|
||
transform: translate(-2px, 2px);
|
||
}
|
||
94% {
|
||
transform: translate(2px, -2px);
|
||
}
|
||
96% {
|
||
transform: translate(-2px, -2px);
|
||
}
|
||
98% {
|
||
transform: translate(2px, 2px);
|
||
}
|
||
}
|
||
|
||
/* ==================== 神经网络开场动画 ==================== */
|
||
.intro-container {
|
||
position: fixed;
|
||
top: 0;
|
||
left: 0;
|
||
width: 100%;
|
||
height: 100%;
|
||
background: #000;
|
||
z-index: 10000;
|
||
display: flex;
|
||
justify-content: center;
|
||
align-items: center;
|
||
}
|
||
|
||
.intro-container.hidden {
|
||
opacity: 0;
|
||
pointer-events: none;
|
||
transition: opacity 1s ease-out;
|
||
}
|
||
|
||
#intro-canvas {
|
||
position: absolute;
|
||
top: 0;
|
||
left: 0;
|
||
}
|
||
|
||
.intro-text {
|
||
font-family: 'Orbitron', sans-serif;
|
||
font-size: 3rem;
|
||
font-weight: 900;
|
||
color: var(--cyber-blue);
|
||
text-shadow: var(--text-glow);
|
||
z-index: 1;
|
||
opacity: 0;
|
||
transform: scale(0.5);
|
||
}
|
||
|
||
/* ==================== Three.js 3D场景容器 ==================== */
|
||
#three-container {
|
||
position: fixed;
|
||
top: 0;
|
||
left: 0;
|
||
width: 100%;
|
||
height: 100%;
|
||
z-index: 0;
|
||
}
|
||
|
||
/* ==================== 主界面布局 ==================== */
|
||
.main-interface {
|
||
position: relative;
|
||
z-index: 100;
|
||
pointer-events: none;
|
||
}
|
||
|
||
.main-interface > * {
|
||
pointer-events: auto;
|
||
}
|
||
|
||
/* ==================== 顶部状态栏 ==================== */
|
||
.status-bar {
|
||
position: fixed;
|
||
top: 0;
|
||
left: 0;
|
||
width: 100%;
|
||
height: 50px;
|
||
background: rgba(10, 10, 10, 0.9);
|
||
border-bottom: 1px solid var(--laser-purple);
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
padding: 0 2rem;
|
||
z-index: 1000;
|
||
backdrop-filter: blur(10px);
|
||
}
|
||
|
||
.logo {
|
||
font-family: 'Orbitron', sans-serif;
|
||
font-size: 1.2rem;
|
||
font-weight: 700;
|
||
color: var(--neon-pink);
|
||
text-shadow: var(--pink-glow);
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 0.5rem;
|
||
}
|
||
|
||
.logo i {
|
||
font-size: 1rem;
|
||
}
|
||
|
||
.status-indicators {
|
||
display: flex;
|
||
gap: 2rem;
|
||
font-family: 'Share Tech Mono', monospace;
|
||
font-size: 0.85rem;
|
||
}
|
||
|
||
.status-item {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 0.5rem;
|
||
}
|
||
|
||
.status-dot {
|
||
width: 8px;
|
||
height: 8px;
|
||
border-radius: 50%;
|
||
background: #00ff00;
|
||
animation: pulse-dot 2s infinite;
|
||
}
|
||
|
||
.status-dot.warning {
|
||
background: #ffaa00;
|
||
}
|
||
|
||
.status-dot.danger {
|
||
background: #ff0000;
|
||
}
|
||
|
||
@keyframes pulse-dot {
|
||
0%, 100% { opacity: 1; transform: scale(1); }
|
||
50% { opacity: 0.5; transform: scale(0.8); }
|
||
}
|
||
|
||
/* ==================== 监控面板区域 ==================== */
|
||
.dashboard-grid {
|
||
display: grid;
|
||
grid-template-columns: 350px 1fr 350px;
|
||
grid-template-rows: auto 1fr auto;
|
||
gap: 1.5rem;
|
||
padding: 80px 2rem 2rem;
|
||
min-height: 100vh;
|
||
}
|
||
|
||
.panel {
|
||
background: rgba(26, 26, 46, 0.8);
|
||
border: 1px solid rgba(0, 255, 255, 0.3);
|
||
border-radius: 4px;
|
||
padding: 1.5rem;
|
||
backdrop-filter: blur(10px);
|
||
position: relative;
|
||
overflow: hidden;
|
||
}
|
||
|
||
.panel::before {
|
||
content: '';
|
||
position: absolute;
|
||
top: 0;
|
||
left: 0;
|
||
right: 0;
|
||
height: 2px;
|
||
background: linear-gradient(90deg, var(--neon-pink), var(--cyber-blue), var(--laser-purple));
|
||
}
|
||
|
||
.panel-title {
|
||
font-family: 'Orbitron', sans-serif;
|
||
font-size: 0.9rem;
|
||
font-weight: 700;
|
||
color: var(--cyber-blue);
|
||
margin-bottom: 1rem;
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 0.5rem;
|
||
text-transform: uppercase;
|
||
letter-spacing: 2px;
|
||
}
|
||
|
||
/* 左侧监控面板 */
|
||
.monitoring-panel {
|
||
grid-row: span 2;
|
||
}
|
||
|
||
/* 仪表盘样式 */
|
||
.gauge-container {
|
||
display: grid;
|
||
grid-template-columns: repeat(2, 1fr);
|
||
gap: 1rem;
|
||
margin-bottom: 1.5rem;
|
||
}
|
||
|
||
.gauge {
|
||
text-align: center;
|
||
}
|
||
|
||
.gauge-canvas {
|
||
width: 100%;
|
||
max-width: 120px;
|
||
margin: 0 auto 0.5rem;
|
||
}
|
||
|
||
.gauge-label {
|
||
font-family: 'Share Tech Mono', monospace;
|
||
font-size: 0.75rem;
|
||
color: var(--neon-pink);
|
||
}
|
||
|
||
.gauge-value {
|
||
font-family: 'Orbitron', sans-serif;
|
||
font-size: 1.2rem;
|
||
font-weight: 700;
|
||
color: var(--cyber-blue);
|
||
}
|
||
|
||
/* 示波器图表 */
|
||
.oscilloscope {
|
||
background: rgba(0, 0, 0, 0.5);
|
||
border: 1px solid rgba(0, 255, 255, 0.2);
|
||
border-radius: 4px;
|
||
padding: 1rem;
|
||
margin-bottom: 1rem;
|
||
}
|
||
|
||
.oscilloscope-canvas {
|
||
width: 100%;
|
||
height: 80px;
|
||
background: rgba(0, 20, 0, 0.3);
|
||
}
|
||
|
||
/* 服务器状态网格 */
|
||
.server-grid {
|
||
display: grid;
|
||
grid-template-columns: repeat(4, 1fr);
|
||
gap: 0.5rem;
|
||
}
|
||
|
||
.server-node {
|
||
aspect-ratio: 1;
|
||
background: rgba(0, 255, 0, 0.1);
|
||
border: 1px solid rgba(0, 255, 0, 0.3);
|
||
border-radius: 4px;
|
||
display: flex;
|
||
justify-content: center;
|
||
align-items: center;
|
||
transition: all 0.3s ease;
|
||
}
|
||
|
||
.server-node.warning {
|
||
background: rgba(255, 170, 0, 0.1);
|
||
border-color: rgba(255, 170, 0, 0.5);
|
||
}
|
||
|
||
.server-node.error {
|
||
background: rgba(255, 0, 0, 0.1);
|
||
border-color: rgba(255, 0, 0, 0.5);
|
||
}
|
||
|
||
.server-node i {
|
||
font-size: 0.8rem;
|
||
color: #00ff00;
|
||
}
|
||
|
||
.server-node.warning i {
|
||
color: #ffaa00;
|
||
}
|
||
|
||
.server-node.error i {
|
||
color: #ff0000;
|
||
}
|
||
|
||
/* ==================== 中央神经网络可视化 ==================== */
|
||
.neural-center {
|
||
grid-column: 2;
|
||
grid-row: 1 / 3;
|
||
display: flex;
|
||
flex-direction: column;
|
||
}
|
||
|
||
.neural-container {
|
||
flex: 1;
|
||
position: relative;
|
||
min-height: 400px;
|
||
}
|
||
|
||
#neural-canvas {
|
||
width: 100%;
|
||
height: 100%;
|
||
}
|
||
|
||
.stats-overlay {
|
||
position: absolute;
|
||
top: 0;
|
||
left: 0;
|
||
right: 0;
|
||
bottom: 0;
|
||
pointer-events: none;
|
||
}
|
||
|
||
.stat-item {
|
||
position: absolute;
|
||
font-family: 'Share Tech Mono', monospace;
|
||
font-size: 0.8rem;
|
||
color: var(--laser-purple);
|
||
text-shadow: var(--purple-glow);
|
||
}
|
||
|
||
.stat-item.top-left {
|
||
top: 10%;
|
||
left: 5%;
|
||
}
|
||
|
||
.stat-item.top-right {
|
||
top: 10%;
|
||
right: 5%;
|
||
}
|
||
|
||
.stat-item.bottom-left {
|
||
bottom: 10%;
|
||
left: 5%;
|
||
}
|
||
|
||
.stat-item.bottom-right {
|
||
bottom: 10%;
|
||
right: 5%;
|
||
}
|
||
|
||
.stat-value {
|
||
font-family: 'Orbitron', sans-serif;
|
||
font-size: 1.5rem;
|
||
color: var(--cyber-blue);
|
||
}
|
||
|
||
/* ==================== 右侧数据卡片区域 ==================== */
|
||
.data-cards-panel {
|
||
grid-row: 1 / 3;
|
||
}
|
||
|
||
.cards-container {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 1rem;
|
||
}
|
||
|
||
.holo-card {
|
||
background: linear-gradient(135deg, rgba(136, 0, 255, 0.1) 0%, rgba(0, 255, 255, 0.1) 100%);
|
||
border: 1px solid rgba(0, 255, 255, 0.3);
|
||
border-radius: 8px;
|
||
padding: 1.25rem;
|
||
transform: perspective(1000px) rotateX(5deg);
|
||
transition: all 0.4s ease;
|
||
cursor: pointer;
|
||
position: relative;
|
||
overflow: hidden;
|
||
}
|
||
|
||
.holo-card::before {
|
||
content: '';
|
||
position: absolute;
|
||
top: -50%;
|
||
left: -50%;
|
||
width: 200%;
|
||
height: 200%;
|
||
background: linear-gradient(
|
||
45deg,
|
||
transparent 30%,
|
||
rgba(0, 255, 255, 0.1) 50%,
|
||
transparent 70%
|
||
);
|
||
transform: rotate(45deg);
|
||
transition: all 0.5s ease;
|
||
opacity: 0;
|
||
}
|
||
|
||
.holo-card:hover::before {
|
||
opacity: 1;
|
||
transform: rotate(45deg) translate(50%, 50%);
|
||
}
|
||
|
||
.holo-card:hover {
|
||
transform: perspective(1000px) rotateX(0deg) rotateY(5deg) translateZ(10px);
|
||
border-color: var(--cyber-blue);
|
||
box-shadow: var(--text-glow);
|
||
}
|
||
|
||
.card-header {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
margin-bottom: 0.75rem;
|
||
}
|
||
|
||
.card-title {
|
||
font-family: 'Orbitron', sans-serif;
|
||
font-size: 0.85rem;
|
||
font-weight: 700;
|
||
color: var(--neon-pink);
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 0.5rem;
|
||
}
|
||
|
||
.card-value {
|
||
font-family: 'Orbitron', sans-serif;
|
||
font-size: 1.5rem;
|
||
font-weight: 900;
|
||
color: var(--cyber-blue);
|
||
text-shadow: var(--text-glow);
|
||
}
|
||
|
||
.card-detail {
|
||
font-family: 'Share Tech Mono', monospace;
|
||
font-size: 0.7rem;
|
||
color: rgba(255, 255, 255, 0.6);
|
||
}
|
||
|
||
.card-progress {
|
||
height: 4px;
|
||
background: rgba(0, 255, 255, 0.2);
|
||
border-radius: 2px;
|
||
margin-top: 0.75rem;
|
||
overflow: hidden;
|
||
}
|
||
|
||
.card-progress-bar {
|
||
height: 100%;
|
||
background: linear-gradient(90deg, var(--neon-pink), var(--cyber-blue));
|
||
border-radius: 2px;
|
||
transition: width 0.5s ease;
|
||
}
|
||
|
||
/* ==================== 底部区域 ==================== */
|
||
.bottom-section {
|
||
grid-column: 1 / 4;
|
||
display: grid;
|
||
grid-template-columns: 1fr 1fr;
|
||
gap: 1.5rem;
|
||
}
|
||
|
||
/* 时间线导航 */
|
||
.timeline-section {
|
||
background: rgba(26, 26, 46, 0.8);
|
||
border: 1px solid rgba(0, 255, 255, 0.3);
|
||
border-radius: 4px;
|
||
padding: 1.5rem;
|
||
backdrop-filter: blur(10px);
|
||
}
|
||
|
||
.timeline-title {
|
||
font-family: 'Orbitron', sans-serif;
|
||
font-size: 0.9rem;
|
||
font-weight: 700;
|
||
color: var(--cyber-blue);
|
||
margin-bottom: 1rem;
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 0.5rem;
|
||
}
|
||
|
||
.timeline-container {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 1rem;
|
||
overflow-x: auto;
|
||
padding: 1rem 0;
|
||
}
|
||
|
||
.timeline-node {
|
||
flex-shrink: 0;
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
cursor: pointer;
|
||
transition: all 0.3s ease;
|
||
}
|
||
|
||
.timeline-dot {
|
||
width: 20px;
|
||
height: 20px;
|
||
background: var(--metal-gray);
|
||
border: 2px solid var(--cyber-blue);
|
||
border-radius: 50%;
|
||
margin-bottom: 0.5rem;
|
||
transition: all 0.3s ease;
|
||
position: relative;
|
||
}
|
||
|
||
.timeline-dot::after {
|
||
content: '';
|
||
position: absolute;
|
||
top: 50%;
|
||
left: 50%;
|
||
width: 40px;
|
||
height: 2px;
|
||
background: var(--cyber-blue);
|
||
transform: translateY(-50%);
|
||
opacity: 0.3;
|
||
}
|
||
|
||
.timeline-node:last-child .timeline-dot::after {
|
||
display: none;
|
||
}
|
||
|
||
.timeline-dot:hover {
|
||
background: var(--laser-purple);
|
||
box-shadow: var(--purple-glow);
|
||
transform: scale(1.2);
|
||
}
|
||
|
||
.timeline-dot.active {
|
||
background: var(--neon-pink);
|
||
border-color: var(--neon-pink);
|
||
box-shadow: var(--pink-glow);
|
||
}
|
||
|
||
.timeline-year {
|
||
font-family: 'Share Tech Mono', monospace;
|
||
font-size: 0.75rem;
|
||
color: var(--cyber-blue);
|
||
text-align: center;
|
||
}
|
||
|
||
.timeline-label {
|
||
font-size: 0.65rem;
|
||
color: rgba(255, 255, 255, 0.5);
|
||
text-align: center;
|
||
max-width: 80px;
|
||
}
|
||
|
||
/* 命令控制台 */
|
||
.console-section {
|
||
background: rgba(26, 26, 46, 0.8);
|
||
border: 1px solid rgba(0, 255, 255, 0.3);
|
||
border-radius: 4px;
|
||
padding: 1.5rem;
|
||
backdrop-filter: blur(10px);
|
||
display: flex;
|
||
flex-direction: column;
|
||
}
|
||
|
||
.console-title {
|
||
font-family: 'Orbitron', sans-serif;
|
||
font-size: 0.9rem;
|
||
font-weight: 700;
|
||
color: var(--neon-pink);
|
||
margin-bottom: 1rem;
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 0.5rem;
|
||
}
|
||
|
||
.console-output {
|
||
flex: 1;
|
||
background: rgba(0, 0, 0, 0.5);
|
||
border: 1px solid rgba(0, 255, 255, 0.2);
|
||
border-radius: 4px;
|
||
padding: 1rem;
|
||
font-family: 'Share Tech Mono', monospace;
|
||
font-size: 0.75rem;
|
||
color: #00ff00;
|
||
overflow-y: auto;
|
||
max-height: 80px;
|
||
margin-bottom: 0.75rem;
|
||
}
|
||
|
||
.console-line {
|
||
margin-bottom: 0.25rem;
|
||
opacity: 0;
|
||
animation: fadeInLine 0.3s forwards;
|
||
}
|
||
|
||
@keyframes fadeInLine {
|
||
to { opacity: 1; }
|
||
}
|
||
|
||
.console-line.system {
|
||
color: var(--cyber-blue);
|
||
}
|
||
|
||
.console-line.command {
|
||
color: var(--neon-pink);
|
||
}
|
||
|
||
.console-line.success {
|
||
color: #00ff00;
|
||
}
|
||
|
||
.console-line.error {
|
||
color: #ff0000;
|
||
}
|
||
|
||
.console-input-wrapper {
|
||
display: flex;
|
||
align-items: center;
|
||
background: rgba(0, 0, 0, 0.5);
|
||
border: 1px solid rgba(0, 255, 255, 0.3);
|
||
border-radius: 4px;
|
||
padding: 0.5rem 1rem;
|
||
transition: border-color 0.3s ease;
|
||
}
|
||
|
||
.console-input-wrapper:focus-within {
|
||
border-color: var(--cyber-blue);
|
||
box-shadow: var(--text-glow);
|
||
}
|
||
|
||
.console-prompt {
|
||
font-family: 'Share Tech Mono', monospace;
|
||
color: var(--neon-pink);
|
||
margin-right: 0.5rem;
|
||
}
|
||
|
||
.console-input {
|
||
flex: 1;
|
||
background: transparent;
|
||
border: none;
|
||
outline: none;
|
||
color: var(--cyber-blue);
|
||
font-family: 'Share Tech Mono', monospace;
|
||
font-size: 0.85rem;
|
||
}
|
||
|
||
.console-input::placeholder {
|
||
color: rgba(0, 255, 255, 0.3);
|
||
}
|
||
|
||
.console-cursor {
|
||
width: 10px;
|
||
height: 1em;
|
||
background: var(--neon-pink);
|
||
animation: blink-cursor 1s infinite;
|
||
margin-left: 0.25rem;
|
||
}
|
||
|
||
@keyframes blink-cursor {
|
||
0%, 50% { opacity: 1; }
|
||
51%, 100% { opacity: 0; }
|
||
}
|
||
|
||
.console-submit {
|
||
background: transparent;
|
||
border: none;
|
||
color: var(--cyber-blue);
|
||
cursor: pointer;
|
||
padding: 0.25rem 0.5rem;
|
||
transition: color 0.3s ease;
|
||
}
|
||
|
||
.console-submit:hover {
|
||
color: var(--neon-pink);
|
||
}
|
||
|
||
/* ==================== 响应式设计 ==================== */
|
||
@media (max-width: 1200px) {
|
||
.dashboard-grid {
|
||
grid-template-columns: 300px 1fr;
|
||
}
|
||
|
||
.data-cards-panel {
|
||
grid-column: 2;
|
||
grid-row: 3;
|
||
}
|
||
|
||
.bottom-section {
|
||
grid-column: 1 / 3;
|
||
}
|
||
}
|
||
|
||
@media (max-width: 768px) {
|
||
.dashboard-grid {
|
||
grid-template-columns: 1fr;
|
||
padding: 70px 1rem 1rem;
|
||
}
|
||
|
||
.monitoring-panel,
|
||
.neural-center,
|
||
.data-cards-panel,
|
||
.bottom-section {
|
||
grid-column: 1;
|
||
grid-row: auto;
|
||
}
|
||
|
||
.bottom-section {
|
||
grid-template-columns: 1fr;
|
||
}
|
||
|
||
.status-bar {
|
||
padding: 0 1rem;
|
||
}
|
||
|
||
.status-indicators {
|
||
gap: 1rem;
|
||
font-size: 0.7rem;
|
||
}
|
||
|
||
.loading-title {
|
||
font-size: 1.5rem;
|
||
}
|
||
|
||
.intro-text {
|
||
font-size: 1.5rem;
|
||
}
|
||
}
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<!-- CRT效果层 -->
|
||
<div class="crt-overlay"></div>
|
||
<div class="vignette"></div>
|
||
|
||
<!-- 加载界面 -->
|
||
<div class="loading-screen" id="loading-screen">
|
||
<div class="loading-title">NEURO CITY 2078</div>
|
||
<div class="loading-ring"></div>
|
||
<div class="loading-text">正在建立神经连接...</div>
|
||
</div>
|
||
|
||
<!-- 开场动画容器 -->
|
||
<div class="intro-container" id="intro-container">
|
||
<canvas id="intro-canvas"></canvas>
|
||
<div class="intro-text">NEURAL LINK ESTABLISHED</div>
|
||
</div>
|
||
|
||
<!-- Three.js 3D场景 -->
|
||
<div id="three-container"></div>
|
||
|
||
<!-- 主界面 -->
|
||
<div class="main-interface">
|
||
<!-- 顶部状态栏 -->
|
||
<header class="status-bar">
|
||
<div class="logo">
|
||
<i class="fas fa-network-wired"></i>
|
||
NEURO CITY 2078
|
||
</div>
|
||
<div class="status-indicators">
|
||
<div class="status-item">
|
||
<span class="status-dot" id="system-status"></span>
|
||
<span>系统状态: <span id="system-text">在线</span></span>
|
||
</div>
|
||
<div class="status-item">
|
||
<i class="far fa-clock"></i>
|
||
<span id="current-time">2078-12-24 08:18:16</span>
|
||
</div>
|
||
<div class="status-item">
|
||
<i class="fas fa-wifi"></i>
|
||
<span>连接节点: <span id="node-count">2,847</span></span>
|
||
</div>
|
||
</div>
|
||
</header>
|
||
|
||
<!-- 仪表板网格 -->
|
||
<div class="dashboard-grid">
|
||
<!-- 左侧监控面板 -->
|
||
<aside class="monitoring-panel panel">
|
||
<div class="panel-title">
|
||
<i class="fas fa-microchip"></i>
|
||
系统监控
|
||
</div>
|
||
|
||
<!-- 仪表盘 -->
|
||
<div class="gauge-container">
|
||
<div class="gauge">
|
||
<canvas id="cpu-gauge" class="gauge-canvas" width="120" height="120"></canvas>
|
||
<div class="gauge-label">CPU 负载</div>
|
||
<div class="gauge-value" id="cpu-value">67%</div>
|
||
</div>
|
||
<div class="gauge">
|
||
<canvas id="memory-gauge" class="gauge-canvas" width="120" height="120"></canvas>
|
||
<div class="gauge-label">内存占用</div>
|
||
<div class="gauge-value" id="memory-value">82%</div>
|
||
</div>
|
||
<div class="gauge">
|
||
<canvas id="network-gauge" class="gauge-canvas" width="120" height="120"></canvas>
|
||
<div class="gauge-label">网络流量</div>
|
||
<div class="gauge-value" id="network-value">94%</div>
|
||
</div>
|
||
<div class="gauge">
|
||
<canvas id="ai-gauge" class="gauge-canvas" width="120" height="120"></canvas>
|
||
<div class="gauge-label">AI 同步率</div>
|
||
<div class="gauge-value" id="ai-value">99%</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 示波器 -->
|
||
<div class="oscilloscope">
|
||
<div class="panel-title" style="font-size: 0.75rem; margin-bottom: 0.5rem;">
|
||
<i class="fas fa-wave-square"></i>
|
||
网络流量波形
|
||
</div>
|
||
<canvas id="oscilloscope-canvas" class="oscilloscope-canvas"></canvas>
|
||
</div>
|
||
|
||
<!-- 服务器状态 -->
|
||
<div class="panel-title" style="font-size: 0.75rem;">
|
||
<i class="fas fa-server"></i>
|
||
服务器集群状态
|
||
</div>
|
||
<div class="server-grid" id="server-grid">
|
||
<!-- 服务器节点由JS生成 -->
|
||
</div>
|
||
</aside>
|
||
|
||
<!-- 中央神经网络可视化 -->
|
||
<main class="neural-center panel">
|
||
<div class="panel-title">
|
||
<i class="fas fa-brain"></i>
|
||
神经网络实时可视化
|
||
</div>
|
||
<div class="neural-container">
|
||
<svg id="neural-canvas"></svg>
|
||
<div class="stats-overlay">
|
||
<div class="stat-item top-left">
|
||
<div>神经元活跃度</div>
|
||
<div class="stat-value" id="neuron-activity">94.7%</div>
|
||
</div>
|
||
<div class="stat-item top-right">
|
||
<div>数据吞吐量</div>
|
||
<div class="stat-value" id="data-throughput">847 TB/s</div>
|
||
</div>
|
||
<div class="stat-item bottom-left">
|
||
<div>延迟指标</div>
|
||
<div class="stat-value" id="latency">0.3ms</div>
|
||
</div>
|
||
<div class="stat-item bottom-right">
|
||
<div>错误率</div>
|
||
<div class="stat-value" id="error-rate">0.001%</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</main>
|
||
|
||
<!-- 右侧数据卡片 -->
|
||
<aside class="data-cards-panel panel">
|
||
<div class="panel-title">
|
||
<i class="fas fa-chart-pie"></i>
|
||
城市数据概览
|
||
</div>
|
||
<div class="cards-container">
|
||
<div class="holo-card" data-card="population">
|
||
<div class="card-header">
|
||
<div class="card-title">
|
||
<i class="fas fa-users"></i>
|
||
人口统计
|
||
</div>
|
||
<div class="card-value">2.8B</div>
|
||
</div>
|
||
<div class="card-detail">神经连接公民数:2.7B (96.4%)</div>
|
||
<div class="card-progress">
|
||
<div class="card-progress-bar" style="width: 96.4%"></div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="holo-card" data-card="energy">
|
||
<div class="card-header">
|
||
<div class="card-title">
|
||
<i class="fas fa-bolt"></i>
|
||
能源消耗
|
||
</div>
|
||
<div class="card-value">47 TW</div>
|
||
</div>
|
||
<div class="card-detail">核聚变供能:89% | 太阳能:11%</div>
|
||
<div class="card-progress">
|
||
<div class="card-progress-bar" style="width: 73%"></div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="holo-card" data-card="nodes">
|
||
<div class="card-header">
|
||
<div class="card-title">
|
||
<i class="fas fa-project-diagram"></i>
|
||
网络节点
|
||
</div>
|
||
<div class="card-value">1.2M</div>
|
||
</div>
|
||
<div class="card-detail">活跃节点:1,198,472 | 冗余节点:12,847</div>
|
||
<div class="card-progress">
|
||
<div class="card-progress-bar" style="width: 98.9%"></div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="holo-card" data-card="ai">
|
||
<div class="card-header">
|
||
<div class="card-title">
|
||
<i class="fas fa-robot"></i>
|
||
AI 意识同步
|
||
</div>
|
||
<div class="card-value">99.7%</div>
|
||
</div>
|
||
<div class="card-detail">觉醒AI数量:847 | 同步中:842</div>
|
||
<div class="card-progress">
|
||
<div class="card-progress-bar" style="width: 99.7%"></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</aside>
|
||
|
||
<!-- 底部区域 -->
|
||
<div class="bottom-section">
|
||
<!-- 时间线导航 -->
|
||
<section class="timeline-section">
|
||
<div class="timeline-title">
|
||
<i class="fas fa-history"></i>
|
||
历史演进时间线
|
||
</div>
|
||
<div class="timeline-container" id="timeline-container">
|
||
<!-- 时间节点由JS生成 -->
|
||
</div>
|
||
</section>
|
||
|
||
<!-- 命令控制台 -->
|
||
<section class="console-section">
|
||
<div class="console-title">
|
||
<i class="fas fa-terminal"></i>
|
||
脑机接口命令控制台
|
||
</div>
|
||
<div class="console-output" id="console-output">
|
||
<div class="console-line system">系统初始化完成...</div>
|
||
<div class="console-line system">神经连接建立成功。</div>
|
||
<div class="console-line system">等待用户指令。输入 /help 查看可用命令。</div>
|
||
</div>
|
||
<div class="console-input-wrapper">
|
||
<span class="console-prompt">></span>
|
||
<input type="text" class="console-input" id="console-input" placeholder="输入命令..." autocomplete="off">
|
||
<button class="console-submit" id="console-submit">
|
||
<i class="fas fa-paper-plane"></i>
|
||
</button>
|
||
</div>
|
||
</section>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<script>
|
||
// ==================== 全局变量与初始化 ====================
|
||
let threeScene, threeCamera, threeRenderer, controls;
|
||
let introCanvas, introCtx, introAnimationId;
|
||
let neuralGraph, neuralSimulation;
|
||
let clock = new THREE.Clock();
|
||
|
||
// 命令处理函数映射
|
||
const commands = {
|
||
'/help': showHelp,
|
||
'/scan': scanNetwork,
|
||
'/hack': triggerHack,
|
||
'/reset': resetSystem,
|
||
'/status': showStatus,
|
||
'/time': showTime,
|
||
'/nodes': showNodes,
|
||
'/clear': clearConsole,
|
||
'/weather': showWeather,
|
||
'/energy': showEnergy
|
||
};
|
||
|
||
// 时间线数据
|
||
const timelineData = [
|
||
{ year: '2078', label: '神经都市', active: true },
|
||
{ year: '2065', label: '意识上传', active: false },
|
||
{ year: '2052', label: 'AI觉醒', active: false },
|
||
{ year: '2040', label: '脑机接口', active: false },
|
||
{ year: '2028', label: '量子突破', active: false },
|
||
{ year: '2015', label: '奇点临近', active: false }
|
||
];
|
||
|
||
// ==================== 页面加载完成后初始化 ====================
|
||
document.addEventListener('DOMContentLoaded', function() {
|
||
// 延迟初始化以确保所有CDN加载完成
|
||
setTimeout(() => {
|
||
initLoadingScreen();
|
||
}, 500);
|
||
});
|
||
|
||
// ==================== 加载屏幕动画 ====================
|
||
function initLoadingScreen() {
|
||
const loadingScreen = document.getElementById('loading-screen');
|
||
|
||
// 模拟加载进度
|
||
let progress = 0;
|
||
const loadingInterval = setInterval(() => {
|
||
progress += Math.random() * 15;
|
||
if (progress >= 100) {
|
||
progress = 100;
|
||
clearInterval(loadingInterval);
|
||
|
||
// 隐藏加载屏幕,开始开场动画
|
||
setTimeout(() => {
|
||
loadingScreen.classList.add('hidden');
|
||
initIntroAnimation();
|
||
}, 500);
|
||
}
|
||
}, 200);
|
||
}
|
||
|
||
// ==================== 开场神经网络动画 ====================
|
||
function initIntroAnimation() {
|
||
const container = document.getElementById('intro-container');
|
||
introCanvas = document.getElementById('intro-canvas');
|
||
introCtx = introCanvas.getContext('2d');
|
||
|
||
// 设置画布大小
|
||
function resizeCanvas() {
|
||
introCanvas.width = window.innerWidth;
|
||
introCanvas.height = window.innerHeight;
|
||
}
|
||
resizeCanvas();
|
||
window.addEventListener('resize', resizeCanvas);
|
||
|
||
// 节点类
|
||
class Node {
|
||
constructor(x, y) {
|
||
this.x = x;
|
||
this.y = y;
|
||
this.targetX = x;
|
||
this.targetY = y;
|
||
this.radius = 0;
|
||
this.maxRadius = 3 + Math.random() * 3;
|
||
this.connected = false;
|
||
this.connections = [];
|
||
this.pulsePhase = Math.random() * Math.PI * 2;
|
||
}
|
||
|
||
update(time) {
|
||
// 缓动效果
|
||
this.x += (this.targetX - this.x) * 0.05;
|
||
this.y += (this.targetY - this.y) * 0.05;
|
||
|
||
// 脉动效果
|
||
const pulse = Math.sin(time * 2 + this.pulsePhase) * 0.5 + 0.5;
|
||
this.radius = this.maxRadius * (0.5 + pulse * 0.5);
|
||
}
|
||
|
||
draw(ctx) {
|
||
const gradient = ctx.createRadialGradient(
|
||
this.x, this.y, 0,
|
||
this.x, this.y, this.radius * 2
|
||
);
|
||
gradient.addColorStop(0, 'rgba(0, 255, 255, 1)');
|
||
gradient.addColorStop(0.5, 'rgba(255, 0, 255, 0.5)');
|
||
gradient.addColorStop(1, 'rgba(136, 0, 255, 0)');
|
||
|
||
ctx.beginPath();
|
||
ctx.arc(this.x, this.y, this.radius * 2, 0, Math.PI * 2);
|
||
ctx.fillStyle = gradient;
|
||
ctx.fill();
|
||
}
|
||
}
|
||
|
||
// 连接线类
|
||
class Connection {
|
||
constructor(node1, node2) {
|
||
this.node1 = node1;
|
||
this.node2 = node2;
|
||
this.progress = 0;
|
||
this.maxProgress = 1;
|
||
this.pulseOffset = Math.random() * Math.PI * 2;
|
||
}
|
||
|
||
update(time) {
|
||
if (this.progress < this.maxProgress) {
|
||
this.progress += 0.02;
|
||
}
|
||
}
|
||
|
||
draw(ctx, time) {
|
||
const x1 = this.node1.x;
|
||
const y1 = this.node1.y;
|
||
const x2 = this.node1.x + (this.node2.x - this.node1.x) * this.progress;
|
||
const y2 = this.node1.y + (this.node2.y - this.node1.y) * this.progress;
|
||
|
||
const pulse = Math.sin(time * 3 + this.pulseOffset) * 0.5 + 0.5;
|
||
const alpha = pulse * 0.8 * this.progress;
|
||
|
||
ctx.beginPath();
|
||
ctx.moveTo(x1, y1);
|
||
ctx.lineTo(x2, y2);
|
||
ctx.strokeStyle = `rgba(0, 255, 255, ${alpha})`;
|
||
ctx.lineWidth = 1;
|
||
ctx.stroke();
|
||
|
||
// 脉冲点
|
||
if (this.progress > 0.1) {
|
||
ctx.beginPath();
|
||
ctx.arc(x2, y2, 2, 0, Math.PI * 2);
|
||
ctx.fillStyle = `rgba(255, 0, 255, ${alpha})`;
|
||
ctx.fill();
|
||
}
|
||
}
|
||
}
|
||
|
||
// 创建节点
|
||
const nodes = [];
|
||
const centerX = introCanvas.width / 2;
|
||
const centerY = introCanvas.height / 2;
|
||
const numNodes = 50;
|
||
|
||
for (let i = 0; i < numNodes; i++) {
|
||
const angle = (i / numNodes) * Math.PI * 2;
|
||
const distance = 50 + Math.random() * 200;
|
||
const x = centerX + Math.cos(angle) * distance;
|
||
const y = centerY + Math.sin(angle) * distance;
|
||
nodes.push(new Node(x, y));
|
||
}
|
||
|
||
// 创建连接
|
||
const connections = [];
|
||
for (let i = 0; i < nodes.length; i++) {
|
||
for (let j = i + 1; j < nodes.length; j++) {
|
||
if (Math.random() < 0.15) {
|
||
connections.push(new Connection(nodes[i], nodes[j]));
|
||
}
|
||
}
|
||
}
|
||
|
||
// 动画变量
|
||
let startTime = Date.now();
|
||
let phase = 'expand'; // expand, connect, zoom, fade
|
||
let cameraZ = 1000;
|
||
|
||
// 动画循环
|
||
function animate() {
|
||
const elapsed = (Date.now() - startTime) / 1000;
|
||
const time = elapsed;
|
||
|
||
// 清空画布
|
||
introCtx.fillStyle = 'rgba(0, 0, 0, 0.1)';
|
||
introCtx.fillRect(0, 0, introCanvas.width, introCanvas.height);
|
||
|
||
// 阶段控制
|
||
if (elapsed < 2) {
|
||
// 阶段1:节点扩散
|
||
nodes.forEach((node, i) => {
|
||
const delay = i / numNodes * 0.5;
|
||
const progress = Math.min(1, Math.max(0, (elapsed - delay) / 1.5));
|
||
node.targetX = centerX + Math.cos((i / numNodes) * Math.PI * 2 + elapsed * 0.2) * (100 + progress * 300);
|
||
node.targetY = centerY + Math.sin((i / numNodes) * Math.PI * 2 + elapsed * 0.2) * (100 + progress * 300);
|
||
node.update(time);
|
||
node.draw(introCtx);
|
||
});
|
||
} else if (elapsed < 4) {
|
||
// 阶段2:建立连接
|
||
connections.forEach((conn, i) => {
|
||
const delay = i / connections.length * 0.5;
|
||
if (elapsed - 2 > delay) {
|
||
conn.update(time);
|
||
conn.draw(introCtx, time);
|
||
}
|
||
});
|
||
nodes.forEach(node => {
|
||
node.update(time);
|
||
node.draw(introCtx);
|
||
});
|
||
} else if (elapsed < 6) {
|
||
// 阶段3:视角拉远
|
||
cameraZ = 1000 - (elapsed - 4) * 500;
|
||
nodes.forEach(node => {
|
||
node.x = centerX + (node.x - centerX) * 1.01;
|
||
node.y = centerY + (node.y - centerY) * 1.01;
|
||
node.update(time);
|
||
node.draw(introCtx);
|
||
});
|
||
connections.forEach(conn => {
|
||
conn.draw(introCtx, time);
|
||
});
|
||
|
||
// 显示文字
|
||
const introText = document.querySelector('.intro-text');
|
||
introText.style.opacity = Math.min(1, (elapsed - 4));
|
||
introText.style.transform = `scale(${0.5 + (elapsed - 4) * 0.25})`;
|
||
} else {
|
||
// 阶段4:淡出
|
||
container.style.opacity = 1 - (elapsed - 6);
|
||
if (elapsed > 7) {
|
||
container.classList.add('hidden');
|
||
initThreeJS();
|
||
initDashboard();
|
||
return;
|
||
}
|
||
}
|
||
|
||
introAnimationId = requestAnimationFrame(animate);
|
||
}
|
||
|
||
animate();
|
||
}
|
||
|
||
// ==================== Three.js 3D城市场景 ====================
|
||
function initThreeJS() {
|
||
const container = document.getElementById('three-container');
|
||
|
||
// 场景
|
||
threeScene = new THREE.Scene();
|
||
threeScene.background = new THREE.Color(0x050505);
|
||
threeScene.fog = new THREE.FogExp2(0x0a0a0a, 0.002);
|
||
|
||
// 相机
|
||
threeCamera = new THREE.PerspectiveCamera(
|
||
60,
|
||
window.innerWidth / window.innerHeight,
|
||
0.1,
|
||
2000
|
||
);
|
||
threeCamera.position.set(100, 150, 200);
|
||
threeCamera.lookAt(0, 0, 0);
|
||
|
||
// 渲染器
|
||
threeRenderer = new THREE.WebGLRenderer({ antialias: true });
|
||
threeRenderer.setSize(window.innerWidth, window.innerHeight);
|
||
threeRenderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
|
||
container.appendChild(threeRenderer.domElement);
|
||
|
||
// 控制器
|
||
controls = new THREE.OrbitControls(threeCamera, threeRenderer.domElement);
|
||
controls.enableDamping = true;
|
||
controls.dampingFactor = 0.05;
|
||
controls.minDistance = 50;
|
||
controls.maxDistance = 500;
|
||
controls.maxPolarAngle = Math.PI / 2;
|
||
|
||
// 后处理 - Bloom效果
|
||
const composer = new THREE.EffectComposer(threeRenderer);
|
||
const renderPass = new THREE.RenderPass(threeScene, threeCamera);
|
||
composer.addPass(renderPass);
|
||
|
||
const bloomPass = new THREE.UnrealBloomPass(
|
||
new THREE.Vector2(window.innerWidth, window.innerHeight),
|
||
1.5, // 强度
|
||
0.4, // 半径
|
||
0.85 // 阈值
|
||
);
|
||
composer.addPass(bloomPass);
|
||
|
||
// 灯光
|
||
const ambientLight = new THREE.AmbientLight(0x404040, 0.5);
|
||
threeScene.add(ambientLight);
|
||
|
||
const directionalLight = new THREE.DirectionalLight(0xffffff, 0.5);
|
||
directionalLight.position.set(100, 100, 50);
|
||
threeScene.add(directionalLight);
|
||
|
||
// 城市建筑
|
||
createCity();
|
||
|
||
// 飞行载具粒子
|
||
createFlyingVehicles();
|
||
|
||
// 街道光带
|
||
createStreetLights();
|
||
|
||
// 窗口大小调整
|
||
window.addEventListener('resize', onWindowResize);
|
||
|
||
// 动画循环
|
||
animate();
|
||
}
|
||
|
||
function createCity() {
|
||
const buildingCount = 400;
|
||
const gridSize = 20;
|
||
const buildingSize = 8;
|
||
const spacing = 15;
|
||
|
||
// 创建建筑几何体(使用InstancedMesh优化性能)
|
||
const geometry = new THREE.BoxGeometry(buildingSize, 1, buildingSize);
|
||
|
||
// 创建LED纹理
|
||
const ledTexture = createLEDTexture();
|
||
const material = new THREE.MeshPhongMaterial({
|
||
color: 0x1a1a2e,
|
||
emissive: 0x000000,
|
||
emissiveMap: ledTexture,
|
||
emissiveIntensity: 1,
|
||
shininess: 30
|
||
});
|
||
|
||
const instancedMesh = new THREE.InstancedMesh(geometry, material, buildingCount);
|
||
const dummy = new THREE.Object3D();
|
||
const colors = [0x00ffff, 0xff00ff, 0x8800ff, 0x00ff88];
|
||
|
||
let index = 0;
|
||
for (let x = -gridSize; x <= gridSize; x++) {
|
||
for (let z = -gridSize; z <= gridSize; z++) {
|
||
if (index >= buildingCount) break;
|
||
|
||
const height = 10 + Math.random() * 80 + Math.abs(x) * 2 + Math.abs(z) * 2;
|
||
|
||
dummy.position.set(x * spacing, height / 2, z * spacing);
|
||
dummy.scale.set(0.8 + Math.random() * 0.4, height, 0.8 + Math.random() * 0.4);
|
||
dummy.updateMatrix();
|
||
|
||
instancedMesh.setMatrixAt(index, dummy.matrix);
|
||
instancedMesh.setColorAt(index, new THREE.Color(colors[Math.floor(Math.random() * colors.length)]));
|
||
|
||
index++;
|
||
}
|
||
}
|
||
|
||
instancedMesh.instanceMatrix.needsUpdate = true;
|
||
threeScene.add(instancedMesh);
|
||
|
||
// 地面网格
|
||
const gridHelper = new THREE.GridHelper(1000, 50, 0x00ffff, 0x1a1a2e);
|
||
gridHelper.position.y = 0.1;
|
||
threeScene.add(gridHelper);
|
||
}
|
||
|
||
function createLEDTexture() {
|
||
const canvas = document.createElement('canvas');
|
||
canvas.width = 64;
|
||
canvas.height = 64;
|
||
const ctx = canvas.getContext('2d');
|
||
|
||
ctx.fillStyle = '#000000';
|
||
ctx.fillRect(0, 0, 64, 64);
|
||
|
||
// 随机LED点阵
|
||
for (let x = 0; x < 8; x++) {
|
||
for (let y = 0; y < 8; y++) {
|
||
if (Math.random() > 0.5) {
|
||
const intensity = Math.random();
|
||
ctx.fillStyle = `rgba(0, 255, 255, ${intensity})`;
|
||
ctx.fillRect(x * 8 + 2, y * 8 + 2, 4, 4);
|
||
}
|
||
}
|
||
}
|
||
|
||
const texture = new THREE.CanvasTexture(canvas);
|
||
texture.wrapS = THREE.RepeatWrapping;
|
||
texture.wrapT = THREE.RepeatWrapping;
|
||
return texture;
|
||
}
|
||
|
||
function createFlyingVehicles() {
|
||
const particleCount = 500;
|
||
const geometry = new THREE.BufferGeometry();
|
||
const positions = new Float32Array(particleCount * 3);
|
||
const colors = new Float32Array(particleCount * 3);
|
||
|
||
for (let i = 0; i < particleCount; i++) {
|
||
positions[i * 3] = (Math.random() - 0.5) * 800;
|
||
positions[i * 3 + 1] = 50 + Math.random() * 150;
|
||
positions[i * 3 + 2] = (Math.random() - 0.5) * 800;
|
||
|
||
colors[i * 3] = 0;
|
||
colors[i * 3 + 1] = 1;
|
||
colors[i * 3 + 2] = 1;
|
||
}
|
||
|
||
geometry.setAttribute('position', new THREE.BufferAttribute(positions, 3));
|
||
geometry.setAttribute('color', new THREE.BufferAttribute(colors, 3));
|
||
|
||
const material = new THREE.PointsMaterial({
|
||
size: 2,
|
||
vertexColors: true,
|
||
transparent: true,
|
||
opacity: 0.8,
|
||
blending: THREE.AdditiveBlending
|
||
});
|
||
|
||
const particles = new THREE.Points(geometry, material);
|
||
particles.userData.velocities = [];
|
||
|
||
for (let i = 0; i < particleCount; i++) {
|
||
particle.userData.velocities.push({
|
||
x: (Math.random() - 0.5) * 2,
|
||
y: (Math.random() - 0.5) * 0.5,
|
||
z: (Math.random() - 0.5) * 2
|
||
});
|
||
}
|
||
|
||
threeScene.add(particles);
|
||
threeScene.userData.flyingParticles = particles;
|
||
}
|
||
|
||
function createStreetLights() {
|
||
const lineCount = 20;
|
||
const lineLength = 400;
|
||
|
||
for (let i = 0; i < lineCount; i++) {
|
||
const points = [];
|
||
const startX = (Math.random() - 0.5) * lineLength;
|
||
const startZ = (Math.random() - 0.5) * lineLength;
|
||
|
||
for (let j = 0; j < 50; j++) {
|
||
points.push(new THREE.Vector3(
|
||
startX + (Math.random() - 0.5) * 20,
|
||
2,
|
||
startZ + j * 10 + (Math.random() - 0.5) * 20
|
||
));
|
||
}
|
||
|
||
const geometry = new THREE.BufferGeometry().setFromPoints(points);
|
||
const material = new THREE.LineBasicMaterial({
|
||
color: 0x00ffff,
|
||
transparent: true,
|
||
opacity: 0.3
|
||
});
|
||
|
||
const line = new THREE.Line(geometry, material);
|
||
threeScene.add(line);
|
||
}
|
||
}
|
||
|
||
function onWindowResize() {
|
||
threeCamera.aspect = window.innerWidth / window.innerHeight;
|
||
threeCamera.updateProjectionMatrix();
|
||
threeRenderer.setSize(window.innerWidth, window.innerHeight);
|
||
}
|
||
|
||
function animate() {
|
||
requestAnimationFrame(animate);
|
||
|
||
const delta = clock.getDelta();
|
||
const elapsed = clock.getElapsedTime();
|
||
|
||
// 更新控制器
|
||
controls.update();
|
||
|
||
// 更新飞行粒子
|
||
if (threeScene.userData.flyingParticles) {
|
||
const particles = threeScene.userData.flyingParticles;
|
||
const positions = particles.geometry.attributes.position.array;
|
||
|
||
for (let i = 0; i < positions.length / 3; i++) {
|
||
positions[i * 3] += Math.sin(elapsed + i) * 0.5;
|
||
positions[i * 3 + 1] += Math.cos(elapsed * 0.5 + i) * 0.2;
|
||
positions[i * 3 + 2] += 1;
|
||
|
||
if (positions[i * 3 + 2] > 400) {
|
||
positions[i * 3 + 2] = -400;
|
||
}
|
||
}
|
||
|
||
particles.geometry.attributes.position.needsUpdate = true;
|
||
}
|
||
|
||
// 渲染场景
|
||
threeRenderer.render(threeScene, threeCamera);
|
||
}
|
||
|
||
// ==================== 仪表板初始化 ====================
|
||
function initDashboard() {
|
||
initGauges();
|
||
initOscilloscope();
|
||
initServerGrid();
|
||
initNeuralGraph();
|
||
initTimeline();
|
||
initConsole();
|
||
initDataCards();
|
||
startRealTimeUpdates();
|
||
}
|
||
|
||
// ==================== 仪表盘绘制 ====================
|
||
function initGauges() {
|
||
const gauges = [
|
||
{ id: 'cpu-gauge', value: 67, max: 100, color: '#ff00ff' },
|
||
{ id: 'memory-gauge', value: 82, max: 100, color: '#00ffff' },
|
||
{ id: 'network-gauge', value: 94, max: 100, color: '#8800ff' },
|
||
{ id: 'ai-gauge', value: 99, max: 100, color: '#00ff88' }
|
||
];
|
||
|
||
gauges.forEach(gauge => {
|
||
drawGauge(gauge.id, gauge.value, gauge.max, gauge.color);
|
||
});
|
||
}
|
||
|
||
function drawGauge(canvasId, value, max, color) {
|
||
const canvas = document.getElementById(canvasId);
|
||
if (!canvas) return;
|
||
|
||
const ctx = canvas.getContext('2d');
|
||
const centerX = canvas.width / 2;
|
||
const centerY = canvas.height / 2;
|
||
const radius = 50;
|
||
const startAngle = -Math.PI * 0.75;
|
||
const endAngle = Math.PI * 0.75;
|
||
const progress = value / max;
|
||
|
||
// 背景圆弧
|
||
ctx.beginPath();
|
||
ctx.arc(centerX, centerY, radius, startAngle, endAngle);
|
||
ctx.strokeStyle = 'rgba(0, 255, 255, 0.1)';
|
||
ctx.lineWidth = 8;
|
||
ctx.stroke();
|
||
|
||
// 进度圆弧
|
||
const gradient = ctx.createLinearGradient(0, 0, canvas.width, 0);
|
||
gradient.addColorStop(0, '#ff00ff');
|
||
gradient.addColorStop(0.5, '#00ffff');
|
||
gradient.addColorStop(1, '#8800ff');
|
||
|
||
ctx.beginPath();
|
||
ctx.arc(centerX, centerY, radius, startAngle, startAngle + (endAngle - startAngle) * progress);
|
||
ctx.strokeStyle = gradient;
|
||
ctx.lineWidth = 8;
|
||
ctx.lineCap = 'round';
|
||
ctx.stroke();
|
||
|
||
// 发光效果
|
||
ctx.shadowColor = color;
|
||
ctx.shadowBlur = 10;
|
||
ctx.stroke();
|
||
ctx.shadowBlur = 0;
|
||
}
|
||
|
||
// ==================== 示波器波形图 ====================
|
||
function initOscilloscope() {
|
||
const canvas = document.getElementById('oscilloscope-canvas');
|
||
if (!canvas) return;
|
||
|
||
const ctx = canvas.getContext('2d');
|
||
const width = canvas.width = canvas.offsetWidth * 2;
|
||
const height = canvas.height = canvas.offsetHeight * 2;
|
||
|
||
let offset = 0;
|
||
|
||
function drawOscilloscope() {
|
||
ctx.fillStyle = 'rgba(0, 20, 0, 0.3)';
|
||
ctx.fillRect(0, 0, width, height);
|
||
|
||
// 绘制网格
|
||
ctx.strokeStyle = 'rgba(0, 255, 255, 0.1)';
|
||
ctx.lineWidth = 1;
|
||
for (let y = 0; y < height; y += 20) {
|
||
ctx.beginPath();
|
||
ctx.moveTo(0, y);
|
||
ctx.lineTo(width, y);
|
||
ctx.stroke();
|
||
}
|
||
|
||
// 绘制波形
|
||
ctx.beginPath();
|
||
ctx.moveTo(0, height / 2);
|
||
|
||
for (let x = 0; x < width; x++) {
|
||
const y = height / 2 +
|
||
Math.sin(x * 0.02 + offset) * 30 * Math.sin(x * 0.005 + offset * 0.5) +
|
||
Math.random() * 5;
|
||
ctx.lineTo(x, y);
|
||
}
|
||
|
||
ctx.strokeStyle = '#00ff00';
|
||
ctx.lineWidth = 2;
|
||
ctx.shadowColor = '#00ff00';
|
||
ctx.shadowBlur = 10;
|
||
ctx.stroke();
|
||
ctx.shadowBlur = 0;
|
||
|
||
offset += 0.1;
|
||
requestAnimationFrame(drawOscilloscope);
|
||
}
|
||
|
||
drawOscilloscope();
|
||
}
|
||
|
||
// ==================== 服务器状态网格 ====================
|
||
function initServerGrid() {
|
||
const grid = document.getElementById('server-grid');
|
||
if (!grid) return;
|
||
|
||
for (let i = 0; i < 16; i++) {
|
||
const node = document.createElement('div');
|
||
node.className = 'server-node';
|
||
node.innerHTML = '<i class="fas fa-check"></i>';
|
||
node.dataset.status = 'normal';
|
||
grid.appendChild(node);
|
||
}
|
||
|
||
// 模拟服务器状态变化
|
||
setInterval(() => {
|
||
const nodes = grid.querySelectorAll('.server-node');
|
||
nodes.forEach((node, i) => {
|
||
const rand = Math.random();
|
||
node.className = 'server-node';
|
||
if (rand > 0.95) {
|
||
node.classList.add('error');
|
||
node.innerHTML = '<i class="fas fa-times"></i>';
|
||
node.dataset.status = 'error';
|
||
} else if (rand > 0.85) {
|
||
node.classList.add('warning');
|
||
node.innerHTML = '<i class="fas fa-exclamation"></i>';
|
||
node.dataset.status = 'warning';
|
||
} else {
|
||
node.innerHTML = '<i class="fas fa-check"></i>';
|
||
node.dataset.status = 'normal';
|
||
}
|
||
});
|
||
}, 3000);
|
||
}
|
||
|
||
// ==================== 神经网络可视化 ====================
|
||
function initNeuralGraph() {
|
||
const container = document.getElementById('neural-canvas');
|
||
if (!container) return;
|
||
|
||
const width = container.clientWidth;
|
||
const height = container.clientHeight;
|
||
|
||
// 设置SVG尺寸
|
||
container.setAttribute('width', width);
|
||
container.setAttribute('height', height);
|
||
|
||
// 创建节点数据
|
||
const nodes = [];
|
||
const links = [];
|
||
const nodeCount = 30;
|
||
|
||
for (let i = 0; i < nodeCount; i++) {
|
||
nodes.push({
|
||
id: i,
|
||
x: Math.random() * width,
|
||
y: Math.random() * height,
|
||
r: 3 + Math.random() * 5
|
||
});
|
||
}
|
||
|
||
// 创建连接
|
||
for (let i = 0; i < nodeCount; i++) {
|
||
for (let j = i + 1; j < nodeCount; j++) {
|
||
if (Math.random() < 0.15) {
|
||
links.push({
|
||
source: i,
|
||
target: j
|
||
});
|
||
}
|
||
}
|
||
}
|
||
|
||
// D3力导向图
|
||
neuralSimulation = d3.forceSimulation(nodes)
|
||
.force('link', d3.forceLink(links).id(d => d.id).distance(50))
|
||
.force('charge', d3.forceManyBody().strength(-100))
|
||
.force('center', d3.forceCenter(width / 2, height / 2))
|
||
.force('collision', d3.forceCollide().radius(d => d.r + 5));
|
||
|
||
// 绘制连接线
|
||
const linkGroup = document.createElementNS('http://www.w3.org/2000/svg', 'g');
|
||
container.appendChild(linkGroup);
|
||
|
||
// 绘制节点
|
||
const nodeGroup = document.createElementNS('http://www.w3.org/2000/svg', 'g');
|
||
container.appendChild(nodeGroup);
|
||
|
||
// 创建脉冲动画数据
|
||
const pulseData = links.map((link, i) => ({
|
||
link,
|
||
progress: Math.random(),
|
||
speed: 0.02 + Math.random() * 0.02
|
||
}));
|
||
|
||
function updateGraph() {
|
||
// 清除旧内容
|
||
while (linkGroup.firstChild) {
|
||
linkGroup.removeChild(linkGroup.firstChild);
|
||
}
|
||
while (nodeGroup.firstChild) {
|
||
nodeGroup.removeChild(nodeGroup.firstChild);
|
||
}
|
||
|
||
// 绘制连接线
|
||
links.forEach(link => {
|
||
const line = document.createElementNS('http://www.w3.org/2000/svg', 'line');
|
||
line.setAttribute('x1', link.source.x);
|
||
line.setAttribute('y1', link.source.y);
|
||
line.setAttribute('x2', link.target.x);
|
||
line.setAttribute('y2', link.target.y);
|
||
line.setAttribute('stroke', 'rgba(0, 255, 255, 0.2)');
|
||
line.setAttribute('stroke-width', '1');
|
||
linkGroup.appendChild(line);
|
||
|
||
// 脉冲效果
|
||
const pulse = pulseData.find(p => p.link === link);
|
||
if (pulse) {
|
||
pulse.progress += pulse.speed;
|
||
if (pulse.progress > 1) pulse.progress = 0;
|
||
|
||
const pulseX = link.source.x + (link.target.x - link.source.x) * pulse.progress;
|
||
const pulseY = link.source.y + (link.target.y - link.source.y) * pulse.progress;
|
||
|
||
const pulseCircle = document.createElementNS('http://www.w3.org/2000/svg', 'circle');
|
||
pulseCircle.setAttribute('cx', pulseX);
|
||
pulseCircle.setAttribute('cy', pulseY);
|
||
pulseCircle.setAttribute('r', '3');
|
||
pulseCircle.setAttribute('fill', '#ff00ff');
|
||
pulseCircle.setAttribute('filter', 'url(#glow)');
|
||
linkGroup.appendChild(pulseCircle);
|
||
}
|
||
});
|
||
|
||
// 绘制节点
|
||
nodes.forEach(node => {
|
||
const circle = document.createElementNS('http://www.w3.org/2000/svg', 'circle');
|
||
circle.setAttribute('cx', node.x);
|
||
circle.setAttribute('cy', node.y);
|
||
circle.setAttribute('r', node.r);
|
||
circle.setAttribute('fill', '#00ffff');
|
||
circle.setAttribute('opacity', '0.8');
|
||
|
||
// 节点发光效果
|
||
const glow = document.createElementNS('http://www.w3.org/2000/svg', 'circle');
|
||
glow.setAttribute('cx', node.x);
|
||
glow.setAttribute('cy', node.y);
|
||
glow.setAttribute('r', node.r * 2);
|
||
glow.setAttribute('fill', 'none');
|
||
glow.setAttribute('stroke', 'rgba(255, 0, 255, 0.5)');
|
||
glow.setAttribute('stroke-width', '1');
|
||
nodeGroup.appendChild(glow);
|
||
nodeGroup.appendChild(circle);
|
||
});
|
||
}
|
||
|
||
// 添加发光滤镜
|
||
const defs = document.createElementNS('http://www.w3.org/2000/svg', 'defs');
|
||
defs.innerHTML = `
|
||
<filter id="glow" x="-50%" y="-50%" width="200%" height="200%">
|
||
<feGaussianBlur stdDeviation="3" result="coloredBlur"/>
|
||
<feMerge>
|
||
<feMergeNode in="coloredBlur"/>
|
||
<feMergeNode in="SourceGraphic"/>
|
||
</feMerge>
|
||
</filter>
|
||
`;
|
||
container.appendChild(defs);
|
||
|
||
// 更新模拟
|
||
neuralSimulation.on('tick', updateGraph);
|
||
updateGraph();
|
||
}
|
||
|
||
// ==================== 时间线导航 ====================
|
||
function initTimeline() {
|
||
const container = document.getElementById('timeline-container');
|
||
if (!container) return;
|
||
|
||
timelineData.forEach((item, index) => {
|
||
const node = document.createElement('div');
|
||
node.className = `timeline-node ${item.active ? 'active' : ''}`;
|
||
node.innerHTML = `
|
||
<div class="timeline-dot"></div>
|
||
<div class="timeline-year">${item.year}</div>
|
||
<div class="timeline-label">${item.label}</div>
|
||
`;
|
||
node.addEventListener('click', () => selectTimelineNode(index));
|
||
container.appendChild(node);
|
||
});
|
||
}
|
||
|
||
function selectTimelineNode(index) {
|
||
const nodes = document.querySelectorAll('.timeline-node');
|
||
nodes.forEach((node, i) => {
|
||
node.classList.toggle('active', i === index);
|
||
timelineData[i].active = i === index;
|
||
});
|
||
|
||
// 触发3D场景变化
|
||
const year = timelineData[index].year;
|
||
logToConsole(`切换到 ${year} 年建筑风格`, 'success');
|
||
|
||
// 模拟建筑风格变化
|
||
gsap.to(threeScene.fog, {
|
||
density: 0.002 + (5 - index) * 0.001,
|
||
duration: 1
|
||
});
|
||
}
|
||
|
||
// ==================== 命令控制台 ====================
|
||
function initConsole() {
|
||
const input = document.getElementById('console-input');
|
||
const submit = document.getElementById('console-submit');
|
||
const output = document.getElementById('console-output');
|
||
|
||
if (!input || !submit || !output) return;
|
||
|
||
function executeCommand() {
|
||
const command = input.value.trim();
|
||
if (!command) return;
|
||
|
||
// 显示用户输入
|
||
addConsoleLine(`> ${command}`, 'command');
|
||
input.value = '';
|
||
|
||
// 处理命令
|
||
const handler = commands[command.split(' ')[0]];
|
||
if (handler) {
|
||
handler(command);
|
||
} else if (command.startsWith('/')) {
|
||
addConsoleLine(`未知命令: ${command.split(' ')[0]},输入 /help 查看可用命令`, 'error');
|
||
} else {
|
||
addConsoleLine('命令必须以 / 开头', 'error');
|
||
}
|
||
}
|
||
|
||
submit.addEventListener('click', executeCommand);
|
||
input.addEventListener('keypress', (e) => {
|
||
if (e.key === 'Enter') executeCommand();
|
||
});
|
||
}
|
||
|
||
function addConsoleLine(text, type = 'system') {
|
||
const output = document.getElementById('console-output');
|
||
if (!output) return;
|
||
|
||
const line = document.createElement('div');
|
||
line.className = `console-line ${type}`;
|
||
line.textContent = text;
|
||
output.appendChild(line);
|
||
output.scrollTop = output.scrollHeight;
|
||
}
|
||
|
||
function logToConsole(text, type = 'system') {
|
||
addConsoleLine(text, type);
|
||
}
|
||
|
||
// 命令处理函数
|
||
function showHelp() {
|
||
addConsoleLine('可用命令列表:', 'system');
|
||
addConsoleLine('/help - 显示帮助信息', 'success');
|
||
addConsoleLine('/scan - 扫描网络节点', 'success');
|
||
addConsoleLine('/hack - 触发安全测试', 'success');
|
||
addConsoleLine('/status - 显示系统状态', 'success');
|
||
addConsoleLine('/time - 显示当前时间', 'success');
|
||
addConsoleLine('/nodes - 显示网络节点统计', 'success');
|
||
addConsoleLine('/clear - 清空控制台', 'success');
|
||
addConsoleLine('/weather - 显示天气数据', 'success');
|
||
addConsoleLine('/energy - 显示能源分布', 'success');
|
||
addConsoleLine('/reset - 重置系统', 'error');
|
||
}
|
||
|
||
function scanNetwork() {
|
||
addConsoleLine('正在扫描网络节点...', 'system');
|
||
setTimeout(() => {
|
||
addConsoleLine('扫描完成。发现 2,847 个活跃节点', 'success');
|
||
document.getElementById('node-count').textContent = '2,847';
|
||
}, 1500);
|
||
}
|
||
|
||
function triggerHack() {
|
||
addConsoleLine('警告:安全测试模式已激活', 'error');
|
||
document.body.style.filter = 'hue-rotate(90deg)';
|
||
setTimeout(() => {
|
||
addConsoleLine('模拟攻击已拦截,系统安全', 'success');
|
||
document.body.style.filter = '';
|
||
}, 2000);
|
||
}
|
||
|
||
function resetSystem() {
|
||
addConsoleLine('正在重置系统...', 'system');
|
||
setTimeout(() => {
|
||
addConsoleLine('系统重置完成', 'success');
|
||
}, 2000);
|
||
}
|
||
|
||
function showStatus() {
|
||
addConsoleLine('系统状态报告:', 'system');
|
||
addConsoleLine('CPU 负载: 67%', 'success');
|
||
addConsoleLine('内存占用: 82%', 'success');
|
||
addConsoleLine('网络流量: 94%', 'success');
|
||
addConsoleLine('AI 同步率: 99%', 'success');
|
||
}
|
||
|
||
function showTime() {
|
||
const now = new Date();
|
||
addConsoleLine(`当前时间: ${now.toLocaleString('zh-CN')}`, 'system');
|
||
}
|
||
|
||
function showNodes() {
|
||
addConsoleLine('网络节点分布:', 'system');
|
||
addConsoleLine('东城区: 847 节点', 'success');
|
||
addConsoleLine('西城区: 623 节点', 'success');
|
||
addConsoleLine('南城区: 756 节点', 'success');
|
||
addConsoleLine('北城区: 621 节点', 'success');
|
||
}
|
||
|
||
function clearConsole() {
|
||
const output = document.getElementById('console-output');
|
||
if (output) {
|
||
output.innerHTML = '';
|
||
addConsoleLine('控制台已清空', 'system');
|
||
}
|
||
}
|
||
|
||
function showWeather() {
|
||
addConsoleLine('城市天气数据:', 'system');
|
||
addConsoleLine('温度: 23°C', 'success');
|
||
addConsoleLine('湿度: 45%', 'success');
|
||
addConsoleLine('空气质量: 优', 'success');
|
||
addConsoleLine('大气压强: 1013 hPa', 'success');
|
||
}
|
||
|
||
function showEnergy() {
|
||
addConsoleLine('能源分布情况:', 'system');
|
||
addConsoleLine('核聚变电站: 89% (41.83 TW)', 'success');
|
||
addConsoleLine('太阳能阵列: 11% (5.17 TW)', 'success');
|
||
addConsoleLine('储能系统: 67% (满载)', 'success');
|
||
}
|
||
|
||
// ==================== 数据卡片交互 ====================
|
||
function initDataCards() {
|
||
const cards = document.querySelectorAll('.holo-card');
|
||
cards.forEach(card => {
|
||
card.addEventListener('mouseenter', () => {
|
||
card.style.transform = 'perspective(1000px) rotateX(0deg) rotateY(5deg) translateZ(15px)';
|
||
});
|
||
card.addEventListener('mouseleave', () => {
|
||
card.style.transform = 'perspective(1000px) rotateX(5deg)';
|
||
});
|
||
card.addEventListener('click', () => {
|
||
const cardType = card.dataset.card;
|
||
showCardDetail(cardType);
|
||
});
|
||
});
|
||
}
|
||
|
||
function showCardDetail(type) {
|
||
const details = {
|
||
population: '人口数据详情:总人口28亿,其中神经连接公民27亿,覆盖率96.4%',
|
||
energy: '能源数据详情:总消耗47太瓦,核聚变供能42太瓦,太阳能5太瓦',
|
||
nodes: '节点数据详情:120万网络节点,活跃节点119.8万,冗余节点1.2万',
|
||
ai: 'AI同步详情:觉醒AI共847台842台正在同步,同步率99.7%'
|
||
};
|
||
logToConsole(details[type], 'success');
|
||
}
|
||
|
||
// ==================== 实时数据更新 ====================
|
||
function startRealTimeUpdates() {
|
||
// 更新时间
|
||
function updateTime() {
|
||
const now = new Date();
|
||
const timeStr = `2078-${String(now.getMonth() + 1).padStart(2, '0')} ${String(now.getDate()).padStart(2, '0')} ${String(now.getHours()).padStart(2, '0')}:${String(now.getMinutes()).padStart(2, '0')}:${String(now.getSeconds()).padStart(2, '0')}`;
|
||
const timeEl = document.getElementById('current-time');
|
||
if (timeEl) timeEl.textContent = timeStr;
|
||
}
|
||
setInterval(updateTime, 1000);
|
||
updateTime();
|
||
|
||
// 随机更新仪表盘数值
|
||
function updateGauges() {
|
||
const gaugeConfigs = [
|
||
{ id: 'cpu-gauge', valueId: 'cpu-value', min: 50, max: 90 },
|
||
{ id: 'memory-gauge', valueId: 'memory-value', min: 70, max: 95 },
|
||
{ id: 'network-gauge', valueId: 'network-value', min: 80, max: 99 },
|
||
{ id: 'ai-gauge', valueId: 'ai-value', min: 95, max: 100 }
|
||
];
|
||
|
||
gaugeConfigs.forEach(config => {
|
||
const value = Math.floor(Math.random() * (config.max - config.min) + config.min);
|
||
const gaugeEl = document.getElementById(config.id);
|
||
const valueEl = document.getElementById(config.valueId);
|
||
|
||
if (gaugeEl && valueEl) {
|
||
drawGauge(config.id, value, 100, '#00ffff');
|
||
valueEl.textContent = value + '%';
|
||
}
|
||
});
|
||
}
|
||
setInterval(updateGauges, 3000);
|
||
|
||
// 更新神经网络统计
|
||
function updateNeuralStats() {
|
||
const stats = {
|
||
'neuron-activity': (94 + Math.random() * 5).toFixed(1),
|
||
'data-throughput': (800 + Math.random() * 100).toFixed(0),
|
||
'latency': (0.2 + Math.random() * 0.3).toFixed(1),
|
||
'error-rate': (0.001 + Math.random() * 0.002).toFixed(3)
|
||
};
|
||
|
||
Object.entries(stats).forEach(([id, value]) => {
|
||
const el = document.getElementById(id);
|
||
if (el) {
|
||
if (id === 'data-throughput') {
|
||
el.textContent = value + ' TB/s';
|
||
} else if (id === 'latency') {
|
||
el.textContent = value + 'ms';
|
||
} else if (id === 'error-rate') {
|
||
el.textContent = value + '%';
|
||
} else {
|
||
el.textContent = value + '%';
|
||
}
|
||
}
|
||
});
|
||
}
|
||
setInterval(updateNeuralStats, 2000);
|
||
|
||
// 随机日志输出
|
||
function randomLog() {
|
||
const logs = [
|
||
'数据包已接收',
|
||
'新节点已连接',
|
||
'AI同步完成',
|
||
'安全扫描完成',
|
||
'数据加密处理中',
|
||
'网络负载均衡',
|
||
'备份已创建'
|
||
];
|
||
const randomLog = logs[Math.floor(Math.random() * logs.length)];
|
||
addConsoleLine(randomLog, 'system');
|
||
}
|
||
setInterval(randomLog, 5000);
|
||
}
|
||
</script>
|
||
</body>
|
||
</html>
|