838 lines
30 KiB
HTML
838 lines
30 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 Visualization</title>
|
|
|
|
<!-- External Resources -->
|
|
<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;500;700;900&family=Rajdhani:wght@300;500;700&family=Share+Tech+Mono&display=swap" rel="stylesheet">
|
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
|
|
|
<!-- Libraries -->
|
|
<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/gsap/3.12.2/ScrollTrigger.min.js"></script>
|
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/7.8.5/d3.min.js"></script>
|
|
|
|
<style>
|
|
:root {
|
|
--neon-pink: #ff00ff;
|
|
--neon-blue: #00ffff;
|
|
--neon-purple: #8800ff;
|
|
--neon-green: #00ff41;
|
|
--dark-bg: #050505;
|
|
--panel-bg: rgba(10, 10, 16, 0.65);
|
|
--border-glow: 0 0 10px var(--neon-blue);
|
|
--text-glow: 0 0 5px currentColor;
|
|
--font-main: 'Orbitron', sans-serif;
|
|
--font-body: 'Rajdhani', sans-serif;
|
|
--font-mono: 'Share Tech Mono', monospace;
|
|
}
|
|
|
|
* {
|
|
margin: 0;
|
|
padding: 0;
|
|
box-sizing: border-box;
|
|
scrollbar-width: none;
|
|
}
|
|
|
|
body {
|
|
background-color: var(--dark-bg);
|
|
color: #fff;
|
|
font-family: var(--font-body);
|
|
overflow-x: hidden;
|
|
height: 100vh;
|
|
width: 100vw;
|
|
}
|
|
|
|
/* --- Loading Screen --- */
|
|
#loader {
|
|
position: fixed;
|
|
top: 0;
|
|
left: 0;
|
|
width: 100%;
|
|
height: 100%;
|
|
background: #000;
|
|
z-index: 9999;
|
|
display: flex;
|
|
justify-content: center;
|
|
align-items: center;
|
|
flex-direction: column;
|
|
}
|
|
|
|
.loader-node {
|
|
width: 20px;
|
|
height: 20px;
|
|
background: var(--neon-blue);
|
|
border-radius: 50%;
|
|
box-shadow: 0 0 20px var(--neon-blue);
|
|
}
|
|
|
|
.loader-text {
|
|
margin-top: 20px;
|
|
font-family: var(--font-main);
|
|
color: var(--neon-pink);
|
|
letter-spacing: 2px;
|
|
text-transform: uppercase;
|
|
animation: blink 1s infinite;
|
|
}
|
|
|
|
/* --- 3D Background --- */
|
|
#canvas-container {
|
|
position: fixed;
|
|
top: 0;
|
|
left: 0;
|
|
width: 100%;
|
|
height: 100%;
|
|
z-index: 0;
|
|
pointer-events: all; /* Allow 3D interaction */
|
|
}
|
|
|
|
/* --- UI Overlay Layer --- */
|
|
#ui-layer {
|
|
position: relative;
|
|
z-index: 10;
|
|
pointer-events: none; /* Let clicks pass through to 3D where needed */
|
|
width: 100%;
|
|
min-height: 100vh;
|
|
display: grid;
|
|
grid-template-rows: auto 1fr auto;
|
|
padding: 20px;
|
|
}
|
|
|
|
/* Common UI Elements */
|
|
.hud-panel {
|
|
background: var(--panel-bg);
|
|
backdrop-filter: blur(10px);
|
|
border: 1px solid rgba(0, 255, 255, 0.2);
|
|
border-left: 3px solid var(--neon-blue);
|
|
padding: 15px;
|
|
pointer-events: auto;
|
|
position: relative;
|
|
clip-path: polygon(
|
|
0 0,
|
|
100% 0,
|
|
100% calc(100% - 15px),
|
|
calc(100% - 15px) 100%,
|
|
0 100%
|
|
);
|
|
}
|
|
|
|
.hud-title {
|
|
font-family: var(--font-main);
|
|
font-size: 0.9rem;
|
|
color: var(--neon-blue);
|
|
text-shadow: var(--text-glow);
|
|
margin-bottom: 10px;
|
|
border-bottom: 1px solid rgba(0, 255, 255, 0.3);
|
|
padding-bottom: 5px;
|
|
display: flex;
|
|
justify-content: space-between;
|
|
}
|
|
|
|
/* --- Header / Top Bar --- */
|
|
header {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
padding: 10px 30px;
|
|
pointer-events: auto;
|
|
}
|
|
|
|
.logo {
|
|
font-family: var(--font-main);
|
|
font-size: 2rem;
|
|
font-weight: 900;
|
|
color: #fff;
|
|
text-transform: uppercase;
|
|
text-shadow: 2px 2px 0px var(--neon-pink), -2px -2px 0px var(--neon-blue);
|
|
letter-spacing: 2px;
|
|
}
|
|
|
|
.sys-status {
|
|
display: flex;
|
|
gap: 20px;
|
|
font-family: var(--font-mono);
|
|
font-size: 0.8rem;
|
|
color: var(--neon-green);
|
|
}
|
|
|
|
/* --- Main Content Area --- */
|
|
main {
|
|
display: grid;
|
|
grid-template-columns: 300px 1fr 300px;
|
|
gap: 20px;
|
|
padding: 20px;
|
|
height: calc(100vh - 140px);
|
|
}
|
|
|
|
/* Left Column: Monitoring */
|
|
.monitor-col {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 15px;
|
|
}
|
|
|
|
.chart-container {
|
|
height: 100px;
|
|
width: 100%;
|
|
background: rgba(0,0,0,0.3);
|
|
position: relative;
|
|
overflow: hidden;
|
|
}
|
|
|
|
.log-window {
|
|
height: 150px;
|
|
overflow: hidden;
|
|
font-family: var(--font-mono);
|
|
font-size: 0.7rem;
|
|
color: #aaa;
|
|
line-height: 1.4;
|
|
}
|
|
.log-entry { margin-bottom: 4px; }
|
|
.log-entry::before { content: '> '; color: var(--neon-pink); }
|
|
.log-warn { color: #ffff00; }
|
|
.log-err { color: #ff0000; }
|
|
|
|
/* Center Column: Neural Net & Interaction */
|
|
.center-col {
|
|
display: flex;
|
|
flex-direction: column;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
position: relative;
|
|
}
|
|
|
|
#neural-canvas-container {
|
|
width: 100%;
|
|
height: 40%;
|
|
pointer-events: auto;
|
|
position: relative;
|
|
}
|
|
|
|
#neural-canvas {
|
|
width: 100%;
|
|
height: 100%;
|
|
}
|
|
|
|
/* Timeline */
|
|
.timeline-container {
|
|
width: 100%;
|
|
height: 80px;
|
|
background: linear-gradient(90deg, transparent, rgba(0, 255, 255, 0.1), transparent);
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
overflow-x: auto;
|
|
gap: 40px;
|
|
pointer-events: auto;
|
|
mask-image: linear-gradient(90deg, transparent, black 20%, black 80%, transparent);
|
|
}
|
|
|
|
.time-node {
|
|
padding: 5px 15px;
|
|
border: 1px solid var(--neon-purple);
|
|
border-radius: 15px;
|
|
color: var(--neon-purple);
|
|
cursor: pointer;
|
|
font-family: var(--font-mono);
|
|
transition: all 0.3s ease;
|
|
white-space: nowrap;
|
|
}
|
|
|
|
.time-node:hover, .time-node.active {
|
|
background: var(--neon-purple);
|
|
color: #fff;
|
|
box-shadow: 0 0 15px var(--neon-purple);
|
|
}
|
|
|
|
/* Right Column: Holographic Cards */
|
|
.right-col {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 20px;
|
|
perspective: 1000px; /* Essential for 3D tilt */
|
|
}
|
|
|
|
.holo-card {
|
|
background: linear-gradient(135deg, rgba(255, 255, 255, 0.05), rgba(255, 255, 255, 0.01));
|
|
border: 1px solid rgba(255, 255, 255, 0.1);
|
|
padding: 20px;
|
|
border-radius: 8px;
|
|
transform-style: preserve-3d;
|
|
transition: transform 0.1s ease-out;
|
|
pointer-events: auto;
|
|
position: relative;
|
|
overflow: hidden;
|
|
}
|
|
|
|
.holo-card::after {
|
|
content: '';
|
|
position: absolute;
|
|
top: -50%; left: -50%;
|
|
width: 200%; height: 200%;
|
|
background: linear-gradient(to bottom right, transparent, rgba(255, 255, 255, 0.1), transparent);
|
|
transform: rotate(45deg);
|
|
pointer-events: none;
|
|
}
|
|
|
|
.holo-card h3 {
|
|
font-family: var(--font-main);
|
|
color: var(--neon-pink);
|
|
margin-bottom: 10px;
|
|
font-size: 1.1rem;
|
|
}
|
|
|
|
.holo-stat {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
margin-bottom: 5px;
|
|
font-size: 0.9rem;
|
|
border-bottom: 1px dashed rgba(255,255,255,0.1);
|
|
}
|
|
|
|
/* --- Footer / Console --- */
|
|
footer {
|
|
padding: 20px;
|
|
pointer-events: auto;
|
|
}
|
|
|
|
.command-bar {
|
|
width: 100%;
|
|
max-width: 800px;
|
|
margin: 0 auto;
|
|
background: rgba(0, 0, 0, 0.8);
|
|
border: 1px solid var(--neon-blue);
|
|
display: flex;
|
|
align-items: center;
|
|
padding: 10px 20px;
|
|
box-shadow: 0 0 20px rgba(0, 255, 255, 0.1);
|
|
}
|
|
|
|
.cmd-prompt {
|
|
color: var(--neon-green);
|
|
margin-right: 10px;
|
|
font-family: var(--font-mono);
|
|
}
|
|
|
|
#cmd-input {
|
|
background: transparent;
|
|
border: none;
|
|
color: #fff;
|
|
width: 100%;
|
|
font-family: var(--font-mono);
|
|
font-size: 1rem;
|
|
outline: none;
|
|
text-shadow: 0 0 2px var(--neon-blue);
|
|
}
|
|
|
|
/* --- Glitch Keyframes --- */
|
|
@keyframes blink {
|
|
0%, 100% { opacity: 1; }
|
|
50% { opacity: 0.3; }
|
|
}
|
|
|
|
/* Responsive */
|
|
@media (max-width: 1024px) {
|
|
main {
|
|
grid-template-columns: 1fr;
|
|
grid-template-rows: auto auto auto;
|
|
height: auto;
|
|
overflow-y: scroll;
|
|
}
|
|
.center-col { order: -1; height: 400px; }
|
|
body { overflow-y: auto; }
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
|
|
<!-- Module 1: Intro Animation -->
|
|
<div id="loader">
|
|
<div class="loader-node" id="loader-node"></div>
|
|
<div class="loader-text">Initializing Neural Uplink...</div>
|
|
</div>
|
|
|
|
<!-- Module 2: Three.js Background -->
|
|
<div id="canvas-container"></div>
|
|
|
|
<!-- Main UI Layer -->
|
|
<div id="ui-layer">
|
|
<header>
|
|
<div class="logo">NightCity<span style="color:var(--neon-blue)">.AI</span></div>
|
|
<div class="sys-status">
|
|
<span><i class="fas fa-satellite-dish"></i> CONNECTED</span>
|
|
<span id="clock">2078.05.12 23:45:01</span>
|
|
<span style="color:var(--neon-pink)">FPS: <span id="fps-counter">60</span></span>
|
|
</div>
|
|
</header>
|
|
|
|
<main>
|
|
<!-- Module 4: System Monitor -->
|
|
<aside class="monitor-col">
|
|
<div class="hud-panel">
|
|
<div class="hud-title"><span>CPU CORE LOAD</span><span>SYS.01</span></div>
|
|
<div class="chart-container" id="cpu-chart">
|
|
<!-- Canvas inserted by JS -->
|
|
</div>
|
|
<div style="display:flex; justify-content:space-between; margin-top:5px; font-size:0.8rem;">
|
|
<span>Core 0: <span style="color:var(--neon-blue)">89%</span></span>
|
|
<span>Temp: <span style="color:var(--neon-pink)">92°C</span></span>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="hud-panel">
|
|
<div class="hud-title"><span>NET TRAFFIC</span><span>TBs/s</span></div>
|
|
<canvas id="waveform-canvas" height="60" width="260"></canvas>
|
|
</div>
|
|
|
|
<div class="hud-panel">
|
|
<div class="hud-title"><span>SYSTEM LOGS</span><i class="fas fa-list"></i></div>
|
|
<div class="log-window" id="log-window">
|
|
<!-- Logs injected by JS -->
|
|
</div>
|
|
</div>
|
|
</aside>
|
|
|
|
<!-- Module 3 & 5: Center Visuals -->
|
|
<section class="center-col">
|
|
<div id="neural-canvas-container">
|
|
<!-- D3/Canvas Neural Net -->
|
|
<div style="position:absolute; top:10px; left:10px; font-family:var(--font-mono); font-size:0.7rem; color:rgba(255,255,255,0.5);">
|
|
NEURAL INTERFACE :: ACTIVE<br>
|
|
NODES: 4,096<br>
|
|
SYNAPSE RATE: 99.8%
|
|
</div>
|
|
<canvas id="neural-canvas"></canvas>
|
|
</div>
|
|
|
|
<!-- Module 5: Timeline -->
|
|
<div class="timeline-container">
|
|
<div class="time-node active" data-year="2078">2078</div>
|
|
<div class="time-node" data-year="2070">2070</div>
|
|
<div class="time-node" data-year="2063">2063</div>
|
|
<div class="time-node" data-year="2055">2055</div>
|
|
<div class="time-node" data-year="2049">2049</div>
|
|
<div class="time-node" data-year="2024">2024</div>
|
|
</div>
|
|
</section>
|
|
|
|
<!-- Module 6: Holographic Cards -->
|
|
<aside class="right-col">
|
|
<div class="hud-panel holo-card">
|
|
<h3><i class="fas fa-users"></i> POPULATION</h3>
|
|
<div class="holo-stat"><span>Citizens</span> <span style="color:var(--neon-blue)">12,405,921</span></div>
|
|
<div class="holo-stat"><span>Androids</span> <span style="color:var(--neon-purple)">8,200,103</span></div>
|
|
<div class="holo-stat"><span>Augmented</span> <span style="color:var(--neon-pink)">95.4%</span></div>
|
|
</div>
|
|
|
|
<div class="hud-panel holo-card">
|
|
<h3><i class="fas fa-bolt"></i> ENERGY GRID</h3>
|
|
<div class="holo-stat"><span>Fusion Output</span> <span>104%</span></div>
|
|
<div class="holo-stat"><span>Consumption</span> <span class="blink">CRITICAL</span></div>
|
|
<div style="width:100%; height:4px; background:#333; margin-top:5px;">
|
|
<div style="width:85%; height:100%; background:linear-gradient(90deg, var(--neon-blue), var(--neon-pink));"></div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="hud-panel holo-card">
|
|
<h3><i class="fas fa-network-wired"></i> SECURITY</h3>
|
|
<div class="holo-stat"><span>Firewall</span> <span style="color:var(--neon-green)">ACTIVE</span></div>
|
|
<div class="holo-stat"><span>Intrusions</span> <span>0</span></div>
|
|
<div class="holo-stat"><span>Threat Level</span> <span style="color:var(--neon-blue)">LOW</span></div>
|
|
</div>
|
|
</aside>
|
|
</main>
|
|
|
|
<!-- Module 7: Command Console -->
|
|
<footer>
|
|
<div class="command-bar">
|
|
<span class="cmd-prompt">root@nightcity:~#</span>
|
|
<input type="text" id="cmd-input" placeholder="Enter system command..." autocomplete="off">
|
|
</div>
|
|
</footer>
|
|
</div>
|
|
|
|
<!-- Logic -->
|
|
<script>
|
|
// --- Configuration & Global State ---
|
|
const config = {
|
|
colors: {
|
|
pink: 0xff00ff,
|
|
blue: 0x00ffff,
|
|
purple: 0x8800ff,
|
|
black: 0x050505
|
|
}
|
|
};
|
|
|
|
// --- Module 1: Intro Sequence ---
|
|
function initIntro() {
|
|
const tl = gsap.timeline();
|
|
|
|
tl.to("#loader-node", {
|
|
scale: 50,
|
|
duration: 1.5,
|
|
ease: "power2.inOut",
|
|
backgroundColor: "#fff"
|
|
})
|
|
.to("#loader", {
|
|
opacity: 0,
|
|
duration: 1,
|
|
pointerEvents: "none",
|
|
onComplete: () => {
|
|
initCity3D(); // Start heavy rendering after loader
|
|
}
|
|
})
|
|
.from("header", { y: -50, opacity: 0, duration: 0.5 })
|
|
.from("main", { opacity: 0, scale: 0.95, duration: 0.8 }, "-=0.3")
|
|
.from(".command-bar", { y: 50, opacity: 0, duration: 0.5 }, "-=0.5");
|
|
}
|
|
|
|
// --- Module 2: Three.js City ---
|
|
let scene, camera, renderer, cityGroup;
|
|
const clock = new THREE.Clock();
|
|
|
|
function initCity3D() {
|
|
const container = document.getElementById('canvas-container');
|
|
|
|
// Scene Setup
|
|
scene = new THREE.Scene();
|
|
scene.background = new THREE.Color(config.colors.black);
|
|
scene.fog = new THREE.FogExp2(config.colors.black, 0.002);
|
|
|
|
// Camera
|
|
camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 0.1, 1000);
|
|
camera.position.set(0, 40, 80);
|
|
camera.lookAt(0, 0, 0);
|
|
|
|
// Renderer
|
|
renderer = new THREE.WebGLRenderer({ antialias: true, alpha: false });
|
|
renderer.setSize(window.innerWidth, window.innerHeight);
|
|
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
|
|
container.appendChild(renderer.domElement);
|
|
|
|
// Lighting
|
|
const ambientLight = new THREE.AmbientLight(0x404040, 2);
|
|
scene.add(ambientLight);
|
|
|
|
const dirLight = new THREE.DirectionalLight(config.colors.blue, 0.8);
|
|
dirLight.position.set(100, 100, 50);
|
|
scene.add(dirLight);
|
|
|
|
// City Generation
|
|
cityGroup = new THREE.Group();
|
|
const geometry = new THREE.BoxGeometry(1, 1, 1);
|
|
// Instanced mesh for performance would be better, but simple merging for visual variety here
|
|
|
|
const materialCore = new THREE.MeshPhongMaterial({ color: 0x111111, shininess: 100 });
|
|
const materialGlow = new THREE.LineBasicMaterial({ color: config.colors.blue, transparent: true, opacity: 0.3 });
|
|
|
|
for (let i = 0; i < 200; i++) {
|
|
const h = Math.random() * 20 + 5;
|
|
const x = (Math.random() - 0.5) * 200;
|
|
const z = (Math.random() - 0.5) * 200;
|
|
|
|
// Building Mesh
|
|
const building = new THREE.Mesh(geometry, materialCore);
|
|
building.position.set(x, h/2, z);
|
|
building.scale.set(Math.random() * 5 + 2, h, Math.random() * 5 + 2);
|
|
building.castShadow = true;
|
|
building.receiveShadow = true;
|
|
|
|
// Edges (Neon Lines)
|
|
const edges = new THREE.EdgesGeometry(building.geometry);
|
|
const line = new THREE.LineSegments(edges, materialGlow);
|
|
building.add(line);
|
|
|
|
cityGroup.add(building);
|
|
}
|
|
scene.add(cityGroup);
|
|
|
|
// Floor Grid
|
|
const gridHelper = new THREE.GridHelper(300, 60, config.colors.pink, 0x222222);
|
|
scene.add(gridHelper);
|
|
|
|
// Flying Particles
|
|
const particlesGeom = new THREE.BufferGeometry();
|
|
const particleCount = 1000;
|
|
const posArray = new Float32Array(particleCount * 3);
|
|
|
|
for(let i = 0; i < particleCount * 3; i++) {
|
|
posArray[i] = (Math.random() - 0.5) * 250;
|
|
}
|
|
|
|
particlesGeom.setAttribute('position', new THREE.BufferAttribute(posArray, 3));
|
|
const particlesMat = new THREE.PointsMaterial({
|
|
size: 0.5,
|
|
color: config.colors.pink,
|
|
transparent: true,
|
|
opacity: 0.8,
|
|
blending: THREE.AdditiveBlending
|
|
});
|
|
const particlesMesh = new THREE.Points(particlesGeom, particlesMat);
|
|
scene.add(particlesMesh);
|
|
|
|
// Animation Loop
|
|
function animate() {
|
|
requestAnimationFrame(animate);
|
|
|
|
const delta = clock.getDelta();
|
|
const time = clock.getElapsedTime();
|
|
|
|
// Rotate City slowly
|
|
cityGroup.rotation.y = Math.sin(time * 0.05) * 0.1;
|
|
|
|
// Move Particles
|
|
particlesMesh.rotation.y = time * 0.02;
|
|
particlesMesh.position.y = Math.sin(time * 0.5) * 2 + 10;
|
|
|
|
// Camera subtle movement
|
|
camera.position.x = Math.sin(time * 0.1) * 80;
|
|
camera.position.z = Math.cos(time * 0.1) * 80;
|
|
camera.lookAt(0, 10, 0);
|
|
|
|
renderer.render(scene, camera);
|
|
}
|
|
animate();
|
|
|
|
// Resize Handler
|
|
window.addEventListener('resize', () => {
|
|
camera.aspect = window.innerWidth / window.innerHeight;
|
|
camera.updateProjectionMatrix();
|
|
renderer.setSize(window.innerWidth, window.innerHeight);
|
|
});
|
|
}
|
|
|
|
// --- Module 3: Neural Network Canvas ---
|
|
function initNeuralNet() {
|
|
const canvas = document.getElementById('neural-canvas');
|
|
const ctx = canvas.getContext('2d');
|
|
let nodes = [];
|
|
let width, height;
|
|
|
|
function resize() {
|
|
const parent = canvas.parentElement;
|
|
width = parent.clientWidth;
|
|
height = parent.clientHeight;
|
|
canvas.width = width;
|
|
canvas.height = height;
|
|
initNodes();
|
|
}
|
|
|
|
function initNodes() {
|
|
nodes = [];
|
|
for(let i=0; i<40; i++) {
|
|
nodes.push({
|
|
x: Math.random() * width,
|
|
y: Math.random() * height,
|
|
vx: (Math.random() - 0.5) * 0.5,
|
|
vy: (Math.random() - 0.5) * 0.5,
|
|
pulse: 0
|
|
});
|
|
}
|
|
}
|
|
|
|
function draw() {
|
|
ctx.clearRect(0, 0, width, height);
|
|
ctx.fillStyle = '#00ffff';
|
|
ctx.strokeStyle = 'rgba(0, 255, 255, 0.2)';
|
|
|
|
// Draw Connections
|
|
for(let i=0; i<nodes.length; i++) {
|
|
let nodeA = nodes[i];
|
|
|
|
// Update Position
|
|
nodeA.x += nodeA.vx;
|
|
nodeA.y += nodeA.vy;
|
|
|
|
// Bounce
|
|
if(nodeA.x < 0 || nodeA.x > width) nodeA.vx *= -1;
|
|
if(nodeA.y < 0 || nodeA.y > height) nodeA.vy *= -1;
|
|
|
|
// Draw Node
|
|
ctx.beginPath();
|
|
ctx.arc(nodeA.x, nodeA.y, 2, 0, Math.PI*2);
|
|
ctx.fill();
|
|
|
|
// Pulse effect
|
|
if(Math.random() > 0.98) nodeA.pulse = 1;
|
|
if(nodeA.pulse > 0) {
|
|
ctx.beginPath();
|
|
ctx.arc(nodeA.x, nodeA.y, 10 * (1-nodeA.pulse) + 2, 0, Math.PI*2);
|
|
ctx.strokeStyle = `rgba(255, 0, 255, ${nodeA.pulse})`;
|
|
ctx.stroke();
|
|
nodeA.pulse -= 0.05;
|
|
}
|
|
|
|
// Links
|
|
for(let j=i+1; j<nodes.length; j++) {
|
|
let nodeB = nodes[j];
|
|
let dx = nodeA.x - nodeB.x;
|
|
let dy = nodeA.y - nodeB.y;
|
|
let dist = Math.sqrt(dx*dx + dy*dy);
|
|
|
|
if(dist < 100) {
|
|
ctx.beginPath();
|
|
ctx.moveTo(nodeA.x, nodeA.y);
|
|
ctx.lineTo(nodeB.x, nodeB.y);
|
|
ctx.strokeStyle = `rgba(0, 255, 255, ${1 - dist/100})`;
|
|
ctx.stroke();
|
|
|
|
// Data Packet
|
|
if(Math.random() > 0.99) {
|
|
ctx.fillStyle = '#fff';
|
|
let t = (Date.now() % 1000) / 1000;
|
|
let px = nodeA.x + (nodeB.x - nodeA.x) * t;
|
|
let py = nodeA.y + (nodeB.y - nodeA.y) * t;
|
|
ctx.beginPath();
|
|
ctx.arc(px, py, 1.5, 0, Math.PI*2);
|
|
ctx.fill();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
requestAnimationFrame(draw);
|
|
}
|
|
|
|
window.addEventListener('resize', resize);
|
|
resize();
|
|
draw();
|
|
}
|
|
|
|
// --- Module 4: Monitoring Logic ---
|
|
function initMonitor() {
|
|
// Clock
|
|
setInterval(() => {
|
|
const now = new Date();
|
|
document.getElementById('clock').innerText = "2078.05.12 " + now.toLocaleTimeString();
|
|
}, 1000);
|
|
|
|
// Log Generator
|
|
const logs = [
|
|
"Verifying neural handshake...",
|
|
"Encryption protocols updated.",
|
|
"Packet loss detected in Sector 7.",
|
|
"AI Core temperature stabilizing.",
|
|
"Warning: Unauthorized scan attempt blocked.",
|
|
"Syncing timeline database...",
|
|
"Memory heap optimized."
|
|
];
|
|
const logWindow = document.getElementById('log-window');
|
|
|
|
setInterval(() => {
|
|
const p = document.createElement('div');
|
|
p.className = 'log-entry';
|
|
const msg = logs[Math.floor(Math.random() * logs.length)];
|
|
|
|
// Add color class
|
|
if(msg.includes("Warning")) p.classList.add('log-warn');
|
|
|
|
p.innerText = msg + ` [${Math.floor(Math.random()*999)}]`;
|
|
logWindow.appendChild(p);
|
|
logWindow.scrollTop = logWindow.scrollHeight;
|
|
|
|
if(logWindow.children.length > 20) logWindow.removeChild(logWindow.firstChild);
|
|
}, 2000);
|
|
|
|
// Waveform (Simple Canvas)
|
|
const wfCanvas = document.getElementById('waveform-canvas');
|
|
const wfCtx = wfCanvas.getContext('2d');
|
|
let offset = 0;
|
|
|
|
function drawWave() {
|
|
wfCtx.fillStyle = 'rgba(0,0,0,0.1)';
|
|
wfCtx.fillRect(0,0,260,60);
|
|
wfCtx.lineWidth = 2;
|
|
wfCtx.strokeStyle = '#00ff41';
|
|
wfCtx.beginPath();
|
|
|
|
for(let x=0; x<260; x+=2) {
|
|
const y = 30 + Math.sin(x * 0.1 + offset) * 20 * Math.random();
|
|
if(x===0) wfCtx.moveTo(x,y);
|
|
else wfCtx.lineTo(x,y);
|
|
}
|
|
wfCtx.stroke();
|
|
offset += 0.2;
|
|
requestAnimationFrame(drawWave);
|
|
}
|
|
drawWave();
|
|
}
|
|
|
|
// --- Module 6: Holo Tilt Effect ---
|
|
function initHoloCards() {
|
|
const cards = document.querySelectorAll('.holo-card');
|
|
|
|
cards.forEach(card => {
|
|
card.addEventListener('mousemove', (e) => {
|
|
const rect = card.getBoundingClientRect();
|
|
const x = e.clientX - rect.left;
|
|
const y = e.clientY - rect.top;
|
|
|
|
const centerX = rect.width / 2;
|
|
const centerY = rect.height / 2;
|
|
|
|
const rotateX = ((y - centerY) / centerY) * -10; // Max 10deg
|
|
const rotateY = ((x - centerX) / centerX) * 10;
|
|
|
|
card.style.transform = `perspective(1000px) rotateX(${rotateX}deg) rotateY(${rotateY}deg)`;
|
|
});
|
|
|
|
card.addEventListener('mouseleave', () => {
|
|
card.style.transform = `perspective(1000px) rotateX(0) rotateY(0)`;
|
|
});
|
|
});
|
|
}
|
|
|
|
// --- Module 7: Command Console ---
|
|
function initConsole() {
|
|
const input = document.getElementById('cmd-input');
|
|
input.addEventListener('keydown', (e) => {
|
|
if(e.key === 'Enter') {
|
|
const val = input.value.toLowerCase();
|
|
input.value = '';
|
|
|
|
// Visual Feedback
|
|
document.querySelector('.command-bar').style.boxShadow = "0 0 30px #00ff41";
|
|
setTimeout(() => document.querySelector('.command-bar').style.boxShadow = "0 0 20px rgba(0, 255, 255, 0.1)", 200);
|
|
|
|
// Basic Logic
|
|
if(val.includes('scan')) {
|
|
alert('System Scan Initiated... No Threats Found.');
|
|
} else if (val.includes('shutdown')) {
|
|
document.body.style.filter = "grayscale(100%)";
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
// --- Initialization ---
|
|
window.addEventListener('DOMContentLoaded', () => {
|
|
initIntro();
|
|
initMonitor();
|
|
initNeuralNet();
|
|
initHoloCards();
|
|
initConsole();
|
|
|
|
// Timeline interaction
|
|
document.querySelectorAll('.time-node').forEach(node => {
|
|
node.addEventListener('click', function() {
|
|
document.querySelectorAll('.time-node').forEach(n => n.classList.remove('active'));
|
|
this.classList.add('active');
|
|
|
|
// Visual glitch on city change
|
|
gsap.to(camera.position, {
|
|
z: Math.random() * 100 + 50,
|
|
duration: 1.5,
|
|
ease: "power2.inOut"
|
|
});
|
|
});
|
|
});
|
|
});
|
|
|
|
</script>
|
|
</body>
|
|
</html>
|