1044 lines
35 KiB
HTML
1044 lines
35 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>
|
|
|
|
<!-- External CDN Resources -->
|
|
<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://d3js.org/d3.v7.min.js"></script>
|
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
<link href="https://fonts.googleapis.com/css2?family=Orbitron:wght@400;700;900&family=Rajdhani:wght@300;400;600&display=swap" rel="stylesheet">
|
|
<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>
|
|
/* CSS Custom Properties */
|
|
:root {
|
|
--neon-pink: #ff00ff;
|
|
--electric-blue: #00ffff;
|
|
--laser-purple: #8800ff;
|
|
--deep-black: #0a0a0a;
|
|
--metal-gray: #1a1a1a;
|
|
--glass-bg: rgba(20, 20, 20, 0.25);
|
|
--glass-border: rgba(255, 0, 255, 0.3);
|
|
}
|
|
|
|
* {
|
|
margin: 0;
|
|
padding: 0;
|
|
box-sizing: border-box;
|
|
}
|
|
|
|
body {
|
|
font-family: 'Rajdhani', sans-serif;
|
|
background: var(--deep-black);
|
|
color: var(--electric-blue);
|
|
overflow-x: hidden;
|
|
cursor: crosshair;
|
|
position: relative;
|
|
}
|
|
|
|
h1, h2, h3, .title {
|
|
font-family: 'Orbitron', sans-serif;
|
|
text-transform: uppercase;
|
|
letter-spacing: 2px;
|
|
}
|
|
|
|
/* Particles Background */
|
|
#particles-js {
|
|
position: fixed;
|
|
width: 100%;
|
|
height: 100%;
|
|
z-index: -1;
|
|
}
|
|
|
|
/* Loading Screen */
|
|
.loader {
|
|
position: fixed;
|
|
top: 0;
|
|
left: 0;
|
|
width: 100%;
|
|
height: 100%;
|
|
background: var(--deep-black);
|
|
display: flex;
|
|
justify-content: center;
|
|
align-items: center;
|
|
z-index: 9999;
|
|
}
|
|
|
|
.loader-circle {
|
|
width: 60px;
|
|
height: 60px;
|
|
border: 3px solid transparent;
|
|
border-top-color: var(--neon-pink);
|
|
border-radius: 50%;
|
|
animation: spin 1s linear infinite;
|
|
box-shadow: 0 0 20px var(--neon-pink);
|
|
}
|
|
|
|
@keyframes spin {
|
|
to { transform: rotate(360deg); }
|
|
}
|
|
|
|
/* Fullscreen 3D Container */
|
|
#three-container {
|
|
position: fixed;
|
|
top: 0;
|
|
left: 0;
|
|
width: 100%;
|
|
height: 100%;
|
|
z-index: 1;
|
|
}
|
|
|
|
/* HUD Container */
|
|
.hud-container {
|
|
position: relative;
|
|
z-index: 10;
|
|
pointer-events: none;
|
|
}
|
|
|
|
.hud-container > * {
|
|
pointer-events: auto;
|
|
}
|
|
|
|
/* Glassmorphism Panel */
|
|
.panel {
|
|
background: var(--glass-bg);
|
|
backdrop-filter: blur(12px);
|
|
-webkit-backdrop-filter: blur(12px);
|
|
border: 1px solid var(--glass-border);
|
|
border-radius: 8px;
|
|
box-shadow: 0 0 15px rgba(0, 255, 255, 0.2);
|
|
padding: 20px;
|
|
}
|
|
|
|
/* Top Info Bar */
|
|
.top-info-bar {
|
|
position: fixed;
|
|
top: 20px;
|
|
left: 50%;
|
|
transform: translateX(-50%);
|
|
display: flex;
|
|
gap: 30px;
|
|
z-index: 100;
|
|
}
|
|
|
|
.info-item {
|
|
background: rgba(0, 0, 0, 0.8);
|
|
border: 1px solid var(--electric-blue);
|
|
padding: 10px 20px;
|
|
font-size: 14px;
|
|
color: var(--electric-blue);
|
|
box-shadow: 0 0 10px var(--electric-blue);
|
|
animation: pulse 2s infinite;
|
|
}
|
|
|
|
@keyframes pulse {
|
|
0%, 100% { opacity: 0.8; }
|
|
50% { opacity: 1; }
|
|
}
|
|
|
|
/* Main Title */
|
|
.main-title {
|
|
position: fixed;
|
|
top: 80px;
|
|
left: 50%;
|
|
transform: translateX(-50%);
|
|
font-size: 48px;
|
|
font-weight: 900;
|
|
color: var(--neon-pink);
|
|
text-shadow: 0 0 30px var(--neon-pink), 0 0 60px var(--neon-pink);
|
|
z-index: 100;
|
|
opacity: 0;
|
|
animation: glitch 3s infinite;
|
|
}
|
|
|
|
@keyframes glitch {
|
|
0%, 100% { transform: translateX(-50%); }
|
|
20% { transform: translateX(-52%); }
|
|
40% { transform: translateX(-48%); }
|
|
60% { transform: translateX(-51%); }
|
|
80% { transform: translateX(-49%); }
|
|
}
|
|
|
|
/* Neural Network Visualization */
|
|
#neural-network {
|
|
position: fixed;
|
|
top: 50%;
|
|
left: 50%;
|
|
transform: translate(-50%, -50%);
|
|
width: 600px;
|
|
height: 400px;
|
|
z-index: 50;
|
|
}
|
|
|
|
/* Control Panel */
|
|
.control-panel {
|
|
position: fixed;
|
|
bottom: 20px;
|
|
left: 50%;
|
|
transform: translateX(-50%);
|
|
width: 80%;
|
|
max-width: 800px;
|
|
background: rgba(0, 0, 0, 0.9);
|
|
border: 1px solid var(--neon-pink);
|
|
padding: 20px;
|
|
z-index: 100;
|
|
}
|
|
|
|
.command-input {
|
|
width: 100%;
|
|
background: transparent;
|
|
border: none;
|
|
border-bottom: 2px solid var(--electric-blue);
|
|
color: var(--electric-blue);
|
|
font-size: 18px;
|
|
padding: 10px 0;
|
|
outline: none;
|
|
font-family: 'Courier New', monospace;
|
|
}
|
|
|
|
.command-input::placeholder {
|
|
color: var(--electric-blue);
|
|
opacity: 0.5;
|
|
}
|
|
|
|
/* Data Cards */
|
|
.data-cards {
|
|
position: fixed;
|
|
right: 20px;
|
|
top: 50%;
|
|
transform: translateY(-50%);
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 15px;
|
|
z-index: 100;
|
|
}
|
|
|
|
.data-card {
|
|
background: rgba(136, 0, 255, 0.1);
|
|
border: 1px solid var(--laser-purple);
|
|
padding: 15px;
|
|
width: 200px;
|
|
transition: all 0.3s ease;
|
|
cursor: pointer;
|
|
}
|
|
|
|
.data-card:hover {
|
|
transform: translateX(-10px);
|
|
box-shadow: -5px 0 20px var(--laser-purple);
|
|
border-color: var(--neon-pink);
|
|
}
|
|
|
|
.card-title {
|
|
font-size: 12px;
|
|
color: var(--neon-pink);
|
|
margin-bottom: 10px;
|
|
}
|
|
|
|
.card-value {
|
|
font-size: 24px;
|
|
font-weight: bold;
|
|
color: var(--electric-blue);
|
|
}
|
|
|
|
/* Timeline */
|
|
.timeline {
|
|
position: fixed;
|
|
bottom: 120px;
|
|
left: 50%;
|
|
transform: translateX(-50%);
|
|
width: 80%;
|
|
max-width: 1000px;
|
|
height: 100px;
|
|
z-index: 100;
|
|
}
|
|
|
|
.timeline-track {
|
|
position: relative;
|
|
width: 100%;
|
|
height: 2px;
|
|
background: linear-gradient(90deg, var(--neon-pink), var(--electric-blue));
|
|
top: 50%;
|
|
}
|
|
|
|
.timeline-item {
|
|
position: absolute;
|
|
width: 20px;
|
|
height: 20px;
|
|
background: var(--neon-pink);
|
|
border-radius: 50%;
|
|
top: -9px;
|
|
cursor: pointer;
|
|
transition: all 0.3s ease;
|
|
box-shadow: 0 0 10px var(--neon-pink);
|
|
}
|
|
|
|
.timeline-item:hover {
|
|
transform: scale(1.5);
|
|
box-shadow: 0 0 20px var(--neon-pink);
|
|
}
|
|
|
|
/* Status Panel */
|
|
.status-panel {
|
|
position: fixed;
|
|
left: 20px;
|
|
top: 50%;
|
|
transform: translateY(-50%);
|
|
width: 250px;
|
|
z-index: 100;
|
|
}
|
|
|
|
.status-item {
|
|
margin-bottom: 20px;
|
|
}
|
|
|
|
.status-label {
|
|
font-size: 12px;
|
|
color: var(--electric-blue);
|
|
margin-bottom: 5px;
|
|
}
|
|
|
|
.status-bar {
|
|
height: 4px;
|
|
background: rgba(0, 255, 255, 0.2);
|
|
position: relative;
|
|
overflow: hidden;
|
|
}
|
|
|
|
.status-fill {
|
|
height: 100%;
|
|
background: var(--electric-blue);
|
|
box-shadow: 0 0 10px var(--electric-blue);
|
|
transition: width 0.5s ease;
|
|
position: relative;
|
|
}
|
|
|
|
.status-fill::after {
|
|
content: '';
|
|
position: absolute;
|
|
top: 0;
|
|
left: -100%;
|
|
width: 100%;
|
|
height: 100%;
|
|
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.6), transparent);
|
|
animation: shimmer 1.5s infinite;
|
|
}
|
|
|
|
@keyframes shimmer {
|
|
0% { left: -100%; }
|
|
100% { left: 100%; }
|
|
}
|
|
|
|
/* Responsive */
|
|
@media (max-width: 768px) {
|
|
.main-title {
|
|
font-size: 32px;
|
|
}
|
|
|
|
.data-cards {
|
|
right: 10px;
|
|
}
|
|
|
|
.data-card {
|
|
width: 150px;
|
|
padding: 10px;
|
|
}
|
|
|
|
.status-panel {
|
|
left: 10px;
|
|
width: 200px;
|
|
}
|
|
}
|
|
|
|
/* Scroll Reveal Animation */
|
|
.reveal {
|
|
opacity: 0;
|
|
transform: translateY(30px);
|
|
transition: all 0.6s ease;
|
|
}
|
|
|
|
.reveal.active {
|
|
opacity: 1;
|
|
transform: translateY(0);
|
|
}
|
|
|
|
/* Mobile Controls */
|
|
.mobile-controls {
|
|
position: fixed;
|
|
bottom: 20px;
|
|
left: 20px;
|
|
display: none;
|
|
z-index: 200;
|
|
}
|
|
|
|
@media (max-width: 768px) {
|
|
.mobile-controls {
|
|
display: block;
|
|
}
|
|
|
|
.control-btn {
|
|
display: inline-block;
|
|
width: 50px;
|
|
height: 50px;
|
|
background: rgba(0, 0, 0, 0.8);
|
|
border: 1px solid var(--electric-blue);
|
|
color: var(--electric-blue);
|
|
text-align: center;
|
|
line-height: 50px;
|
|
margin-right: 10px;
|
|
cursor: pointer;
|
|
}
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<!-- Loading Screen -->
|
|
<div class="loader" id="loader">
|
|
<div class="loader-circle"></div>
|
|
</div>
|
|
|
|
<!-- Particles Background -->
|
|
<div id="particles-js"></div>
|
|
|
|
<!-- Three.js Container -->
|
|
<div id="three-container"></div>
|
|
|
|
<!-- HUD Container -->
|
|
<div class="hud-container">
|
|
<!-- Top Info Bar -->
|
|
<div class="top-info-bar">
|
|
<div class="info-item">NEURAL CITY v3.0.7</div>
|
|
<div class="info-item" id="system-time">2078.01.01 00:00:00</div>
|
|
<div class="info-item" id="user-id">USER: NOMAD_2078</div>
|
|
</div>
|
|
|
|
<!-- Main Title -->
|
|
<h1 class="main-title reveal">NEURAL NETWORK CITY</h1>
|
|
|
|
<!-- Neural Network Visualization -->
|
|
<div id="neural-network"></div>
|
|
|
|
<!-- Data Cards -->
|
|
<div class="data-cards">
|
|
<div class="data-card reveal">
|
|
<div class="card-title">POPULATION</div>
|
|
<div class="card-value" id="population">15.2M</div>
|
|
</div>
|
|
<div class="data-card reveal">
|
|
<div class="card-title">ENERGY CONSUMPTION</div>
|
|
<div class="card-value" id="energy">847 GW</div>
|
|
</div>
|
|
<div class="data-card reveal">
|
|
<div class="card-title">NETWORK NODES</div>
|
|
<div class="card-value" id="nodes">45,892</div>
|
|
</div>
|
|
<div class="data-card reveal">
|
|
<div class="card-title">AI SYNC RATE</div>
|
|
<div class="card-value" id="ai-sync">98.7%</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Status Panel -->
|
|
<div class="status-panel">
|
|
<div class="status-item reveal">
|
|
<div class="status-label">SYSTEM LOAD</div>
|
|
<div class="status-bar">
|
|
<div class="status-fill" style="width: 75%"></div>
|
|
</div>
|
|
</div>
|
|
<div class="status-item reveal">
|
|
<div class="status-label">NEURAL ACTIVITY</div>
|
|
<div class="status-bar">
|
|
<div class="status-fill" style="width: 92%"></div>
|
|
</div>
|
|
</div>
|
|
<div class="status-item reveal">
|
|
<div class="status-label">DATA THROUGHPUT</div>
|
|
<div class="status-bar">
|
|
<div class="status-fill" style="width: 88%"></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Timeline -->
|
|
<div class="timeline">
|
|
<div class="timeline-track">
|
|
<div class="timeline-item" style="left: 10%" data-year="2025"></div>
|
|
<div class="timeline-item" style="left: 25%" data-year="2040"></div>
|
|
<div class="timeline-item" style="left: 40%" data-year="2055"></div>
|
|
<div class="timeline-item" style="left: 55%" data-year="2065"></div>
|
|
<div class="timeline-item" style="left: 70%" data-year="2070"></div>
|
|
<div class="timeline-item" style="left: 85%" data-year="2075"></div>
|
|
<div class="timeline-item" style="left: 95%" data-year="2078"></div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Control Panel -->
|
|
<div class="control-panel reveal">
|
|
<input type="text" class="command-input" placeholder="> ENTER COMMAND..." id="command-input">
|
|
</div>
|
|
|
|
<!-- Mobile Controls -->
|
|
<div class="mobile-controls">
|
|
<div class="control-btn"><i class="fas fa-search"></i></div>
|
|
<div class="control-btn"><i class="fas fa-cog"></i></div>
|
|
<div class="control-btn"><i class="fas fa-info"></i></div>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
// 系统初始化
|
|
class CyberPunkSystem {
|
|
constructor() {
|
|
this.scene = null;
|
|
this.camera = null;
|
|
this.renderer = null;
|
|
this.city = null;
|
|
this.particles = [];
|
|
this.isLoaded = false;
|
|
this.init();
|
|
}
|
|
|
|
init() {
|
|
this.setupParticles();
|
|
this.setupThreeJS();
|
|
this.setupEventListeners();
|
|
this.startAnimation();
|
|
this.updateSystemTime();
|
|
this.setupNeuralNetwork();
|
|
this.setupCommandInput();
|
|
|
|
// 移除加载画面
|
|
setTimeout(() => {
|
|
this.hideLoader();
|
|
}, 3000);
|
|
}
|
|
|
|
setupParticles() {
|
|
particlesJS('particles-js', {
|
|
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: 2, random: true },
|
|
line_linked: {
|
|
enable: true,
|
|
distance: 150,
|
|
color: '#00ffff',
|
|
opacity: 0.2,
|
|
width: 1
|
|
},
|
|
move: {
|
|
enable: true,
|
|
speed: 2,
|
|
direction: 'none',
|
|
random: true,
|
|
straight: false
|
|
}
|
|
},
|
|
interactivity: {
|
|
detect_on: 'canvas',
|
|
events: {
|
|
onhover: { enable: true, mode: 'grab' },
|
|
onclick: { enable: true, mode: 'push' }
|
|
}
|
|
},
|
|
retina_detect: true
|
|
});
|
|
}
|
|
|
|
setupThreeJS() {
|
|
const container = document.getElementById('three-container');
|
|
|
|
// Scene
|
|
this.scene = new THREE.Scene();
|
|
this.scene.fog = new THREE.FogExp2(0x0a0a0a, 0.001);
|
|
|
|
// Camera
|
|
this.camera = new THREE.PerspectiveCamera(
|
|
75,
|
|
window.innerWidth / window.innerHeight,
|
|
0.1,
|
|
1000
|
|
);
|
|
this.camera.position.set(0, 50, 100);
|
|
|
|
// Renderer
|
|
this.renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true });
|
|
this.renderer.setSize(window.innerWidth, window.innerHeight);
|
|
this.renderer.setClearColor(0x0a0a0a, 0);
|
|
container.appendChild(this.renderer.domElement);
|
|
|
|
// Lighting
|
|
const ambientLight = new THREE.AmbientLight(0x404040);
|
|
this.scene.add(ambientLight);
|
|
|
|
const pointLight1 = new THREE.PointLight(0xff00ff, 2, 200);
|
|
pointLight1.position.set(50, 50, 50);
|
|
this.scene.add(pointLight1);
|
|
|
|
const pointLight2 = new THREE.PointLight(0x00ffff, 2, 200);
|
|
pointLight2.position.set(-50, 50, -50);
|
|
this.scene.add(pointLight2);
|
|
|
|
// 创建未来城市
|
|
this.createFuturisticCity();
|
|
|
|
// 添加粒子系统
|
|
this.createParticleSystem();
|
|
}
|
|
|
|
createFuturisticCity() {
|
|
this.city = new THREE.Group();
|
|
|
|
// 创建建筑物
|
|
for (let i = 0; i < 50; i++) {
|
|
const geometry = new THREE.BoxGeometry(
|
|
Math.random() * 10 + 5,
|
|
Math.random() * 50 + 20,
|
|
Math.random() * 10 + 5
|
|
);
|
|
|
|
const material = new THREE.MeshPhongMaterial({
|
|
color: new THREE.Color().setHSL(Math.random(), 1, 0.5),
|
|
emissive: new THREE.Color().setHSL(Math.random(), 1, 0.1),
|
|
emissiveIntensity: 0.5
|
|
});
|
|
|
|
const building = new THREE.Mesh(geometry, material);
|
|
building.position.set(
|
|
(Math.random() - 0.5) * 200,
|
|
geometry.parameters.height / 2,
|
|
(Math.random() - 0.5) * 200
|
|
);
|
|
|
|
this.city.add(building);
|
|
|
|
// 添加窗户灯光
|
|
const windowsGeometry = new THREE.BoxGeometry(
|
|
geometry.parameters.width * 0.9,
|
|
geometry.parameters.height,
|
|
geometry.parameters.depth * 0.9
|
|
);
|
|
|
|
const windowsMaterial = new THREE.MeshBasicMaterial({
|
|
color: 0xff00ff,
|
|
wireframe: true,
|
|
opacity: 0.5,
|
|
transparent: true
|
|
});
|
|
|
|
const windows = new THREE.Mesh(windowsGeometry, windowsMaterial);
|
|
windows.position.copy(building.position);
|
|
this.city.add(windows);
|
|
}
|
|
|
|
// 创建地面
|
|
const groundGeometry = new THREE.PlaneGeometry(500, 500);
|
|
const groundMaterial = new THREE.MeshPhongMaterial({
|
|
color: 0x1a1a1a,
|
|
emissive: 0x0a0a0a,
|
|
emissiveIntensity: 0.2
|
|
});
|
|
|
|
const ground = new THREE.Mesh(groundGeometry, groundMaterial);
|
|
ground.rotation.x = -Math.PI / 2;
|
|
ground.position.y = -1;
|
|
this.city.add(ground);
|
|
|
|
// 创建网格
|
|
const gridHelper = new THREE.GridHelper(500, 50, 0x00ffff, 0x00ffff);
|
|
gridHelper.material.opacity = 0.3;
|
|
gridHelper.material.transparent = true;
|
|
this.city.add(gridHelper);
|
|
|
|
this.scene.add(this.city);
|
|
|
|
// 建筑物动画
|
|
this.animateBuildings();
|
|
}
|
|
|
|
createParticleSystem() {
|
|
const particleCount = 1000;
|
|
const geometry = new THREE.BufferGeometry();
|
|
const positions = new Float32Array(particleCount * 3);
|
|
const colors = new Float32Array(particleCount * 3);
|
|
|
|
for (let i = 0; i < particleCount; i++) {
|
|
positions[i * 3] = (Math.random() - 0.5) * 200;
|
|
positions[i * 3 + 1] = Math.random() * 100;
|
|
positions[i * 3 + 2] = (Math.random() - 0.5) * 200;
|
|
|
|
colors[i * 3] = Math.random();
|
|
colors[i * 3 + 1] = Math.random();
|
|
colors[i * 3 + 2] = Math.random();
|
|
}
|
|
|
|
geometry.setAttribute('position', new THREE.BufferAttribute(positions, 3));
|
|
geometry.setAttribute('color', new THREE.BufferAttribute(colors, 3));
|
|
|
|
const material = new THREE.PointsMaterial({
|
|
size: 2,
|
|
vertexColors: true,
|
|
blending: THREE.AdditiveBlending,
|
|
transparent: true,
|
|
opacity: 0.6
|
|
});
|
|
|
|
this.particles = new THREE.Points(geometry, material);
|
|
this.scene.add(this.particles);
|
|
}
|
|
|
|
animateBuildings() {
|
|
const buildings = this.city.children.filter(child => child.type === 'Mesh' && child.geometry.type === 'BoxGeometry');
|
|
|
|
buildings.forEach((building, index) => {
|
|
gsap.to(building.rotation, {
|
|
y: Math.PI * 2,
|
|
duration: 20 + index * 0.1,
|
|
repeat: -1,
|
|
ease: "none"
|
|
});
|
|
|
|
gsap.to(building.material.emissiveIntensity, {
|
|
value: 1,
|
|
duration: 2,
|
|
repeat: -1,
|
|
yoyo: true,
|
|
ease: "power2.inOut"
|
|
});
|
|
});
|
|
}
|
|
|
|
setupNeuralNetwork() {
|
|
const svg = d3.select("#neural-network")
|
|
.append("svg")
|
|
.attr("width", 600)
|
|
.attr("height", 400);
|
|
|
|
const nodes = [];
|
|
const links = [];
|
|
|
|
// 创建节点
|
|
for (let i = 0; i < 20; i++) {
|
|
nodes.push({
|
|
id: i,
|
|
x: Math.random() * 600,
|
|
y: Math.random() * 400,
|
|
vx: (Math.random() - 0.5) * 2,
|
|
vy: (Math.random() - 0.5) * 2,
|
|
group: Math.floor(Math.random() * 3)
|
|
});
|
|
}
|
|
|
|
// 创建连接
|
|
for (let i = 0; i < 30; i++) {
|
|
const source = Math.floor(Math.random() * nodes.length);
|
|
const target = Math.floor(Math.random() * nodes.length);
|
|
if (source !== target) {
|
|
links.push({
|
|
source: source,
|
|
target: target,
|
|
value: Math.random() * 10
|
|
});
|
|
}
|
|
}
|
|
|
|
// 绘制连接线
|
|
const link = svg.selectAll(".link")
|
|
.data(links)
|
|
.enter().append("line")
|
|
.attr("class", "link")
|
|
.style("stroke", "#00ffff")
|
|
.style("stroke-opacity", 0.6)
|
|
.style("stroke-width", d => Math.sqrt(d.value));
|
|
|
|
// 绘制节点
|
|
const node = svg.selectAll(".node")
|
|
.data(nodes)
|
|
.enter().append("circle")
|
|
.attr("class", "node")
|
|
.attr("r", d => 5 + Math.random() * 5)
|
|
.style("fill", d => ["#ff00ff", "#00ffff", "#8800ff"][d.group])
|
|
.style("stroke", "#fff")
|
|
.style("stroke-width", 1.5);
|
|
|
|
// 动画
|
|
const animateNeuralNetwork = () => {
|
|
// 更新节点位置
|
|
nodes.forEach(node => {
|
|
node.x += node.vx;
|
|
node.y += node.vy;
|
|
|
|
if (node.x < 0 || node.x > 600) node.vx *= -1;
|
|
if (node.y < 0 || node.y > 400) node.vy *= -1;
|
|
});
|
|
|
|
// 更新连接
|
|
link
|
|
.attr("x1", d => nodes[d.source].x)
|
|
.attr("y1", d => nodes[d.source].y)
|
|
.attr("x2", d => nodes[d.target].x)
|
|
.attr("y2", d => nodes[d.target].y);
|
|
|
|
// 更新节点
|
|
node
|
|
.attr("cx", d => d.x)
|
|
.attr("cy", d => d.y);
|
|
|
|
// 数据脉冲效果
|
|
link.style("stroke-opacity", d => 0.3 + Math.random() * 0.7);
|
|
|
|
requestAnimationFrame(animateNeuralNetwork);
|
|
};
|
|
|
|
animateNeuralNetwork();
|
|
}
|
|
|
|
setupCommandInput() {
|
|
const input = document.getElementById('command-input');
|
|
const commandMap = {
|
|
'show energy': () => this.showEnergyDistribution(),
|
|
'switch district': (district) => this.switchDistrict(district),
|
|
'diagnostic': () => this.startDiagnostic(),
|
|
'help': () => this.showHelp()
|
|
};
|
|
|
|
input.addEventListener('keypress', (e) => {
|
|
if (e.key === 'Enter') {
|
|
const command = input.value.toLowerCase();
|
|
this.executeCommand(command);
|
|
input.value = '';
|
|
}
|
|
});
|
|
}
|
|
|
|
executeCommand(command) {
|
|
const commands = Object.keys(commandMap);
|
|
for (let cmd of commands) {
|
|
if (command.includes(cmd)) {
|
|
commandMap[cmd]();
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
showEnergyDistribution() {
|
|
// 创建能量分布可视化
|
|
const energyData = {
|
|
residential: Math.random() * 30 + 20,
|
|
commercial: Math.random() * 40 + 30,
|
|
industrial: Math.random() * 20 + 10,
|
|
transport: Math.random() * 15 + 5
|
|
};
|
|
|
|
// 更新数据卡片
|
|
Object.keys(energyData).forEach((sector, index) => {
|
|
if (index < 4) {
|
|
const cards = document.querySelectorAll('.data-card');
|
|
const card = cards[index];
|
|
const valueElement = card.querySelector('.card-value');
|
|
valueElement.textContent = `${energyData[sector].toFixed(1)}%`;
|
|
}
|
|
});
|
|
}
|
|
|
|
switchDistrict(district) {
|
|
// 切换城区视图
|
|
gsap.to(this.camera.position, {
|
|
x: (Math.random() - 0.5) * 100,
|
|
y: 30 + Math.random() * 40,
|
|
z: 50 + Math.random() * 50,
|
|
duration: 2,
|
|
ease: "power2.inOut"
|
|
});
|
|
}
|
|
|
|
startDiagnostic() {
|
|
// 启动系统诊断
|
|
const diagnosticPanel = document.createElement('div');
|
|
diagnosticPanel.className = 'panel diagnostic-panel';
|
|
diagnosticPanel.innerHTML = `
|
|
<h3>SYSTEM DIAGNOSTIC</h3>
|
|
<div id="diagnostic-log"></div>
|
|
`;
|
|
diagnosticPanel.style.position = 'fixed';
|
|
diagnosticPanel.style.top = '50%';
|
|
diagnosticPanel.style.left = '50%';
|
|
diagnosticPanel.style.transform = 'translate(-50%, -50%)';
|
|
diagnosticPanel.style.zIndex = '200';
|
|
document.body.appendChild(diagnosticPanel);
|
|
|
|
// 模拟诊断过程
|
|
const log = document.getElementById('diagnostic-log');
|
|
const diagnosticSteps = [
|
|
'Scanning neural networks...',
|
|
'Checking data integrity...',
|
|
'Analyzing system load...',
|
|
'Verifying security protocols...',
|
|
'All systems operational'
|
|
];
|
|
|
|
diagnosticSteps.forEach((step, index) => {
|
|
setTimeout(() => {
|
|
log.innerHTML += `<p>${step}</p>`;
|
|
if (index === diagnosticSteps.length - 1) {
|
|
setTimeout(() => {
|
|
diagnosticPanel.remove();
|
|
}, 2000);
|
|
}
|
|
}, index * 1000);
|
|
});
|
|
}
|
|
|
|
showHelp() {
|
|
alert('Available commands:\n- "show energy": Display energy distribution\n- "switch district [name]": Change view district\n- "diagnostic": Run system diagnostic\n- Type any district name to focus view');
|
|
}
|
|
|
|
updateSystemTime() {
|
|
const timeElement = document.getElementById('system-time');
|
|
|
|
const updateTime = () => {
|
|
const now = new Date();
|
|
const year = 2078;
|
|
const month = String(now.getMonth() + 1).padStart(2, '0');
|
|
const day = String(now.getDate()).padStart(2, '0');
|
|
const hours = String(now.getHours()).padStart(2, '0');
|
|
const minutes = String(now.getMinutes()).padStart(2, '0');
|
|
const seconds = String(now.getSeconds()).padStart(2, '0');
|
|
|
|
timeElement.textContent = `${year}.${month}.${day} ${hours}:${minutes}:${seconds}`;
|
|
};
|
|
|
|
updateTime();
|
|
setInterval(updateTime, 1000);
|
|
}
|
|
|
|
setupEventListeners() {
|
|
// Mouse/Touch controls for camera
|
|
let mouseX = 0;
|
|
let mouseY = 0;
|
|
let targetX = 0;
|
|
let targetY = 0;
|
|
|
|
document.addEventListener('mousemove', (e) => {
|
|
mouseX = (e.clientX - window.innerWidth / 2) / 100;
|
|
mouseY = (e.clientY - window.innerHeight / 2) / 100;
|
|
});
|
|
|
|
// Scroll reveal
|
|
const revealElements = document.querySelectorAll('.reveal');
|
|
const revealOnScroll = () => {
|
|
revealElements.forEach(element => {
|
|
const elementTop = element.getBoundingClientRect().top;
|
|
const elementVisible = 150;
|
|
|
|
if (elementTop < window.innerHeight - elementVisible) {
|
|
element.classList.add('active');
|
|
}
|
|
});
|
|
};
|
|
|
|
window.addEventListener('scroll', revealOnScroll);
|
|
revealOnScroll();
|
|
|
|
// Window resize
|
|
window.addEventListener('resize', () => {
|
|
this.camera.aspect = window.innerWidth / window.innerHeight;
|
|
this.camera.updateProjectionMatrix();
|
|
this.renderer.setSize(window.innerWidth, window.innerHeight);
|
|
});
|
|
|
|
// Animate camera
|
|
const animateCamera = () => {
|
|
targetX += (mouseX - targetX) * 0.05;
|
|
targetY += (mouseY - targetY) * 0.05;
|
|
|
|
gsap.to(this.camera.position, {
|
|
x: targetX * 10,
|
|
y: 50 + targetY * 20,
|
|
duration: 0.5,
|
|
ease: "power2.out"
|
|
});
|
|
|
|
requestAnimationFrame(animateCamera);
|
|
};
|
|
|
|
animateCamera();
|
|
}
|
|
|
|
startAnimation() {
|
|
const animate = () => {
|
|
requestAnimationFrame(animate);
|
|
|
|
// 旋转粒子
|
|
if (this.particles) {
|
|
this.particles.rotation.y += 0.001;
|
|
this.particles.rotation.x += 0.0005;
|
|
}
|
|
|
|
// 旋转城市
|
|
if (this.city) {
|
|
this.city.rotation.y += 0.0005;
|
|
}
|
|
|
|
// 更新相机位置
|
|
this.camera.lookAt(0, 0, 0);
|
|
|
|
// 渲染场景
|
|
this.renderer.render(this.scene, this.camera);
|
|
|
|
// 更新 TWEEN
|
|
if (window.TWEEN) {
|
|
TWEEN.update();
|
|
}
|
|
};
|
|
|
|
animate();
|
|
}
|
|
|
|
hideLoader() {
|
|
const loader = document.getElementById('loader');
|
|
gsap.to(loader, {
|
|
opacity: 0,
|
|
duration: 1,
|
|
onComplete: () => {
|
|
loader.style.display = 'none';
|
|
this.isLoaded = true;
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
// 页面加载完成后初始化系统
|
|
document.addEventListener('DOMContentLoaded', () => {
|
|
try {
|
|
window.cyberPunkSystem = new CyberPunkSystem();
|
|
} catch (error) {
|
|
console.error('System initialization failed:', error);
|
|
}
|
|
});
|
|
|
|
// 紧急备用方案
|
|
window.addEventListener('error', (e) => {
|
|
console.error('Critical error detected:', e.error);
|
|
document.body.innerHTML = `
|
|
<div style="background: #0a0a0a; color: #00ffff; padding: 20px; font-family: monospace;">
|
|
<h1 style="color: #ff00ff;">SYSTEM CRITICAL FAILURE</h1>
|
|
<p>Neural network connection lost...</p>
|
|
<p>Error: ${e.error.message}</p>
|
|
<button onclick="location.reload()" style="background: #ff00ff; color: #000; border: none; padding: 10px 20px; cursor: pointer; margin-top: 20px;">REBOOT SYSTEM</button>
|
|
</div>
|
|
`;
|
|
});
|
|
</script>
|
|
</body>
|
|
</html>
|