如何让垂直图片视差动画更流畅?
如何让垂直图片视差动画更流畅?
我明白你现在遇到的问题啦——鼠标刚移上去图片会突然跳动,加了过渡又跟不上鼠标移动的速度,确实挺影响体验的。咱们一步步调整代码,把这个视差效果改得全程丝滑:
先说说现有代码的核心问题
- 过渡逻辑冲突:你在
mouseenter、mousemove、mouseleave里反复修改transition属性,导致刚进入时的第一个位置变化是瞬间的,直接跳帧; - 视差计算有误:当前的位置比例计算逻辑不对,会导致图片移动范围不符合预期;
- 未做性能优化:鼠标移动事件触发极频繁,直接修改样式容易出现卡顿,跟不上鼠标速度。
优化后的完整代码
1. CSS 部分(调整过渡和优化动画)
#display-grid{ display: grid; width: 80%; gap: 1rem; grid-template-columns: repeat(auto-fit, minmax(24%, 1fr)); } .cell{ border: 1px solid #EEEEEE; border-radius: 1rem; width: 100%; overflow: hidden; position: relative; border-radius: 12px; aspect-ratio: 1 / 1.2; } .cell .image-container{ width: 100%; height: 90%; position: relative; aspect-ratio: 1 / 1; border-radius: 1rem; overflow: hidden; cursor: pointer; /* 加个鼠标样式更友好 */ } .cell img { width: 100%; height: auto; object-fit: cover; object-position: top; border-radius: 1rem; will-change: transform; /* 告诉浏览器提前优化这个属性的动画 */ }
2. JavaScript 部分(修正计算+优化动画逻辑)
document.querySelectorAll('.image-container').forEach(item => { const img = item.querySelector('img'); let isFirstMove = true; // 标记是否是鼠标进入后的第一次移动 // 复用的位置计算函数 const calculateImagePosition = (e) => { const rect = item.getBoundingClientRect(); const mouseY = e.clientY - rect.top; // 鼠标在容器内的相对Y坐标 const containerHeight = rect.height; const imgExcessHeight = img.offsetHeight - containerHeight; // 图片超出容器的高度(可移动范围) // 计算图片需要移动的距离:鼠标在容器内的比例 * 可移动范围 const translateYValue = - (mouseY / containerHeight) * imgExcessHeight; return translateYValue; } item.addEventListener('mousemove', (e) => { // 用requestAnimationFrame确保动画在浏览器的动画帧执行,避免卡顿 requestAnimationFrame(() => { if (isFirstMove) { // 第一次移动用短过渡,避免跳帧 img.style.transition = 'transform 0.2s ease-out'; isFirstMove = false; } else { // 后续移动关闭过渡,保证即时跟随鼠标 img.style.transition = 'transform 0s'; } const moveY = calculateImagePosition(e); img.style.transform = `translateY(${moveY}px)`; }); }); item.addEventListener('mouseleave', () => { // 鼠标离开时用慢过渡复位 img.style.transition = 'transform 0.8s ease-out'; img.style.transform = 'translateY(0)'; isFirstMove = true; // 重置标记,下次进入重新触发 }); item.addEventListener('mouseenter', (e) => { // 鼠标刚进入时,直接计算当前位置并平滑过渡过去 isFirstMove = true; requestAnimationFrame(() => { img.style.transition = 'transform 0.2s ease-out'; const moveY = calculateImagePosition(e); img.style.transform = `translateY(${moveY}px)`; }); }); });
3. HTML 部分(保持不变即可)
<div id="display-grid"> <div class="cell"> <div class="image-container"> <img src="https://framerusercontent.com/images/B7ykrtzOQa5hEXGFIhcq8gyaE.jpg?scale-down-to=1024" alt="作品预览图"> </div> <p>Image Title</p> </div> <div class="cell"> <div class="image-container"> <img src="https://framerusercontent.com/images/B7ykrtzOQa5hEXGFIhcq8gyaE.jpg?scale-down-to=1024" alt="作品预览图"> </div> <p>InfoSwap</p> </div> </div>
关键优化点说明
- 修正视差计算:现在的逻辑是根据鼠标在容器内的位置比例,让图片在「可移动范围」内精准跟随,鼠标在容器顶部时图片顶部对齐容器,鼠标在底部时图片底部对齐容器;
- 分离过渡逻辑:
- 鼠标进入/第一次移动:用0.2s短过渡避免跳帧;
- 鼠标持续移动:关闭过渡,用
requestAnimationFrame保证即时跟随,不会滞后; - 鼠标离开:用慢过渡平滑复位;
- 性能优化:
- 用
requestAnimationFrame包裹动画更新,确保浏览器在合适的时机执行,避免丢帧; - 给图片加
will-change: transform,让浏览器提前准备优化这个属性的动画,进一步提升流畅度。
- 用
备注:内容来源于stack exchange,提问作者Skiddswarmik
相关产品推荐
相关产品推荐

