鼠标移动触发的图片动画实现优化求助
优化视差鼠标交互动画流畅度的方案
核心问题分析
原实现的卡顿主要来自:
setTimeout与requestAnimationFrame的时序冲突,导致动画帧不同步- 每次鼠标移动都生成新的延迟任务,造成任务队列堆积
- 未使用自然的缓动曲线,且偏移计算未考虑容器位置偏移
具体优化措施
移除
setTimeout,用缓动系数实现层级延迟效果
放弃定时器,给每个图片设置不同的深度系数,让层级靠后的图片移动幅度更小、响应更慢,模拟延迟效果,完全贴合浏览器渲染帧。使用容器相对坐标计算偏移
用getBoundingClientRect()获取容器在视口的位置,计算鼠标相对于容器中心的偏移,避免视口滚动导致的坐标错误。添加平滑的缓动过渡
给CSS的transition设置自然的缓动函数(如cubic-bezier(0.25, 0.46, 0.45, 0.94)),替换生硬的linear;同时在JS中用增量更新实现帧间平滑,避免跳变。优化鼠标移出的复位动画
统一用CSS过渡处理复位,避免JS直接覆盖样式导致的突兀。
修改后的完整代码
HTML
<h5>PRACTICE TESTING ANIMATIONS</h5> <div class="image-container"> <img src="image1.jpg" class="image" data-depth="0.1" id="image1"> <img src="image2.jpg" class="image" data-depth="0.2" id="image2"> <img src="image3.jpg" class="image" data-depth="0.3" id="image3"> <img src="image4.jpg" class="image" data-depth="0.4" id="image4"> </div>
CSS
body { background-color: azure; margin: 0; padding: 0; overflow: hidden; } h5 { text-align: center; } img { width: 100px; height: 100px; } .image-container { position: relative; width: 100vw; height: 100vh; overflow: hidden; } .image { position: absolute; /* 使用自然的缓动曲线 */ transition: transform 0.3s cubic-bezier(0.25, 0.46, 0.45, 0.94); /* 提示浏览器提前优化渲染 */ will-change: transform; } .image:hover { transform: translate(0, 0) !important; } #image1 { left: 20%; top: 20%; } #image2 { left: 27%; bottom: 20%; } #image3 { left: 60%; top: 60%; } #image4 { right: 20%; top: 20%; }
JavaScript
const images = document.querySelectorAll(".image"); const container = document.querySelector(".image-container"); let animationFrameId = null; // 存储当前偏移量,用于帧间平滑过渡 let currentX = 0; let currentY = 0; container.addEventListener("mousemove", (e) => { const rect = container.getBoundingClientRect(); // 计算鼠标相对于容器中心的偏移 const mouseX = e.clientX - rect.left - rect.width / 2; const mouseY = e.clientY - rect.top - rect.height / 2; if (animationFrameId) { cancelAnimationFrame(animationFrameId); } animationFrameId = requestAnimationFrame(() => { // 缓动更新当前偏移量,0.1为缓动系数,值越小动画越平滑 currentX += (mouseX * -0.1 - currentX) * 0.1; currentY += (mouseY * -0.1 - currentY) * 0.1; images.forEach(image => { // 用data-depth属性控制每个图片的移动幅度 const depth = parseFloat(image.dataset.depth); const xOffset = currentX * depth; const yOffset = currentY * depth; if (!image.classList.contains("hovered")) { image.style.transform = `translate(${xOffset}px, ${yOffset}px)`; } }); }); }); container.addEventListener("mouseout", () => { if (animationFrameId) { cancelAnimationFrame(animationFrameId); } // 重置偏移量,让动画平滑复位 currentX = 0; currentY = 0; images.forEach(image => { image.style.transform = "translate(0, 0)"; }); });
额外优化提示
如果图片数量较多,可使用节流函数限制mousemove事件的触发频率,进一步减少计算压力。
内容的提问来源于stack exchange,提问作者David K.
相关产品推荐
相关产品推荐

