3d粒子观赏edit icon

作者:
邓朝元
Fork(复制)
下载
嵌入
BUG反馈
index.html
现在支持上传本地图片了!
index.html
            
            <!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>从单调到绚丽的3D世界</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);
        }
        
        canvas {
            display: block;
        }
        
        #info {
            position: absolute;
            bottom: 20px;
            left: 20px;
            color: white;
            font-family: Arial, sans-serif;
            font-size: 14px;
            z-index: 10;
            opacity: 0.7;
        }
        
        #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: 20px;
            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;
        }
    </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>
    
    <div id="modeInfo" style="display: none;">
        <span id="currentMode">当前模式: </span>
    </div>
    
    <div id="controls" style="display: none;">
        <div id="freeControls">
            自动旋转<br>
            按住鼠标拖拽旋转
        </div>
        <div id="touchControls" style="display: none;">
            滑动屏幕移动视角<br>
            双指缩放
        </div>
    </div>
    
    <div id="musicInfo">
        背景音乐: <span id="musicTitle">深邃夜空</span><br>
        来源: freesound.org [[8]]
    </div>

    <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
    <script>
        // 初始化变量
        let scene, camera, renderer;
        let particles, geometry, material;
        let startScreen, freeModeButton, touchModeButton;
        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 = 5;
        
        // 音频元素
        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, 1000);
            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(0x333333);
            scene.add(ambientLight);
            
            const pointLight = new THREE.PointLight(0xffffff, 1, 100);
            pointLight.position.set(10, 10, 10);
            scene.add(pointLight);
            
            // 隐藏开始屏幕
            startScreen = document.getElementById('startScreen');
            freeModeButton = document.getElementById('freeMode');
            touchModeButton = document.getElementById('touchMode');
            
            freeModeButton.addEventListener('click', () => startTransition('free'));
            touchModeButton.addEventListener('click', () => startTransition('touch'));
            
            // 添加事件监听器
            addEventListeners();
            
            // 窗口大小调整
            window.addEventListener('resize', onWindowResize);
            
            // 初始化音频
            initAudio();
        }
        
        // 初始化音频
        function initAudio() {
            // 这里使用一个深邃的背景音乐示例
            // 在实际应用中,你可能需要使用一个真实的音乐文件URL
            // 根据搜索结果,可以选择类似"深邃的夜"这样的爵士乐作为背景音乐 [[8]]
            audio = new Audio();
            audio.loop = true;
            
            // 由于版权问题,这里不直接嵌入音乐文件
            // 在实际使用时,你可以替换为一个合适的免费音乐文件URL
            // 例如:https://example.com/path/to/your/deep-space-music.mp3
            console.log("背景音乐已准备就绪");
        }
        
        // 创建粒子系统
        function createParticleSystem() {
            const particleCount = 20000;
            geometry = new THREE.BufferGeometry();
            const positions = new Float32Array(particleCount * 3);
            const colors = new Float32Array(particleCount * 3);
            const originalPositions = new Float32Array(particleCount * 3); // 保存原始位置
            
            for (let i = 0; i < particleCount * 3; i += 3) {
                // 随机位置 (使用球形分布使效果更好)
                const radius = Math.random() * 10;
                const theta = Math.random() * Math.PI * 2;
                const phi = Math.acos(2 * Math.random() - 1);
                
                originalPositions[i] = radius * Math.sin(phi) * Math.cos(theta);
                originalPositions[i + 1] = radius * Math.sin(phi) * Math.sin(theta);
                originalPositions[i + 2] = radius * Math.cos(phi);
                
                // 复制到当前位置
                positions[i] = originalPositions[i];
                positions[i + 1] = originalPositions[i + 1];
                positions[i + 2] = originalPositions[i + 2];
                
                // 初始颜色(灰色)
                colors[i] = 0.5;
                colors[i + 1] = 0.5;
                colors[i + 2] = 0.5;
            }
            
            geometry.setAttribute('position', new THREE.BufferAttribute(positions, 3));
            geometry.setAttribute('color', new THREE.BufferAttribute(colors, 3));
            
            // 保存原始位置以便后续使用
            geometry.setAttribute('originalPosition', new THREE.BufferAttribute(originalPositions, 3));
            
            material = new THREE.PointsMaterial({
                size: 0.1,
                vertexColors: true,
                transparent: true,
                opacity: 0.8
            });
            
            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(1, Math.min(20, 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(1, Math.min(20, cameraDistance));
                }
                
                touchStartDistance = distance;
            }
            event.preventDefault();
        }
        
        function onTouchEnd() {
            if (currentMode !== 'touch') return;
            touchStartDistance = 0;
        }
        
        // 开始过渡动画
        function startTransition(mode) {
            if (isTransitioning) return;
            
            isTransitioning = true;
            currentMode = mode;
            
            // 更新UI
            document.getElementById('currentMode').textContent = 
                mode === 'free' ? '当前模式: 自由探索模式' : '当前模式: 触控移动模式';
            
            // 显示控制说明
            document.getElementById('modeInfo').style.display = 'block';
            document.getElementById('controls').style.display = 'block';
            
            if (mode === 'touch') {
                document.getElementById('touchControls').style.display = 'block';
                document.getElementById('freeControls').style.display = 'none';
            } else {
                document.getElementById('touchControls').style.display = 'none';
                document.getElementById('freeControls').style.display = 'block';
            }
            
            // 开始过渡
            startScreen.style.opacity = '0';
            
            setTimeout(() => {
                startScreen.style.display = 'none';
                // 播放背景音乐
                // 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.005;
                targetRotationX = Math.sin(time * 0.3) * 0.3;
            }
            
            // 平滑插值旋转
            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 + i * 0.001;
                positions[i3] = originalPositions[i3] + Math.sin(offsetTime) * 0.5;
                positions[i3 + 1] = originalPositions[i3 + 1] + Math.cos(offsetTime * 0.7) * 0.5;
                positions[i3 + 2] = originalPositions[i3 + 2] + Math.sin(offsetTime * 0.5) * 0.5;
                
                // 颜色变化
                const colorTime = time + i * 0.005;
                colors[i3] = Math.abs(Math.sin(colorTime)) * 0.8 + 0.2; // R
                colors[i3 + 1] = Math.abs(Math.sin(colorTime + 2)) * 0.8 + 0.2; // G
                colors[i3 + 2] = Math.abs(Math.sin(colorTime + 4)) * 0.8 + 0.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>
        
编辑器加载中
预览
控制台