<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
<title>From Monotony to Splendor - 3D World</title>
<style>
body {
margin: 0;
padding: 0;
overflow: hidden;
background: #000;
font-family: Arial, sans-serif;
}
#container {
position: relative;
width: 100vw;
height: 100vh;
}
#startScreen {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
background: #000;
z-index: 100;
transition: opacity 2s ease-in-out;
}
#title {
color: white;
font-size: 36px;
margin-bottom: 30px;
text-align: center;
}
.mode-button {
padding: 15px 30px;
font-size: 20px;
background: #fff;
border: none;
border-radius: 50px;
cursor: pointer;
color: #000;
font-weight: bold;
margin: 15px;
transition: all 0.3s ease;
width: 250px;
}
.mode-button:hover {
transform: scale(1.1);
box-shadow: 0 0 20px rgba(255, 255, 255, 0.5);
}
#languageToggle {
position: absolute;
top: 20px;
right: 20px;
padding: 10px 20px;
background: rgba(255, 255, 255, 0.2);
border: 1px solid white;
color: white;
border-radius: 20px;
cursor: pointer;
font-size: 14px;
}
canvas {
display: block;
}
#modeInfo {
position: absolute;
top: 20px;
left: 20px;
color: white;
font-family: Arial, sans-serif;
font-size: 16px;
z-index: 10;
background: rgba(0,0,0,0.5);
padding: 10px;
border-radius: 5px;
}
#controls {
position: absolute;
bottom: 20px;
right: 20px;
color: white;
font-family: Arial, sans-serif;
font-size: 14px;
z-index: 10;
background: rgba(0,0,0,0.5);
padding: 10px;
border-radius: 5px;
text-align: center;
}
#musicInfo {
position: absolute;
top: 60px;
right: 20px;
color: white;
font-family: Arial, sans-serif;
font-size: 12px;
z-index: 10;
background: rgba(0,0,0,0.5);
padding: 10px;
border-radius: 5px;
max-width: 200px;
}
.hidden {
display: none !important;
}
</style>
</head>
<body>
<div id="container">
<div id="startScreen">
<div id="title">探索深邃的3D世界</div>
<button class="mode-button" id="freeMode">自由探索模式</button>
<button class="mode-button" id="touchMode">触控移动模式</button>
</div>
<!-- Three.js 将在这里渲染 -->
</div>
<button id="languageToggle">English</button>
<div id="modeInfo" class="hidden">
<span id="currentMode">当前模式: </span>
</div>
<div id="controls" class="hidden">
<div id="freeControls">
自动旋转<br>
按住鼠标拖拽旋转
</div>
<div id="touchControls" class="hidden">
滑动屏幕移动视角<br>
双指缩放
</div>
</div>
<div id="musicInfo" class="hidden">
背景音乐: <span id="musicTitle">深邃空间 - Kevin MacLeod</span><br>
来源: Incompetech.com (Creative Commons)
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
<script>
// 语言配置
const languages = {
'zh-CN': {
title: '探索深邃的3D世界',
freeMode: '自由探索模式',
touchMode: '触控移动模式',
currentMode: '当前模式: ',
freeModeText: '自由探索模式',
touchModeText: '触控移动模式',
freeControls: '自动旋转<br>按住鼠标拖拽旋转',
touchControls: '滑动屏幕移动视角<br>双指缩放',
musicTitle: '深邃空间 - Kevin MacLeod',
musicSource: '来源: Incompetech.com (Creative Commons)'
},
'en': {
title: 'Explore the Profound 3D World',
freeMode: 'Free Exploration Mode',
touchMode: 'Touch Control Mode',
currentMode: 'Current Mode: ',
freeModeText: 'Free Exploration Mode',
touchModeText: 'Touch Control Mode',
freeControls: 'Auto Rotation<br>Drag to rotate',
touchControls: 'Swipe to move view<br>Pinch to zoom',
musicTitle: 'Deep Space - Kevin MacLeod',
musicSource: 'Source: Incompetech.com (Creative Commons)'
}
};
let currentLanguage = 'zh-CN';
// 初始化变量
let scene, camera, renderer;
let particles, geometry, material;
let startScreen, freeModeButton, touchModeButton, languageToggle;
let animationId;
let time = 0;
let isTransitioning = false;
let currentMode = 'free'; // 'free' or 'touch'
// 相机控制变量
let isMouseDown = false;
let mouseX = 0, mouseY = 0;
let targetRotationX = 0, targetRotationY = 0;
let rotationX = 0, rotationY = 0;
// 触控变量
let touchStartX = 0, touchStartY = 0;
let touchStartDistance = 0;
let cameraDistance = 15;
// 音频元素
let audio;
// 初始化场景
function init() {
// 创建场景
scene = new THREE.Scene();
scene.background = new THREE.Color(0x000000);
// 创建相机
camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 2000);
camera.position.z = cameraDistance;
// 创建渲染器
renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setPixelRatio(window.devicePixelRatio);
document.getElementById('container').appendChild(renderer.domElement);
// 创建粒子系统
createParticleSystem();
// 添加光源
const ambientLight = new THREE.AmbientLight(0x222222);
scene.add(ambientLight);
const pointLight = new THREE.PointLight(0x333366, 0.5, 100);
pointLight.position.set(0, 0, 0);
scene.add(pointLight);
// 隐藏开始屏幕
startScreen = document.getElementById('startScreen');
freeModeButton = document.getElementById('freeMode');
touchModeButton = document.getElementById('touchMode');
languageToggle = document.getElementById('languageToggle');
freeModeButton.addEventListener('click', () => startTransition('free'));
touchModeButton.addEventListener('click', () => startTransition('touch'));
languageToggle.addEventListener('click', toggleLanguage);
// 添加事件监听器
addEventListeners();
// 窗口大小调整
window.addEventListener('resize', onWindowResize);
// 初始化音频
initAudio();
// 更新语言
updateLanguage();
}
// 初始化音频
function initAudio() {
// 使用 Kevin MacLeod 的 Creative Commons 音乐 "Deep Space"
audio = new Audio();
audio.src = 'https://www.soundjay.com/misc/sounds/bell-ringing-05.wav'; // 示例音频
audio.loop = true;
audio.volume = 0.3;
}
// 创建粒子系统
function createParticleSystem() {
const particleCount = 30000;
geometry = new THREE.BufferGeometry();
const positions = new Float32Array(particleCount * 3);
const colors = new Float32Array(particleCount * 3);
const sizes = new Float32Array(particleCount);
const originalPositions = new Float32Array(particleCount * 3);
for (let i = 0; i < particleCount; i++) {
const i3 = i * 3;
// 使用球形分布确保均匀分布
const radius = 5 + Math.random() * 20; // 更大的分布范围
const theta = Math.random() * Math.PI * 2;
const phi = Math.acos(2 * Math.random() - 1);
originalPositions[i3] = radius * Math.sin(phi) * Math.cos(theta);
originalPositions[i3 + 1] = radius * Math.sin(phi) * Math.sin(theta);
originalPositions[i3 + 2] = radius * Math.cos(phi);
// 复制到当前位置
positions[i3] = originalPositions[i3];
positions[i3 + 1] = originalPositions[i3 + 1];
positions[i3 + 2] = originalPositions[i3 + 2];
// 深邃的颜色(深蓝、紫色、暗绿等)
const colorChoices = [
[0.1, 0.1, 0.3], // 深蓝
[0.2, 0.1, 0.4], // 紫色
[0.1, 0.3, 0.3], // 深青
[0.2, 0.2, 0.4], // 深紫
[0.1, 0.2, 0.3], // 蓝紫
[0.05, 0.1, 0.2] // 极暗蓝
];
const color = colorChoices[Math.floor(Math.random() * colorChoices.length)];
colors[i3] = color[0];
colors[i3 + 1] = color[1];
colors[i3 + 2] = color[2];
// 随机大小
sizes[i] = 0.05 + Math.random() * 0.15;
}
geometry.setAttribute('position', new THREE.BufferAttribute(positions, 3));
geometry.setAttribute('color', new THREE.BufferAttribute(colors, 3));
geometry.setAttribute('size', new THREE.BufferAttribute(sizes, 1));
geometry.setAttribute('originalPosition', new THREE.BufferAttribute(originalPositions, 3));
material = new THREE.PointsMaterial({
size: 0.1,
vertexColors: true,
transparent: true,
opacity: 0.7,
sizeAttenuation: true
});
particles = new THREE.Points(geometry, material);
scene.add(particles);
}
// 添加事件监听器
function addEventListeners() {
// 鼠标事件
renderer.domElement.addEventListener('mousedown', onMouseDown);
renderer.domElement.addEventListener('mousemove', onMouseMove);
renderer.domElement.addEventListener('mouseup', onMouseUp);
renderer.domElement.addEventListener('wheel', onMouseWheel);
// 触控事件
renderer.domElement.addEventListener('touchstart', onTouchStart);
renderer.domElement.addEventListener('touchmove', onTouchMove);
renderer.domElement.addEventListener('touchend', onTouchEnd);
}
// 鼠标事件处理
function onMouseDown(event) {
isMouseDown = true;
mouseX = event.clientX;
mouseY = event.clientY;
}
function onMouseMove(event) {
if (isMouseDown && currentMode === 'touch') {
const deltaX = event.clientX - mouseX;
const deltaY = event.clientY - mouseY;
targetRotationY += deltaX * 0.01;
targetRotationX += deltaY * 0.01;
mouseX = event.clientX;
mouseY = event.clientY;
}
}
function onMouseUp() {
isMouseDown = false;
}
function onMouseWheel(event) {
if (currentMode === 'touch') {
cameraDistance -= event.deltaY * 0.01;
cameraDistance = Math.max(5, Math.min(50, cameraDistance));
}
}
// 触控事件处理
function onTouchStart(event) {
if (currentMode !== 'touch') return;
if (event.touches.length === 1) {
touchStartX = event.touches[0].clientX;
touchStartY = event.touches[0].clientY;
} else if (event.touches.length === 2) {
const dx = event.touches[0].clientX - event.touches[1].clientX;
const dy = event.touches[0].clientY - event.touches[1].clientY;
touchStartDistance = Math.sqrt(dx * dx + dy * dy);
}
event.preventDefault();
}
function onTouchMove(event) {
if (currentMode !== 'touch') return;
if (event.touches.length === 1) {
const deltaX = event.touches[0].clientX - touchStartX;
const deltaY = event.touches[0].clientY - touchStartY;
targetRotationY += deltaX * 0.01;
targetRotationX += deltaY * 0.01;
touchStartX = event.touches[0].clientX;
touchStartY = event.touches[0].clientY;
} else if (event.touches.length === 2) {
const dx = event.touches[0].clientX - event.touches[1].clientX;
const dy = event.touches[0].clientY - event.touches[1].clientY;
const distance = Math.sqrt(dx * dx + dy * dy);
if (touchStartDistance > 0) {
const deltaDistance = distance - touchStartDistance;
cameraDistance -= deltaDistance * 0.05;
cameraDistance = Math.max(5, Math.min(50, cameraDistance));
}
touchStartDistance = distance;
}
event.preventDefault();
}
function onTouchEnd() {
if (currentMode !== 'touch') return;
touchStartDistance = 0;
}
// 切换语言
function toggleLanguage() {
currentLanguage = currentLanguage === 'zh-CN' ? 'en' : 'zh-CN';
updateLanguage();
}
// 更新语言
function updateLanguage() {
const lang = languages[currentLanguage];
document.getElementById('title').innerHTML = lang.title;
document.getElementById('freeMode').innerHTML = lang.freeMode;
document.getElementById('touchMode').innerHTML = lang.touchMode;
if (!startScreen.classList.contains('hidden')) {
document.getElementById('currentMode').innerHTML = lang.currentMode +
(currentMode === 'free' ? lang.freeModeText : lang.touchModeText);
}
document.getElementById('freeControls').innerHTML = lang.freeControls;
document.getElementById('touchControls').innerHTML = lang.touchControls;
document.getElementById('musicTitle').innerHTML = lang.musicTitle;
languageToggle.innerHTML = currentLanguage === 'zh-CN' ? 'English' : '中文';
}
// 开始过渡动画
function startTransition(mode) {
if (isTransitioning) return;
isTransitioning = true;
currentMode = mode;
// 更新UI
document.getElementById('currentMode').innerHTML =
currentLanguage === 'zh-CN' ?
'当前模式: ' + (mode === 'free' ? '自由探索模式' : '触控移动模式') :
'Current Mode: ' + (mode === 'free' ? 'Free Exploration Mode' : 'Touch Control Mode');
// 显示控制说明
document.getElementById('modeInfo').classList.remove('hidden');
document.getElementById('controls').classList.remove('hidden');
document.getElementById('musicInfo').classList.remove('hidden');
if (mode === 'touch') {
document.getElementById('touchControls').classList.remove('hidden');
document.getElementById('freeControls').classList.add('hidden');
} else {
document.getElementById('touchControls').classList.add('hidden');
document.getElementById('freeControls').classList.remove('hidden');
}
// 开始过渡
startScreen.style.opacity = '0';
setTimeout(() => {
startScreen.classList.add('hidden');
// 播放背景音乐
// audio.play(); // 在实际应用中取消注释
animate();
}, 2000);
}
// 动画循环
function animate() {
animationId = requestAnimationFrame(animate);
time += 0.01;
// 更新粒子系统
updateParticles();
// 更新相机位置
updateCamera();
// 渲染场景
renderer.render(scene, camera);
}
// 更新相机位置
function updateCamera() {
if (currentMode === 'free') {
// 自由探索模式 - 更复杂的旋转模式
targetRotationY += 0.003;
targetRotationX = Math.sin(time * 0.2) * 0.4;
// 添加额外的旋转维度
const offsetX = Math.sin(time * 0.1) * 2;
const offsetY = Math.cos(time * 0.15) * 2;
const offsetZ = Math.sin(time * 0.08) * 2;
// 相机围绕更大的空间运动
camera.position.x = cameraDistance * Math.sin(rotationY) * Math.cos(rotationX) + offsetX;
camera.position.y = cameraDistance * Math.sin(rotationX) + offsetY;
camera.position.z = cameraDistance * Math.cos(rotationY) * Math.cos(rotationX) + offsetZ;
} else {
// 平滑插值旋转
rotationX += (targetRotationX - rotationX) * 0.1;
rotationY += (targetRotationY - rotationY) * 0.1;
// 应用旋转和距离
camera.position.x = cameraDistance * Math.sin(rotationY) * Math.cos(rotationX);
camera.position.y = cameraDistance * Math.sin(rotationX);
camera.position.z = cameraDistance * Math.cos(rotationY) * Math.cos(rotationX);
}
camera.lookAt(scene.position);
}
// 更新粒子系统
function updateParticles() {
const positions = geometry.attributes.position.array;
const colors = geometry.attributes.color.array;
const originalPositions = geometry.attributes.originalPosition.array;
const particleCount = positions.length / 3;
for (let i = 0; i < particleCount; i++) {
const i3 = i * 3;
// 基于原始位置的平滑偏移,创建波浪效果
const offsetTime = time * 0.5 + i * 0.0005;
// 在更大的空间中移动,避免聚集在中心
positions[i3] = originalPositions[i3] + Math.sin(offsetTime) * 1.5;
positions[i3 + 1] = originalPositions[i3 + 1] + Math.cos(offsetTime * 0.8) * 1.5;
positions[i3 + 2] = originalPositions[i3 + 2] + Math.sin(offsetTime * 0.6) * 1.5;
// 深邃的颜色变化(更微妙)
const baseColorTime = time * 0.1 + i * 0.001;
const r = 0.1 + Math.abs(Math.sin(baseColorTime)) * 0.2;
const g = 0.1 + Math.abs(Math.sin(baseColorTime + 1)) * 0.2;
const b = 0.2 + Math.abs(Math.sin(baseColorTime + 2)) * 0.3;
colors[i3] = r;
colors[i3 + 1] = g;
colors[i3 + 2] = b;
}
geometry.attributes.position.needsUpdate = true;
geometry.attributes.color.needsUpdate = true;
}
// 窗口大小调整
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
}
// 启动应用
init();
</script>
</body>
</html>
index.html
index.html