1389 lines
58 KiB
HTML
1389 lines
58 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>Cyberpunk 2078 - Neural Network City</title>
|
|
<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;500;700&display=swap" rel="stylesheet">
|
|
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css" rel="stylesheet">
|
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
|
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.2/gsap.min.js"></script>
|
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/7.8.5/d3.min.js"></script>
|
|
<script src="https://cdn.bootcdn.net/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;
|
|
--neon-blue: #00ffff;
|
|
--neon-purple: #8800ff;
|
|
--dark-bg: #0a0a0a;
|
|
--darker-bg: #050508;
|
|
--text-primary: #e0e0e0;
|
|
--text-secondary: #888;
|
|
--glow-pink: 0 0 10px #ff00ff, 0 0 20px #ff00ff, 0 0 40px #ff00ff;
|
|
--glow-blue: 0 0 10px #00ffff, 0 0 20px #00ffff, 0 0 40px #00ffff;
|
|
--font-title: 'Orbitron', sans-serif;
|
|
--font-body: 'Rajdhani', sans-serif;
|
|
}
|
|
* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
html { scroll-behavior: smooth; }
|
|
body {
|
|
font-family: var(--font-body);
|
|
background: var(--dark-bg);
|
|
color: var(--text-primary);
|
|
overflow-x: hidden;
|
|
min-height: 100vh;
|
|
}
|
|
#intro-overlay {
|
|
position: fixed;
|
|
top: 0; left: 0; width: 100%; height: 100%;
|
|
background: #000;
|
|
z-index: 10000;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
}
|
|
#intro-canvas { position: absolute; top: 0; left: 0; }
|
|
.intro-text {
|
|
font-family: var(--font-title);
|
|
font-size: 3rem;
|
|
font-weight: 900;
|
|
color: var(--neon-blue);
|
|
text-shadow: var(--glow-blue);
|
|
z-index: 1;
|
|
opacity: 0;
|
|
letter-spacing: 0.5em;
|
|
}
|
|
#particles-bg {
|
|
position: fixed;
|
|
top: 0; left: 0; width: 100%; height: 100%;
|
|
z-index: -1;
|
|
opacity: 0.6;
|
|
}
|
|
.main-container { position: relative; z-index: 1; }
|
|
section {
|
|
min-height: 100vh;
|
|
padding: 60px 40px;
|
|
position: relative;
|
|
overflow: hidden;
|
|
}
|
|
.section-title {
|
|
font-family: var(--font-title);
|
|
font-size: 2.5rem;
|
|
font-weight: 700;
|
|
color: var(--neon-blue);
|
|
text-shadow: var(--glow-blue);
|
|
margin-bottom: 40px;
|
|
position: relative;
|
|
display: inline-block;
|
|
}
|
|
.section-title::after {
|
|
content: '';
|
|
position: absolute;
|
|
bottom: -10px; left: 0;
|
|
width: 100px; height: 3px;
|
|
background: linear-gradient(90deg, var(--neon-pink), var(--neon-blue));
|
|
}
|
|
@keyframes glitch {
|
|
0%, 90%, 100% { transform: translate(0); }
|
|
92% { transform: translate(-2px, 1px); }
|
|
94% { transform: translate(2px, -1px); }
|
|
96% { transform: translate(-1px, 2px); }
|
|
98% { transform: translate(1px, -2px); }
|
|
}
|
|
.glitch { animation: glitch 0.3s infinite; }
|
|
.glow-card {
|
|
background: rgba(26, 26, 46, 0.8);
|
|
border: 1px solid rgba(0, 255, 255, 0.3);
|
|
border-radius: 4px;
|
|
padding: 30px;
|
|
backdrop-filter: blur(10px);
|
|
position: relative;
|
|
overflow: hidden;
|
|
}
|
|
.glow-card::before {
|
|
content: '';
|
|
position: absolute;
|
|
top: 0; left: -100%;
|
|
width: 100%; height: 100%;
|
|
background: linear-gradient(90deg, transparent, rgba(0, 255, 255, 0.1), transparent);
|
|
transition: left 0.5s;
|
|
}
|
|
.glow-card:hover::before { left: 100%; }
|
|
.glow-card:hover {
|
|
border-color: var(--neon-blue);
|
|
box-shadow: var(--glow-blue);
|
|
}
|
|
#city-section { position: relative; }
|
|
#city-canvas {
|
|
position: absolute;
|
|
top: 0; left: 0; width: 100%; height: 100%;
|
|
}
|
|
.city-overlay {
|
|
position: absolute;
|
|
top: 20px; right: 20px;
|
|
background: rgba(10, 10, 10, 0.9);
|
|
border: 1px solid var(--neon-purple);
|
|
padding: 20px;
|
|
border-radius: 4px;
|
|
z-index: 100;
|
|
}
|
|
.city-time {
|
|
font-family: var(--font-title);
|
|
font-size: 1.8rem;
|
|
color: var(--neon-pink);
|
|
text-shadow: var(--glow-pink);
|
|
}
|
|
.city-date {
|
|
font-size: 0.9rem;
|
|
color: var(--text-secondary);
|
|
margin-top: 5px;
|
|
}
|
|
.city-stats { margin-top: 15px; font-size: 0.85rem; }
|
|
.stat-item {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
margin: 8px 0;
|
|
padding: 5px 0;
|
|
border-bottom: 1px solid rgba(255, 0, 255, 0.2);
|
|
}
|
|
.stat-value { color: var(--neon-blue); font-weight: 700; }
|
|
#neural-section {
|
|
background: linear-gradient(180deg, var(--dark-bg) 0%, var(--darker-bg) 100%);
|
|
}
|
|
.neural-container {
|
|
position: relative;
|
|
width: 100%;
|
|
height: 500px;
|
|
background: rgba(0, 0, 0, 0.5);
|
|
border: 1px solid rgba(136, 0, 255, 0.3);
|
|
border-radius: 8px;
|
|
overflow: hidden;
|
|
}
|
|
#neural-canvas { width: 100%; height: 100%; }
|
|
.neural-stats-ring {
|
|
position: absolute;
|
|
top: 50%; left: 50%;
|
|
transform: translate(-50%, -50%);
|
|
width: 600px; height: 600px;
|
|
pointer-events: none;
|
|
}
|
|
.stat-ring-item {
|
|
position: absolute;
|
|
font-family: var(--font-title);
|
|
font-size: 0.75rem;
|
|
color: var(--neon-blue);
|
|
text-shadow: 0 0 5px var(--neon-blue);
|
|
}
|
|
#monitor-section { background: var(--darker-bg); }
|
|
.monitor-grid {
|
|
display: grid;
|
|
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
|
|
gap: 30px;
|
|
margin-top: 40px;
|
|
}
|
|
.monitor-panel {
|
|
background: rgba(10, 10, 15, 0.9);
|
|
border: 1px solid rgba(0, 255, 255, 0.2);
|
|
padding: 25px;
|
|
position: relative;
|
|
}
|
|
.panel-title {
|
|
font-family: var(--font-title);
|
|
font-size: 1rem;
|
|
color: var(--neon-pink);
|
|
margin-bottom: 20px;
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 10px;
|
|
}
|
|
.panel-title i { color: var(--neon-blue); }
|
|
.gauge-container {
|
|
display: flex;
|
|
justify-content: space-around;
|
|
flex-wrap: wrap;
|
|
gap: 20px;
|
|
}
|
|
.gauge { width: 120px; height: 70px; position: relative; }
|
|
.gauge-bg { fill: none; stroke: rgba(255, 255, 255, 0.1); stroke-width: 10; }
|
|
.gauge-fill {
|
|
fill: none;
|
|
stroke: url(#gaugeGradient);
|
|
stroke-width: 10;
|
|
stroke-linecap: round;
|
|
transition: stroke-dashoffset 0.5s ease;
|
|
}
|
|
.gauge-text {
|
|
position: absolute;
|
|
bottom: 0; left: 50%;
|
|
transform: translateX(-50%);
|
|
font-family: var(--font-title);
|
|
font-size: 1.2rem;
|
|
color: var(--neon-blue);
|
|
}
|
|
.oscilloscope {
|
|
width: 100%; height: 150px;
|
|
background: rgba(0, 20, 10, 0.8);
|
|
border: 1px solid rgba(0, 255, 255, 0.2);
|
|
position: relative;
|
|
overflow: hidden;
|
|
}
|
|
.oscilloscope-grid {
|
|
position: absolute;
|
|
top: 0; left: 0; width: 100%; height: 100%;
|
|
background-image:
|
|
linear-gradient(rgba(0, 255, 255, 0.1) 1px, transparent 1px),
|
|
linear-gradient(90deg, rgba(0, 255, 255, 0.1) 1px, transparent 1px);
|
|
background-size: 20px 20px;
|
|
}
|
|
#waveform-canvas { position: absolute; top: 0; left: 0; }
|
|
.server-grid {
|
|
display: grid;
|
|
grid-template-columns: repeat(4, 1fr);
|
|
gap: 10px;
|
|
}
|
|
.server-node {
|
|
aspect-ratio: 1;
|
|
background: rgba(0, 50, 30, 0.5);
|
|
border: 1px solid rgba(0, 255, 100, 0.3);
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
font-size: 0.7rem;
|
|
color: var(--text-secondary);
|
|
transition: all 0.3s ease;
|
|
}
|
|
.server-node.active {
|
|
background: rgba(0, 255, 100, 0.2);
|
|
border-color: #00ff64;
|
|
box-shadow: 0 0 10px rgba(0, 255, 100, 0.5);
|
|
}
|
|
.server-node.warning {
|
|
background: rgba(255, 200, 0, 0.2);
|
|
border-color: #ffc800;
|
|
}
|
|
.server-node.error {
|
|
background: rgba(255, 0, 50, 0.2);
|
|
border-color: #ff0032;
|
|
animation: pulse 1s infinite;
|
|
}
|
|
@keyframes pulse {
|
|
0%, 100% { opacity: 1; }
|
|
50% { opacity: 0.5; }
|
|
}
|
|
.log-window {
|
|
height: 200px;
|
|
background: rgba(0, 10, 0, 0.9);
|
|
border: 1px solid rgba(0, 255, 100, 0.3);
|
|
padding: 15px;
|
|
font-family: 'Courier New', monospace;
|
|
font-size: 0.75rem;
|
|
overflow-y: auto;
|
|
position: relative;
|
|
}
|
|
.log-window::before {
|
|
content: '';
|
|
position: absolute;
|
|
top: 0; left: 0; width: 100%; height: 3px;
|
|
background: linear-gradient(90deg, transparent, rgba(0, 255, 255, 0.5), transparent);
|
|
animation: scanline 3s linear infinite;
|
|
}
|
|
@keyframes scanline {
|
|
0% { transform: translateY(0); opacity: 1; }
|
|
100% { transform: translateY(200px); opacity: 0; }
|
|
}
|
|
.log-entry { margin: 5px 0; padding: 3px 0; border-bottom: 1px solid rgba(0, 255, 100, 0.1); }
|
|
.log-time { color: var(--neon-purple); margin-right: 10px; }
|
|
.log-info { color: var(--neon-blue); }
|
|
.log-success { color: #00ff64; }
|
|
.log-warning { color: #ffc800; }
|
|
.log-error { color: #ff0032; }
|
|
#timeline-section { padding: 100px 40px; overflow-x: auto; }
|
|
.timeline-container {
|
|
display: flex;
|
|
align-items: center;
|
|
min-width: max-content;
|
|
padding: 40px 0;
|
|
}
|
|
.timeline-line {
|
|
position: absolute;
|
|
top: 50%; left: 0; right: 0;
|
|
height: 2px;
|
|
background: linear-gradient(90deg, var(--neon-pink), var(--neon-blue), var(--neon-purple));
|
|
z-index: 0;
|
|
}
|
|
.timeline-node {
|
|
position: relative;
|
|
z-index: 1;
|
|
margin: 0 60px;
|
|
cursor: pointer;
|
|
}
|
|
.node-content {
|
|
width: 80px; height: 80px;
|
|
background: rgba(10, 10, 20, 0.9);
|
|
border: 2px solid var(--neon-blue);
|
|
border-radius: 50%;
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
justify-content: center;
|
|
transition: all 0.3s ease;
|
|
position: relative;
|
|
}
|
|
.node-content::before {
|
|
content: '';
|
|
position: absolute;
|
|
width: 100%; height: 100%;
|
|
border-radius: 50%;
|
|
border: 2px solid var(--neon-pink);
|
|
opacity: 0;
|
|
transform: scale(1.2);
|
|
transition: all 0.3s ease;
|
|
}
|
|
.timeline-node:hover .node-content::before {
|
|
opacity: 0.5;
|
|
transform: scale(1.3);
|
|
}
|
|
.timeline-node.active .node-content {
|
|
border-color: var(--neon-pink);
|
|
box-shadow: var(--glow-pink);
|
|
}
|
|
.node-year {
|
|
font-family: var(--font-title);
|
|
font-size: 1.2rem;
|
|
font-weight: 700;
|
|
color: var(--neon-blue);
|
|
}
|
|
.node-label {
|
|
font-size: 0.7rem;
|
|
color: var(--text-secondary);
|
|
margin-top: 5px;
|
|
}
|
|
.node-detail {
|
|
position: absolute;
|
|
bottom: -60px; left: 50%;
|
|
transform: translateX(-50%);
|
|
width: 200px;
|
|
background: rgba(10, 10, 20, 0.95);
|
|
border: 1px solid var(--neon-purple);
|
|
padding: 15px;
|
|
border-radius: 4px;
|
|
opacity: 0;
|
|
visibility: hidden;
|
|
transition: all 0.3s ease;
|
|
text-align: center;
|
|
font-size: 0.85rem;
|
|
}
|
|
.timeline-node:hover .node-detail {
|
|
opacity: 1;
|
|
visibility: visible;
|
|
bottom: -80px;
|
|
}
|
|
#cards-section {
|
|
background: linear-gradient(180deg, var(--darker-bg) 0%, var(--dark-bg) 100%);
|
|
}
|
|
.cards-grid {
|
|
display: grid;
|
|
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
|
|
gap: 40px;
|
|
margin-top: 50px;
|
|
perspective: 1000px;
|
|
}
|
|
.holo-card {
|
|
background: linear-gradient(135deg, rgba(136, 0, 255, 0.1) 0%, rgba(0, 255, 255, 0.05) 100%);
|
|
border: 1px solid rgba(136, 0, 255, 0.3);
|
|
padding: 30px;
|
|
transform: rotateX(45deg) rotateY(0deg) rotateZ(0deg);
|
|
transform-style: preserve-3d;
|
|
transition: all 0.4s ease;
|
|
position: relative;
|
|
overflow: hidden;
|
|
}
|
|
.holo-card::before {
|
|
content: '';
|
|
position: absolute;
|
|
top: -50%; left: -50%; width: 200%; height: 200%;
|
|
background: linear-gradient(45deg, transparent 40%, rgba(0, 255, 255, 0.1) 50%, transparent 60%);
|
|
transform: rotate(45deg);
|
|
transition: all 0.5s ease;
|
|
}
|
|
.holo-card:hover {
|
|
transform: rotateX(30deg) translateY(-10px);
|
|
border-color: var(--neon-blue);
|
|
box-shadow: var(--glow-blue);
|
|
}
|
|
.holo-card:hover::before { left: 100%; }
|
|
.card-icon {
|
|
font-size: 2.5rem;
|
|
color: var(--neon-pink);
|
|
margin-bottom: 20px;
|
|
text-shadow: var(--glow-pink);
|
|
}
|
|
.card-value {
|
|
font-family: var(--font-title);
|
|
font-size: 2.5rem;
|
|
font-weight: 700;
|
|
color: var(--neon-blue);
|
|
text-shadow: var(--glow-blue);
|
|
margin: 10px 0;
|
|
}
|
|
.card-label {
|
|
font-size: 0.9rem;
|
|
color: var(--text-secondary);
|
|
text-transform: uppercase;
|
|
letter-spacing: 2px;
|
|
}
|
|
.card-hidden {
|
|
margin-top: 20px;
|
|
padding-top: 20px;
|
|
border-top: 1px solid rgba(0, 255, 255, 0.2);
|
|
opacity: 0;
|
|
transform: translateY(10px);
|
|
transition: all 0.3s ease;
|
|
}
|
|
.holo-card:hover .card-hidden {
|
|
opacity: 1;
|
|
transform: translateY(0);
|
|
}
|
|
.hidden-stat {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
margin: 8px 0;
|
|
font-size: 0.85rem;
|
|
}
|
|
#console-section {
|
|
position: fixed;
|
|
bottom: 0; left: 0; right: 0;
|
|
padding: 0;
|
|
min-height: auto;
|
|
background: linear-gradient(180deg, transparent, rgba(10, 10, 10, 0.95));
|
|
z-index: 1000;
|
|
}
|
|
.console-container {
|
|
display: flex;
|
|
align-items: center;
|
|
background: rgba(10, 10, 15, 0.95);
|
|
border-top: 1px solid rgba(0, 255, 255, 0.3);
|
|
padding: 15px 30px;
|
|
}
|
|
.console-label {
|
|
font-family: var(--font-title);
|
|
color: var(--neon-pink);
|
|
margin-right: 15px;
|
|
font-size: 0.9rem;
|
|
}
|
|
.console-input-wrapper {
|
|
flex: 1;
|
|
position: relative;
|
|
display: flex;
|
|
align-items: center;
|
|
}
|
|
.console-input {
|
|
width: 100%;
|
|
background: transparent;
|
|
border: none;
|
|
outline: none;
|
|
color: var(--neon-blue);
|
|
font-family: 'Courier New', monospace;
|
|
font-size: 1rem;
|
|
padding: 10px 15px;
|
|
}
|
|
.console-input::placeholder { color: rgba(0, 255, 255, 0.3); }
|
|
.cursor-blink {
|
|
position: absolute;
|
|
right: 15px;
|
|
width: 10px; height: 20px;
|
|
background: var(--neon-blue);
|
|
animation: blink 1s infinite;
|
|
}
|
|
@keyframes blink {
|
|
0%, 50% { opacity: 1; }
|
|
51%, 100% { opacity: 0; }
|
|
}
|
|
.console-actions { display: flex; gap: 10px; margin-left: 20px; }
|
|
.console-btn {
|
|
background: rgba(0, 255, 255, 0.1);
|
|
border: 1px solid var(--neon-blue);
|
|
color: var(--neon-blue);
|
|
padding: 8px 16px;
|
|
border-radius: 4px;
|
|
cursor: pointer;
|
|
transition: all 0.3s ease;
|
|
font-family: var(--font-body);
|
|
}
|
|
.console-btn:hover {
|
|
background: var(--neon-blue);
|
|
color: var(--dark-bg);
|
|
box-shadow: var(--glow-blue);
|
|
}
|
|
.feedback-overlay {
|
|
position: fixed;
|
|
top: 0; left: 0; width: 100%; height: 100%;
|
|
background: rgba(0, 0, 0, 0.8);
|
|
z-index: 2000;
|
|
display: none;
|
|
align-items: center;
|
|
justify-content: center;
|
|
}
|
|
.feedback-overlay.active { display: flex; }
|
|
.feedback-content {
|
|
background: rgba(10, 10, 20, 0.95);
|
|
border: 2px solid var(--neon-blue);
|
|
padding: 40px;
|
|
text-align: center;
|
|
position: relative;
|
|
overflow: hidden;
|
|
}
|
|
.feedback-content::before {
|
|
content: '';
|
|
position: absolute;
|
|
top: 0; left: -100%; width: 100%; height: 100%;
|
|
background: linear-gradient(90deg, transparent, rgba(0, 255, 255, 0.2), transparent);
|
|
animation: sweep 0.5s ease forwards;
|
|
}
|
|
@keyframes sweep {
|
|
0% { left: -100%; }
|
|
100% { left: 100%; }
|
|
}
|
|
.feedback-icon {
|
|
font-size: 3rem;
|
|
color: var(--neon-blue);
|
|
margin-bottom: 20px;
|
|
}
|
|
.feedback-text {
|
|
font-family: var(--font-title);
|
|
font-size: 1.2rem;
|
|
color: var(--neon-blue);
|
|
}
|
|
::-webkit-scrollbar { width: 8px; height: 8px; }
|
|
::-webkit-scrollbar-track { background: var(--darker-bg); }
|
|
::-webkit-scrollbar-thumb { background: var(--neon-purple); border-radius: 4px; }
|
|
::-webkit-scrollbar-thumb:hover { background: var(--neon-blue); }
|
|
@media (max-width: 768px) {
|
|
section { padding: 40px 20px; }
|
|
.section-title { font-size: 1.8rem; }
|
|
.neural-container { height: 350px; }
|
|
.monitor-grid { grid-template-columns: 1fr; }
|
|
.cards-grid { grid-template-columns: 1fr; }
|
|
.console-container { padding: 10px 15px; }
|
|
.console-label { display: none; }
|
|
}
|
|
.loading-spinner {
|
|
width: 50px; height: 50px;
|
|
border: 3px solid rgba(0, 255, 255, 0.2);
|
|
border-top-color: var(--neon-blue);
|
|
border-radius: 50%;
|
|
animation: spin 1s linear infinite;
|
|
}
|
|
@keyframes spin { to { transform: rotate(360deg); } }
|
|
.loading-overlay {
|
|
position: fixed;
|
|
top: 0; left: 0; width: 100%; height: 100%;
|
|
background: var(--dark-bg);
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
justify-content: center;
|
|
z-index: 9999;
|
|
transition: opacity 0.5s ease;
|
|
}
|
|
.loading-overlay.hidden { opacity: 0; pointer-events: none; }
|
|
.loading-text {
|
|
font-family: var(--font-title);
|
|
color: var(--neon-blue);
|
|
margin-top: 20px;
|
|
letter-spacing: 3px;
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div class="loading-overlay" id="loading">
|
|
<div class="loading-spinner"></div>
|
|
<div class="loading-text">SYSTEM INITIALIZING...</div>
|
|
</div>
|
|
<div id="intro-overlay">
|
|
<canvas id="intro-canvas"></canvas>
|
|
<div class="intro-text">NEURAL LINK</div>
|
|
</div>
|
|
<div id="particles-bg"></div>
|
|
<div class="feedback-overlay" id="feedback">
|
|
<div class="feedback-content">
|
|
<i class="fas fa-check-circle feedback-icon"></i>
|
|
<div class="feedback-text" id="feedback-text">命令执行成功</div>
|
|
</div>
|
|
</div>
|
|
<div class="main-container">
|
|
<section id="city-section">
|
|
<canvas id="city-canvas"></canvas>
|
|
<div class="city-overlay">
|
|
<div class="city-time" id="city-time">00:00:00</div>
|
|
<div class="city-date" id="city-date">2078.12.24</div>
|
|
<div class="city-stats">
|
|
<div class="stat-item">
|
|
<span>神经网络活跃度</span>
|
|
<span class="stat-value" id="neural-activity">98.7%</span>
|
|
</div>
|
|
<div class="stat-item">
|
|
<span>数据吞吐量</span>
|
|
<span class="stat-value" id="data-throughput">847 TB/s</span>
|
|
</div>
|
|
<div class="stat-item">
|
|
<span>城市能量场</span>
|
|
<span class="stat-value" id="energy-field">12.4 GW</span>
|
|
</div>
|
|
<div class="stat-item">
|
|
<span>无人机流量</span>
|
|
<span class="stat-value" id="drone-traffic">45,892</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
<section id="neural-section">
|
|
<h2 class="section-title"><i class="fas fa-brain"></i> 神经网络数据流</h2>
|
|
<div class="neural-container">
|
|
<canvas id="neural-canvas"></canvas>
|
|
<div class="neural-stats-ring" id="stats-ring"></div>
|
|
</div>
|
|
</section>
|
|
<section id="monitor-section">
|
|
<h2 class="section-title"><i class="fas fa-server"></i> 智能系统监控</h2>
|
|
<div class="monitor-grid">
|
|
<div class="monitor-panel">
|
|
<div class="panel-title"><i class="fas fa-microchip"></i> 系统负载</div>
|
|
<div class="gauge-container">
|
|
<svg class="gauge" viewBox="0 0 120 70">
|
|
<defs>
|
|
<linearGradient id="gaugeGradient" x1="0%" y1="0%" x2="100%" y2="0%">
|
|
<stop offset="0%" style="stop-color:#00ffff"/>
|
|
<stop offset="100%" style="stop-color:#ff00ff"/>
|
|
</linearGradient>
|
|
</defs>
|
|
<path class="gauge-bg" d="M 10 60 A 50 50 0 0 1 110 60"/>
|
|
<path class="gauge-fill" id="cpu-gauge" d="M 10 60 A 50 50 0 0 1 110 60" stroke-dasharray="157" stroke-dashoffset="47"/>
|
|
<text class="gauge-text" x="60" y="55" id="cpu-text">70%</text>
|
|
</svg>
|
|
<svg class="gauge" viewBox="0 0 120 70">
|
|
<path class="gauge-bg" d="M 10 60 A 50 50 0 0 1 110 60"/>
|
|
<path class="gauge-fill" id="mem-gauge" d="M 10 60 A 50 50 0 0 1 110 60" stroke-dasharray="157" stroke-dashoffset="78"/>
|
|
<text class="gauge-text" x="60" y="55" id="mem-text">50%</text>
|
|
</svg>
|
|
</div>
|
|
</div>
|
|
<div class="monitor-panel">
|
|
<div class="panel-title"><i class="fas fa-wave-square"></i> 网络流量波形</div>
|
|
<div class="oscilloscope">
|
|
<div class="oscilloscope-grid"></div>
|
|
<canvas id="waveform-canvas"></canvas>
|
|
</div>
|
|
</div>
|
|
<div class="monitor-panel">
|
|
<div class="panel-title"><i class="fas fa-database"></i> 服务器集群状态</div>
|
|
<div class="server-grid" id="server-grid"></div>
|
|
</div>
|
|
<div class="monitor-panel">
|
|
<div class="panel-title"><i class="fas fa-list-alt"></i> 系统日志</div>
|
|
<div class="log-window" id="log-window"></div>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
<section id="timeline-section">
|
|
<h2 class="section-title"><i class="fas fa-history"></i> 历史演进</h2>
|
|
<div class="timeline-container" id="timeline">
|
|
<div class="timeline-line"></div>
|
|
</div>
|
|
</section>
|
|
<section id="cards-section">
|
|
<h2 class="section-title"><i class="fas fa-cube"></i> 全息数据概览</h2>
|
|
<div class="cards-grid" id="cards-grid"></div>
|
|
</section>
|
|
</div>
|
|
<section id="console-section">
|
|
<div class="console-container">
|
|
<span class="console-label"><i class="fas fa-terminal"></i> NEUROLINK://</span>
|
|
<div class="console-input-wrapper">
|
|
<input type="text" class="console-input" id="console-input" placeholder="输入命令: 显示能源分布 / 切换到东城区视图 / 开始系统诊断..." autocomplete="off">
|
|
<div class="cursor-blink"></div>
|
|
</div>
|
|
<div class="console-actions">
|
|
<button class="console-btn" id="execute-btn"><i class="fas fa-play"></i> 执行</button>
|
|
<button class="console-btn" id="voice-btn"><i class="fas fa-microphone"></i></button>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
<script>
|
|
const $ = id => document.getElementById(id);
|
|
const createEl = (tag, className = '') => {
|
|
const el = document.createElement(tag);
|
|
if (className) el.className = className;
|
|
return el;
|
|
};
|
|
document.addEventListener('DOMContentLoaded', () => {
|
|
initParticles();
|
|
initIntroAnimation();
|
|
init3DCity();
|
|
initNeuralNetwork();
|
|
initMonitorDashboard();
|
|
initTimeline();
|
|
initCards();
|
|
initConsole();
|
|
updateTime();
|
|
setTimeout(() => $('loading').classList.add('hidden'), 1500);
|
|
});
|
|
function initParticles() {
|
|
if (typeof particlesJS !== 'undefined') {
|
|
particlesJS('particles-bg', {
|
|
particles: {
|
|
number: { value: 80, density: { enable: true, value_area: 800 } },
|
|
color: { value: ['#ff00ff', '#00ffff', '#8800ff'] },
|
|
shape: { type: 'circle' },
|
|
opacity: { value: 0.5, random: true },
|
|
size: { value: 3, random: true },
|
|
move: {
|
|
enable: true, speed: 1, direction: 'none', random: true, out_mode: 'out'
|
|
}
|
|
},
|
|
interactivity: {
|
|
events: {
|
|
onhover: { enable: true, mode: 'repulse' },
|
|
onclick: { enable: true, mode: 'push' }
|
|
},
|
|
modes: { repulse: { distance: 100, duration: 0.4 } }
|
|
}
|
|
});
|
|
}
|
|
}
|
|
function initIntroAnimation() {
|
|
const canvas = $('intro-canvas');
|
|
const ctx = canvas.getContext('2d');
|
|
const overlay = $('intro-overlay');
|
|
const text = document.querySelector('.intro-text');
|
|
canvas.width = window.innerWidth;
|
|
canvas.height = window.innerHeight;
|
|
class Node {
|
|
constructor(x, y) {
|
|
this.x = x; this.y = y;
|
|
this.targetX = x + (Math.random() - 0.5) * 200;
|
|
this.targetY = y + (Math.random() - 0.5) * 200;
|
|
this.radius = 0; this.targetRadius = Math.random() * 5 + 3;
|
|
this.pulse = 0;
|
|
}
|
|
update() {
|
|
this.x += (this.targetX - this.x) * 0.02;
|
|
this.y += (this.targetY - this.y) * 0.02;
|
|
this.radius += (this.targetRadius - this.radius) * 0.05;
|
|
this.pulse += 0.1;
|
|
}
|
|
draw(ctx) {
|
|
const safeRadius = Math.max(0.1, this.radius);
|
|
const glow = ctx.createRadialGradient(this.x, this.y, 0, this.x, this.y, safeRadius * 3);
|
|
glow.addColorStop(0, 'rgba(0, 255, 255, 0.8)');
|
|
glow.addColorStop(0.5, 'rgba(255, 0, 255, 0.3)');
|
|
glow.addColorStop(1, 'transparent');
|
|
ctx.beginPath();
|
|
ctx.arc(this.x, this.y, safeRadius * 3, 0, Math.PI * 2);
|
|
ctx.fillStyle = glow;
|
|
ctx.fill();
|
|
ctx.beginPath();
|
|
ctx.arc(this.x, this.y, safeRadius, 0, Math.PI * 2);
|
|
ctx.fillStyle = '#00ffff';
|
|
ctx.fill();
|
|
}
|
|
}
|
|
const nodes = [];
|
|
const centerX = canvas.width / 2;
|
|
const centerY = canvas.height / 2;
|
|
for (let i = 0; i < 12; i++) {
|
|
nodes.push(new Node(centerX, centerY));
|
|
}
|
|
let frame = 0;
|
|
let connected = false;
|
|
const animate = () => {
|
|
ctx.fillStyle = 'rgba(0, 0, 0, 0.1)';
|
|
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
|
nodes.forEach(node => { node.update(); node.draw(ctx); });
|
|
ctx.strokeStyle = 'rgba(136, 0, 255, 0.3)';
|
|
ctx.lineWidth = 1;
|
|
for (let i = 0; i < nodes.length; i++) {
|
|
for (let j = i + 1; j < nodes.length; j++) {
|
|
const dx = nodes[i].x - nodes[j].x;
|
|
const dy = nodes[i].y - nodes[j].y;
|
|
const dist = Math.sqrt(dx * dx + dy * dy);
|
|
if (dist < 250) {
|
|
const alpha = (1 - dist / 250) * 0.5;
|
|
ctx.strokeStyle = `rgba(0, 255, 255, ${alpha})`;
|
|
ctx.beginPath();
|
|
ctx.moveTo(nodes[i].x, nodes[i].y);
|
|
ctx.lineTo(nodes[j].x, nodes[j].y);
|
|
ctx.stroke();
|
|
}
|
|
}
|
|
}
|
|
frame++;
|
|
if (frame > 60 && frame < 180) {
|
|
nodes.forEach(node => {
|
|
node.targetX = node.x + (Math.random() - 0.5) * 5;
|
|
node.targetY = node.y + (Math.random() - 0.5) * 5;
|
|
});
|
|
connected = true;
|
|
}
|
|
if (frame > 120) {
|
|
text.style.opacity = Math.min((frame - 120) / 60, 1);
|
|
}
|
|
if (frame < 240) {
|
|
requestAnimationFrame(animate);
|
|
} else {
|
|
gsap.to(overlay, {
|
|
opacity: 0, duration: 1,
|
|
onComplete: () => { overlay.style.display = 'none'; }
|
|
});
|
|
}
|
|
};
|
|
animate();
|
|
window.addEventListener('resize', () => {
|
|
canvas.width = window.innerWidth;
|
|
canvas.height = window.innerHeight;
|
|
});
|
|
}
|
|
function init3DCity() {
|
|
const canvas = $('city-canvas');
|
|
const scene = new THREE.Scene();
|
|
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
|
|
const renderer = new THREE.WebGLRenderer({ canvas, antialias: true, alpha: true });
|
|
renderer.setSize(window.innerWidth, window.innerHeight);
|
|
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
|
|
scene.fog = new THREE.FogExp2(0x0a0a0a, 0.008);
|
|
camera.position.set(50, 80, 100);
|
|
camera.lookAt(0, 0, 0);
|
|
const ambientLight = new THREE.AmbientLight(0x404040, 0.5);
|
|
scene.add(ambientLight);
|
|
const pointLight1 = new THREE.PointLight(0xff00ff, 1, 200);
|
|
pointLight1.position.set(50, 100, 50);
|
|
scene.add(pointLight1);
|
|
const pointLight2 = new THREE.PointLight(0x00ffff, 1, 200);
|
|
pointLight2.position.set(-50, 100, -50);
|
|
scene.add(pointLight2);
|
|
const buildings = [];
|
|
const buildingGeometry = new THREE.BoxGeometry(1, 1, 1);
|
|
for (let i = 0; i < 200; i++) {
|
|
const height = Math.random() * 30 + 10;
|
|
const width = Math.random() * 5 + 3;
|
|
const depth = Math.random() * 5 + 3;
|
|
const material = new THREE.MeshPhongMaterial({
|
|
color: 0x1a1a2e,
|
|
emissive: Math.random() > 0.7 ? 0x00ffff : 0x000000,
|
|
emissiveIntensity: 0.3,
|
|
transparent: true,
|
|
opacity: 0.9
|
|
});
|
|
const building = new THREE.Mesh(buildingGeometry, material);
|
|
building.position.set(
|
|
(Math.random() - 0.5) * 200,
|
|
height / 2,
|
|
(Math.random() - 0.5) * 200
|
|
);
|
|
building.scale.set(width, height, depth);
|
|
building.userData.originalHeight = height;
|
|
scene.add(building);
|
|
buildings.push(building);
|
|
}
|
|
const gridHelper = new THREE.GridHelper(300, 50, 0xff00ff, 0x1a1a2e);
|
|
gridHelper.position.y = -0.1;
|
|
scene.add(gridHelper);
|
|
const particles = [];
|
|
const particleGeometry = new THREE.BufferGeometry();
|
|
const particlePositions = new Float32Array(500 * 3);
|
|
for (let i = 0; i < 500; i++) {
|
|
particlePositions[i * 3] = (Math.random() - 0.5) * 300;
|
|
particlePositions[i * 3 + 1] = Math.random() * 100 + 20;
|
|
particlePositions[i * 3 + 2] = (Math.random() - 0.5) * 300;
|
|
}
|
|
particleGeometry.setAttribute('position', new THREE.BufferAttribute(particlePositions, 3));
|
|
const particleMaterial = new THREE.PointsMaterial({
|
|
color: 0x00ffff,
|
|
size: 0.5,
|
|
transparent: true,
|
|
opacity: 0.8
|
|
});
|
|
const particleSystem = new THREE.Points(particleGeometry, particleMaterial);
|
|
scene.add(particleSystem);
|
|
let isDragging = false;
|
|
let previousMousePosition = { x: 0, y: 0 };
|
|
let rotation = { x: 0, y: 0 };
|
|
let targetRotation = { x: 0, y: 0 };
|
|
canvas.addEventListener('mousedown', (e) => {
|
|
isDragging = true;
|
|
previousMousePosition = { x: e.clientX, y: e.clientY };
|
|
});
|
|
canvas.addEventListener('mousemove', (e) => {
|
|
if (isDragging) {
|
|
const deltaX = e.clientX - previousMousePosition.x;
|
|
const deltaY = e.clientY - previousMousePosition.y;
|
|
targetRotation.y += deltaX * 0.005;
|
|
targetRotation.x += deltaY * 0.005;
|
|
targetRotation.x = Math.max(-Math.PI / 3, Math.min(Math.PI / 4, targetRotation.x));
|
|
previousMousePosition = { x: e.clientX, y: e.clientY };
|
|
}
|
|
});
|
|
canvas.addEventListener('mouseup', () => isDragging = false);
|
|
canvas.addEventListener('mouseleave', () => isDragging = false);
|
|
canvas.addEventListener('wheel', (e) => {
|
|
camera.position.z += e.deltaY * 0.1;
|
|
camera.position.z = Math.max(50, Math.min(200, camera.position.z));
|
|
camera.position.x = camera.position.z * Math.sin(targetRotation.y);
|
|
camera.position.z = camera.position.z * Math.cos(targetRotation.y);
|
|
});
|
|
let frame = 0;
|
|
const animate = () => {
|
|
requestAnimationFrame(animate);
|
|
frame++;
|
|
rotation.x += (targetRotation.x - rotation.x) * 0.05;
|
|
rotation.y += (targetRotation.y - rotation.y) * 0.05;
|
|
camera.position.x = 100 * Math.sin(rotation.y) * Math.cos(rotation.x);
|
|
camera.position.y = 80 + rotation.x * 50;
|
|
camera.position.z = 100 * Math.cos(rotation.y) * Math.cos(rotation.x);
|
|
camera.lookAt(0, 20, 0);
|
|
buildings.forEach((building) => {
|
|
const rand = Math.random();
|
|
if (rand > 0.99) {
|
|
building.material.emissive.setHex(0xff00ff);
|
|
} else if (rand > 0.98) {
|
|
building.material.emissive.setHex(0x00ffff);
|
|
} else {
|
|
building.material.emissive.setHex(0x000000);
|
|
}
|
|
});
|
|
const positions = particleSystem.geometry.attributes.position.array;
|
|
for (let i = 0; i < 500; i++) {
|
|
positions[i * 3] += Math.sin(frame * 0.01 + i) * 0.1;
|
|
positions[i * 3 + 1] += Math.cos(frame * 0.02 + i) * 0.05;
|
|
positions[i * 3 + 2] += Math.cos(frame * 0.01 + i) * 0.1;
|
|
if (positions[i * 3 + 1] > 150) positions[i * 3 + 1] = 20;
|
|
}
|
|
particleSystem.geometry.attributes.position.needsUpdate = true;
|
|
renderer.render(scene, camera);
|
|
};
|
|
animate();
|
|
window.addEventListener('resize', () => {
|
|
camera.aspect = window.innerWidth / window.innerHeight;
|
|
camera.updateProjectionMatrix();
|
|
renderer.setSize(window.innerWidth, window.innerHeight);
|
|
});
|
|
}
|
|
function initNeuralNetwork() {
|
|
const canvas = $('neural-canvas');
|
|
const ctx = canvas.getContext('2d');
|
|
const container = canvas.parentElement;
|
|
canvas.width = container.offsetWidth;
|
|
canvas.height = container.offsetHeight;
|
|
class NeuralNode {
|
|
constructor(x, y, layer) {
|
|
this.x = x; this.y = y;
|
|
this.radius = 0; this.targetRadius = Math.random() * 8 + 5;
|
|
this.layer = layer;
|
|
this.pulsePhase = Math.random() * Math.PI * 2;
|
|
this.connections = [];
|
|
}
|
|
update() {
|
|
this.radius += (this.targetRadius - this.radius) * 0.1;
|
|
this.pulsePhase += 0.05;
|
|
}
|
|
draw(ctx) {
|
|
const pulse = Math.max(1, Math.sin(this.pulsePhase) * 3 + this.radius);
|
|
const gradient = ctx.createRadialGradient(this.x, this.y, 0, this.x, this.y, pulse * 2);
|
|
gradient.addColorStop(0, 'rgba(0, 255, 255, 0.8)');
|
|
gradient.addColorStop(0.5, 'rgba(255, 0, 255, 0.3)');
|
|
gradient.addColorStop(1, 'transparent');
|
|
ctx.beginPath();
|
|
ctx.arc(this.x, this.y, pulse * 2, 0, Math.PI * 2);
|
|
ctx.fillStyle = gradient;
|
|
ctx.fill();
|
|
ctx.beginPath();
|
|
ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2);
|
|
ctx.fillStyle = '#00ffff';
|
|
ctx.fill();
|
|
}
|
|
}
|
|
class DataPulse {
|
|
constructor(start, end) {
|
|
this.start = start; this.end = end;
|
|
this.progress = 0;
|
|
this.speed = Math.random() * 0.02 + 0.01;
|
|
}
|
|
update() {
|
|
this.progress += this.speed;
|
|
return this.progress < 1;
|
|
}
|
|
draw(ctx) {
|
|
const x = this.start.x + (this.end.x - this.start.x) * this.progress;
|
|
const y = this.start.y + (this.end.y - this.start.y) * this.progress;
|
|
ctx.beginPath();
|
|
ctx.arc(x, y, 4, 0, Math.PI * 2);
|
|
ctx.fillStyle = '#ff00ff';
|
|
ctx.shadowColor = '#ff00ff';
|
|
ctx.shadowBlur = 10;
|
|
ctx.fill();
|
|
ctx.shadowBlur = 0;
|
|
}
|
|
}
|
|
const nodes = [];
|
|
const layers = [4, 6, 8, 6, 4];
|
|
const layerGap = canvas.width / (layers.length + 1);
|
|
layers.forEach((count, layerIndex) => {
|
|
const layerX = layerGap * (layerIndex + 1);
|
|
const nodeGap = canvas.height / (count + 1);
|
|
for (let i = 0; i < count; i++) {
|
|
const node = new NeuralNode(layerX, nodeGap * (i + 1), layerIndex);
|
|
nodes.push(node);
|
|
if (layerIndex < layers.length - 1) {
|
|
const nextLayerNodes = nodes.filter(n => n.layer === layerIndex + 1);
|
|
const numConnections = Math.min(2, nextLayerNodes.length);
|
|
for (let j = 0; j < numConnections; j++) {
|
|
const target = nextLayerNodes[Math.floor(Math.random() * nextLayerNodes.length)];
|
|
if (!node.connections.includes(target)) {
|
|
node.connections.push(target);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
});
|
|
const pulses = [];
|
|
let frame = 0;
|
|
const statsRing = $('stats-ring');
|
|
const stats = [
|
|
{ label: '神经元活跃度', value: '98.7%' },
|
|
{ label: '数据吞吐量', value: '847 TB/s' },
|
|
{ label: '延迟指标', value: '2.4 ms' },
|
|
{ label: '同步率', value: '99.9%' }
|
|
];
|
|
stats.forEach((stat, i) => {
|
|
const el = createEl('div', 'stat-ring-item');
|
|
el.innerHTML = `<span>${stat.label}</span><br><span style="color: #ff00ff">${stat.value}</span>`;
|
|
const angle = (i / stats.length) * Math.PI * 2 - Math.PI / 2;
|
|
const radius = 280;
|
|
el.style.left = `${300 + Math.cos(angle) * radius - 80}px`;
|
|
el.style.top = `${300 + Math.sin(angle) * radius - 20}px`;
|
|
statsRing.appendChild(el);
|
|
});
|
|
const animate = () => {
|
|
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
|
ctx.strokeStyle = 'rgba(136, 0, 255, 0.2)';
|
|
ctx.lineWidth = 1;
|
|
nodes.forEach(node => {
|
|
node.connections.forEach(target => {
|
|
ctx.beginPath();
|
|
ctx.moveTo(node.x, node.y);
|
|
ctx.lineTo(target.x, target.y);
|
|
ctx.stroke();
|
|
});
|
|
});
|
|
if (frame % 30 === 0) {
|
|
nodes.filter(n => n.connections.length > 0).forEach(node => {
|
|
if (Math.random() > 0.5) {
|
|
const target = node.connections[Math.floor(Math.random() * node.connections.length)];
|
|
pulses.push(new DataPulse(node, target));
|
|
}
|
|
});
|
|
}
|
|
for (let i = pulses.length - 1; i >= 0; i--) {
|
|
if (!pulses[i].update()) {
|
|
pulses.splice(i, 1);
|
|
} else {
|
|
pulses[i].draw(ctx);
|
|
}
|
|
}
|
|
nodes.forEach(node => {
|
|
node.update();
|
|
node.draw(ctx);
|
|
});
|
|
frame++;
|
|
requestAnimationFrame(animate);
|
|
};
|
|
animate();
|
|
window.addEventListener('resize', () => {
|
|
canvas.width = container.offsetWidth;
|
|
canvas.height = container.offsetHeight;
|
|
});
|
|
}
|
|
function initMonitorDashboard() {
|
|
initGauges();
|
|
initWaveform();
|
|
initServerGrid();
|
|
initLogs();
|
|
}
|
|
function initGauges() {
|
|
const updateGauges = () => {
|
|
const cpu = 50 + Math.random() * 40;
|
|
const mem = 40 + Math.random() * 30;
|
|
const cpuFill = $('cpu-gauge');
|
|
const memFill = $('mem-gauge');
|
|
const cpuText = $('cpu-text');
|
|
const memText = $('mem-text');
|
|
const cpuOffset = 157 - (cpu / 100) * 157;
|
|
const memOffset = 157 - (mem / 100) * 157;
|
|
cpuFill.style.strokeDashoffset = cpuOffset;
|
|
memFill.style.strokeDashoffset = memOffset;
|
|
cpuText.textContent = `${Math.round(cpu)}%`;
|
|
memText.textContent = `${Math.round(mem)}%`;
|
|
};
|
|
updateGauges();
|
|
setInterval(updateGauges, 2000);
|
|
}
|
|
function initWaveform() {
|
|
const canvas = $('waveform-canvas');
|
|
const ctx = canvas.getContext('2d');
|
|
const container = canvas.parentElement;
|
|
canvas.width = container.offsetWidth;
|
|
canvas.height = container.offsetHeight;
|
|
const data = new Array(canvas.width).fill(canvas.height / 2);
|
|
let frame = 0;
|
|
const animate = () => {
|
|
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
|
for (let i = 0; i < data.length - 1; i++) {
|
|
data[i] = data[i + 1];
|
|
}
|
|
data[data.length - 1] = canvas.height / 2 +
|
|
Math.sin(frame * 0.1) * 30 +
|
|
Math.sin(frame * 0.05) * 20 +
|
|
(Math.random() - 0.5) * 10;
|
|
ctx.beginPath();
|
|
ctx.moveTo(0, data[0]);
|
|
for (let i = 1; i < data.length; i++) {
|
|
ctx.lineTo(i, data[i]);
|
|
}
|
|
ctx.strokeStyle = '#00ffff';
|
|
ctx.lineWidth = 2;
|
|
ctx.shadowColor = '#00ffff';
|
|
ctx.shadowBlur = 10;
|
|
ctx.stroke();
|
|
ctx.shadowBlur = 0;
|
|
ctx.lineTo(canvas.width, canvas.height);
|
|
ctx.lineTo(0, canvas.height);
|
|
ctx.closePath();
|
|
const gradient = ctx.createLinearGradient(0, 0, 0, canvas.height);
|
|
gradient.addColorStop(0, 'rgba(0, 255, 255, 0.3)');
|
|
gradient.addColorStop(1, 'rgba(0, 255, 255, 0)');
|
|
ctx.fillStyle = gradient;
|
|
ctx.fill();
|
|
frame++;
|
|
requestAnimationFrame(animate);
|
|
};
|
|
animate();
|
|
window.addEventListener('resize', () => {
|
|
canvas.width = container.offsetWidth;
|
|
canvas.height = container.offsetHeight;
|
|
});
|
|
}
|
|
function initServerGrid() {
|
|
const grid = $('server-grid');
|
|
const statuses = ['active', 'active', 'active', 'active', 'active', 'warning', 'active', 'active',
|
|
'active', 'active', 'warning', 'active', 'active', 'active', 'active', 'active'];
|
|
statuses.forEach((status, i) => {
|
|
const node = createEl('div', 'server-node');
|
|
node.innerHTML = `<i class="fas fa-server"></i><br>SVR-${String(i).padStart(2, '0')}`;
|
|
node.dataset.status = status;
|
|
grid.appendChild(node);
|
|
});
|
|
const updateStatus = () => {
|
|
const nodes = grid.querySelectorAll('.server-node');
|
|
nodes.forEach(node => {
|
|
if (Math.random() > 0.95) {
|
|
const newStatus = Math.random() > 0.9 ? 'error' : (Math.random() > 0.5 ? 'warning' : 'active');
|
|
node.dataset.status = newStatus;
|
|
}
|
|
});
|
|
};
|
|
setInterval(updateStatus, 1000);
|
|
}
|
|
function initLogs() {
|
|
const logWindow = $('log-window');
|
|
const messages = [
|
|
{ text: '神经网络节点同步完成', type: 'success' },
|
|
{ text: '数据流优化算法启动', type: 'info' },
|
|
{ text: '检测到异常数据波动', type: 'warning' },
|
|
{ text: '自动修复程序执行中', type: 'info' },
|
|
{ text: '新节点加入网络', type: 'success' },
|
|
{ text: '量子加密握手成功', type: 'success' },
|
|
{ text: '带宽分配调整', type: 'info' },
|
|
{ text: '缓存同步完成', type: 'success' },
|
|
{ text: '网络负载均衡', type: 'info' },
|
|
{ text: '安全扫描完成', type: 'success' }
|
|
];
|
|
const addLog = () => {
|
|
const msg = messages[Math.floor(Math.random() * messages.length)];
|
|
const entry = createEl('div', 'log-entry');
|
|
const time = new Date().toLocaleTimeString();
|
|
entry.innerHTML = `<span class="log-time">[${time}]</span><span class="log-${msg.type}">${msg.text}</span>`;
|
|
logWindow.appendChild(entry);
|
|
logWindow.scrollTop = logWindow.scrollHeight;
|
|
while (logWindow.children.length > 50) {
|
|
logWindow.removeChild(logWindow.firstChild);
|
|
}
|
|
};
|
|
for (let i = 0; i < 5; i++) addLog();
|
|
setInterval(addLog, 2000);
|
|
}
|
|
function initTimeline() {
|
|
const timeline = $('timeline');
|
|
const events = [
|
|
{ year: '2078', label: '神经链接时代', desc: '全脑接口普及,意识可上传至云端' },
|
|
{ year: '2075', label: 'AI觉醒', desc: '超级AI产生自我意识' },
|
|
{ year: '2052', label: '量子突破', desc: '量子计算商用化,计算能力爆发' },
|
|
{ year: '2038', label: '纳米普及', desc: '纳米机器人应用于医疗与制造' },
|
|
{ year: '2025', label: '脑机接口', desc: 'Neuralink等设备开始商业化' }
|
|
];
|
|
events.forEach((event, index) => {
|
|
const node = createEl('div', 'timeline-node');
|
|
node.innerHTML = `
|
|
<div class="node-content">
|
|
<span class="node-year">${event.year}</span>
|
|
<span class="node-label">${event.label}</span>
|
|
</div>
|
|
<div class="node-detail">${event.desc}</div>
|
|
`;
|
|
if (index === 0) node.classList.add('active');
|
|
node.addEventListener('click', () => {
|
|
timeline.querySelectorAll('.timeline-node').forEach(n => n.classList.remove('active'));
|
|
node.classList.add('active');
|
|
gsap.to($('city-canvas'), {
|
|
filter: 'hue-rotate(90deg)',
|
|
duration: 0.5,
|
|
onComplete: () => {
|
|
gsap.to($('city-canvas'), {
|
|
filter: 'hue-rotate(0deg)',
|
|
duration: 0.5
|
|
});
|
|
}
|
|
});
|
|
});
|
|
timeline.appendChild(node);
|
|
});
|
|
}
|
|
function initCards() {
|
|
const grid = $('cards-grid');
|
|
const cardData = [
|
|
{
|
|
icon: 'fa-users',
|
|
value: '12.8B',
|
|
label: '城市人口',
|
|
stats: [
|
|
{ label: '数字意识', value: '45%' },
|
|
{ label: '生物人口', value: '55%' },
|
|
{ label: '上传次数', value: '2.1M/日' }
|
|
]
|
|
},
|
|
{
|
|
icon: 'fa-bolt',
|
|
value: '12.4 GW',
|
|
label: '能源消耗',
|
|
stats: [
|
|
{ label: '核聚变供电', value: '78%' },
|
|
{ label: '太阳能', value: '15%' },
|
|
{ label: '其余来源', value: '7%' }
|
|
]
|
|
},
|
|
{
|
|
icon: 'fa-network-wired',
|
|
value: '1.2M',
|
|
label: '网络节点',
|
|
stats: [
|
|
{ label: '活跃节点', value: '98.7%' },
|
|
{ label: '新增节点', value: '+5,234' },
|
|
{ label: '总带宽', value: '847 TB/s' }
|
|
]
|
|
},
|
|
{
|
|
icon: 'fa-brain',
|
|
value: '99.9%',
|
|
label: 'AI同步率',
|
|
stats: [
|
|
{ label: '决策延迟', value: '2.4ms' },
|
|
{ label: '学习效率', value: '156x' },
|
|
{ label: '准确率', value: '99.99%' }
|
|
]
|
|
}
|
|
];
|
|
cardData.forEach(data => {
|
|
const card = createEl('article', 'holo-card');
|
|
card.innerHTML = `
|
|
<i class="fas ${data.icon} card-icon"></i>
|
|
<div class="card-value">${data.value}</div>
|
|
<div class="card-label">${data.label}</div>
|
|
<div class="card-hidden">
|
|
${data.stats.map(s => `
|
|
<div class="hidden-stat">
|
|
<span style="color: var(--text-secondary)">${s.label}</span>
|
|
<span style="color: var(--neon-pink)">${s.value}</span>
|
|
</div>
|
|
`).join('')}
|
|
</div>
|
|
`;
|
|
grid.appendChild(card);
|
|
});
|
|
}
|
|
function initConsole() {
|
|
const input = $('console-input');
|
|
const executeBtn = $('execute-btn');
|
|
const commands = {
|
|
'显示能源分布': () => showFeedback('正在分析能源分布数据...'),
|
|
'切换到东城区视图': () => {
|
|
showFeedback('正在切换到东城区视角...');
|
|
gsap.to($('city-canvas'), { filter: 'hue-rotate(180deg)', duration: 0.5 });
|
|
setTimeout(() => gsap.to($('city-canvas'), { filter: 'hue-rotate(0)', duration: 0.5 }), 2000);
|
|
},
|
|
'开始系统诊断': () => showFeedback('系统诊断中,请稍候...'),
|
|
'显示人口统计': () => showFeedback('正在获取人口统计数据...'),
|
|
'网络状态': () => showFeedback('网络运行正常,延迟: 2.4ms'),
|
|
'帮助': () => showFeedback('可用命令: 显示能源分布、切换到东城区视图、开始系统诊断、显示人口统计、网络状态'),
|
|
'清屏': () => {
|
|
input.value = '';
|
|
showFeedback('控制台已清空');
|
|
}
|
|
};
|
|
const showFeedback = (text) => {
|
|
const overlay = $('feedback');
|
|
$('feedback-text').textContent = text;
|
|
overlay.classList.add('active');
|
|
setTimeout(() => {
|
|
overlay.classList.remove('active');
|
|
}, 2000);
|
|
};
|
|
const executeCommand = () => {
|
|
const cmd = input.value.trim().toLowerCase();
|
|
if (!cmd) return;
|
|
const matchedCmd = Object.keys(commands).find(key =>
|
|
key.toLowerCase().includes(cmd) || cmd.includes(key.toLowerCase())
|
|
);
|
|
if (matchedCmd) {
|
|
commands[matchedCmd]();
|
|
} else {
|
|
showFeedback(`未知命令: ${input.value}`);
|
|
}
|
|
input.value = '';
|
|
};
|
|
executeBtn.addEventListener('click', executeCommand);
|
|
input.addEventListener('keypress', (e) => {
|
|
if (e.key === 'Enter') executeCommand();
|
|
});
|
|
}
|
|
function updateTime() {
|
|
const update = () => {
|
|
const now = new Date();
|
|
const timeStr = now.toLocaleTimeString('en-GB');
|
|
const dateStr = `2078.${String(now.getMonth() + 1).padStart(2, '0')}.${String(now.getDate()).padStart(2, '0')}`;
|
|
$('city-time').textContent = timeStr;
|
|
$('city-date').textContent = dateStr;
|
|
if (now.getSeconds() % 10 === 0) {
|
|
$('neural-activity').textContent = (95 + Math.random() * 5).toFixed(1) + '%';
|
|
$('data-throughput').textContent = Math.round(800 + Math.random() * 100) + ' TB/s';
|
|
$('drone-traffic').textContent = Math.round(45000 + Math.random() * 1000).toLocaleString();
|
|
}
|
|
};
|
|
update();
|
|
setInterval(update, 1000);
|
|
}
|
|
</script>
|
|
</body>
|
|
</html>
|