如何优化JavaScript实现的ReviewsWrapper内div水平循环滚动动画卡顿问题
卡顿原因
setInterval回调执行时机受主线程其他任务阻塞,时间精度不可控,且5ms的执行间隔远高于常规屏幕60fps刷新率对应的16.6ms单帧时长,会导致大量无效执行和丢帧- 每次调用
getBoundingClientRect、scrollTo、调整DOM节点顺序都会触发浏览器重排,高频执行会大量占用主线程资源,直接引发卡顿
最优方案:纯CSS实现无缝滚动(推荐)
利用CSS动画跑在合成线程的特性,完全不占用主线程,流畅度最高。实现原理是复制一份相同的滚动内容,整体向左平移到第一份内容完全移出视口时重置动画,实现无缝循环。
修改后的完整代码
CSS
#ReviewsWrapper { position: relative; max-width: 100vw; overflow: hidden; -ms-overflow-style: none; scrollbar-width: none; } .scroll-content { display: flex; width: fit-content; animation: scroll 20s linear infinite; /* 20s是滚动一周的时长,可自行调整 */ } .review { min-width: 300px; display: flex; align-items: center; justify-content: center; padding: 8px; margin: 8px; background: yellow; } p { margin-left: 8px; } @keyframes scroll { 0% { transform: translateX(0); } 100% { transform: translateX(-50%); } } /* 可选:鼠标悬停暂停滚动 */ #ReviewsWrapper:hover .scroll-content { animation-play-state: paused; }
HTML
在原来的容器里加一层scroll-content包裹所有review:
<div id="ReviewsWrapper"> <div class="scroll-content"> <div class="review"> <img src="https://via.placeholder.com/150"> <p>Review ONE</p> </div> <div class="review"> <img src="https://via.placeholder.com/150"> <p>Review TWO</p> </div> <div class="review"> <img src="https://via.placeholder.com/150"> <p>Review THREE</p> </div> <div class="review"> <img src="https://via.placeholder.com/150"> <p>Review FOUR</p> </div> <div class="review"> <img src="https://via.placeholder.com/150"> <p>Review FIVE</p> </div> <div class="review"> <img src="https://via.placeholder.com/150"> <p>Review SIX</p> </div> <!-- 复制一份相同的内容,也可以用JS动态生成 --> <div class="review"> <img src="https://via.placeholder.com/150"> <p>Review ONE</p> </div> <div class="review"> <img src="https://via.placeholder.com/150"> <p>Review TWO</p> </div> <div class="review"> <img src="https://via.placeholder.com/150"> <p>Review THREE</p> </div> <div class="review"> <img src="https://via.placeholder.com/150"> <p>Review FOUR</p> </div> <div class="review"> <img src="https://via.placeholder.com/150"> <p>Review FIVE</p> </div> <div class="review"> <img src="https://via.placeholder.com/150"> <p>Review SIX</p> </div> </div> </div>
如果不想手动复制内容,可以加一段极简JS动态复制:
window.addEventListener('load', () => { const content = document.querySelector('.scroll-content'); content.innerHTML += content.innerHTML; })
替代方案:优化原JS逻辑
如果必须用JS实现,可以把setInterval换成requestAnimationFrame,和浏览器刷新频率同步,减少重排触发次数,优化后逻辑如下:
const flavoursContainer = document.getElementById('ReviewsWrapper'); let first = document.querySelector('#ReviewsWrapper div'); let firstWidth = first.offsetWidth; // 关闭滚动的平滑效果,避免默认动画冲突 flavoursContainer.style.scrollBehavior = 'auto'; function animate() { if (first.getBoundingClientRect().right <= 0) { flavoursContainer.appendChild(first); flavoursContainer.scrollLeft -= firstWidth; // 更新当前的第一个元素和宽度 first = document.querySelector('#ReviewsWrapper div'); firstWidth = first.offsetWidth; } flavoursContainer.scrollLeft += 1; requestAnimationFrame(animate); } window.addEventListener('load', () => { requestAnimationFrame(animate); });
内容的提问来源于stack exchange,提问作者Matúš Rebroš
相关产品推荐
相关产品推荐

