为什么 touchmove 事件在移动端会触发多次甚至卡顿?

Mr-玉英 阅读 96

我在做一个滑动删除功能,监听了 touchstart 和 touchmove,但发现手指稍微一动就触发好多次 touchmove,而且页面还会卡一下。是不是我哪里写错了?

我试过用 e.preventDefault() 但好像没用,代码大概是这样:

element.addEventListener('touchmove', (e) => {
  const touch = e.touches[0];
  console.log('移动位置:', touch.clientX, touch.clientY);
  // 这里做了一些 DOM 操作
});
我来解答 赞 8 收藏
二维码
手机扫码查看
2 条解答
秀英 Dev
touchmove 事件在移动端频繁触发是正常的,因为手指移动会不断产生新的触摸点。这会导致事件处理器被频繁调用,尤其是在你做了 DOM 操作的情况下,容易引起性能问题,比如卡顿。

首先,确保你在 touchmove 事件中没有做耗时操作。DOM 操作比较耗性能,尽量减少或者优化这些操作。你可以尝试只更新必要的部分,或者使用 requestAnimationFrame 来限制更新频率。

其次,使用 e.preventDefault() 是正确的做法,它能防止默认的滚动行为,从而避免浏览器在处理 touchmove 事件时出现不必要的重绘。不过,如果你在 touchstart 或者 touchmove 中阻止了默认行为,可能会导致其他触摸事件无法正常工作,所以要注意这一点。

最后,为了安全起见,确保你的事件处理器不会抛出未捕获的异常,否则可能会导致整个应用崩溃。你可以用 try...catch 块来包裹事件处理逻辑。

这里有一个简单的优化示例,使用 requestAnimationFrame 来限制更新频率:

let lastX, lastY;
let isMoving = false;

element.addEventListener('touchstart', (e) => {
const touch = e.touches[0];
lastX = touch.clientX;
lastY = touch.clientY;
isMoving = true;
});

element.addEventListener('touchmove', (e) => {
if (!isMoving) return;

e.preventDefault();
const touch = e.touches[0];

if (Math.abs(touch.clientX - lastX) > 5 || Math.abs(touch.clientY - lastY) > 5) {
lastX = touch.clientX;
lastY = touch.clientY;

requestAnimationFrame(() => {
// 在这里做 DOM 更新
console.log('移动位置:', touch.clientX, touch.clientY);
});
}
});

element.addEventListener('touchend', () => {
isMoving = false;
});


这段代码通过设置一个阈值来减少 touchmove 的处理次数,并且使用 requestAnimationFrame 来优化 DOM 更新。注意安全,确保你的应用在各种情况下都能稳定运行。
点赞
2026-03-24 18:14
振巧
振巧 Lv1
touchmove 这玩意儿触发频率极高,你每动一下手指可能触发几十次,你还在回调里直接操作 DOM,不卡才怪。

两个解决办法:

一是用节流,把 DOM 操作从每次 touchmove 变成每隔一帧执行一次:

let ticking = false;
element.addEventListener('touchmove', (e) => {
if (!ticking) {
requestAnimationFrame(() => {
const touch = e.touches[0];
console.log('移动位置:', touch.clientX, touch.clientY);
// 这里做 DOM 操作
ticking = false;
});
ticking = true;
}
});


二是别直接改 left/top、width 这些会触发重排的属性,用 transform: translate() 这种 GPU 加速的属性,流畅得多。

还有,绑定事件时别忘了 passive: false,否则 preventDefault 是不生效的,不过看你的场景应该是不想阻止默认滚动,那就用上面第一种办法就行。
点赞
2026-03-18 09:04