本文主要介绍 TouchEvent 相关的一些对象与属性如 Touch, TouchList, touhces, targetTouches 等,以及使用的注意点和误区。
触摸事件有以下几种类型: touchstart
, touchmove
, touchend
这三种用的比较多,还有不常用的 touchcancel
事件。当然 MDN 上还介绍了 touchenter
, touchleave
事件,具体适用的场景及兼容性如何还未做测试,感兴趣的可自行研究。
js中不同的事件类型, event
对象包含的属性也有所差异。我们先了解几个 TouchEvent
涉及的对象。
提示:文中的demo都是在 chrome 模拟器,iPhone6s(iOS9.3.2) safari,iOS微信上运行,安卓的兼容性未做测试
Touch
对象代表一个触点,可以通过 event.touches[0]获取,
每个触点包含位置,大小,形状,压力大小,和目标 element属性。
{ screenX: 511, screenY: 400,//触点相对于屏幕左边沿的Y坐标 clientX: 244.37899780273438, clientY: 189.3820037841797,//相对于可视区域 pageX: 244.37, pageY: 189.37,//相对于HTML文档顶部,当页面有滚动的时候与clientX=Y 不等 force: 1,//压力大小,是从0.0(没有压力)到1.0(最大压力)的浮点数 identifier: 1036403715,//一次触摸动作的唯一标识符 radiusX: 37.565673828125, //能够包围用户和触摸平面的接触面的最小椭圆的水平轴(X轴)半径 radiusY: 37.565673828125, rotationAngle: 0,//它是这样一个角度值:由radiusX 和 radiusY 描述的正方向的椭圆,需要通过顺时针旋转这个角度值,才能最精确地覆盖住用户和触摸平面的接触面 target: {} // 此次触摸事件的目标element }
identifier
这个属性大家可能有疑惑,使用 Chrome 的模拟器发现多次触摸动作,值始终不变。用真机测试则不会有问题(我这里用的safari连接mac调试)。每次触摸包括start,move,end这整个过程,标志符都不变。下一次触摸动作开始,标志符就会变化。
screenY
clientY
在 safari 中 screenY
与 clientY
值是相等的,在iOS微信中两个数值不等,但单位应该也不一样。
radiusX
radiusY
rotationAngle
测试过程中safari及微信内置浏览器都不支持这些属性,chrome模拟器可以。
由 Touch
对象构成的数组,通过 event.touches
取到。一个 Touch
对象代表一个触点,当有多个手指触摸屏幕时, TouchList
就会存储多个 Touch
对象,前面说到的 identifier
就用来区分每个手指对应的 Touch
对象。
TouchEvent
就是用来描述手指触摸屏幕的状态变化事件,除了一般DOM事件中 event
对像具备的属性,还有一些特有的属性。
一个 TouchList
对象,包含当前所有 接触屏幕 的触点的 Touch
对象,不论 touchstart 事件从哪个elment上触发。
也是一个 TouchList
对象,包含了如下触点的 Touch 对象:touchstart从当前事件的目标element上触发
这里大家可能产生了疑惑,这两个对象到底有什么区别?尤其是我们使用chrome模拟器中运行 demo,打印两个对象发现他们其实是一样的。
这两个对象的区别可以类比 event.target
与 event.currentTarget
的区别,如果以前没留意,自行 js 高级程序设计。
我们先看一个 demo2,来了解 touch 事件的特性。
在线编辑: http://jsrun.net/3XKKp
预览地址: http://jsrun.net/rtd/3XKKp
大家进行以下两个操作,观察控制台发现了什么?
操作一:一根手指触摸蓝色box
,并滑动,继续滑动出蓝色 box
操作二:一根手指触摸非蓝色 box
区域,然后慢慢滑动到蓝色 box
大家会发现:操作一中即使滑出蓝色 box
,而 touchmove
, touchend
事件会继续触发, touches
, targetTouches
存储着相同的 Touch 对象,touchmove事件的目标元素仍然是box。
操作二中相关的 touch 事件都不会触发。很神奇的是 touchmove 事件,明明在 box 上滑动,却不会触发 touchmove 事件。
我们可以猜测,touch相关的事件是一个整体,一开始touchstart不可能被触发,则后续touch事件也不会被触发。当然你可以不监听 touchstart 事件,按照操作一 touchmove,touchend 还是可以触发的。
再看下面这个demo2
在线编辑: http://jsrun.net/XXKKp
访问地址: http://jsrun.net/rtd/XXKKp
这里我们对白色区域 body
也添加了 touch 事件的监听,继续上述 demo1中的两个操作。
我们可以发现:
操作一可以发现:touch 相关的事件可以冒泡,触发了 box
, body
的touch事件。操作二只能触发 body 的touch 事件,和demo1同理。
我们可以观察下操作一的两个对象 TouchEvent.targetTouches
, TouchEvent.touches
,无论是 box
还是 body
触发的 touch 事件,他们的存储的 Touch对象都是相同的,而且 target 都是 box。
接下来进行操作三:
用两根手指,一根手指触摸蓝色 box
,另一根触摸白色区域,然后滑动。
然后再次比较下 targetTouches
和 touches
,就可以发现他们的不同。
也是一个 TouchList 对象,对于 touchstart 事件, 这个 TouchList 对象列出在此次事件中新增加的触点。对于 touchmove 事件,列出和上一次事件相比较,发生了变化的触点。对于 touchend ,列出离开触摸平面的触点(这些触点对应已经不接触触摸平面的手指)。
touchend
这里要特别注意,touches和targetTouches只存储接触屏幕的触点,要获取触点最后离开的状态要使用changedTouches。
之前也经常用 touches[0]
来获取Touch 对象,如果知道了 touches,targetTouches,changedTouches 的不同之处。在编写代码时可以更好的选择使用,程序也可以更严谨。
续篇想研究的问题:
touchmove的触发频率问题
如何判定用户是快速滑动(swipe事件)
如何实现Tap
一些使用总结或最佳实践
MDN: https://developer.mozilla.org/zh-CN/docs/Web/API/TouchEvent
js高级程序设计
文中如理解有误,还请多多指出!