跳转到主内容
趣航编程网 - 趣学编程,启航技术之路!

如何用 requestAnimationFrame 限制高频事件的触发频率

requestAnimationFrame 限制高频事件的核心是将回调挂起至下一帧执行且帧内去重,天然对齐浏览器60fps渲染节奏,避免跳帧、节省后台资源并防止布局抖动。 用
requestAnimationFrame
限制高频事件(如
scroll
resize
mousemove
)的触发频率,核心思路是:**把事件回调“挂起”到下一帧执行,并在新帧到来前忽略重复触发**。它不是节流(throttle)或防抖(debounce)的替代品,而是一种更贴合浏览器渲染节奏的优化方式,尤其适合需要与视觉更新同步的操作。 为什么 rAF 比 setTimeout 节流更合适? 浏览器每秒约绘制 60 帧,即每帧约 16.7ms。
requestAnimationFrame
会把回调安排在**下一次重绘之前执行**,天然对齐渲染时机。相比固定毫秒数的
setTimeout
节流: 不会因 JS 执行阻塞而跳帧或累积延迟 在页面非激活(如切到其他 tab)时自动暂停,节省资源 动画类操作(如滚动位置读取+样式更新)能避免布局抖动(layout thrashing) 基础实现:单次帧内去重 适用于只需确保同一帧最多执行一次的场景(例如更新 UI 状态、记录鼠标位置):
let isScheduled = false;

function handleScroll() { // 实际业务逻辑,比如更新某个元素的 top 值 updateUIBasedOnScroll();

isScheduled = false; }

function scrollListener() { if (!isScheduled) { isScheduled = true; requestAnimationFrame(handleScroll); } }

window.addEventListener('scroll', scrollListener);

关键点: 标志位 + rAF 组合**,保证多次触发只排队一个回调。 进阶用法:带参数透传与取消能力 当需要传递事件对象、或支持中途取消时,可封装成可复用函数:
function rafThrottle(fn, options = {}) { let scheduled = false; let lastArgs = [];

return function(...args) { lastArgs = args; if (!scheduled) { scheduled = true; requestAnimationFrame(() => { fn.apply(this, lastArgs); scheduled = false; }); } }; }

// 使用 const throttledHandleMouseMove = rafThrottle((e) => { console.log('Mouse at:', e.clientX, e.clientY); });

window.addEventListener('mousemove', throttledHandleMouseMove);

注意: 该模式不保留每次事件的细节,只保留最后一次参数**——适合“以最新状态为准”的场景(如拖拽跟随、视差滚动)。 和 CSS 或现代 API 配合效果更好 rAF 的优势在结合其他浏览器机制时更明显: 读取 DOM 尺寸/位置(
getBoundingClientRect
)放在 rAF 回调里,避免强制同步布局 写入样式(如
element.style.transform
)也放同一 rAF 中,让浏览器合并渲染 搭配
IntersectionObserver
ResizeObserver
可进一步减少对事件监听的依赖 例如滚动中计算元素可见性,优先用
IntersectionObserver
;仅当需像素级控制时,再用 rAF 包裹
scroll
回调。

相关文章