feat(christmas): 优化粒子动画以适配不同屏幕尺寸
- 根据屏幕宽度动态调整散射模式下粒子的分布范围 - 调整焦点照片的位置和距离以适应小屏幕设备 - 根据屏幕尺寸改变噪声强度,使动画更自然 - 优化手势识别的影响范围和灵敏度,提升触摸体验 - 调整插值速度和缩放比例,确保动画在各种设备上流畅运行 - 重构窗口大小变化处理函数,提高代码可维护性
This commit is contained in:
117
christmas.html
117
christmas.html
@@ -507,8 +507,15 @@
|
|||||||
|
|
||||||
// 限制散射模式下粒子的分布范围,防止超出屏幕
|
// 限制散射模式下粒子的分布范围,防止超出屏幕
|
||||||
let rScatter = this.isDust ? (12 + Math.random() * 15) : (8 + Math.random() * 10);
|
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 theta = Math.random() * Math.PI * 2;
|
||||||
const phi = Math.acos(2 * Math.random() - 1);
|
const phi = Math.acos(2 * Math.random() - 1);
|
||||||
this.posScatter.set(
|
this.posScatter.set(
|
||||||
@@ -528,7 +535,15 @@
|
|||||||
if (mode === 'TREE') target = this.posTree;
|
if (mode === 'TREE') target = this.posTree;
|
||||||
else if (mode === 'FOCUS') {
|
else if (mode === 'FOCUS') {
|
||||||
if (this.mesh === focusTargetMesh) {
|
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();
|
const invMatrix = new THREE.Matrix4().copy(mainGroup.matrixWorld).invert();
|
||||||
target = desiredWorldPos.applyMatrix4(invMatrix);
|
target = desiredWorldPos.applyMatrix4(invMatrix);
|
||||||
} else {
|
} else {
|
||||||
@@ -538,32 +553,62 @@
|
|||||||
|
|
||||||
// 在散射模式下添加噪声扰动,让粒子运动更生动
|
// 在散射模式下添加噪声扰动,让粒子运动更生动
|
||||||
if (mode === 'SCATTER') {
|
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;
|
let noiseStrength = CONFIG.animation.noiseStrength;
|
||||||
const noiseZ = this.noise(elapsedTime * 0.5 + this.noiseOffset.z) * 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.x += noiseX;
|
||||||
target.y += noiseY;
|
target.y += noiseY;
|
||||||
target.z += noiseZ;
|
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) {
|
if (target.length() > maxDistance) {
|
||||||
target.normalize().multiplyScalar(maxDistance);
|
target.normalize().multiplyScalar(maxDistance);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 实时手势影响 - 只关注当前手势位置,不使用历史轨迹
|
// 实时手势影响 - 只关注当前手势位置,不使用历史轨迹
|
||||||
if (handState.detected) {
|
// 在触摸屏设备上增加手势灵敏度
|
||||||
|
if (handState.detected && ('ontouchstart' in window || navigator.maxTouchPoints > 0)) {
|
||||||
// 将2D手势坐标转换为3D空间中的影响点
|
// 将2D手势坐标转换为3D空间中的影响点
|
||||||
|
// 根据屏幕尺寸调整手势影响范围
|
||||||
|
let handInfluenceRange = 15;
|
||||||
|
if (window.innerWidth <= 480) {
|
||||||
|
handInfluenceRange = 10; // 小屏幕设备上减小影响范围
|
||||||
|
} else if (window.innerWidth <= 768) {
|
||||||
|
handInfluenceRange = 12; // 中等屏幕设备
|
||||||
|
}
|
||||||
|
|
||||||
const handInfluencePoint = new THREE.Vector3(
|
const handInfluencePoint = new THREE.Vector3(
|
||||||
handState.x * 15, // 调整影响范围
|
handState.x * handInfluenceRange, // 调整影响范围
|
||||||
handState.y * 15,
|
handState.y * handInfluenceRange,
|
||||||
0
|
0
|
||||||
);
|
);
|
||||||
|
|
||||||
// 计算粒子与手势影响点之间的距离
|
// 计算粒子与手势影响点之间的距离
|
||||||
const distance = this.mesh.position.distanceTo(handInfluencePoint);
|
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) {
|
if (distance < maxInfluenceDistance) {
|
||||||
@@ -601,14 +646,27 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 应用影响到目标位置
|
// 应用影响到目标位置
|
||||||
|
// 在触摸屏设备上增强手势影响
|
||||||
|
let influenceMultiplier = 2.0;
|
||||||
|
if ('ontouchstart' in window || navigator.maxTouchPoints > 0) {
|
||||||
|
influenceMultiplier = 2.5; // 触摸屏设备增强影响
|
||||||
|
}
|
||||||
|
|
||||||
target.add(
|
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));
|
this.mesh.position.lerp(target, Math.min(lerpSpeed * dt, 1.0));
|
||||||
|
|
||||||
if (mode === 'SCATTER') {
|
if (mode === 'SCATTER') {
|
||||||
@@ -632,11 +690,27 @@
|
|||||||
} else if (mode === 'SCATTER' && this.type === 'PHOTO') {
|
} else if (mode === 'SCATTER' && this.type === 'PHOTO') {
|
||||||
s = this.baseScale * 2.5;
|
s = this.baseScale * 2.5;
|
||||||
} else if (mode === 'FOCUS') {
|
} 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;
|
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 ---
|
// --- 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 ---
|
// --- Event Handling ---
|
||||||
function setupEvents() {
|
function setupEvents() {
|
||||||
let resizeTimeout;
|
let resizeTimeout;
|
||||||
window.addEventListener('resize', () => {
|
window.addEventListener('resize', () => {
|
||||||
clearTimeout(resizeTimeout);
|
clearTimeout(resizeTimeout);
|
||||||
resizeTimeout = setTimeout(() => {
|
resizeTimeout = setTimeout(handleWindowResize, 100);
|
||||||
camera.aspect = window.innerWidth / window.innerHeight;
|
|
||||||
camera.updateProjectionMatrix();
|
|
||||||
renderer.setSize(window.innerWidth, window.innerHeight);
|
|
||||||
composer.setSize(window.innerWidth, window.innerHeight);
|
|
||||||
}, 100);
|
|
||||||
});
|
});
|
||||||
document.getElementById('file-input').addEventListener('change', handleImageUpload);
|
document.getElementById('file-input').addEventListener('change', handleImageUpload);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user