如何使用requestAnimationFrame对scroll滚动事件监听器进行节流?
当前所有功能运行正常,但我希望使用requestAnimationFrame对该滚动事件进行节流处理
原实现代码:
const header = document.querySelector("header#site-header"); let lastScroll = Math.round(window.scrollY); function onScroll() { const currentScroll = Math.round(window.scrollY); if (currentScroll <= 50) return header.classList.remove("scrolled"); if (currentScroll > lastScroll + 1) header.classList.add("scrolled"); else if (currentScroll < lastScroll - 1) header.classList.remove("scrolled"); lastScroll = currentScroll; // console.log("scrolled"); } window.addEventListener("scroll", onScroll);
优化实现方案
使用requestAnimationFrame做滚动事件节流的核心是让DOM操作对齐浏览器的重绘频率,同一帧内多次触发的滚动事件只会执行一次状态更新,大幅减少不必要的计算和DOM操作,提升页面滚动流畅度。
修改后的完整代码如下:
const header = document.querySelector("header#site-header"); let lastScroll = Math.round(window.scrollY); // 标记是否存在待执行的帧更新任务 let isTicking = false; function updateHeaderState() { const currentScroll = Math.round(window.scrollY); if (currentScroll <= 50) { header.classList.remove("scrolled"); } else if (currentScroll > lastScroll + 1) { header.classList.add("scrolled"); } else if (currentScroll < lastScroll - 1) { header.classList.remove("scrolled"); } lastScroll = currentScroll; // 重置标记,允许下一次更新任务入队 isTicking = false; } function onScroll() { // 仅当无待执行任务时,请求下一帧执行更新 if (!isTicking) { requestAnimationFrame(updateHeaderState); isTicking = true; } } window.addEventListener("scroll", onScroll);
改动说明
- 新增
isTicking标记位,控制同一时间只会有一个更新任务进入等待队列,避免重复提交执行请求 - 将原有的DOM更新逻辑抽离为独立的
updateHeaderState函数,仅在requestAnimationFrame的回调中执行 - 滚动事件回调仅做任务入队判断,不执行DOM操作,高频触发时也不会产生多余性能开销
优化后原有业务逻辑完全不变,滚动流畅度会有明显提升,尤其是在高刷新率设备和快速滚动的场景下效果更突出。
内容的提问来源于stack exchange,提问作者Nitin Kumar
相关产品推荐
相关产品推荐

