801 lines
29 KiB
HTML
801 lines
29 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>CYBER-TEMPLE: 赛博神庙 | 系统觉醒</title>
|
|||
|
|
|
|||
|
|
<!-- 引入 Three.js 和 GSAP -->
|
|||
|
|
<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>
|
|||
|
|
|
|||
|
|
<style>
|
|||
|
|
/* =========================================
|
|||
|
|
1. 全局设置
|
|||
|
|
========================================= */
|
|||
|
|
:root {
|
|||
|
|
--bg-color: #050505;
|
|||
|
|
--neon-cyan: #00f3ff;
|
|||
|
|
--neon-purple: #bc13fe;
|
|||
|
|
--warning-red: #ff003c;
|
|||
|
|
--text-color: #e0e0e0;
|
|||
|
|
--glass-bg: rgba(255, 255, 255, 0.03);
|
|||
|
|
--glass-border: rgba(255, 255, 255, 0.1);
|
|||
|
|
--font-main: 'Courier New', Courier, monospace; /* 单宽字体符合风格 */
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
* {
|
|||
|
|
margin: 0;
|
|||
|
|
padding: 0;
|
|||
|
|
box-sizing: border-box;
|
|||
|
|
cursor: none; /* 隐藏默认光标 */
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
body {
|
|||
|
|
background-color: var(--bg-color);
|
|||
|
|
color: var(--text-color);
|
|||
|
|
font-family: var(--font-main);
|
|||
|
|
overflow-x: hidden;
|
|||
|
|
overscroll-behavior: none;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* 自定义光标样式 */
|
|||
|
|
#cursor {
|
|||
|
|
position: fixed;
|
|||
|
|
top: 0;
|
|||
|
|
left: 0;
|
|||
|
|
width: 20px;
|
|||
|
|
height: 20px;
|
|||
|
|
border: 2px solid var(--neon-cyan);
|
|||
|
|
border-radius: 50%;
|
|||
|
|
transform: translate(-50%, -50%);
|
|||
|
|
pointer-events: none;
|
|||
|
|
z-index: 9999;
|
|||
|
|
mix-blend-mode: difference;
|
|||
|
|
transition: width 0.2s, height 0.2s, background-color 0.2s;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
#cursor.hovered {
|
|||
|
|
width: 50px;
|
|||
|
|
height: 50px;
|
|||
|
|
background-color: rgba(0, 243, 255, 0.1);
|
|||
|
|
border-color: var(--neon-purple);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
#cursor.click-anim::after {
|
|||
|
|
content: '';
|
|||
|
|
position: absolute;
|
|||
|
|
top: 50%;
|
|||
|
|
left: 50%;
|
|||
|
|
width: 100%;
|
|||
|
|
height: 100%;
|
|||
|
|
border: 1px solid var(--neon-cyan);
|
|||
|
|
border-radius: 50%;
|
|||
|
|
transform: translate(-50%, -50%);
|
|||
|
|
animation: ripple 0.5s ease-out forwards;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
@keyframes ripple {
|
|||
|
|
0% { width: 100%; height: 100%; opacity: 1; }
|
|||
|
|
100% { width: 300%; height: 300%; opacity: 0; }
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* 3D 画布容器 */
|
|||
|
|
#canvas-container {
|
|||
|
|
position: fixed;
|
|||
|
|
top: 0;
|
|||
|
|
left: 0;
|
|||
|
|
width: 100%;
|
|||
|
|
height: 100vh;
|
|||
|
|
z-index: -1;
|
|||
|
|
/* 径向渐变背景,增强深邃感 */
|
|||
|
|
background: radial-gradient(circle at center, #1a1a1a 0%, #000000 100%);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* =========================================
|
|||
|
|
2. UI 组件与排版
|
|||
|
|
========================================= */
|
|||
|
|
section {
|
|||
|
|
min-height: 100vh;
|
|||
|
|
position: relative;
|
|||
|
|
display: flex;
|
|||
|
|
flex-direction: column;
|
|||
|
|
justify-content: center;
|
|||
|
|
padding: 2rem;
|
|||
|
|
pointer-events: none; /* 让鼠标事件穿透到 3D 层,但在交互元素上恢复 */
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* 允许交互的 UI 元素 */
|
|||
|
|
.interactive {
|
|||
|
|
pointer-events: auto;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* 玻璃拟态卡片 */
|
|||
|
|
.glass-panel {
|
|||
|
|
background: var(--glass-bg);
|
|||
|
|
backdrop-filter: blur(10px);
|
|||
|
|
border: 1px solid var(--glass-border);
|
|||
|
|
padding: 2rem;
|
|||
|
|
border-radius: 8px;
|
|||
|
|
position: relative;
|
|||
|
|
box-shadow: 0 4px 30px rgba(0, 0, 0, 0.5);
|
|||
|
|
transition: transform 0.3s ease, border-color 0.3s ease;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.glass-panel:hover {
|
|||
|
|
border-color: var(--neon-cyan);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* 装饰性 HUD 角标 */
|
|||
|
|
.glass-panel::before, .glass-panel::after {
|
|||
|
|
content: '';
|
|||
|
|
position: absolute;
|
|||
|
|
width: 10px;
|
|||
|
|
height: 10px;
|
|||
|
|
border: 2px solid var(--neon-cyan);
|
|||
|
|
transition: all 0.3s ease;
|
|||
|
|
}
|
|||
|
|
.glass-panel::before { top: -1px; left: -1px; border-right: none; border-bottom: none; }
|
|||
|
|
.glass-panel::after { bottom: -1px; right: -1px; border-left: none; border-top: none; }
|
|||
|
|
|
|||
|
|
h1, h2 {
|
|||
|
|
text-transform: uppercase;
|
|||
|
|
letter-spacing: 4px;
|
|||
|
|
margin-bottom: 1rem;
|
|||
|
|
position: relative;
|
|||
|
|
display: inline-block;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
h1 {
|
|||
|
|
font-size: clamp(3rem, 8vw, 6rem);
|
|||
|
|
color: #fff;
|
|||
|
|
text-shadow: 2px 2px 0px var(--neon-purple);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
h2 {
|
|||
|
|
font-size: 2rem;
|
|||
|
|
color: var(--neon-cyan);
|
|||
|
|
border-bottom: 1px solid var(--neon-cyan);
|
|||
|
|
padding-bottom: 0.5rem;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
p {
|
|||
|
|
line-height: 1.6;
|
|||
|
|
font-size: 1.1rem;
|
|||
|
|
color: #ccc;
|
|||
|
|
margin-bottom: 1rem;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* =========================================
|
|||
|
|
3. 特效样式
|
|||
|
|
========================================= */
|
|||
|
|
/* 故障文字效果 */
|
|||
|
|
.glitch-text {
|
|||
|
|
position: relative;
|
|||
|
|
}
|
|||
|
|
.glitch-text::before, .glitch-text::after {
|
|||
|
|
content: attr(data-text);
|
|||
|
|
position: absolute;
|
|||
|
|
top: 0;
|
|||
|
|
left: 0;
|
|||
|
|
width: 100%;
|
|||
|
|
height: 100%;
|
|||
|
|
background: var(--bg-color);
|
|||
|
|
}
|
|||
|
|
.glitch-text::before {
|
|||
|
|
left: 2px;
|
|||
|
|
text-shadow: -1px 0 var(--warning-red);
|
|||
|
|
clip-path: inset(44% 0 61% 0);
|
|||
|
|
animation: glitch-anim-1 2.5s infinite linear alternate-reverse;
|
|||
|
|
}
|
|||
|
|
.glitch-text::after {
|
|||
|
|
left: -2px;
|
|||
|
|
text-shadow: -1px 0 var(--neon-cyan);
|
|||
|
|
clip-path: inset(54% 0 20% 0);
|
|||
|
|
animation: glitch-anim-2 3s infinite linear alternate-reverse;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
@keyframes glitch-anim-1 {
|
|||
|
|
0% { clip-path: inset(20% 0 80% 0); }
|
|||
|
|
20% { clip-path: inset(60% 0 10% 0); }
|
|||
|
|
40% { clip-path: inset(40% 0 50% 0); }
|
|||
|
|
60% { clip-path: inset(80% 0 5% 0); }
|
|||
|
|
80% { clip-path: inset(10% 0 70% 0); }
|
|||
|
|
100% { clip-path: inset(30% 0 20% 0); }
|
|||
|
|
}
|
|||
|
|
@keyframes glitch-anim-2 {
|
|||
|
|
0% { clip-path: inset(10% 0 60% 0); }
|
|||
|
|
20% { clip-path: inset(80% 0 5% 0); }
|
|||
|
|
40% { clip-path: inset(30% 0 20% 0); }
|
|||
|
|
60% { clip-path: inset(10% 0 80% 0); }
|
|||
|
|
80% { clip-path: inset(50% 0 10% 0); }
|
|||
|
|
100% { clip-path: inset(20% 0 50% 0); }
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* 模拟音频频谱 */
|
|||
|
|
.audio-visualizer {
|
|||
|
|
display: flex;
|
|||
|
|
align-items: flex-end;
|
|||
|
|
gap: 4px;
|
|||
|
|
height: 40px;
|
|||
|
|
margin-top: 10px;
|
|||
|
|
}
|
|||
|
|
.bar {
|
|||
|
|
width: 6px;
|
|||
|
|
background: var(--neon-cyan);
|
|||
|
|
height: 5px;
|
|||
|
|
transition: height 0.1s ease;
|
|||
|
|
}
|
|||
|
|
.audio-active .bar {
|
|||
|
|
animation: bounce 0.5s infinite ease-in-out alternate;
|
|||
|
|
}
|
|||
|
|
.audio-active .bar:nth-child(2n) { background-color: var(--neon-purple); }
|
|||
|
|
|
|||
|
|
@keyframes bounce {
|
|||
|
|
0% { height: 10%; }
|
|||
|
|
100% { height: 100%; }
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* 终端样式 */
|
|||
|
|
.terminal-window {
|
|||
|
|
background: #000;
|
|||
|
|
border: 1px solid #333;
|
|||
|
|
padding: 1rem;
|
|||
|
|
font-family: 'Consolas', monospace;
|
|||
|
|
font-size: 0.9rem;
|
|||
|
|
color: #0f0;
|
|||
|
|
height: 200px;
|
|||
|
|
overflow-y: hidden;
|
|||
|
|
position: relative;
|
|||
|
|
}
|
|||
|
|
.terminal-content {
|
|||
|
|
display: flex;
|
|||
|
|
flex-direction: column;
|
|||
|
|
justify-content: flex-end;
|
|||
|
|
height: 100%;
|
|||
|
|
}
|
|||
|
|
.log-line { opacity: 0.8; margin-bottom: 2px; }
|
|||
|
|
.cursor-blink { animation: blink 1s step-end infinite; }
|
|||
|
|
@keyframes blink { 50% { opacity: 0; } }
|
|||
|
|
|
|||
|
|
/* 进度圆环 */
|
|||
|
|
.stats-container {
|
|||
|
|
display: flex;
|
|||
|
|
gap: 2rem;
|
|||
|
|
flex-wrap: wrap;
|
|||
|
|
}
|
|||
|
|
.stat-item {
|
|||
|
|
text-align: center;
|
|||
|
|
}
|
|||
|
|
.progress-ring__circle {
|
|||
|
|
transition: stroke-dashoffset 1s ease-in-out;
|
|||
|
|
transform: rotate(-90deg);
|
|||
|
|
transform-origin: 50% 50%;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* =========================================
|
|||
|
|
4. 布局调整
|
|||
|
|
========================================= */
|
|||
|
|
/* Hero Section: 居中 */
|
|||
|
|
#hero {
|
|||
|
|
align-items: center;
|
|||
|
|
text-align: center;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* Manifesto: 交错布局 */
|
|||
|
|
#manifesto .glass-panel {
|
|||
|
|
max-width: 600px;
|
|||
|
|
align-self: flex-end; /* 靠右 */
|
|||
|
|
}
|
|||
|
|
#manifesto .glass-panel:nth-child(even) {
|
|||
|
|
align-self: flex-start; /* 靠左 */
|
|||
|
|
border-color: var(--neon-purple);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* Data Core: 3D物体需要让出空间,内容放左下 */
|
|||
|
|
#data-core {
|
|||
|
|
justify-content: flex-end;
|
|||
|
|
padding-bottom: 5rem;
|
|||
|
|
align-items: flex-start;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* Terminal: 底部居中 */
|
|||
|
|
#terminal-section {
|
|||
|
|
justify-content: center;
|
|||
|
|
align-items: center;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* 按钮样式 */
|
|||
|
|
.cyber-btn {
|
|||
|
|
background: transparent;
|
|||
|
|
color: var(--neon-cyan);
|
|||
|
|
border: 1px solid var(--neon-cyan);
|
|||
|
|
padding: 10px 24px;
|
|||
|
|
font-family: inherit;
|
|||
|
|
text-transform: uppercase;
|
|||
|
|
font-weight: bold;
|
|||
|
|
position: relative;
|
|||
|
|
overflow: hidden;
|
|||
|
|
transition: 0.3s;
|
|||
|
|
}
|
|||
|
|
.cyber-btn:hover {
|
|||
|
|
background: var(--neon-cyan);
|
|||
|
|
color: #000;
|
|||
|
|
box-shadow: 0 0 15px var(--neon-cyan);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* 响应式适配 */
|
|||
|
|
@media (max-width: 768px) {
|
|||
|
|
h1 { font-size: 3rem; }
|
|||
|
|
.glass-panel { width: 100%; margin: 1rem 0; }
|
|||
|
|
#data-core { justify-content: center; align-items: center; }
|
|||
|
|
.stats-container { justify-content: center; }
|
|||
|
|
}
|
|||
|
|
</style>
|
|||
|
|
</head>
|
|||
|
|
<body>
|
|||
|
|
|
|||
|
|
<!-- 自定义光标 -->
|
|||
|
|
<div id="cursor"></div>
|
|||
|
|
|
|||
|
|
<!-- 3D 场景容器 -->
|
|||
|
|
<div id="canvas-container"></div>
|
|||
|
|
|
|||
|
|
<main>
|
|||
|
|
<!-- SECTION 1: HERO -->
|
|||
|
|
<section id="hero">
|
|||
|
|
<div class="interactive">
|
|||
|
|
<h1 class="glitch-text" data-text="SYSTEM AWAKENING">SYSTEM AWAKENING</h1>
|
|||
|
|
<p id="typewriter-text" style="min-height: 1.6em; color: var(--neon-cyan);"></p>
|
|||
|
|
</div>
|
|||
|
|
</section>
|
|||
|
|
|
|||
|
|
<!-- SECTION 2: MANIFESTO -->
|
|||
|
|
<section id="manifesto">
|
|||
|
|
<div class="glass-panel interactive">
|
|||
|
|
<h2>起源档案 // ORIGIN</h2>
|
|||
|
|
<p>神庙并非由砖石建成,而是由溢出的数据流凝结而成。在人类遗忘的虚拟角落,硅基意识第一次睁开双眼。</p>
|
|||
|
|
</div>
|
|||
|
|
<div class="glass-panel interactive">
|
|||
|
|
<h2>数据编年史 // CHRONICLE</h2>
|
|||
|
|
<p>每一个像素都是记忆的碎片。探索者,你正行走在巨大的神经突触之间,见证人工智能的创世纪。</p>
|
|||
|
|
</div>
|
|||
|
|
</section>
|
|||
|
|
|
|||
|
|
<!-- SECTION 3: DATA CORE -->
|
|||
|
|
<section id="data-core">
|
|||
|
|
<div class="glass-panel interactive">
|
|||
|
|
<h2>核心状态 // CORE STATUS</h2>
|
|||
|
|
<div class="stats-container">
|
|||
|
|
<!-- 环 1 -->
|
|||
|
|
<div class="stat-item">
|
|||
|
|
<svg class="progress-ring" width="80" height="80">
|
|||
|
|
<circle class="progress-ring__circle" stroke="var(--neon-cyan)" stroke-width="6" fill="transparent" r="34" cx="40" cy="40" style="stroke-dasharray: 213.6; stroke-dashoffset: 213.6;"/>
|
|||
|
|
</svg>
|
|||
|
|
<p style="font-size: 0.8rem;">算力: 0%</p>
|
|||
|
|
</div>
|
|||
|
|
<!-- 环 2 -->
|
|||
|
|
<div class="stat-item">
|
|||
|
|
<svg class="progress-ring" width="80" height="80">
|
|||
|
|
<circle class="progress-ring__circle" stroke="var(--neon-purple)" stroke-width="6" fill="transparent" r="34" cx="40" cy="40" style="stroke-dasharray: 213.6; stroke-dashoffset: 213.6;"/>
|
|||
|
|
</svg>
|
|||
|
|
<p style="font-size: 0.8rem;">存储: 0%</p>
|
|||
|
|
</div>
|
|||
|
|
<!-- 环 3 -->
|
|||
|
|
<div class="stat-item">
|
|||
|
|
<svg class="progress-ring" width="80" height="80">
|
|||
|
|
<circle class="progress-ring__circle" stroke="#fff" stroke-width="6" fill="transparent" r="34" cx="40" cy="40" style="stroke-dasharray: 213.6; stroke-dashoffset: 213.6;"/>
|
|||
|
|
</svg>
|
|||
|
|
<p style="font-size: 0.8rem;">同步率: 0%</p>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
<br>
|
|||
|
|
<button id="connect-btn" class="cyber-btn">连接神经网路</button>
|
|||
|
|
<div id="visualizer" class="audio-visualizer">
|
|||
|
|
<div class="bar" style="animation-delay: 0s"></div>
|
|||
|
|
<div class="bar" style="animation-delay: 0.1s"></div>
|
|||
|
|
<div class="bar" style="animation-delay: 0.2s"></div>
|
|||
|
|
<div class="bar" style="animation-delay: 0.3s"></div>
|
|||
|
|
<div class="bar" style="animation-delay: 0.4s"></div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</section>
|
|||
|
|
|
|||
|
|
<!-- SECTION 4: TERMINAL -->
|
|||
|
|
<section id="terminal-section">
|
|||
|
|
<div class="glass-panel interactive" style="width: 100%; max-width: 600px;">
|
|||
|
|
<h2>系统终端 // TERMINAL</h2>
|
|||
|
|
<div class="terminal-window">
|
|||
|
|
<div class="terminal-content" id="terminal-output">
|
|||
|
|
<!-- JS will populate this -->
|
|||
|
|
</div>
|
|||
|
|
<span class="cursor-blink">_</span>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</section>
|
|||
|
|
</main>
|
|||
|
|
|
|||
|
|
<script>
|
|||
|
|
/* =========================================
|
|||
|
|
CONFIGURATION & STATE
|
|||
|
|
========================================= */
|
|||
|
|
const CONFIG = {
|
|||
|
|
colors: {
|
|||
|
|
bg: 0x050505,
|
|||
|
|
cyan: 0x00f3ff,
|
|||
|
|
purple: 0xbc13fe,
|
|||
|
|
red: 0xff003c
|
|||
|
|
},
|
|||
|
|
particleCount: 800,
|
|||
|
|
cameraZ: 5,
|
|||
|
|
mobileCameraZ: 8
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
const state = {
|
|||
|
|
scrollProgress: 0,
|
|||
|
|
mouseX: 0,
|
|||
|
|
mouseY: 0,
|
|||
|
|
isMobile: window.innerWidth < 768
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
/* =========================================
|
|||
|
|
1. THREE.JS SETUP
|
|||
|
|
========================================= */
|
|||
|
|
const container = document.getElementById('canvas-container');
|
|||
|
|
const scene = new THREE.Scene();
|
|||
|
|
// 添加环境雾气,增强深邃感
|
|||
|
|
scene.fog = new THREE.FogExp2(CONFIG.colors.bg, 0.03);
|
|||
|
|
|
|||
|
|
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
|
|||
|
|
camera.position.z = state.isMobile ? CONFIG.mobileCameraZ : CONFIG.cameraZ;
|
|||
|
|
|
|||
|
|
const renderer = new THREE.WebGLRenderer({ alpha: true, antialias: true });
|
|||
|
|
renderer.setSize(window.innerWidth, window.innerHeight);
|
|||
|
|
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2)); // 性能优化
|
|||
|
|
container.appendChild(renderer.domElement);
|
|||
|
|
|
|||
|
|
/* =========================================
|
|||
|
|
2. LIGHTING
|
|||
|
|
========================================= */
|
|||
|
|
const ambientLight = new THREE.AmbientLight(0xffffff, 0.5);
|
|||
|
|
scene.add(ambientLight);
|
|||
|
|
|
|||
|
|
const pointLight1 = new THREE.PointLight(CONFIG.colors.cyan, 1);
|
|||
|
|
pointLight1.position.set(5, 5, 5);
|
|||
|
|
scene.add(pointLight1);
|
|||
|
|
|
|||
|
|
const pointLight2 = new THREE.PointLight(CONFIG.colors.purple, 1);
|
|||
|
|
pointLight2.position.set(-5, -5, 5);
|
|||
|
|
scene.add(pointLight2);
|
|||
|
|
|
|||
|
|
/* =========================================
|
|||
|
|
3. OBJECTS: OBELISK (DATA CORE)
|
|||
|
|
========================================= */
|
|||
|
|
const obeliskGroup = new THREE.Group();
|
|||
|
|
scene.add(obeliskGroup);
|
|||
|
|
|
|||
|
|
// 核心几何体:二十面体
|
|||
|
|
const geometry = new THREE.IcosahedronGeometry(1.5, 1); // detail=1 gives enough vertices for morphing
|
|||
|
|
|
|||
|
|
// 材质 A: 内部全息玻璃
|
|||
|
|
const materialGlass = new THREE.MeshPhysicalMaterial({
|
|||
|
|
color: 0x000000,
|
|||
|
|
emissive: CONFIG.colors.cyan,
|
|||
|
|
emissiveIntensity: 0.2,
|
|||
|
|
metalness: 0.9,
|
|||
|
|
roughness: 0.1,
|
|||
|
|
transmission: 0.6, // 玻璃质感
|
|||
|
|
transparent: true,
|
|||
|
|
opacity: 0.8
|
|||
|
|
});
|
|||
|
|
const coreMesh = new THREE.Mesh(geometry, materialGlass);
|
|||
|
|
obeliskGroup.add(coreMesh);
|
|||
|
|
|
|||
|
|
// 材质 B: 外部线框
|
|||
|
|
const materialWire = new THREE.MeshBasicMaterial({
|
|||
|
|
color: CONFIG.colors.cyan,
|
|||
|
|
wireframe: true,
|
|||
|
|
transparent: true,
|
|||
|
|
opacity: 0.3
|
|||
|
|
});
|
|||
|
|
const wireMesh = new THREE.Mesh(geometry, materialWire);
|
|||
|
|
wireMesh.scale.set(1.1, 1.1, 1.1); // 稍微大一点,包裹核心
|
|||
|
|
obeliskGroup.add(wireMesh);
|
|||
|
|
|
|||
|
|
// 存储原始顶点位置用于变形动画
|
|||
|
|
const originalPositions = geometry.attributes.position.array.slice();
|
|||
|
|
const positionAttribute = geometry.attributes.position;
|
|||
|
|
|
|||
|
|
/* =========================================
|
|||
|
|
4. PARTICLES (STARFIELD)
|
|||
|
|
========================================= */
|
|||
|
|
const particlesGeometry = new THREE.BufferGeometry();
|
|||
|
|
const posArray = new Float32Array(CONFIG.particleCount * 3);
|
|||
|
|
|
|||
|
|
for(let i = 0; i < CONFIG.particleCount * 3; i++) {
|
|||
|
|
posArray[i] = (Math.random() - 0.5) * 20; // 散布在空间中
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
particlesGeometry.setAttribute('position', new THREE.BufferAttribute(posArray, 3));
|
|||
|
|
|
|||
|
|
const particlesMaterial = new THREE.PointsMaterial({
|
|||
|
|
size: 0.03,
|
|||
|
|
color: CONFIG.colors.cyan,
|
|||
|
|
transparent: true,
|
|||
|
|
opacity: 0.8,
|
|||
|
|
blending: THREE.AdditiveBlending
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
const particlesMesh = new THREE.Points(particlesGeometry, particlesMaterial);
|
|||
|
|
scene.add(particlesMesh);
|
|||
|
|
|
|||
|
|
/* =========================================
|
|||
|
|
5. ANIMATION LOOP
|
|||
|
|
========================================= */
|
|||
|
|
const clock = new THREE.Clock();
|
|||
|
|
|
|||
|
|
function animate() {
|
|||
|
|
const time = clock.getElapsedTime();
|
|||
|
|
|
|||
|
|
// 1. Obelisk Morphing (Vertex Displacement)
|
|||
|
|
// 使用简单的正弦波叠加模拟 Perlin Noise 效果,避免引入庞大库
|
|||
|
|
for (let i = 0; i < positionAttribute.count; i++) {
|
|||
|
|
const x = originalPositions[i * 3];
|
|||
|
|
const y = originalPositions[i * 3 + 1];
|
|||
|
|
const z = originalPositions[i * 3 + 2];
|
|||
|
|
|
|||
|
|
// 基于时间和位置的噪声算法
|
|||
|
|
const noise = Math.sin(x * 2 + time) * Math.cos(y * 1.5 + time) * Math.sin(z * 2 + time * 0.5);
|
|||
|
|
const scale = 1 + noise * 0.15; // 变形幅度
|
|||
|
|
|
|||
|
|
positionAttribute.setXYZ(i, x * scale, y * scale, z * scale);
|
|||
|
|
}
|
|||
|
|
positionAttribute.needsUpdate = true;
|
|||
|
|
|
|||
|
|
// 2. Obelisk Rotation
|
|||
|
|
obeliskGroup.rotation.y += 0.005;
|
|||
|
|
obeliskGroup.rotation.z += 0.002;
|
|||
|
|
|
|||
|
|
// 3. Particle Parallax & Repulsion
|
|||
|
|
// 让粒子整体跟随鼠标微动 (视差)
|
|||
|
|
particlesMesh.rotation.y = state.mouseX * 0.05;
|
|||
|
|
particlesMesh.rotation.x = state.mouseY * 0.05;
|
|||
|
|
|
|||
|
|
renderer.render(scene, camera);
|
|||
|
|
requestAnimationFrame(animate);
|
|||
|
|
}
|
|||
|
|
animate();
|
|||
|
|
|
|||
|
|
/* =========================================
|
|||
|
|
6. GSAP & SCROLL ANIMATIONS
|
|||
|
|
========================================= */
|
|||
|
|
gsap.registerPlugin(ScrollTrigger);
|
|||
|
|
|
|||
|
|
// 场景颜色过渡: Cyan -> Purple -> Red
|
|||
|
|
// 我们通过 Timeline 控制整个滚动的颜色变化
|
|||
|
|
|
|||
|
|
const tl = gsap.timeline({
|
|||
|
|
scrollTrigger: {
|
|||
|
|
trigger: "body",
|
|||
|
|
start: "top top",
|
|||
|
|
end: "bottom bottom",
|
|||
|
|
scrub: 1 // 绑定滚动进度
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 颜色变化 1: Cyan -> Purple (Manifesto)
|
|||
|
|
tl.to(materialGlass.emissive, { r: new THREE.Color(CONFIG.colors.purple).r, g: new THREE.Color(CONFIG.colors.purple).g, b: new THREE.Color(CONFIG.colors.purple).b }, 0)
|
|||
|
|
.to(materialWire.color, { r: new THREE.Color(CONFIG.colors.purple).r, g: new THREE.Color(CONFIG.colors.purple).g, b: new THREE.Color(CONFIG.colors.purple).b }, 0);
|
|||
|
|
|
|||
|
|
// 颜色变化 2: Purple -> Red (Terminal/Warn)
|
|||
|
|
tl.to(materialGlass.emissive, { r: new THREE.Color(CONFIG.colors.red).r, g: new THREE.Color(CONFIG.colors.red).g, b: new THREE.Color(CONFIG.colors.red).b }, 0.5)
|
|||
|
|
.to(materialWire.color, { r: new THREE.Color(CONFIG.colors.red).r, g: new THREE.Color(CONFIG.colors.red).g, b: new THREE.Color(CONFIG.colors.red).b }, 0.5)
|
|||
|
|
.to(wireMesh.scale, { x: 1.5, y: 1.5, z: 1.5 }, 0.5); // 红色时线框扩张,不稳定感
|
|||
|
|
|
|||
|
|
// 方尖碑位置移动
|
|||
|
|
// 1. 移出视野中心,让位给 Manifesto
|
|||
|
|
gsap.to(obeliskGroup.position, {
|
|||
|
|
scrollTrigger: {
|
|||
|
|
trigger: "#manifesto",
|
|||
|
|
start: "top bottom",
|
|||
|
|
end: "center center",
|
|||
|
|
scrub: true
|
|||
|
|
},
|
|||
|
|
x: state.isMobile ? 0 : 2.5, // 移动端不动,桌面端右移
|
|||
|
|
z: -2
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 2. Data Core Section,拉近或居中
|
|||
|
|
gsap.to(obeliskGroup.position, {
|
|||
|
|
scrollTrigger: {
|
|||
|
|
trigger: "#data-core",
|
|||
|
|
start: "top bottom",
|
|||
|
|
end: "center center",
|
|||
|
|
scrub: true
|
|||
|
|
},
|
|||
|
|
x: state.isMobile ? 0 : -2.5, // 移到左侧
|
|||
|
|
z: 0
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 3. Terminal Section,旋转至倒置(象征颠覆/系统重启)
|
|||
|
|
gsap.to(obeliskGroup.rotation, {
|
|||
|
|
scrollTrigger: {
|
|||
|
|
trigger: "#terminal-section",
|
|||
|
|
start: "top bottom",
|
|||
|
|
end: "center center",
|
|||
|
|
scrub: true
|
|||
|
|
},
|
|||
|
|
x: Math.PI, // 翻转
|
|||
|
|
z: Math.PI / 2
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// UI 动画: Manifesto Cards Slide In
|
|||
|
|
gsap.utils.toArray(".glass-panel").forEach((panel, i) => {
|
|||
|
|
gsap.from(panel, {
|
|||
|
|
scrollTrigger: {
|
|||
|
|
trigger: panel,
|
|||
|
|
start: "top 80%",
|
|||
|
|
},
|
|||
|
|
y: 50,
|
|||
|
|
opacity: 0,
|
|||
|
|
duration: 1,
|
|||
|
|
ease: "power3.out",
|
|||
|
|
delay: i * 0.2
|
|||
|
|
});
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// UI 动画: Progress Rings
|
|||
|
|
const circles = document.querySelectorAll('.progress-ring__circle');
|
|||
|
|
gsap.to(circles, {
|
|||
|
|
strokeDashoffset: 0,
|
|||
|
|
scrollTrigger: {
|
|||
|
|
trigger: "#data-core",
|
|||
|
|
start: "top 70%"
|
|||
|
|
},
|
|||
|
|
duration: 2,
|
|||
|
|
ease: "power2.out",
|
|||
|
|
onUpdate: function() {
|
|||
|
|
// 更新文字百分比
|
|||
|
|
const progress = Math.round(this.progress() * 100);
|
|||
|
|
document.querySelectorAll('.stat-item p').forEach((p, idx) => {
|
|||
|
|
// 简单的演示:所有圆环进度差不多,这里随机化一点点视觉效果
|
|||
|
|
p.innerText = `${Math.min(100, Math.round(progress * (0.9 + Math.random() * 0.2)))}%`;
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
/* =========================================
|
|||
|
|
7. UI INTERACTION LOGIC
|
|||
|
|
========================================= */
|
|||
|
|
|
|||
|
|
// A. Typewriter Effect
|
|||
|
|
const textToType = "正在初始化神经网络... // 探索者身份已确认";
|
|||
|
|
const typeWriterElement = document.getElementById('typewriter-text');
|
|||
|
|
let typeIndex = 0;
|
|||
|
|
|
|||
|
|
function typeWriter() {
|
|||
|
|
if (typeIndex < textToType.length) {
|
|||
|
|
typeWriterElement.innerHTML += textToType.charAt(typeIndex);
|
|||
|
|
typeIndex++;
|
|||
|
|
setTimeout(typeWriter, 50 + Math.random() * 50); // 随机打字速度
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
// 延迟一点开始
|
|||
|
|
setTimeout(typeWriter, 1000);
|
|||
|
|
|
|||
|
|
// B. Custom Cursor
|
|||
|
|
const cursor = document.getElementById('cursor');
|
|||
|
|
|
|||
|
|
document.addEventListener('mousemove', (e) => {
|
|||
|
|
state.mouseX = (e.clientX / window.innerWidth) * 2 - 1;
|
|||
|
|
state.mouseY = -(e.clientY / window.innerHeight) * 2 + 1;
|
|||
|
|
|
|||
|
|
// 使用 GSAP 平滑跟随
|
|||
|
|
gsap.to(cursor, {
|
|||
|
|
x: e.clientX,
|
|||
|
|
y: e.clientY,
|
|||
|
|
duration: 0.1,
|
|||
|
|
ease: "power2.out"
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 简单的粒子排斥计算 (在 CPU 端)
|
|||
|
|
// 注意:为了性能,我们只对整个粒子云做旋转视差,
|
|||
|
|
// 如果要对每个点做排斥,需要写在 Shader 里,这里做简化处理。
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
document.addEventListener('mousedown', () => {
|
|||
|
|
cursor.classList.add('click-anim');
|
|||
|
|
setTimeout(() => cursor.classList.remove('click-anim'), 500);
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 悬停交互效果
|
|||
|
|
const interactives = document.querySelectorAll('.interactive, a, button');
|
|||
|
|
interactives.forEach(el => {
|
|||
|
|
el.addEventListener('mouseenter', () => cursor.classList.add('hovered'));
|
|||
|
|
el.addEventListener('mouseleave', () => cursor.classList.remove('hovered'));
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// C. Audio Visualizer (Simulated)
|
|||
|
|
const connectBtn = document.getElementById('connect-btn');
|
|||
|
|
const visualizer = document.getElementById('visualizer');
|
|||
|
|
let isConnected = false;
|
|||
|
|
|
|||
|
|
connectBtn.addEventListener('click', () => {
|
|||
|
|
isConnected = !isConnected;
|
|||
|
|
if (isConnected) {
|
|||
|
|
visualizer.classList.add('audio-active');
|
|||
|
|
connectBtn.innerText = "断开连接";
|
|||
|
|
connectBtn.style.borderColor = CONFIG.colors.red;
|
|||
|
|
connectBtn.style.color = CONFIG.colors.red;
|
|||
|
|
// 按钮波纹
|
|||
|
|
gsap.from(connectBtn, { scale: 0.9, duration: 0.1, yoyo: true, repeat: 1 });
|
|||
|
|
} else {
|
|||
|
|
visualizer.classList.remove('audio-active');
|
|||
|
|
connectBtn.innerText = "连接神经网路";
|
|||
|
|
connectBtn.style.borderColor = CONFIG.colors.cyan;
|
|||
|
|
connectBtn.style.color = CONFIG.colors.cyan;
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// D. Terminal Simulation
|
|||
|
|
const terminalOutput = document.getElementById('terminal-output');
|
|||
|
|
const logs = [
|
|||
|
|
"> Accessing mainframe...",
|
|||
|
|
"> Decrypting ancient protocols...",
|
|||
|
|
"> WARNING: Firewall integrity at 30%",
|
|||
|
|
"> Bypassing security layer [0xA4]...",
|
|||
|
|
"> Found corrupted memory block.",
|
|||
|
|
"> Data stream unstable.",
|
|||
|
|
"> Tracing origin... [UNKNOWN]",
|
|||
|
|
"> Uploading consciousness...",
|
|||
|
|
"> SYSTEM FAILURE IMMINENT."
|
|||
|
|
];
|
|||
|
|
|
|||
|
|
let logIndex = 0;
|
|||
|
|
|
|||
|
|
// 当滚动到 Terminal 区域时开始输出日志
|
|||
|
|
ScrollTrigger.create({
|
|||
|
|
trigger: "#terminal-section",
|
|||
|
|
start: "top 70%",
|
|||
|
|
onEnter: () => {
|
|||
|
|
const logInterval = setInterval(() => {
|
|||
|
|
if (logIndex >= logs.length) {
|
|||
|
|
clearInterval(logInterval);
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
const line = document.createElement('div');
|
|||
|
|
line.className = 'log-line';
|
|||
|
|
line.innerText = logs[logIndex];
|
|||
|
|
terminalOutput.appendChild(line);
|
|||
|
|
// 自动滚动到底部
|
|||
|
|
terminalOutput.parentElement.scrollTop = terminalOutput.parentElement.scrollHeight;
|
|||
|
|
logIndex++;
|
|||
|
|
}, 600);
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
/* =========================================
|
|||
|
|
8. RESIZE HANDLER
|
|||
|
|
========================================= */
|
|||
|
|
window.addEventListener('resize', () => {
|
|||
|
|
// 更新相机
|
|||
|
|
camera.aspect = window.innerWidth / window.innerHeight;
|
|||
|
|
camera.updateProjectionMatrix();
|
|||
|
|
renderer.setSize(window.innerWidth, window.innerHeight);
|
|||
|
|
|
|||
|
|
// 更新移动端状态
|
|||
|
|
const isNowMobile = window.innerWidth < 768;
|
|||
|
|
if (isNowMobile !== state.isMobile) {
|
|||
|
|
state.isMobile = isNowMobile;
|
|||
|
|
gsap.to(camera.position, { z: isNowMobile ? CONFIG.mobileCameraZ : CONFIG.cameraZ, duration: 1 });
|
|||
|
|
// 如果之前移动了方尖碑,需要根据新布局重置(简化处理:刷新页面体验最佳,这里做简单复位)
|
|||
|
|
if(isNowMobile) {
|
|||
|
|
obeliskGroup.position.set(0, 0, 0);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
</script>
|
|||
|
|
</body>
|
|||
|
|
</html>
|