做手势识别必须用 touchstart/touchmove;scroll 事件触发滞后、无法捕获初始滑动,iOS 还会默认阻止其监听;pointer 事件因兼容性差、采样低、易误判也不推荐。

滑动事件用 touchstart/touchmove 还是 scroll?
直接说结论:做手势识别(比如左滑返回、上滑加载)必须用 touchstart/touchmove,scroll 事件根本捕获不到初始滑动手势,且触发滞后、无法获取手指移动轨迹。
原因很简单:scroll 是滚动发生后才触发,而手势识别要的是“正在滑”的过程;iOS Safari 还会默认阻止非用户手势触发的 scroll,导致监听失效。
-
touchstart记录起始坐标(event.touches[0].clientX) -
touchmove实时计算位移差,判断方向和速度 - 务必在
touchstart中调用event.preventDefault(),否则 iOS 会抢走 touch 流,后续touchmove直接不触发 - 别依赖
scrollLeft/scrollTop做方向判断——它们更新慢、有延迟、还受 CSSoverscroll-behavior干扰
为什么 pointerdown/pointermove 不推荐用于手势识别?
虽然 pointer 事件统一了 mouse/touch/pen,但对手势识别来说,它反而更难用:
- 在 iOS Safari 上,
pointermove默认被系统 scroll 行为拦截,除非设置touch-action: none,但这会同时禁掉所有原生滚动,得自己重写滚动逻辑 -
pointermove的event.pointerType可能是"touch"或"mouse",但鼠标没有“滑动距离”概念,混用会导致方向误判 - Android Chrome 对
pointermove的采样频率低于touchmove,快速滑动时容易漏点,影响速度估算
真要兼容鼠标拖拽场景(比如桌面端画布平移),建议分开处理:移动端走 touch,桌面端走 mousedown/mousemove,别强行统一。
立即学习“前端免费学习笔记(深入)”;
touchmove 中怎么避免页面跟着手指滚动?
不是加 event.preventDefault() 就万事大吉。粗暴调用会导致整个页面无法滚动,尤其当手势区域嵌套在可滚动容器里时。
- 只在明确需要拦截的场景下 prevent:比如水平滑动时,检测到
Math.abs(dx) > Math.abs(dy)(x 方向位移更大),再preventDefault() - 对垂直滚动容器(如列表),允许 y 方向自然滚动,只拦截 x 方向:CSS 加
touch-action: pan-y - 避免在
touchmove里频繁读取scrollTop或触发重排,会卡顿;位移计算全用event.touches[0].clientX等只读属性 - iOS 16+ 开始支持
passive: false选项,但必须在addEventListener时显式声明,否则即使写了preventDefault()也无效
手势识别阈值设多少才不误触?
没标准值,但有实测靠谱区间。太小(30px)会让用户觉得“反应迟钝”。
- 方向判定阈值建议:水平滑动用
12px,垂直下拉刷新用60px(因需克服阻力感) - 速度判定(防慢速误滑):要求
Math.abs(dx / dt) > 0.3(单位:px/ms),dt 是从touchstart到当前touchmove的毫秒差 - 别用单次
touchmove做判定——手指刚落屏会有微小抖动,至少等 2–3 次事件后再开始计算位移 - Android 和 iOS 的 touch 采样节奏不同,iOS 更快,所以 dt 要用
event.timeStamp而非Date.now(),避免时间漂移
最常被忽略的一点:手势结束(touchend)后,别立刻清空状态变量。留一个 setTimeout 延迟 50ms 再重置,能过滤掉部分系统模拟的“假 touchend”。



















