一、触摸事件基础:从手指按下到事件触发

触摸事件,说白了就是浏览器告诉你「用户的手指在屏幕上干了什么」。我刚开始做移动端开发时,总觉得触摸事件跟鼠标事件差不多,结果第一个项目就踩了坑——页面滚动卡顿、点击穿透、延迟响应……嗯,后来我才明白,触摸事件有它自己的一套规则。

这一章我们先打好地基。把触摸事件的类型、对象属性、坐标获取这三个核心点吃透,后面的延迟排查才能有的放矢。

1.1 触摸事件类型:三个核心兄弟

触摸事件主要有三个:touchstart、touchmove、touchend。它们对应手指的三个动作:按下、移动、抬起。

核心记忆点:触摸事件的生命周期就是「按下 → 移动(可选)→ 抬起」。任何触摸交互都逃不出这个模型。

事件类型 触发时机 典型场景
touchstart 手指刚接触屏幕 开始拖拽、开始缩放
touchmove 手指在屏幕上滑动 滚动列表、拖拽元素
touchend 手指离开屏幕 确认点击、结束拖拽

我个人习惯在 touchstart 里记录起始坐标,在 touchmove 里计算偏移量,在 touchend 里做最终判断。这样逻辑清晰,不容易乱。

注意:还有一个 touchcancel 事件,当触摸被系统中断时触发(比如来电、弹窗)。很多新手会忽略它,导致状态没重置。我曾经在一个手势库中漏掉了 touchcancel,结果用户接电话回来,页面上的拖拽元素就「粘」在手指上了……

1.2 触摸事件对象:藏在 event 里的秘密

每个触摸事件都会带一个 TouchEvent 对象。它里面有三个重要的触摸点列表:

  • touches:当前屏幕上所有手指的列表
  • targetTouches:在当前元素上的手指列表
  • changedTouches:引起事件变化的手指列表

这三个列表有什么区别?我刚开始也搞混过。举个例子:

  • 你用两根手指同时触摸屏幕,touches 里有 2 个点
  • 如果只有一根手指碰到了按钮,targetTouches 只有 1 个点
  • 当手指抬起时,changedTouches 记录的是「离开」的那根手指

实战建议:在 touchstart 和 touchmove 中,优先用 targetTouches;在 touchend 中,必须用 changedTouches。因为手指抬起后,它已经不在 touches 里了。

每个触摸点(Touch 对象)还包含这些关键属性:

属性 含义 单位
clientX / clientY 触摸点相对于视口的坐标 px
pageX / pageY 触摸点相对于整个页面的坐标 px
screenX / screenY 触摸点相对于屏幕的坐标 px
radiusX / radiusY 触摸点的椭圆半径 px
force 触摸压力(3D Touch 设备) 0~1

你想想看,clientX 和 pageX 有什么区别?说白了,clientX 是「你看到的位置」,pageX 是「文档实际的位置」。如果页面没有滚动,两者相等;如果滚动了,pageX 会更大。

1.3 触摸点坐标获取:代码怎么写

直接上代码。这是我常用的坐标获取模板:

element.addEventListener('touchstart', function(e) {
  // 阻止默认行为,避免页面滚动干扰
  e.preventDefault();

  // 获取第一个触摸点
  var touch = e.targetTouches[0];

  // 记录起始坐标
  var startX = touch.pageX;
  var startY = touch.pageY;

  console.log('触摸开始于:', startX, startY);
});

element.addEventListener('touchmove', function(e) {
  e.preventDefault();

  var touch = e.targetTouches[0];
  var moveX = touch.pageX;
  var moveY = touch.pageY;

  // 计算偏移量
  var deltaX = moveX - startX;
  var deltaY = moveY - startY;

  console.log('偏移量:', deltaX, deltaY);
});

element.addEventListener('touchend', function(e) {
  // 注意:这里必须用 changedTouches
  var touch = e.changedTouches[0];
  var endX = touch.pageX;
  var endY = touch.pageY;

  console.log('触摸结束于:', endX, endY);
});

避坑指南:我曾经在 touchend 里用了 e.touches,结果死活拿不到坐标。后来才发现,手指都抬起来了,touches 列表已经空了。记住:touchend 里只用 changedTouches。

1.4 知识体系总览

下面这张图把本章的核心知识点串起来了。你可以把它当作触摸事件排查的「地图」:

触摸事件基础 · 知识体系 触摸事件 TouchEvent 事件类型 touchstart touchmove touchend 触摸点列表 touches targetTouches changedTouches 坐标属性 clientX/Y pageX/Y screenX/Y 排查要点 • touchstart 记录起始坐标,touchmove 计算偏移,touchend 做最终判断 • touchend 中必须使用 changedTouches,不要用 touches • 坐标优先用 pageX/pageY,避免滚动干扰 • 别忘了处理 touchcancel,防止状态残留

1.5 本章小结

触摸事件的基础就这些。记住三个要点:

  • 事件类型:start → move → end,生命周期要清晰
  • 触摸点列表:分清 touches、targetTouches、changedTouches
  • 坐标获取:pageX/pageY 最稳定,touchend 用 changedTouches