From ad744a0690c42bdeedd2288e9a914701f5ca8d24 Mon Sep 17 00:00:00 2001 From: hehh Date: Fri, 12 Dec 2025 18:20:42 +0800 Subject: [PATCH] =?UTF-8?q?feat(christmas):=20=E4=BC=98=E5=8C=96=E7=B2=92?= =?UTF-8?q?=E5=AD=90=E5=8A=A8=E7=94=BB=E4=BB=A5=E9=80=82=E9=85=8D=E4=B8=8D?= =?UTF-8?q?=E5=90=8C=E5=B1=8F=E5=B9=95=E5=B0=BA=E5=AF=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 根据屏幕宽度动态调整散射模式下粒子的分布范围 - 调整焦点照片的位置和距离以适应小屏幕设备 - 根据屏幕尺寸改变噪声强度,使动画更自然 - 优化手势识别的影响范围和灵敏度,提升触摸体验 - 调整插值速度和缩放比例,确保动画在各种设备上流畅运行 - 重构窗口大小变化处理函数,提高代码可维护性 --- christmas.html | 117 ++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 97 insertions(+), 20 deletions(-) diff --git a/christmas.html b/christmas.html index 6dcde05..2c8a443 100644 --- a/christmas.html +++ b/christmas.html @@ -507,8 +507,15 @@ // 限制散射模式下粒子的分布范围,防止超出屏幕 let rScatter = this.isDust ? (12 + Math.random() * 15) : (8 + Math.random() * 10); + // 根据屏幕尺寸调整最大范围 + let maxRScatter = 25; + if (window.innerWidth <= 480) { + maxRScatter = 15; // 小屏幕设备上减小范围 + } else if (window.innerWidth <= 768) { + maxRScatter = 20; // 中等屏幕设备 + } // 限制最大范围,防止照片飞出屏幕 - rScatter = Math.min(rScatter, 25); + rScatter = Math.min(rScatter, maxRScatter); const theta = Math.random() * Math.PI * 2; const phi = Math.acos(2 * Math.random() - 1); this.posScatter.set( @@ -528,7 +535,15 @@ if (mode === 'TREE') target = this.posTree; else if (mode === 'FOCUS') { if (this.mesh === focusTargetMesh) { - const desiredWorldPos = new THREE.Vector3(0, 2, 35); + // 根据屏幕尺寸调整焦点照片的位置和距离 + let focusZ = 35; + if (window.innerWidth <= 480) { + focusZ = 25; // 小屏幕设备上更靠近摄像机 + } else if (window.innerWidth <= 768) { + focusZ = 30; // 中等屏幕设备 + } + + const desiredWorldPos = new THREE.Vector3(0, 2, focusZ); const invMatrix = new THREE.Matrix4().copy(mainGroup.matrixWorld).invert(); target = desiredWorldPos.applyMatrix4(invMatrix); } else { @@ -538,32 +553,62 @@ // 在散射模式下添加噪声扰动,让粒子运动更生动 if (mode === 'SCATTER') { - const noiseX = this.noise(elapsedTime * 0.5 + this.noiseOffset.x) * CONFIG.animation.noiseStrength; - const noiseY = this.noise(elapsedTime * 0.5 + this.noiseOffset.y) * CONFIG.animation.noiseStrength; - const noiseZ = this.noise(elapsedTime * 0.5 + this.noiseOffset.z) * CONFIG.animation.noiseStrength; + // 根据屏幕尺寸调整噪声强度 + let noiseStrength = CONFIG.animation.noiseStrength; + if (window.innerWidth <= 480) { + noiseStrength *= 0.7; // 小屏幕设备上减小噪声 + } else if (window.innerWidth <= 768) { + noiseStrength *= 0.8; // 中等屏幕设备 + } + + const noiseX = this.noise(elapsedTime * 0.5 + this.noiseOffset.x) * noiseStrength; + const noiseY = this.noise(elapsedTime * 0.5 + this.noiseOffset.y) * noiseStrength; + const noiseZ = this.noise(elapsedTime * 0.5 + this.noiseOffset.z) * noiseStrength; target.x += noiseX; target.y += noiseY; target.z += noiseZ; // 限制粒子位置,防止飞出屏幕 - const maxDistance = 30; + let maxDistance = 30; + // 根据屏幕尺寸调整粒子分布范围 + if (window.innerWidth <= 480) { + maxDistance = 20; // 小屏幕设备上减小范围 + } else if (window.innerWidth <= 768) { + maxDistance = 25; // 中等屏幕设备 + } + if (target.length() > maxDistance) { target.normalize().multiplyScalar(maxDistance); } // 实时手势影响 - 只关注当前手势位置,不使用历史轨迹 - if (handState.detected) { + // 在触摸屏设备上增加手势灵敏度 + if (handState.detected && ('ontouchstart' in window || navigator.maxTouchPoints > 0)) { // 将2D手势坐标转换为3D空间中的影响点 + // 根据屏幕尺寸调整手势影响范围 + let handInfluenceRange = 15; + if (window.innerWidth <= 480) { + handInfluenceRange = 10; // 小屏幕设备上减小影响范围 + } else if (window.innerWidth <= 768) { + handInfluenceRange = 12; // 中等屏幕设备 + } + const handInfluencePoint = new THREE.Vector3( - handState.x * 15, // 调整影响范围 - handState.y * 15, + handState.x * handInfluenceRange, // 调整影响范围 + handState.y * handInfluenceRange, 0 ); // 计算粒子与手势影响点之间的距离 const distance = this.mesh.position.distanceTo(handInfluencePoint); - const maxInfluenceDistance = 8.0; + // 根据屏幕尺寸调整最大影响距离 + let maxInfluenceDistance = 8.0; + if (window.innerWidth <= 480) { + maxInfluenceDistance = 6.0; // 小屏幕设备上减小影响范围 + } else if (window.innerWidth <= 768) { + maxInfluenceDistance = 7.0; // 中等屏幕设备 + } // 如果粒子在影响范围内,则受到手势牵引 if (distance < maxInfluenceDistance) { @@ -601,14 +646,27 @@ } // 应用影响到目标位置 + // 在触摸屏设备上增强手势影响 + let influenceMultiplier = 2.0; + if ('ontouchstart' in window || navigator.maxTouchPoints > 0) { + influenceMultiplier = 2.5; // 触摸屏设备增强影响 + } + target.add( - direction.multiplyScalar(influenceStrength * 2.0) + direction.multiplyScalar(influenceStrength * influenceMultiplier) ); } } } - const lerpSpeed = (mode === 'FOCUS' && this.mesh === focusTargetMesh) ? 7.0 : CONFIG.animation.positionLerp; + // 根据屏幕尺寸调整插值速度 + let lerpSpeed = (mode === 'FOCUS' && this.mesh === focusTargetMesh) ? 7.0 : CONFIG.animation.positionLerp; + if (window.innerWidth <= 480) { + lerpSpeed *= 1.2; // 小屏幕设备上加快动画速度 + } else if (window.innerWidth <= 768) { + lerpSpeed *= 1.1; // 中等屏幕设备 + } + this.mesh.position.lerp(target, Math.min(lerpSpeed * dt, 1.0)); if (mode === 'SCATTER') { @@ -632,11 +690,27 @@ } else if (mode === 'SCATTER' && this.type === 'PHOTO') { s = this.baseScale * 2.5; } else if (mode === 'FOCUS') { - if (this.mesh === focusTargetMesh) s = 4.5; + // 根据屏幕尺寸调整焦点照片的缩放比例 + let focusScale = 4.5; + if (window.innerWidth <= 480) { + focusScale = 3.0; // 小屏幕设备上较小的缩放比例 + } else if (window.innerWidth <= 768) { + focusScale = 3.5; // 中等屏幕设备 + } + + if (this.mesh === focusTargetMesh) s = focusScale; else s = this.baseScale * 0.8; } - this.mesh.scale.lerp(new THREE.Vector3(s, s, s), Math.min(CONFIG.animation.scaleLerp * dt, 1.0)); + // 根据屏幕尺寸调整缩放插值速度 + let scaleLerpSpeed = CONFIG.animation.scaleLerp; + if (window.innerWidth <= 480) { + scaleLerpSpeed *= 1.2; // 小屏幕设备上加快缩放动画 + } else if (window.innerWidth <= 768) { + scaleLerpSpeed *= 1.1; // 中等屏幕设备 + } + + this.mesh.scale.lerp(new THREE.Vector3(s, s, s), Math.min(scaleLerpSpeed * dt, 1.0)); } } // --- End of Particle Class --- @@ -1011,17 +1085,20 @@ } } + function handleWindowResize() { + // 监听窗口大小变化,更新渲染器和相机 + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + renderer.setSize(window.innerWidth, window.innerHeight); + composer.setSize(window.innerWidth, window.innerHeight); + } + // --- Event Handling --- function setupEvents() { let resizeTimeout; window.addEventListener('resize', () => { clearTimeout(resizeTimeout); - resizeTimeout = setTimeout(() => { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - renderer.setSize(window.innerWidth, window.innerHeight); - composer.setSize(window.innerWidth, window.innerHeight); - }, 100); + resizeTimeout = setTimeout(handleWindowResize, 100); }); document.getElementById('file-input').addEventListener('change', handleImageUpload);