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> |