触控事件需节流而非防抖,因拖拽等场景须持续响应;推荐时间戳+requestAnimationFrame协同节流,16ms适配60fps绘图,32ms适配列表拖拽,并配合生命周期绑定/解绑监听器。

触控事件(如 touchmove)在移动端极易高频触发——手指滑动时每秒可达 60–120 次,若每次都执行 DOM 更新、计算或 Canvas 绘图,主线程会严重阻塞,导致卡顿、掉帧甚至触摸响应延迟。节流不是“让动画变慢”,而是“把不可控的密集信号变成可控的稳定节奏”,关键在于选对时间间隔、实现方式和绑定时机。
为什么 touchmove 特别需要节流而非防抖
防抖会等用户抬手才执行,但拖拽、缩放、手势识别等场景必须在过程中持续响应。比如手指划过屏幕画线,若用防抖,只会画出起点和终点;而节流能保证每 16ms(≈60fps)或每 32ms(≈30fps)采样一次坐标,线条连续不跳跃。
- touchmove 触发频率远高于 scroll,原生每帧可能触发多次
- 浏览器对 touchmove 的默认行为(如滚动、缩放)可被
preventDefault()阻止,但阻塞后必须自己保证响应及时性 - 直接在 touchmove 回调里做
requestAnimationFrame调度虽更精准,但节流仍是轻量、兼容、易调试的第一道防线
推荐节流实现:时间戳版 + requestAnimationFrame 协同
纯定时器节流(setTimeout)在快速滑动中仍可能漏帧;纯 requestAnimationFrame 又无法控制最小间隔。二者结合最稳妥:
DMap GL地图JSAPI(dmapgl)开发指南。用于编写、审查、调试 DMap 地图 API 代码,涵盖地图初始化、覆盖物渲染、图层管理、事件处理、控件交互等任务。当用户提及 dmap、dmapgl、jsapi‑gl 或相关地图开发需求时自动触发。
function throttleTouch(fn, delay = 16) {
let lastTime = 0;
return function (e) {
const now = performance.now();
if (now - lastTime >= delay) {
// 用 rAF 做最终调度,确保在下一帧执行
requestAnimationFrame(() => {
fn.call(this, e);
});
lastTime = now;
}
};
}- delay 设为 16ms 对应 60fps,适合精细操作(如绘图);设为 32ms 更省资源,适合列表拖拽
- 用
performance.now()替代Date.now(),精度更高(微秒级),避免时间回拨误差 - 不依赖闭包 timer 变量,无内存泄漏风险,适合长期监听的触控区域
绑定与清理的关键细节
节流函数本身不能解决事件监听器残留问题。真实项目中需配合生命周期管理:
- 在
touchstart时绑定节流后的touchmove,避免全局监听浪费性能 - 在
touchend或touchcancel时立即解绑,防止重复注册 - 若使用 React/Vue,务必在组件卸载(
useEffect cleanup或beforeUnmount)时移除监听器 - 慎用
{ passive: false }—— 只有当你明确调用preventDefault()时才设为 false,否则浏览器可能忽略节流逻辑直接滚动
实际优化效果对比
以一个 Canvas 手写签名组件为例:
- 未节流:每秒触发 90+ 次
touchmove,CPU 占用峰值达 85%,线条锯齿明显、断点频出 - 节流至 16ms:
touchmove实际处理约 60 次/秒,CPU 降至 35%,线条平滑,笔迹连贯 - 再叠加
will-change: transform和离屏 Canvas 缓存,可进一步压至 20% CPU

















