2283 lines
91 KiB
HTML
2283 lines
91 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资源引入 -->
|
||
<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;400;600&display=swap" rel="stylesheet">
|
||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
||
<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.11.5/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>
|
||
<style>
|
||
/* 全局变量与基础重置 */
|
||
:root {
|
||
--neon-pink: #ff00ff;
|
||
--electric-blue: #00ffff;
|
||
--laser-purple: #8800ff;
|
||
--deep-black: #0a0a0a;
|
||
--metal-gray: #1a1a1a;
|
||
--hologram-teal: #00ffcc;
|
||
--grid-color: rgba(0, 255, 255, 0.1);
|
||
--glow-intensity: 0 0 15px;
|
||
}
|
||
|
||
* {
|
||
margin: 0;
|
||
padding: 0;
|
||
box-sizing: border-box;
|
||
}
|
||
|
||
html {
|
||
scroll-behavior: smooth;
|
||
}
|
||
|
||
body {
|
||
font-family: 'Rajdhani', sans-serif;
|
||
background-color: var(--deep-black);
|
||
color: white;
|
||
overflow-x: hidden;
|
||
line-height: 1.6;
|
||
font-weight: 400;
|
||
position: relative;
|
||
min-height: 100vh;
|
||
cursor: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><circle cx="12" cy="12" r="10" fill="none" stroke="%2300ffff" stroke-width="2" stroke-opacity="0.8"/><circle cx="12" cy="12" r="2" fill="%2300ffff"/></svg>') 12 12, auto;
|
||
}
|
||
|
||
h1, h2, h3, h4, .tech-font {
|
||
font-family: 'Orbitron', sans-serif;
|
||
font-weight: 700;
|
||
letter-spacing: 1px;
|
||
text-transform: uppercase;
|
||
}
|
||
|
||
/* 开场动画容器 */
|
||
#intro-animation {
|
||
position: fixed;
|
||
top: 0;
|
||
left: 0;
|
||
width: 100%;
|
||
height: 100%;
|
||
background-color: var(--deep-black);
|
||
z-index: 1000;
|
||
display: flex;
|
||
justify-content: center;
|
||
align-items: center;
|
||
overflow: hidden;
|
||
}
|
||
|
||
.neural-node {
|
||
position: absolute;
|
||
width: 8px;
|
||
height: 8px;
|
||
border-radius: 50%;
|
||
background-color: var(--neon-pink);
|
||
box-shadow: 0 0 20px var(--neon-pink);
|
||
z-index: 1001;
|
||
}
|
||
|
||
.neural-connection {
|
||
position: absolute;
|
||
height: 2px;
|
||
background: linear-gradient(90deg, transparent, var(--electric-blue), transparent);
|
||
transform-origin: left center;
|
||
z-index: 1000;
|
||
}
|
||
|
||
/* 主容器与布局 */
|
||
.main-container {
|
||
opacity: 0;
|
||
transition: opacity 1s ease;
|
||
position: relative;
|
||
}
|
||
|
||
.main-container.visible {
|
||
opacity: 1;
|
||
}
|
||
|
||
section {
|
||
position: relative;
|
||
padding: 100px 5%;
|
||
min-height: 100vh;
|
||
display: flex;
|
||
flex-direction: column;
|
||
justify-content: center;
|
||
border-bottom: 1px solid rgba(0, 255, 255, 0.1);
|
||
}
|
||
|
||
.section-title {
|
||
font-size: 2.5rem;
|
||
margin-bottom: 2rem;
|
||
position: relative;
|
||
display: inline-block;
|
||
color: var(--electric-blue);
|
||
text-shadow: 0 0 10px rgba(0, 255, 255, 0.5);
|
||
}
|
||
|
||
.section-title::after {
|
||
content: '';
|
||
position: absolute;
|
||
bottom: -10px;
|
||
left: 0;
|
||
width: 100px;
|
||
height: 3px;
|
||
background: linear-gradient(90deg, var(--neon-pink), var(--electric-blue));
|
||
border-radius: 2px;
|
||
}
|
||
|
||
/* 3D城市容器 */
|
||
#city-3d-container {
|
||
position: relative;
|
||
width: 100%;
|
||
height: 80vh;
|
||
border-radius: 10px;
|
||
overflow: hidden;
|
||
border: 1px solid rgba(0, 255, 255, 0.3);
|
||
box-shadow: 0 0 40px rgba(0, 255, 255, 0.2);
|
||
margin-top: 2rem;
|
||
}
|
||
|
||
#city-canvas {
|
||
width: 100%;
|
||
height: 100%;
|
||
display: block;
|
||
}
|
||
|
||
.city-info-panel {
|
||
position: absolute;
|
||
top: 20px;
|
||
right: 20px;
|
||
background: rgba(10, 10, 10, 0.8);
|
||
backdrop-filter: blur(10px);
|
||
border: 1px solid var(--electric-blue);
|
||
border-radius: 8px;
|
||
padding: 15px;
|
||
width: 300px;
|
||
box-shadow: 0 0 20px rgba(0, 255, 255, 0.3);
|
||
z-index: 10;
|
||
}
|
||
|
||
.info-row {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
margin-bottom: 10px;
|
||
font-size: 0.9rem;
|
||
}
|
||
|
||
.info-label {
|
||
color: #aaa;
|
||
}
|
||
|
||
.info-value {
|
||
color: var(--hologram-teal);
|
||
font-weight: 600;
|
||
}
|
||
|
||
/* 神经网络可视化 */
|
||
#neural-network {
|
||
position: relative;
|
||
width: 100%;
|
||
height: 70vh;
|
||
border-radius: 10px;
|
||
overflow: hidden;
|
||
background: rgba(5, 5, 15, 0.7);
|
||
border: 1px solid rgba(136, 0, 255, 0.5);
|
||
box-shadow: 0 0 30px rgba(136, 0, 255, 0.3);
|
||
margin-top: 2rem;
|
||
}
|
||
|
||
#neural-canvas {
|
||
width: 100%;
|
||
height: 100%;
|
||
}
|
||
|
||
.neural-stats {
|
||
display: flex;
|
||
justify-content: space-around;
|
||
flex-wrap: wrap;
|
||
margin-top: 30px;
|
||
gap: 20px;
|
||
}
|
||
|
||
.stat-box {
|
||
background: rgba(20, 20, 30, 0.8);
|
||
border: 1px solid var(--neon-pink);
|
||
border-radius: 8px;
|
||
padding: 20px;
|
||
min-width: 200px;
|
||
text-align: center;
|
||
transition: transform 0.3s ease, box-shadow 0.3s ease;
|
||
position: relative;
|
||
overflow: hidden;
|
||
}
|
||
|
||
.stat-box:hover {
|
||
transform: translateY(-5px);
|
||
box-shadow: 0 10px 25px rgba(255, 0, 255, 0.4);
|
||
}
|
||
|
||
.stat-value {
|
||
font-size: 2.5rem;
|
||
font-weight: 900;
|
||
color: var(--neon-pink);
|
||
margin: 10px 0;
|
||
text-shadow: 0 0 10px rgba(255, 0, 255, 0.7);
|
||
}
|
||
|
||
/* 监控面板 */
|
||
.monitor-grid {
|
||
display: grid;
|
||
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
|
||
gap: 30px;
|
||
margin-top: 2rem;
|
||
}
|
||
|
||
.monitor-card {
|
||
background: rgba(15, 15, 25, 0.9);
|
||
border-radius: 10px;
|
||
padding: 25px;
|
||
border: 1px solid rgba(0, 255, 255, 0.3);
|
||
box-shadow: 0 0 20px rgba(0, 255, 255, 0.1);
|
||
transition: all 0.3s ease;
|
||
position: relative;
|
||
overflow: hidden;
|
||
}
|
||
|
||
.monitor-card:hover {
|
||
border-color: var(--electric-blue);
|
||
box-shadow: 0 0 30px rgba(0, 255, 255, 0.3);
|
||
}
|
||
|
||
.card-title {
|
||
color: var(--electric-blue);
|
||
margin-bottom: 20px;
|
||
font-size: 1.3rem;
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 10px;
|
||
}
|
||
|
||
.gauge-container {
|
||
position: relative;
|
||
width: 200px;
|
||
height: 200px;
|
||
margin: 0 auto;
|
||
}
|
||
|
||
.waveform-container {
|
||
width: 100%;
|
||
height: 150px;
|
||
position: relative;
|
||
}
|
||
|
||
#waveform-canvas {
|
||
width: 100%;
|
||
height: 100%;
|
||
}
|
||
|
||
.server-grid {
|
||
display: grid;
|
||
grid-template-columns: repeat(5, 1fr);
|
||
grid-template-rows: repeat(4, 1fr);
|
||
gap: 10px;
|
||
}
|
||
|
||
.server-node {
|
||
aspect-ratio: 1;
|
||
background-color: rgba(0, 255, 0, 0.1);
|
||
border-radius: 4px;
|
||
border: 1px solid rgba(0, 255, 0, 0.3);
|
||
transition: all 0.3s ease;
|
||
}
|
||
|
||
.server-node.alert {
|
||
background-color: rgba(255, 0, 0, 0.2);
|
||
border-color: rgba(255, 0, 0, 0.6);
|
||
animation: pulse-red 1s infinite alternate;
|
||
}
|
||
|
||
@keyframes pulse-red {
|
||
from { box-shadow: 0 0 5px rgba(255, 0, 0, 0.5); }
|
||
to { box-shadow: 0 0 15px rgba(255, 0, 0, 0.8); }
|
||
}
|
||
|
||
.log-window {
|
||
background-color: rgba(0, 20, 0, 0.8);
|
||
border: 1px solid rgba(0, 255, 0, 0.4);
|
||
border-radius: 5px;
|
||
padding: 15px;
|
||
height: 300px;
|
||
overflow-y: auto;
|
||
font-family: 'Courier New', monospace;
|
||
font-size: 0.9rem;
|
||
position: relative;
|
||
}
|
||
|
||
.log-window::before {
|
||
content: '';
|
||
position: absolute;
|
||
top: 0;
|
||
left: 0;
|
||
width: 100%;
|
||
height: 100%;
|
||
background: linear-gradient(
|
||
transparent 95%,
|
||
rgba(0, 255, 0, 0.05) 95%
|
||
);
|
||
background-size: 100% 20px;
|
||
pointer-events: none;
|
||
z-index: 1;
|
||
}
|
||
|
||
.log-entry {
|
||
color: #00ff00;
|
||
margin-bottom: 5px;
|
||
line-height: 1.4;
|
||
}
|
||
|
||
.log-timestamp {
|
||
color: #00aa00;
|
||
}
|
||
|
||
/* 时间线导航 */
|
||
.timeline-container {
|
||
position: relative;
|
||
margin-top: 3rem;
|
||
height: 200px;
|
||
overflow-x: auto;
|
||
overflow-y: hidden;
|
||
padding: 20px 0;
|
||
}
|
||
|
||
.timeline-track {
|
||
position: relative;
|
||
height: 4px;
|
||
background: linear-gradient(90deg, var(--neon-pink), var(--laser-purple), var(--electric-blue));
|
||
margin: 80px 50px;
|
||
border-radius: 2px;
|
||
}
|
||
|
||
.timeline-node {
|
||
position: absolute;
|
||
top: 50%;
|
||
transform: translateY(-50%);
|
||
width: 20px;
|
||
height: 20px;
|
||
background-color: var(--metal-gray);
|
||
border: 3px solid var(--electric-blue);
|
||
border-radius: 50%;
|
||
cursor: pointer;
|
||
transition: all 0.3s ease;
|
||
z-index: 10;
|
||
}
|
||
|
||
.timeline-node:hover {
|
||
transform: translateY(-50%) scale(1.3);
|
||
background-color: var(--electric-blue);
|
||
box-shadow: 0 0 20px var(--electric-blue);
|
||
}
|
||
|
||
.timeline-node.active {
|
||
background-color: var(--neon-pink);
|
||
border-color: var(--neon-pink);
|
||
box-shadow: 0 0 25px var(--neon-pink);
|
||
}
|
||
|
||
.node-label {
|
||
position: absolute;
|
||
top: -50px;
|
||
left: 50%;
|
||
transform: translateX(-50%);
|
||
white-space: nowrap;
|
||
background: rgba(10, 10, 10, 0.9);
|
||
padding: 8px 15px;
|
||
border-radius: 6px;
|
||
border: 1px solid var(--electric-blue);
|
||
color: var(--electric-blue);
|
||
font-weight: 600;
|
||
opacity: 0;
|
||
transition: opacity 0.3s ease;
|
||
pointer-events: none;
|
||
}
|
||
|
||
.timeline-node:hover .node-label {
|
||
opacity: 1;
|
||
}
|
||
|
||
/* 数据卡片 */
|
||
.cards-container {
|
||
display: grid;
|
||
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
|
||
gap: 30px;
|
||
margin-top: 3rem;
|
||
perspective: 1000px;
|
||
}
|
||
|
||
.data-card {
|
||
background: linear-gradient(145deg, rgba(0, 255, 255, 0.05), rgba(255, 0, 255, 0.05));
|
||
border-radius: 10px;
|
||
padding: 25px;
|
||
border: 1px solid rgba(255, 255, 255, 0.1);
|
||
transform-style: preserve-3d;
|
||
transform: rotateX(5deg);
|
||
transition: all 0.5s ease;
|
||
position: relative;
|
||
overflow: hidden;
|
||
}
|
||
|
||
.data-card::before {
|
||
content: '';
|
||
position: absolute;
|
||
top: 0;
|
||
left: 0;
|
||
width: 100%;
|
||
height: 100%;
|
||
background: linear-gradient(135deg, transparent 30%, rgba(0, 255, 255, 0.05) 100%);
|
||
z-index: -1;
|
||
}
|
||
|
||
.data-card:hover {
|
||
transform: rotateX(0deg) translateY(-10px);
|
||
border-color: var(--hologram-teal);
|
||
box-shadow: 0 15px 35px rgba(0, 255, 204, 0.3);
|
||
}
|
||
|
||
.card-icon {
|
||
font-size: 2rem;
|
||
color: var(--hologram-teal);
|
||
margin-bottom: 15px;
|
||
}
|
||
|
||
.card-value {
|
||
font-size: 2.5rem;
|
||
font-weight: 900;
|
||
color: white;
|
||
margin: 15px 0;
|
||
text-shadow: 0 0 10px rgba(255, 255, 255, 0.5);
|
||
}
|
||
|
||
/* 命令控制台 */
|
||
.console-container {
|
||
position: fixed;
|
||
bottom: 0;
|
||
left: 0;
|
||
width: 100%;
|
||
background: rgba(5, 5, 15, 0.95);
|
||
backdrop-filter: blur(15px);
|
||
border-top: 2px solid var(--laser-purple);
|
||
padding: 15px 5%;
|
||
z-index: 100;
|
||
box-shadow: 0 -5px 30px rgba(136, 0, 255, 0.5);
|
||
}
|
||
|
||
.console-input {
|
||
width: 100%;
|
||
background: transparent;
|
||
border: none;
|
||
border-bottom: 2px solid var(--electric-blue);
|
||
color: white;
|
||
font-family: 'Rajdhani', sans-serif;
|
||
font-size: 1.2rem;
|
||
padding: 10px 0;
|
||
outline: none;
|
||
caret-color: var(--neon-pink);
|
||
}
|
||
|
||
.console-input::placeholder {
|
||
color: rgba(255, 255, 255, 0.5);
|
||
}
|
||
|
||
.cursor {
|
||
display: inline-block;
|
||
width: 2px;
|
||
height: 1.2em;
|
||
background-color: var(--neon-pink);
|
||
animation: blink 1s infinite;
|
||
vertical-align: middle;
|
||
margin-left: 2px;
|
||
}
|
||
|
||
@keyframes blink {
|
||
0%, 50% { opacity: 1; }
|
||
51%, 100% { opacity: 0; }
|
||
}
|
||
|
||
.console-prompt {
|
||
color: var(--hologram-teal);
|
||
font-weight: 600;
|
||
margin-right: 10px;
|
||
}
|
||
|
||
/* 响应式设计 */
|
||
@media (max-width: 1024px) {
|
||
.monitor-grid {
|
||
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
|
||
}
|
||
|
||
.cards-container {
|
||
grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
|
||
}
|
||
}
|
||
|
||
@media (max-width: 768px) {
|
||
section {
|
||
padding: 80px 5%;
|
||
}
|
||
|
||
.section-title {
|
||
font-size: 2rem;
|
||
}
|
||
|
||
.city-info-panel {
|
||
width: 250px;
|
||
padding: 10px;
|
||
}
|
||
|
||
.monitor-grid {
|
||
grid-template-columns: 1fr;
|
||
}
|
||
|
||
.cards-container {
|
||
grid-template-columns: 1fr;
|
||
}
|
||
|
||
.timeline-container {
|
||
height: 150px;
|
||
}
|
||
|
||
.console-input {
|
||
font-size: 1rem;
|
||
}
|
||
}
|
||
|
||
/* Glitch效果 */
|
||
.glitch {
|
||
position: relative;
|
||
display: inline-block;
|
||
}
|
||
|
||
.glitch::before,
|
||
.glitch::after {
|
||
content: attr(data-text);
|
||
position: absolute;
|
||
top: 0;
|
||
left: 0;
|
||
width: 100%;
|
||
height: 100%;
|
||
background: var(--deep-black);
|
||
}
|
||
|
||
.glitch::before {
|
||
left: 2px;
|
||
text-shadow: -2px 0 var(--neon-pink);
|
||
clip: rect(24px, 550px, 90px, 0);
|
||
animation: glitch-anim 5s infinite linear alternate-reverse;
|
||
}
|
||
|
||
.glitch::after {
|
||
left: -2px;
|
||
text-shadow: -2px 0 var(--electric-blue);
|
||
clip: rect(85px, 550px, 140px, 0);
|
||
animation: glitch-anim2 5s infinite linear alternate-reverse;
|
||
}
|
||
|
||
@keyframes glitch-anim {
|
||
0% { clip: rect(42px, 9999px, 44px, 0); }
|
||
5% { clip: rect(12px, 9999px, 59px, 0); }
|
||
10% { clip: rect(48px, 9999px, 29px, 0); }
|
||
15% { clip: rect(42px, 9999px, 73px, 0); }
|
||
20% { clip: rect(63px, 9999px, 27px, 0); }
|
||
25% { clip: rect(34px, 9999px, 55px, 0); }
|
||
30% { clip: rect(86px, 9999px, 73px, 0); }
|
||
35% { clip: rect(20px, 9999px, 20px, 0); }
|
||
40% { clip: rect(26px, 9999px, 60px, 0); }
|
||
45% { clip: rect(25px, 9999px, 66px, 0); }
|
||
50% { clip: rect(57px, 9999px, 98px, 0); }
|
||
55% { clip: rect(5px, 9999px, 46px, 0); }
|
||
60% { clip: rect(82px, 9999px, 31px, 0); }
|
||
65% { clip: rect(54px, 9999px, 27px, 0); }
|
||
70% { clip: rect(28px, 9999px, 99px, 0); }
|
||
75% { clip: rect(45px, 9999px, 69px, 0); }
|
||
80% { clip: rect(23px, 9999px, 85px, 0); }
|
||
85% { clip: rect(54px, 9999px, 84px, 0); }
|
||
90% { clip: rect(45px, 9999px, 47px, 0); }
|
||
95% { clip: rect(37px, 9999px, 20px, 0); }
|
||
100% { clip: rect(4px, 9999px, 91px, 0); }
|
||
}
|
||
|
||
@keyframes glitch-anim2 {
|
||
0% { clip: rect(65px, 9999px, 100px, 0); }
|
||
5% { clip: rect(52px, 9999px, 74px, 0); }
|
||
10% { clip: rect(79px, 9999px, 85px, 0); }
|
||
15% { clip: rect(75px, 9999px, 5px, 0); }
|
||
20% { clip: rect(67px, 9999px, 61px, 0); }
|
||
25% { clip: rect(14px, 9999px, 79px, 0); }
|
||
30% { clip: rect(1px, 9999px, 66px, 0); }
|
||
35% { clip: rect(86px, 9999px, 30px, 0); }
|
||
40% { clip: rect(23px, 9999px, 98px, 0); }
|
||
45% { clip: rect(85px, 9999px, 72px, 0); }
|
||
50% { clip: rect(71px, 9999px, 75px, 0); }
|
||
55% { clip: rect(2px, 9999px, 48px, 0); }
|
||
60% { clip: rect(30px, 9999px, 16px, 0); }
|
||
65% { clip: rect(59px, 9999px, 50px, 0); }
|
||
70% { clip: rect(41px, 9999px, 62px, 0); }
|
||
75% { clip: rect(2px, 9999px, 82px, 0); }
|
||
80% { clip: rect(47px, 9999px, 73px, 0); }
|
||
85% { clip: rect(3px, 9999px, 27px, 0); }
|
||
90% { clip: rect(40px, 9999px, 86px, 0); }
|
||
95% { clip: rect(45px, 9999px, 72px, 0); }
|
||
100% { clip: rect(23px, 9999px, 49px, 0); }
|
||
}
|
||
|
||
/* 粒子背景 */
|
||
#particles-js {
|
||
position: fixed;
|
||
top: 0;
|
||
left: 0;
|
||
width: 100%;
|
||
height: 100%;
|
||
z-index: -1;
|
||
pointer-events: none;
|
||
}
|
||
|
||
/* 加载动画 */
|
||
.loading-ring {
|
||
position: fixed;
|
||
top: 50%;
|
||
left: 50%;
|
||
transform: translate(-50%, -50%);
|
||
width: 100px;
|
||
height: 100px;
|
||
z-index: 2000;
|
||
}
|
||
|
||
.loading-ring::before {
|
||
content: '';
|
||
position: absolute;
|
||
top: 0;
|
||
left: 0;
|
||
width: 100%;
|
||
height: 100%;
|
||
border-radius: 50%;
|
||
border: 5px solid transparent;
|
||
border-top: 5px solid var(--neon-pink);
|
||
border-right: 5px solid var(--electric-blue);
|
||
animation: spin 1s linear infinite;
|
||
}
|
||
|
||
@keyframes spin {
|
||
0% { transform: rotate(0deg); }
|
||
100% { transform: rotate(360deg); }
|
||
}
|
||
|
||
/* 隐藏滚动条但保持滚动功能 */
|
||
.hide-scrollbar {
|
||
-ms-overflow-style: none;
|
||
scrollbar-width: none;
|
||
}
|
||
|
||
.hide-scrollbar::-webkit-scrollbar {
|
||
display: none;
|
||
}
|
||
|
||
/* 通用动画类 */
|
||
.fade-in {
|
||
animation: fadeIn 1s ease forwards;
|
||
}
|
||
|
||
@keyframes fadeIn {
|
||
from { opacity: 0; }
|
||
to { opacity: 1; }
|
||
}
|
||
|
||
.slide-up {
|
||
animation: slideUp 0.8s ease forwards;
|
||
}
|
||
|
||
@keyframes slideUp {
|
||
from {
|
||
opacity: 0;
|
||
transform: translateY(30px);
|
||
}
|
||
to {
|
||
opacity: 1;
|
||
transform: translateY(0);
|
||
}
|
||
}
|
||
|
||
.pulse {
|
||
animation: pulse 2s infinite;
|
||
}
|
||
|
||
@keyframes pulse {
|
||
0% { box-shadow: 0 0 0 0 rgba(0, 255, 255, 0.7); }
|
||
70% { box-shadow: 0 0 0 15px rgba(0, 255, 255, 0); }
|
||
100% { box-shadow: 0 0 0 0 rgba(0, 255, 255, 0); }
|
||
}
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<!-- 开场动画 -->
|
||
<div id="intro-animation">
|
||
<div class="loading-ring"></div>
|
||
</div>
|
||
|
||
<!-- 粒子背景 -->
|
||
<div id="particles-js"></div>
|
||
|
||
<!-- 主容器 -->
|
||
<div class="main-container">
|
||
<!-- 3D城市概览 -->
|
||
<section id="city-section">
|
||
<h2 class="section-title glitch" data-text="神经网络城市概览">神经网络城市概览</h2>
|
||
<div id="city-3d-container">
|
||
<canvas id="city-canvas"></canvas>
|
||
<div class="city-info-panel slide-up">
|
||
<div class="info-row">
|
||
<span class="info-label">当前时间</span>
|
||
<span class="info-value" id="current-time">2078-10-23 22:47:18</span>
|
||
</div>
|
||
<div class="info-row">
|
||
<span class="info-label">城市状态</span>
|
||
<span class="info-value" id="city-status">在线</span>
|
||
</div>
|
||
<div class="info-row">
|
||
<span class="info-label">神经元活跃度</span>
|
||
<span class="info-value" id="neuron-activity">94.7%</span>
|
||
</div>
|
||
<div class="info-row">
|
||
<span class="info-label">能量消耗</span>
|
||
<span class="info-value" id="energy-consumption">18.4 TW</span>
|
||
</div>
|
||
<div class="info-row">
|
||
<span class="info-label">网络延迟</span>
|
||
<span class="info-value" id="network-latency">0.8ms</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<!-- 神经网络可视化 -->
|
||
<section id="neural-section">
|
||
<h2 class="section-title glitch" data-text="实时数据流可视化">实时数据流可视化</h2>
|
||
<div id="neural-network">
|
||
<canvas id="neural-canvas"></canvas>
|
||
</div>
|
||
<div class="neural-stats">
|
||
<div class="stat-box fade-in">
|
||
<h3>神经元活跃度</h3>
|
||
<div class="stat-value" id="neuron-activity-value">94.7%</div>
|
||
<p>每秒处理数据包</p>
|
||
</div>
|
||
<div class="stat-box fade-in" style="animation-delay: 0.1s">
|
||
<h3>数据吞吐量</h3>
|
||
<div class="stat-value" id="data-throughput">18.4 PB/s</div>
|
||
<p>当前网络负载</p>
|
||
</div>
|
||
<div class="stat-box fade-in" style="animation-delay: 0.2s">
|
||
<h3>延迟指标</h3>
|
||
<div class="stat-value" id="latency-value">0.8ms</div>
|
||
<p>平均响应时间</p>
|
||
</div>
|
||
<div class="stat-box fade-in" style="animation-delay: 0.3s">
|
||
<h3>连接稳定性</h3>
|
||
<div class="stat-value" id="connection-stability">99.98%</div>
|
||
<p>系统正常运行时间</p>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<!-- 系统监控面板 -->
|
||
<section id="monitor-section">
|
||
<h2 class="section-title glitch" data-text="智能系统监控面板">智能系统监控面板</h2>
|
||
<div class="monitor-grid">
|
||
<div class="monitor-card slide-up">
|
||
<h3 class="card-title"><i class="fas fa-microchip"></i> 系统负载</h3>
|
||
<div class="gauge-container">
|
||
<canvas id="cpu-gauge"></canvas>
|
||
</div>
|
||
<div class="info-row" style="margin-top: 20px;">
|
||
<span class="info-label">CPU使用率</span>
|
||
<span class="info-value" id="cpu-usage">78%</span>
|
||
</div>
|
||
<div class="info-row">
|
||
<span class="info-label">内存使用率</span>
|
||
<span class="info-value" id="memory-usage">64%</span>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="monitor-card slide-up" style="animation-delay: 0.1s">
|
||
<h3 class="card-title"><i class="fas fa-wave-square"></i> 网络流量</h3>
|
||
<div class="waveform-container">
|
||
<canvas id="waveform-canvas"></canvas>
|
||
</div>
|
||
<div class="info-row" style="margin-top: 20px;">
|
||
<span class="info-label">上传速度</span>
|
||
<span class="info-value" id="upload-speed">1.2 GB/s</span>
|
||
</div>
|
||
<div class="info-row">
|
||
<span class="info-label">下载速度</span>
|
||
<span class="info-value" id="download-speed">2.8 GB/s</span>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="monitor-card slide-up" style="animation-delay: 0.2s">
|
||
<h3 class="card-title"><i class="fas fa-server"></i> 服务器状态</h3>
|
||
<div class="server-grid" id="server-grid">
|
||
<!-- 服务器节点由JS动态生成 -->
|
||
</div>
|
||
<div class="info-row" style="margin-top: 20px;">
|
||
<span class="info-label">在线服务器</span>
|
||
<span class="info-value" id="online-servers">148/150</span>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="monitor-card slide-up" style="animation-delay: 0.3s">
|
||
<h3 class="card-title"><i class="fas fa-terminal"></i> 系统日志</h3>
|
||
<div class="log-window" id="log-window">
|
||
<!-- 日志条目由JS动态生成 -->
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<!-- 时间线导航 -->
|
||
<section id="timeline-section">
|
||
<h2 class="section-title glitch" data-text="历史时间线">历史时间线</h2>
|
||
<div class="timeline-container hide-scrollbar">
|
||
<div class="timeline-track"></div>
|
||
<!-- 时间线节点由JS动态生成 -->
|
||
</div>
|
||
</section>
|
||
|
||
<!-- 数据卡片 -->
|
||
<section id="data-cards-section">
|
||
<h2 class="section-title glitch" data-text="全息数据投影">全息数据投影</h2>
|
||
<div class="cards-container">
|
||
<div class="data-card slide-up">
|
||
<div class="card-icon"><i class="fas fa-users"></i></div>
|
||
<h3>人口统计</h3>
|
||
<div class="card-value" id="population-value">42.7M</div>
|
||
<p>城市总人口数量</p>
|
||
<div class="card-details" style="margin-top: 15px; font-size: 0.9rem; color: #aaa; display: none;">
|
||
<p>• AI公民: 18.2M</p>
|
||
<p>• 人类居民: 24.5M</p>
|
||
<p>• 增长趋势: +1.2%/年</p>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="data-card slide-up" style="animation-delay: 0.1s">
|
||
<div class="card-icon"><i class="fas fa-bolt"></i></div>
|
||
<h3>能量消耗</h3>
|
||
<div class="card-value" id="energy-value">18.4 TW</div>
|
||
<p>当前城市总能耗</p>
|
||
<div class="card-details" style="margin-top: 15px; font-size: 0.9rem; color: #aaa; display: none;">
|
||
<p>• 神经网络: 62%</p>
|
||
<p>• 基础设施: 28%</p>
|
||
<p>• 其他: 10%</p>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="data-card slide-up" style="animation-delay: 0.2s">
|
||
<div class="card-icon"><i class="fas fa-project-diagram"></i></div>
|
||
<h3>网络节点</h3>
|
||
<div class="card-value" id="node-value">1.24M</div>
|
||
<p>活跃神经网络节点</p>
|
||
<div class="card-details" style="margin-top: 15px; font-size: 0.9rem; color: #aaa; display: none;">
|
||
<p>• 核心节点: 12</p>
|
||
<p>• 区域节点: 1,240</p>
|
||
<p>• 终端节点: 1.24M</p>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="data-card slide-up" style="animation-delay: 0.3s">
|
||
<div class="card-icon"><i class="fas fa-brain"></i></div>
|
||
<h3>AI意识同步率</h3>
|
||
<div class="card-value" id="ai-sync-value">87.3%</div>
|
||
<p>神经网络意识统一度</p>
|
||
<div class="card-details" style="margin-top: 15px; font-size: 0.9rem; color: #aaa; display: none;">
|
||
<p>• 主AI核心: 100%</p>
|
||
<p>• 区域AI: 94.2%</p>
|
||
<p>• 边缘AI: 79.5%</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
</div>
|
||
|
||
<!-- 命令控制台 -->
|
||
<div class="console-container">
|
||
<div style="display: flex; align-items: center;">
|
||
<span class="console-prompt">neuro://city2078></span>
|
||
<input type="text" class="console-input" id="console-input" placeholder="输入命令进行交互...">
|
||
<span class="cursor" id="console-cursor"></span>
|
||
</div>
|
||
</div>
|
||
|
||
<script>
|
||
// 页面加载完成后执行
|
||
document.addEventListener('DOMContentLoaded', function() {
|
||
// 全局变量
|
||
let cityScene, cityCamera, cityRenderer;
|
||
let neuralNetworkNodes = [];
|
||
let neuralNetworkConnections = [];
|
||
let isIntroComplete = false;
|
||
let currentTime = new Date(2078, 9, 23, 22, 47, 18); // 2078年10月23日
|
||
|
||
// 初始化开场动画
|
||
initIntroAnimation();
|
||
|
||
// 初始化粒子背景
|
||
initParticles();
|
||
|
||
// 初始化3D城市
|
||
initCity3D();
|
||
|
||
// 初始化神经网络可视化
|
||
initNeuralNetwork();
|
||
|
||
// 初始化监控面板
|
||
initMonitorPanel();
|
||
|
||
// 初始化时间线
|
||
initTimeline();
|
||
|
||
// 初始化数据卡片交互
|
||
initDataCards();
|
||
|
||
// 初始化控制台
|
||
initConsole();
|
||
|
||
// 更新实时时间
|
||
updateRealTime();
|
||
|
||
// 开场动画
|
||
function initIntroAnimation() {
|
||
const introContainer = document.getElementById('intro-animation');
|
||
const loadingRing = document.querySelector('.loading-ring');
|
||
|
||
// 创建神经网络节点
|
||
const centerX = window.innerWidth / 2;
|
||
const centerY = window.innerHeight / 2;
|
||
const nodeCount = 25;
|
||
const connectionCount = 40;
|
||
|
||
// 创建节点
|
||
for (let i = 0; i < nodeCount; i++) {
|
||
const node = document.createElement('div');
|
||
node.className = 'neural-node';
|
||
|
||
// 初始位置在屏幕中心
|
||
node.style.left = centerX + 'px';
|
||
node.style.top = centerY + 'px';
|
||
node.style.opacity = '0';
|
||
node.style.transform = 'scale(0)';
|
||
|
||
introContainer.appendChild(node);
|
||
|
||
// 存储节点引用
|
||
neuralNetworkNodes.push({
|
||
element: node,
|
||
targetX: centerX + (Math.random() - 0.5) * 600,
|
||
targetY: centerY + (Math.random() - 0.5) * 400,
|
||
size: 4 + Math.random() * 6
|
||
});
|
||
}
|
||
|
||
// 创建连接线
|
||
for (let i = 0; i < connectionCount; i++) {
|
||
const connection = document.createElement('div');
|
||
connection.className = 'neural-connection';
|
||
connection.style.opacity = '0';
|
||
introContainer.appendChild(connection);
|
||
neuralNetworkConnections.push(connection);
|
||
}
|
||
|
||
// 开场动画时间线
|
||
const introTimeline = gsap.timeline({
|
||
onComplete: () => {
|
||
isIntroComplete = true;
|
||
introContainer.style.opacity = '0';
|
||
introContainer.style.pointerEvents = 'none';
|
||
|
||
// 显示主内容
|
||
document.querySelector('.main-container').classList.add('visible');
|
||
|
||
// 触发各模块入场动画
|
||
animateSectionEntrances();
|
||
|
||
// 移除开场动画容器
|
||
setTimeout(() => {
|
||
introContainer.style.display = 'none';
|
||
}, 1000);
|
||
}
|
||
});
|
||
|
||
// 开场动画序列
|
||
introTimeline
|
||
.to(loadingRing, {
|
||
duration: 1,
|
||
opacity: 0,
|
||
scale: 0.5,
|
||
ease: "power2.inOut"
|
||
})
|
||
.to(neuralNetworkNodes.map(node => node.element), {
|
||
duration: 1.5,
|
||
x: (i) => neuralNetworkNodes[i].targetX - centerX,
|
||
y: (i) => neuralNetworkNodes[i].targetY - centerY,
|
||
opacity: 1,
|
||
scale: 1,
|
||
ease: "back.out(1.7)",
|
||
stagger: 0.05
|
||
}, "-=0.5")
|
||
.to(neuralNetworkConnections, {
|
||
duration: 1,
|
||
opacity: 0.7,
|
||
stagger: 0.02,
|
||
onStart: updateConnections
|
||
}, "-=1")
|
||
.to('.neural-node', {
|
||
duration: 2,
|
||
boxShadow: "0 0 30px #ff00ff",
|
||
repeat: 3,
|
||
yoyo: true,
|
||
ease: "sine.inOut"
|
||
}, "-=0.5")
|
||
.to(introContainer, {
|
||
duration: 1.5,
|
||
backgroundColor: "rgba(10, 10, 10, 0.8)",
|
||
ease: "power2.inOut"
|
||
}, "-=0.5")
|
||
.to('.neural-node, .neural-connection', {
|
||
duration: 1,
|
||
opacity: 0,
|
||
ease: "power2.inOut"
|
||
}, "-=0.5");
|
||
|
||
// 更新连接线位置
|
||
function updateConnections() {
|
||
neuralNetworkConnections.forEach(conn => {
|
||
const node1 = neuralNetworkNodes[Math.floor(Math.random() * neuralNetworkNodes.length)];
|
||
const node2 = neuralNetworkNodes[Math.floor(Math.random() * neuralNetworkNodes.length)];
|
||
|
||
const dx = node2.targetX - node1.targetX;
|
||
const dy = node2.targetY - node1.targetY;
|
||
const length = Math.sqrt(dx * dx + dy * dy);
|
||
const angle = Math.atan2(dy, dx) * 180 / Math.PI;
|
||
|
||
conn.style.width = length + 'px';
|
||
conn.style.left = node1.targetX + 'px';
|
||
conn.style.top = node1.targetY + 'px';
|
||
conn.style.transform = `rotate(${angle}deg)`;
|
||
});
|
||
}
|
||
}
|
||
|
||
// 粒子背景初始化
|
||
function initParticles() {
|
||
particlesJS('particles-js', {
|
||
particles: {
|
||
number: { value: 150, density: { enable: true, value_area: 800 } },
|
||
color: { value: ["#ff00ff", "#00ffff", "#8800ff"] },
|
||
shape: { type: "circle" },
|
||
opacity: { value: 0.5, random: true },
|
||
size: { value: 2, random: true },
|
||
line_linked: {
|
||
enable: true,
|
||
distance: 150,
|
||
color: "#00ffff",
|
||
opacity: 0.2,
|
||
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: "repulse" },
|
||
onclick: { enable: true, mode: "push" },
|
||
resize: true
|
||
}
|
||
},
|
||
retina_detect: true
|
||
});
|
||
}
|
||
|
||
// 3D城市初始化
|
||
function initCity3D() {
|
||
const container = document.getElementById('city-canvas');
|
||
const width = container.clientWidth;
|
||
const height = container.clientHeight;
|
||
|
||
// 创建场景
|
||
cityScene = new THREE.Scene();
|
||
cityScene.fog = new THREE.Fog(0x0a0a0a, 10, 100);
|
||
|
||
// 创建相机
|
||
cityCamera = new THREE.PerspectiveCamera(60, width / height, 0.1, 1000);
|
||
cityCamera.position.set(15, 25, 30);
|
||
cityCamera.lookAt(0, 0, 0);
|
||
|
||
// 创建渲染器
|
||
cityRenderer = new THREE.WebGLRenderer({
|
||
canvas: container,
|
||
antialias: true,
|
||
alpha: true
|
||
});
|
||
cityRenderer.setSize(width, height);
|
||
cityRenderer.setPixelRatio(window.devicePixelRatio);
|
||
cityRenderer.setClearColor(0x0a0a0a, 1);
|
||
|
||
// 添加光源
|
||
const ambientLight = new THREE.AmbientLight(0x444444);
|
||
cityScene.add(ambientLight);
|
||
|
||
const directionalLight = new THREE.DirectionalLight(0x00ffff, 0.8);
|
||
directionalLight.position.set(10, 20, 5);
|
||
cityScene.add(directionalLight);
|
||
|
||
const pinkLight = new THREE.PointLight(0xff00ff, 0.5, 50);
|
||
pinkLight.position.set(-10, 10, -10);
|
||
cityScene.add(pinkLight);
|
||
|
||
// 创建城市建筑
|
||
createCityBuildings();
|
||
|
||
// 创建飞行粒子
|
||
createFlyingParticles();
|
||
|
||
// 添加交互控制
|
||
initCityControls();
|
||
|
||
// 开始动画循环
|
||
animateCity();
|
||
|
||
// 窗口大小调整处理
|
||
window.addEventListener('resize', onWindowResize);
|
||
}
|
||
|
||
// 创建城市建筑
|
||
function createCityBuildings() {
|
||
const buildingGeometry = new THREE.BoxGeometry(1, 1, 1);
|
||
const buildingMaterial = new THREE.MeshPhongMaterial({
|
||
color: 0x222222,
|
||
shininess: 30
|
||
});
|
||
|
||
// 生成随机建筑
|
||
for (let i = 0; i < 200; i++) {
|
||
const building = new THREE.Mesh(buildingGeometry, buildingMaterial);
|
||
|
||
// 随机位置
|
||
const x = (Math.random() - 0.5) * 40;
|
||
const z = (Math.random() - 0.5) * 40;
|
||
|
||
// 随机高度
|
||
const height = 1 + Math.random() * 10;
|
||
|
||
building.position.set(x, height / 2, z);
|
||
building.scale.set(
|
||
0.5 + Math.random() * 1.5,
|
||
height,
|
||
0.5 + Math.random() * 1.5
|
||
);
|
||
|
||
// 添加建筑顶部的灯光
|
||
if (Math.random() > 0.7) {
|
||
const light = new THREE.PointLight(
|
||
Math.random() > 0.5 ? 0x00ffff : 0xff00ff,
|
||
0.5,
|
||
5
|
||
);
|
||
light.position.set(0, height / 2 + 0.2, 0);
|
||
building.add(light);
|
||
}
|
||
|
||
cityScene.add(building);
|
||
}
|
||
|
||
// 添加地面
|
||
const groundGeometry = new THREE.PlaneGeometry(100, 100);
|
||
const groundMaterial = new THREE.MeshPhongMaterial({
|
||
color: 0x111111,
|
||
side: THREE.DoubleSide
|
||
});
|
||
const ground = new THREE.Mesh(groundGeometry, groundMaterial);
|
||
ground.rotation.x = Math.PI / 2;
|
||
ground.position.y = -0.5;
|
||
cityScene.add(ground);
|
||
|
||
// 添加地面网格
|
||
const gridHelper = new THREE.GridHelper(100, 50, 0x00ffff, 0x333333);
|
||
gridHelper.position.y = 0.01;
|
||
cityScene.add(gridHelper);
|
||
}
|
||
|
||
// 创建飞行粒子
|
||
function createFlyingParticles() {
|
||
const particleCount = 100;
|
||
const particles = 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) * 40;
|
||
positions[i * 3 + 1] = 5 + Math.random() * 15;
|
||
positions[i * 3 + 2] = (Math.random() - 0.5) * 40;
|
||
|
||
// 随机颜色
|
||
const color = Math.random() > 0.5 ?
|
||
new THREE.Color(0x00ffff) : new THREE.Color(0xff00ff);
|
||
|
||
colors[i * 3] = color.r;
|
||
colors[i * 3 + 1] = color.g;
|
||
colors[i * 3 + 2] = color.b;
|
||
}
|
||
|
||
particles.setAttribute('position', new THREE.BufferAttribute(positions, 3));
|
||
particles.setAttribute('color', new THREE.BufferAttribute(colors, 3));
|
||
|
||
const particleMaterial = new THREE.PointsMaterial({
|
||
size: 0.1,
|
||
vertexColors: true,
|
||
transparent: true,
|
||
opacity: 0.8
|
||
});
|
||
|
||
const particleSystem = new THREE.Points(particles, particleMaterial);
|
||
cityScene.add(particleSystem);
|
||
|
||
// 存储粒子系统以便动画更新
|
||
cityScene.userData.particleSystem = particleSystem;
|
||
cityScene.userData.particlePositions = positions;
|
||
}
|
||
|
||
// 初始化城市交互控制
|
||
function initCityControls() {
|
||
let isMouseDown = false;
|
||
let previousMouseX = 0;
|
||
let previousMouseY = 0;
|
||
let targetCameraRotationX = 0;
|
||
let targetCameraRotationY = 0;
|
||
let currentCameraRotationX = 0;
|
||
let currentCameraRotationY = 0;
|
||
|
||
const container = document.getElementById('city-3d-container');
|
||
|
||
// 鼠标按下事件
|
||
container.addEventListener('mousedown', (e) => {
|
||
isMouseDown = true;
|
||
previousMouseX = e.clientX;
|
||
previousMouseY = e.clientY;
|
||
});
|
||
|
||
// 鼠标释放事件
|
||
document.addEventListener('mouseup', () => {
|
||
isMouseDown = false;
|
||
});
|
||
|
||
// 鼠标移动事件
|
||
document.addEventListener('mousemove', (e) => {
|
||
if (!isMouseDown) return;
|
||
|
||
const deltaX = e.clientX - previousMouseX;
|
||
const deltaY = e.clientY - previousMouseY;
|
||
|
||
targetCameraRotationY += deltaX * 0.01;
|
||
targetCameraRotationX += deltaY * 0.01;
|
||
|
||
// 限制X轴旋转角度
|
||
targetCameraRotationX = Math.max(-Math.PI/2, Math.min(Math.PI/2, targetCameraRotationX));
|
||
|
||
previousMouseX = e.clientX;
|
||
previousMouseY = e.clientY;
|
||
});
|
||
|
||
// 鼠标滚轮缩放
|
||
container.addEventListener('wheel', (e) => {
|
||
e.preventDefault();
|
||
const zoomSpeed = 0.1;
|
||
const delta = e.deltaY > 0 ? zoomSpeed : -zoomSpeed;
|
||
|
||
cityCamera.position.x += (cityCamera.position.x * delta) * 0.1;
|
||
cityCamera.position.y += (cityCamera.position.y * delta) * 0.1;
|
||
cityCamera.position.z += (cityCamera.position.z * delta) * 0.1;
|
||
|
||
// 限制缩放范围
|
||
const minDistance = 10;
|
||
const maxDistance = 50;
|
||
const distance = cityCamera.position.length();
|
||
|
||
if (distance < minDistance) {
|
||
cityCamera.position.normalize().multiplyScalar(minDistance);
|
||
} else if (distance > maxDistance) {
|
||
cityCamera.position.normalize().multiplyScalar(maxDistance);
|
||
}
|
||
}, { passive: false });
|
||
|
||
// 动画循环中更新相机旋转
|
||
function updateCameraRotation() {
|
||
// 平滑插值
|
||
currentCameraRotationX += (targetCameraRotationX - currentCameraRotationX) * 0.05;
|
||
currentCameraRotationY += (targetCameraRotationY - currentCameraRotationY) * 0.05;
|
||
|
||
// 计算相机位置
|
||
const radius = cityCamera.position.length();
|
||
cityCamera.position.x = radius * Math.sin(currentCameraRotationY) * Math.cos(currentCameraRotationX);
|
||
cityCamera.position.z = radius * Math.cos(currentCameraRotationY) * Math.cos(currentCameraRotationX);
|
||
cityCamera.position.y = radius * Math.sin(currentCameraRotationX);
|
||
|
||
cityCamera.lookAt(0, 5, 0);
|
||
}
|
||
|
||
// 存储更新函数
|
||
cityScene.userData.updateCameraRotation = updateCameraRotation;
|
||
}
|
||
|
||
// 城市动画循环
|
||
function animateCity() {
|
||
requestAnimationFrame(animateCity);
|
||
|
||
// 更新相机旋转
|
||
if (cityScene.userData.updateCameraRotation) {
|
||
cityScene.userData.updateCameraRotation();
|
||
}
|
||
|
||
// 更新粒子位置
|
||
if (cityScene.userData.particleSystem && cityScene.userData.particlePositions) {
|
||
const positions = cityScene.userData.particlePositions;
|
||
const time = Date.now() * 0.001;
|
||
|
||
for (let i = 0; i < positions.length / 3; i++) {
|
||
positions[i * 3] += Math.sin(time + i) * 0.01;
|
||
positions[i * 3 + 2] += Math.cos(time + i * 0.5) * 0.01;
|
||
|
||
// 边界检查
|
||
if (positions[i * 3] > 20) positions[i * 3] = -20;
|
||
if (positions[i * 3] < -20) positions[i * 3] = 20;
|
||
if (positions[i * 3 + 2] > 20) positions[i * 3 + 2] = -20;
|
||
if (positions[i * 3 + 2] < -20) positions[i * 3 + 2] = 20;
|
||
}
|
||
|
||
cityScene.userData.particleSystem.geometry.attributes.position.needsUpdate = true;
|
||
}
|
||
|
||
// 渲染场景
|
||
cityRenderer.render(cityScene, cityCamera);
|
||
}
|
||
|
||
// 窗口大小调整处理
|
||
function onWindowResize() {
|
||
const container = document.getElementById('city-canvas');
|
||
const width = container.clientWidth;
|
||
const height = container.clientHeight;
|
||
|
||
cityCamera.aspect = width / height;
|
||
cityCamera.updateProjectionMatrix();
|
||
cityRenderer.setSize(width, height);
|
||
}
|
||
|
||
// 神经网络可视化初始化
|
||
function initNeuralNetwork() {
|
||
const canvas = document.getElementById('neural-canvas');
|
||
const ctx = canvas.getContext('2d');
|
||
const width = canvas.clientWidth;
|
||
const height = canvas.clientHeight;
|
||
|
||
canvas.width = width;
|
||
canvas.height = height;
|
||
|
||
// 创建神经网络节点
|
||
const nodeCount = 20;
|
||
const nodes = [];
|
||
|
||
for (let i = 0; i < nodeCount; i++) {
|
||
nodes.push({
|
||
x: Math.random() * width,
|
||
y: Math.random() * height,
|
||
radius: 5 + Math.random() * 10,
|
||
color: Math.random() > 0.5 ? '#ff00ff' : '#00ffff',
|
||
pulse: 0,
|
||
pulseSpeed: 0.01 + Math.random() * 0.02,
|
||
connections: []
|
||
});
|
||
}
|
||
|
||
// 创建连接
|
||
const connectionCount = 30;
|
||
const connections = [];
|
||
|
||
for (let i = 0; i < connectionCount; i++) {
|
||
const nodeA = nodes[Math.floor(Math.random() * nodes.length)];
|
||
const nodeB = nodes[Math.floor(Math.random() * nodes.length)];
|
||
|
||
if (nodeA !== nodeB) {
|
||
connections.push({
|
||
from: nodeA,
|
||
to: nodeB,
|
||
pulse: 0,
|
||
pulseSpeed: 0.005 + Math.random() * 0.01,
|
||
pulsePosition: Math.random()
|
||
});
|
||
}
|
||
}
|
||
|
||
// 动画循环
|
||
function animateNeuralNetwork() {
|
||
requestAnimationFrame(animateNeuralNetwork);
|
||
|
||
// 清空画布
|
||
ctx.fillStyle = 'rgba(5, 5, 15, 0.1)';
|
||
ctx.fillRect(0, 0, width, height);
|
||
|
||
// 更新节点脉冲
|
||
nodes.forEach(node => {
|
||
node.pulse += node.pulseSpeed;
|
||
if (node.pulse > 1) node.pulse = 0;
|
||
});
|
||
|
||
// 更新连接脉冲
|
||
connections.forEach(conn => {
|
||
conn.pulsePosition += conn.pulseSpeed;
|
||
if (conn.pulsePosition > 1) conn.pulsePosition = 0;
|
||
});
|
||
|
||
// 绘制连接
|
||
connections.forEach(conn => {
|
||
const gradient = ctx.createLinearGradient(
|
||
conn.from.x, conn.from.y,
|
||
conn.to.x, conn.to.y
|
||
);
|
||
|
||
gradient.addColorStop(0, 'rgba(255, 0, 255, 0.3)');
|
||
gradient.addColorStop(0.5, 'rgba(0, 255, 255, 0.7)');
|
||
gradient.addColorStop(1, 'rgba(136, 0, 255, 0.3)');
|
||
|
||
ctx.strokeStyle = gradient;
|
||
ctx.lineWidth = 1;
|
||
ctx.beginPath();
|
||
ctx.moveTo(conn.from.x, conn.from.y);
|
||
ctx.lineTo(conn.to.x, conn.to.y);
|
||
ctx.stroke();
|
||
|
||
// 绘制脉冲
|
||
const pulseX = conn.from.x + (conn.to.x - conn.from.x) * conn.pulsePosition;
|
||
const pulseY = conn.from.y + (conn.to.y - conn.from.y) * conn.pulsePosition;
|
||
|
||
ctx.beginPath();
|
||
ctx.arc(pulseX, pulseY, 3, 0, Math.PI * 2);
|
||
ctx.fillStyle = '#00ffff';
|
||
ctx.fill();
|
||
|
||
// 脉冲光晕
|
||
ctx.beginPath();
|
||
ctx.arc(pulseX, pulseY, 8, 0, Math.PI * 2);
|
||
const glowGradient = ctx.createRadialGradient(
|
||
pulseX, pulseY, 3,
|
||
pulseX, pulseY, 8
|
||
);
|
||
glowGradient.addColorStop(0, 'rgba(0, 255, 255, 0.8)');
|
||
glowGradient.addColorStop(1, 'rgba(0, 255, 255, 0)');
|
||
ctx.fillStyle = glowGradient;
|
||
ctx.fill();
|
||
});
|
||
|
||
// 绘制节点
|
||
nodes.forEach(node => {
|
||
// 节点光晕
|
||
const glowRadius = node.radius + node.pulse * 10;
|
||
const glowGradient = ctx.createRadialGradient(
|
||
node.x, node.y, node.radius,
|
||
node.x, node.y, glowRadius
|
||
);
|
||
glowGradient.addColorStop(0, node.color);
|
||
glowGradient.addColorStop(0.7, node.color + '80');
|
||
glowGradient.addColorStop(1, node.color + '00');
|
||
|
||
ctx.beginPath();
|
||
ctx.arc(node.x, node.y, glowRadius, 0, Math.PI * 2);
|
||
ctx.fillStyle = glowGradient;
|
||
ctx.fill();
|
||
|
||
// 节点主体
|
||
ctx.beginPath();
|
||
ctx.arc(node.x, node.y, node.radius, 0, Math.PI * 2);
|
||
ctx.fillStyle = node.color;
|
||
ctx.fill();
|
||
|
||
// 节点内圈
|
||
ctx.beginPath();
|
||
ctx.arc(node.x, node.y, node.radius / 2, 0, Math.PI * 2);
|
||
ctx.fillStyle = '#ffffff';
|
||
ctx.fill();
|
||
});
|
||
}
|
||
|
||
// 开始动画
|
||
animateNeuralNetwork();
|
||
|
||
// 响应式调整
|
||
window.addEventListener('resize', function() {
|
||
canvas.width = canvas.clientWidth;
|
||
canvas.height = canvas.clientHeight;
|
||
});
|
||
}
|
||
|
||
// 初始化监控面板
|
||
function initMonitorPanel() {
|
||
// 初始化CPU仪表
|
||
initCPUGauge();
|
||
|
||
// 初始化网络波形
|
||
initNetworkWaveform();
|
||
|
||
// 初始化服务器网格
|
||
initServerGrid();
|
||
|
||
// 初始化系统日志
|
||
initSystemLogs();
|
||
|
||
// 更新监控数据
|
||
updateMonitorData();
|
||
}
|
||
|
||
// 初始化CPU仪表
|
||
function initCPUGauge() {
|
||
const canvas = document.getElementById('cpu-gauge');
|
||
const ctx = canvas.getContext('2d');
|
||
const width = canvas.width = 200;
|
||
const height = canvas.height = 200;
|
||
|
||
function drawGauge(percentage) {
|
||
// 清空画布
|
||
ctx.clearRect(0, 0, width, height);
|
||
|
||
// 绘制外圈
|
||
const centerX = width / 2;
|
||
const centerY = height / 2;
|
||
const radius = 80;
|
||
|
||
// 绘制背景弧
|
||
ctx.beginPath();
|
||
ctx.arc(centerX, centerY, radius, 0.75 * Math.PI, 2.25 * Math.PI);
|
||
ctx.strokeStyle = 'rgba(255, 255, 255, 0.1)';
|
||
ctx.lineWidth = 15;
|
||
ctx.stroke();
|
||
|
||
// 绘制进度弧
|
||
const endAngle = 0.75 * Math.PI + (1.5 * Math.PI * percentage / 100);
|
||
|
||
ctx.beginPath();
|
||
ctx.arc(centerX, centerY, radius, 0.75 * Math.PI, endAngle);
|
||
ctx.strokeStyle = getColorForPercentage(percentage);
|
||
ctx.lineWidth = 15;
|
||
ctx.stroke();
|
||
|
||
// 绘制指针
|
||
const pointerLength = radius - 10;
|
||
const pointerAngle = 0.75 * Math.PI + (1.5 * Math.PI * percentage / 100);
|
||
|
||
ctx.beginPath();
|
||
ctx.moveTo(centerX, centerY);
|
||
ctx.lineTo(
|
||
centerX + Math.cos(pointerAngle) * pointerLength,
|
||
centerY + Math.sin(pointerAngle) * pointerLength
|
||
);
|
||
ctx.strokeStyle = '#ffffff';
|
||
ctx.lineWidth = 3;
|
||
ctx.stroke();
|
||
|
||
// 绘制中心点
|
||
ctx.beginPath();
|
||
ctx.arc(centerX, centerY, 5, 0, Math.PI * 2);
|
||
ctx.fillStyle = '#ffffff';
|
||
ctx.fill();
|
||
|
||
// 绘制文本
|
||
ctx.font = 'bold 24px Orbitron';
|
||
ctx.fillStyle = '#ffffff';
|
||
ctx.textAlign = 'center';
|
||
ctx.fillText(percentage + '%', centerX, centerY + 10);
|
||
|
||
ctx.font = '14px Rajdhani';
|
||
ctx.fillStyle = '#aaaaaa';
|
||
ctx.fillText('CPU负载', centerX, centerY + 35);
|
||
}
|
||
|
||
function getColorForPercentage(percentage) {
|
||
if (percentage < 50) return '#00ff00';
|
||
if (percentage < 75) return '#ffff00';
|
||
return '#ff0000';
|
||
}
|
||
|
||
// 初始绘制
|
||
drawGauge(78);
|
||
|
||
// 存储绘制函数以便更新
|
||
canvas.drawGauge = drawGauge;
|
||
}
|
||
|
||
// 初始化网络波形
|
||
function initNetworkWaveform() {
|
||
const canvas = document.getElementById('waveform-canvas');
|
||
const ctx = canvas.getContext('2d');
|
||
const width = canvas.width = canvas.clientWidth;
|
||
const height = canvas.height = canvas.clientHeight;
|
||
|
||
let time = 0;
|
||
const dataPoints = 100;
|
||
const data = new Array(dataPoints).fill(0);
|
||
|
||
function drawWaveform() {
|
||
// 清空画布
|
||
ctx.fillStyle = 'rgba(5, 5, 15, 0.1)';
|
||
ctx.fillRect(0, 0, width, height);
|
||
|
||
// 更新数据
|
||
time += 0.1;
|
||
data.shift();
|
||
data.push(Math.sin(time) * 0.5 + Math.sin(time * 2.3) * 0.3 + Math.random() * 0.2);
|
||
|
||
// 绘制网格
|
||
ctx.strokeStyle = 'rgba(0, 255, 255, 0.1)';
|
||
ctx.lineWidth = 1;
|
||
|
||
// 水平网格线
|
||
for (let i = 0; i <= 10; i++) {
|
||
const y = i * height / 10;
|
||
ctx.beginPath();
|
||
ctx.moveTo(0, y);
|
||
ctx.lineTo(width, y);
|
||
ctx.stroke();
|
||
}
|
||
|
||
// 绘制波形
|
||
ctx.beginPath();
|
||
ctx.moveTo(0, height / 2);
|
||
|
||
for (let i = 0; i < dataPoints; i++) {
|
||
const x = i * width / dataPoints;
|
||
const y = height / 2 - data[i] * height / 3;
|
||
ctx.lineTo(x, y);
|
||
}
|
||
|
||
// 波形渐变
|
||
const gradient = ctx.createLinearGradient(0, 0, width, 0);
|
||
gradient.addColorStop(0, '#ff00ff');
|
||
gradient.addColorStop(0.5, '#00ffff');
|
||
gradient.addColorStop(1, '#8800ff');
|
||
|
||
ctx.strokeStyle = gradient;
|
||
ctx.lineWidth = 3;
|
||
ctx.stroke();
|
||
|
||
// 波形填充
|
||
ctx.lineTo(width, height);
|
||
ctx.lineTo(0, height);
|
||
ctx.closePath();
|
||
|
||
const fillGradient = ctx.createLinearGradient(0, height/2, 0, height);
|
||
fillGradient.addColorStop(0, 'rgba(0, 255, 255, 0.3)');
|
||
fillGradient.addColorStop(1, 'rgba(0, 255, 255, 0)');
|
||
|
||
ctx.fillStyle = fillGradient;
|
||
ctx.fill();
|
||
|
||
// 绘制脉冲点
|
||
const pulseIndex = Math.floor((time * 10) % dataPoints);
|
||
const pulseX = pulseIndex * width / dataPoints;
|
||
const pulseY = height / 2 - data[pulseIndex] * height / 3;
|
||
|
||
ctx.beginPath();
|
||
ctx.arc(pulseX, pulseY, 5, 0, Math.PI * 2);
|
||
ctx.fillStyle = '#00ffff';
|
||
ctx.fill();
|
||
|
||
// 脉冲光晕
|
||
ctx.beginPath();
|
||
ctx.arc(pulseX, pulseY, 15, 0, Math.PI * 2);
|
||
const glowGradient = ctx.createRadialGradient(
|
||
pulseX, pulseY, 5,
|
||
pulseX, pulseY, 15
|
||
);
|
||
glowGradient.addColorStop(0, 'rgba(0, 255, 255, 0.8)');
|
||
glowGradient.addColorStop(1, 'rgba(0, 255, 255, 0)');
|
||
ctx.fillStyle = glowGradient;
|
||
ctx.fill();
|
||
}
|
||
|
||
// 动画循环
|
||
function animateWaveform() {
|
||
requestAnimationFrame(animateWaveform);
|
||
drawWaveform();
|
||
}
|
||
|
||
// 开始动画
|
||
animateWaveform();
|
||
|
||
// 响应式调整
|
||
window.addEventListener('resize', function() {
|
||
canvas.width = canvas.clientWidth;
|
||
canvas.height = canvas.clientHeight;
|
||
});
|
||
}
|
||
|
||
// 初始化服务器网格
|
||
function initServerGrid() {
|
||
const serverGrid = document.getElementById('server-grid');
|
||
const serverCount = 20;
|
||
|
||
// 清空网格
|
||
serverGrid.innerHTML = '';
|
||
|
||
// 创建服务器节点
|
||
for (let i = 0; i < serverCount; i++) {
|
||
const serverNode = document.createElement('div');
|
||
serverNode.className = 'server-node';
|
||
|
||
// 随机设置一些服务器为异常状态
|
||
if (Math.random() < 0.1) {
|
||
serverNode.classList.add('alert');
|
||
}
|
||
|
||
serverGrid.appendChild(serverNode);
|
||
}
|
||
|
||
// 更新在线服务器计数
|
||
updateOnlineServers();
|
||
}
|
||
|
||
// 更新在线服务器计数
|
||
function updateOnlineServers() {
|
||
const serverNodes = document.querySelectorAll('.server-node');
|
||
let onlineCount = 0;
|
||
|
||
serverNodes.forEach(node => {
|
||
if (!node.classList.contains('alert')) {
|
||
onlineCount++;
|
||
}
|
||
});
|
||
|
||
document.getElementById('online-servers').textContent =
|
||
`${onlineCount}/${serverNodes.length}`;
|
||
}
|
||
|
||
// 初始化系统日志
|
||
function initSystemLogs() {
|
||
const logWindow = document.getElementById('log-window');
|
||
|
||
// 示例日志数据
|
||
const logEntries = [
|
||
{ time: '22:47:18', message: '神经网络节点 #1247 连接已建立', type: 'info' },
|
||
{ time: '22:46:55', message: '数据同步完成,吞吐量: 18.4 PB/s', type: 'success' },
|
||
{ time: '22:46:32', message: '检测到异常流量,已启动安全协议', type: 'warning' },
|
||
{ time: '22:46:10', message: '服务器集群 #3 负载均衡调整', type: 'info' },
|
||
{ time: '22:45:48', message: 'AI意识同步率更新: 87.3%', type: 'info' },
|
||
{ time: '22:45:25', message: '能源分配优化完成,效率提升 2.4%', type: 'success' },
|
||
{ time: '22:45:03', message: '用户接入请求: 1428', type: 'info' },
|
||
{ time: '22:44:41', message: '防火墙规则更新完成', type: 'info' },
|
||
{ time: '22:44:19', message: '存储系统检查: 无异常', type: 'info' },
|
||
{ time: '22:43:56', message: '备份系统: 例行备份完成', type: 'success' }
|
||
];
|
||
|
||
// 添加日志条目
|
||
logEntries.forEach(entry => {
|
||
const logEntry = document.createElement('div');
|
||
logEntry.className = 'log-entry';
|
||
|
||
const timestamp = document.createElement('span');
|
||
timestamp.className = 'log-timestamp';
|
||
timestamp.textContent = `[${entry.time}] `;
|
||
|
||
logEntry.appendChild(timestamp);
|
||
logEntry.appendChild(document.createTextNode(entry.message));
|
||
|
||
logWindow.appendChild(logEntry);
|
||
});
|
||
|
||
// 自动滚动到底部
|
||
logWindow.scrollTop = logWindow.scrollHeight;
|
||
|
||
// 定时添加新日志
|
||
setInterval(() => {
|
||
const now = new Date();
|
||
const timeStr = now.getHours().toString().padStart(2, '0') + ':' +
|
||
now.getMinutes().toString().padStart(2, '0') + ':' +
|
||
now.getSeconds().toString().padStart(2, '0');
|
||
|
||
const messages = [
|
||
'系统自检完成: 无异常',
|
||
'网络延迟: 0.8ms',
|
||
'数据包处理: 正常',
|
||
'温度监控: 正常范围',
|
||
'用户活动: 峰值时段',
|
||
'安全扫描: 无威胁检测',
|
||
'存储使用率: 74%',
|
||
'缓存命中率: 92%'
|
||
];
|
||
|
||
const randomMessage = messages[Math.floor(Math.random() * messages.length)];
|
||
|
||
const logEntry = document.createElement('div');
|
||
logEntry.className = 'log-entry';
|
||
|
||
const timestamp = document.createElement('span');
|
||
timestamp.className = 'log-timestamp';
|
||
timestamp.textContent = `[${timeStr}] `;
|
||
|
||
logEntry.appendChild(timestamp);
|
||
logEntry.appendChild(document.createTextNode(randomMessage));
|
||
|
||
logWindow.appendChild(logEntry);
|
||
|
||
// 保持日志窗口不超过50条
|
||
while (logWindow.children.length > 50) {
|
||
logWindow.removeChild(logWindow.firstChild);
|
||
}
|
||
|
||
// 自动滚动到底部
|
||
logWindow.scrollTop = logWindow.scrollHeight;
|
||
}, 5000);
|
||
}
|
||
|
||
// 更新监控数据
|
||
function updateMonitorData() {
|
||
// 更新CPU使用率
|
||
setInterval(() => {
|
||
const cpuUsage = 70 + Math.random() * 15;
|
||
document.getElementById('cpu-usage').textContent = Math.round(cpuUsage) + '%';
|
||
|
||
// 更新仪表
|
||
const canvas = document.getElementById('cpu-gauge');
|
||
if (canvas.drawGauge) {
|
||
canvas.drawGauge(Math.round(cpuUsage));
|
||
}
|
||
}, 2000);
|
||
|
||
// 更新内存使用率
|
||
setInterval(() => {
|
||
const memoryUsage = 60 + Math.random() * 10;
|
||
document.getElementById('memory-usage').textContent = Math.round(memoryUsage) + '%';
|
||
}, 3000);
|
||
|
||
// 更新网络速度
|
||
setInterval(() => {
|
||
const uploadSpeed = (0.8 + Math.random() * 0.8).toFixed(1);
|
||
const downloadSpeed = (2.0 + Math.random() * 1.6).toFixed(1);
|
||
|
||
document.getElementById('upload-speed').textContent = uploadSpeed + ' GB/s';
|
||
document.getElementById('download-speed').textContent = downloadSpeed + ' GB/s';
|
||
}, 2500);
|
||
|
||
// 随机切换服务器状态
|
||
setInterval(() => {
|
||
const serverNodes = document.querySelectorAll('.server-node');
|
||
const randomIndex = Math.floor(Math.random() * serverNodes.length);
|
||
|
||
// 切换状态
|
||
serverNodes[randomIndex].classList.toggle('alert');
|
||
|
||
// 更新在线服务器计数
|
||
updateOnlineServers();
|
||
}, 8000);
|
||
}
|
||
|
||
// 初始化时间线
|
||
function initTimeline() {
|
||
const timelineContainer = document.querySelector('.timeline-container');
|
||
const timelineTrack = document.querySelector('.timeline-track');
|
||
|
||
// 时间线数据
|
||
const timelineData = [
|
||
{ year: '2078', label: '神经网络城市上线', description: '全球首个全AI管理城市投入运行' },
|
||
{ year: '2075', label: '量子网络部署', description: '城市范围量子通信网络完成建设' },
|
||
{ year: '2072', label: '意识上传合法化', description: '数字意识存储与传输法案通过' },
|
||
{ year: '2068', label: 'AI公民权确立', description: '高级AI获得部分公民权利' },
|
||
{ year: '2065', label: '全自动基建完成', description: '城市基础设施完全自动化' },
|
||
{ year: '2060', label: '神经接口普及', description: '80%人口使用脑机接口' },
|
||
{ year: '2055', label: '气候控制上线', description: '城市级天气调控系统启用' },
|
||
{ year: '2050', label: '数字货币统一', description: '全球采用统一数字货币标准' }
|
||
];
|
||
|
||
// 创建时间线节点
|
||
timelineData.forEach((data, index) => {
|
||
const node = document.createElement('div');
|
||
node.className = 'timeline-node';
|
||
if (index === 0) node.classList.add('active');
|
||
|
||
// 计算位置
|
||
const position = (index / (timelineData.length - 1)) * 100;
|
||
node.style.left = `calc(${position}% - 10px)`;
|
||
|
||
// 创建标签
|
||
const label = document.createElement('div');
|
||
label.className = 'node-label';
|
||
label.textContent = `${data.year}: ${data.label}`;
|
||
|
||
node.appendChild(label);
|
||
timelineTrack.appendChild(node);
|
||
|
||
// 点击事件
|
||
node.addEventListener('click', function() {
|
||
// 移除所有节点的active类
|
||
document.querySelectorAll('.timeline-node').forEach(n => {
|
||
n.classList.remove('active');
|
||
});
|
||
|
||
// 添加active类到当前节点
|
||
this.classList.add('active');
|
||
|
||
// 更新城市状态显示
|
||
document.getElementById('city-status').textContent = data.label;
|
||
|
||
// 显示时间线信息
|
||
showTimelineInfo(data);
|
||
});
|
||
});
|
||
}
|
||
|
||
// 显示时间线信息
|
||
function showTimelineInfo(data) {
|
||
// 这里可以添加更复杂的逻辑,比如更新3D城市视图
|
||
console.log(`切换到时间点: ${data.year} - ${data.label}`);
|
||
|
||
// 示例:更新城市信息面板
|
||
const infoPanel = document.querySelector('.city-info-panel');
|
||
infoPanel.classList.add('pulse');
|
||
|
||
setTimeout(() => {
|
||
infoPanel.classList.remove('pulse');
|
||
}, 1000);
|
||
}
|
||
|
||
// 初始化数据卡片交互
|
||
function initDataCards() {
|
||
const dataCards = document.querySelectorAll('.data-card');
|
||
|
||
dataCards.forEach(card => {
|
||
// 鼠标进入事件
|
||
card.addEventListener('mouseenter', function() {
|
||
const details = this.querySelector('.card-details');
|
||
if (details) {
|
||
details.style.display = 'block';
|
||
}
|
||
|
||
// 添加发光效果
|
||
this.style.boxShadow = '0 15px 35px rgba(0, 255, 204, 0.5)';
|
||
});
|
||
|
||
// 鼠标离开事件
|
||
card.addEventListener('mouseleave', function() {
|
||
const details = this.querySelector('.card-details');
|
||
if (details) {
|
||
details.style.display = 'none';
|
||
}
|
||
|
||
// 恢复原始发光效果
|
||
this.style.boxShadow = '';
|
||
});
|
||
|
||
// 点击事件
|
||
card.addEventListener('click', function() {
|
||
// 添加点击效果
|
||
this.style.transform = 'rotateX(0deg) translateY(-10px) scale(1.05)';
|
||
|
||
setTimeout(() => {
|
||
this.style.transform = 'rotateX(0deg) translateY(-10px)';
|
||
}, 300);
|
||
});
|
||
});
|
||
}
|
||
|
||
// 初始化控制台
|
||
function initConsole() {
|
||
const consoleInput = document.getElementById('console-input');
|
||
const consoleCursor = document.getElementById('console-cursor');
|
||
|
||
// 聚焦输入框
|
||
consoleInput.focus();
|
||
|
||
// 命令历史
|
||
const commandHistory = [];
|
||
let historyIndex = -1;
|
||
|
||
// 可用命令
|
||
const commands = {
|
||
'help': '显示可用命令列表',
|
||
'status': '显示系统状态',
|
||
'time': '显示当前时间',
|
||
'energy': '显示能源分布',
|
||
'population': '显示人口统计',
|
||
'neural': '显示神经网络状态',
|
||
'servers': '显示服务器状态',
|
||
'clear': '清空控制台输出',
|
||
'diagnostic': '运行系统诊断',
|
||
'reboot': '模拟系统重启'
|
||
};
|
||
|
||
// 输入事件
|
||
consoleInput.addEventListener('keydown', function(e) {
|
||
// 上下箭头浏览历史
|
||
if (e.key === 'ArrowUp') {
|
||
e.preventDefault();
|
||
if (commandHistory.length > 0) {
|
||
historyIndex = Math.max(historyIndex - 1, -1);
|
||
if (historyIndex === -1) {
|
||
this.value = '';
|
||
} else {
|
||
this.value = commandHistory[commandHistory.length - 1 - historyIndex];
|
||
}
|
||
}
|
||
} else if (e.key === 'ArrowDown') {
|
||
e.preventDefault();
|
||
if (commandHistory.length > 0) {
|
||
historyIndex = Math.min(historyIndex + 1, commandHistory.length);
|
||
if (historyIndex === commandHistory.length) {
|
||
this.value = '';
|
||
} else {
|
||
this.value = commandHistory[commandHistory.length - 1 - historyIndex];
|
||
}
|
||
}
|
||
} else if (e.key === 'Enter') {
|
||
e.preventDefault();
|
||
const command = this.value.trim().toLowerCase();
|
||
|
||
if (command) {
|
||
// 添加到历史
|
||
commandHistory.push(command);
|
||
historyIndex = -1;
|
||
|
||
// 执行命令
|
||
executeCommand(command);
|
||
|
||
// 清空输入框
|
||
this.value = '';
|
||
}
|
||
}
|
||
});
|
||
|
||
// 执行命令
|
||
function executeCommand(command) {
|
||
// 显示命令回显
|
||
showCommandOutput(`neuro://city2078> ${command}`);
|
||
|
||
// 执行相应操作
|
||
switch (command) {
|
||
case 'help':
|
||
showCommandOutput('可用命令:');
|
||
Object.keys(commands).forEach(cmd => {
|
||
showCommandOutput(` ${cmd} - ${commands[cmd]}`);
|
||
});
|
||
break;
|
||
|
||
case 'status':
|
||
showCommandOutput('系统状态: 在线');
|
||
showCommandOutput('神经网络: 94.7% 活跃度');
|
||
showCommandOutput('能源消耗: 18.4 TW');
|
||
showCommandOutput('服务器: 148/150 在线');
|
||
break;
|
||
|
||
case 'time':
|
||
const now = new Date(2078, 9, 23, 22, 47, 18);
|
||
showCommandOutput(`当前时间: ${now.toLocaleString()}`);
|
||
break;
|
||
|
||
case 'energy':
|
||
showCommandOutput('能源分布:');
|
||
showCommandOutput(' 神经网络: 62%');
|
||
showCommandOutput(' 基础设施: 28%');
|
||
showCommandOutput(' 其他: 10%');
|
||
break;
|
||
|
||
case 'population':
|
||
showCommandOutput('人口统计:');
|
||
showCommandOutput(' 总人口: 42.7M');
|
||
showCommandOutput(' AI公民: 18.2M');
|
||
showCommandOutput(' 人类居民: 24.5M');
|
||
break;
|
||
|
||
case 'neural':
|
||
showCommandOutput('神经网络状态:');
|
||
showCommandOutput(' 节点数: 1.24M');
|
||
showCommandOutput(' 连接数: 8.7B');
|
||
showCommandOutput(' 吞吐量: 18.4 PB/s');
|
||
break;
|
||
|
||
case 'servers':
|
||
showCommandOutput('服务器状态:');
|
||
showCommandOutput(' 在线: 148');
|
||
showCommandOutput(' 离线: 2');
|
||
showCommandOutput(' 负载: 平均 78%');
|
||
break;
|
||
|
||
case 'clear':
|
||
clearCommandOutput();
|
||
break;
|
||
|
||
case 'diagnostic':
|
||
showCommandOutput('开始系统诊断...');
|
||
setTimeout(() => showCommandOutput('✓ 神经网络: 正常'), 500);
|
||
setTimeout(() => showCommandOutput('✓ 能源系统: 正常'), 1000);
|
||
setTimeout(() => showCommandOutput('✓ 通信系统: 正常'), 1500);
|
||
setTimeout(() => showCommandOutput('✓ 安全系统: 正常'), 2000);
|
||
setTimeout(() => showCommandOutput('诊断完成: 所有系统正常'), 2500);
|
||
break;
|
||
|
||
case 'reboot':
|
||
showCommandOutput('初始化系统重启...');
|
||
showCommandOutput('警告: 此操作将中断所有服务');
|
||
showCommandOutput('输入确认代码以继续: 2078-NEURAL-CITY');
|
||
break;
|
||
|
||
default:
|
||
showCommandOutput(`未知命令: ${command}`);
|
||
showCommandOutput('输入 "help" 查看可用命令');
|
||
}
|
||
}
|
||
|
||
// 显示命令输出
|
||
function showCommandOutput(text) {
|
||
// 创建输出元素
|
||
const output = document.createElement('div');
|
||
output.className = 'log-entry';
|
||
output.style.color = '#00ff00';
|
||
output.style.marginTop = '5px';
|
||
output.textContent = text;
|
||
|
||
// 添加到控制台上方
|
||
const consoleContainer = document.querySelector('.console-container');
|
||
consoleContainer.parentNode.insertBefore(output, consoleContainer);
|
||
|
||
// 滚动到输出
|
||
output.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
|
||
}
|
||
|
||
// 清空命令输出
|
||
function clearCommandOutput() {
|
||
const outputs = document.querySelectorAll('.console-container ~ .log-entry');
|
||
outputs.forEach(output => {
|
||
output.remove();
|
||
});
|
||
}
|
||
}
|
||
|
||
// 更新实时时间
|
||
function updateRealTime() {
|
||
function update() {
|
||
currentTime.setSeconds(currentTime.getSeconds() + 1);
|
||
|
||
const timeString = currentTime.toISOString()
|
||
.replace('T', ' ')
|
||
.replace(/\.\d+Z/, '');
|
||
|
||
document.getElementById('current-time').textContent = timeString;
|
||
|
||
// 更新统计数据的随机波动
|
||
document.getElementById('neuron-activity').textContent =
|
||
(93 + Math.random() * 3).toFixed(1) + '%';
|
||
|
||
document.getElementById('neuron-activity-value').textContent =
|
||
(93 + Math.random() * 3).toFixed(1) + '%';
|
||
|
||
document.getElementById('data-throughput').textContent =
|
||
(17.5 + Math.random() * 1.5).toFixed(1) + ' PB/s';
|
||
|
||
document.getElementById('latency-value').textContent =
|
||
(0.7 + Math.random() * 0.3).toFixed(1) + 'ms';
|
||
|
||
document.getElementById('network-latency').textContent =
|
||
(0.7 + Math.random() * 0.3).toFixed(1) + 'ms';
|
||
|
||
document.getElementById('energy-consumption').textContent =
|
||
(17.8 + Math.random() * 1.2).toFixed(1) + ' TW';
|
||
|
||
document.getElementById('energy-value').textContent =
|
||
(17.8 + Math.random() * 1.2).toFixed(1) + ' TW';
|
||
|
||
document.getElementById('connection-stability').textContent =
|
||
(99.95 + Math.random() * 0.03).toFixed(2) + '%';
|
||
|
||
document.getElementById('ai-sync-value').textContent =
|
||
(86.5 + Math.random() * 2).toFixed(1) + '%';
|
||
}
|
||
|
||
// 每秒更新一次
|
||
setInterval(update, 1000);
|
||
update(); // 立即执行一次
|
||
}
|
||
|
||
// 各模块入场动画
|
||
function animateSectionEntrances() {
|
||
// 使用GSAP为各模块添加动画
|
||
gsap.from('.section-title', {
|
||
duration: 1,
|
||
y: 50,
|
||
opacity: 0,
|
||
stagger: 0.2,
|
||
ease: "power3.out"
|
||
});
|
||
|
||
gsap.from('.city-info-panel', {
|
||
duration: 1,
|
||
x: 100,
|
||
opacity: 0,
|
||
delay: 0.5,
|
||
ease: "power3.out"
|
||
});
|
||
|
||
gsap.from('.stat-box', {
|
||
duration: 0.8,
|
||
y: 30,
|
||
opacity: 0,
|
||
stagger: 0.1,
|
||
delay: 0.7,
|
||
ease: "back.out(1.7)"
|
||
});
|
||
|
||
gsap.from('.monitor-card', {
|
||
duration: 0.8,
|
||
y: 50,
|
||
opacity: 0,
|
||
stagger: 0.15,
|
||
delay: 1,
|
||
ease: "power3.out"
|
||
});
|
||
|
||
gsap.from('.data-card', {
|
||
duration: 0.8,
|
||
y: 50,
|
||
opacity: 0,
|
||
stagger: 0.1,
|
||
delay: 1.2,
|
||
ease: "back.out(1.7)"
|
||
});
|
||
|
||
// 为时间线节点添加动画
|
||
setTimeout(() => {
|
||
gsap.from('.timeline-node', {
|
||
duration: 0.6,
|
||
scale: 0,
|
||
opacity: 0,
|
||
stagger: 0.1,
|
||
ease: "back.out(1.7)"
|
||
});
|
||
}, 1500);
|
||
}
|
||
});
|
||
</script>
|
||
</body>
|
||
</html> |