移动端的touch事件处理

2023-05-07 13:42:43 浏览数 (1)

 在iPhone 3Gs发布的时候,其自带的移动Safari浏览器就提供了一些与触摸(touch)操作相关的新事件。随后,Android上的浏览器也实现了相同的事件。触摸事件(touch)会在用户手指放在屏幕上面的时候、在屏幕上滑动的时候或者是从屏幕上移开的时候出发。下面具体说明:

  touchstart事件:当手指触摸屏幕时候触发,即使已经有一个手指放在屏幕上也会触发。

  touchmove事件:当手指在屏幕上滑动的时候连续地触发。在这个事件发生期间,调用preventDefault()事件可以阻止滚动。

  touchend事件:当手指从屏幕上离开的时候触发。

  touchcancel事件:当系统停止跟踪触摸的时候触发。关于这个事件的确切出发时间,文档中并没有具体说明,咱们只能去猜测了。

触摸事件还包含下面三个用于跟踪触摸的属性

       touches:表示当前跟踪的触摸操作的touch对象的数组。

  targetTouches:特定于事件目标的Touch对象的数组。

  changeTouches:表示自上次触摸以来发生了什么改变的Touch对象的数组。

每个Touch对象包含的属性如下。

  clientX:触摸目标在视口中的x坐标。

  clientY:触摸目标在视口中的y坐标。

  identifier:标识触摸的唯一ID。

  pageX:触摸目标在页面中的x坐标。

  pageY:触摸目标在页面中的y坐标。  

       screenX:触摸目标在屏幕中的x坐标。

screenY:触摸目标在屏幕中的y坐标。

  target:触目的DOM节点目标。

这里,除了前三种changedTouches,targetTouches,touches之外的其他属性,都是我们常见的一些属性值,所以这里对于这些属性就不做处理,而这三个新的属性,是只针对touch事件存在的属性值,并且是我们之后处理时,获取一些关键数据的属性,所以这里就只对这三个属性进行说明。

TouchList

看了上面的列表中的内容,首先先注意到的一点就是,TouchList对象,一个新的也是唯有touch事件的event对象中,才会出现的一种对象,在了解changedTouches,targetTouches,touches这三种属性之前,先对TouchList对象进行一个简单的了解。

TouchList是一个只读的类数组对象,它表示在当前的touch事件中,与触摸屏的接触点的个数,比如:如果你当前是三根手指在同时在触摸屏上,那么每一根手指都会有一个相对应的touch对象,来记录对应手指的操作相关的信息,这个时候的TouchList类数组中,就有三个数据,分别是这三根手指所对应的touch对象,而TouchList类数组的长度也就是3.

根据我们对类数组的了解,既然是类数组,那么就会有length属性,来表示这个类数组的长度,并且可以使用属性的中括号取值方式,取出对应位置的值。OK,下面我们继续以一个小的测试例子,来看一下,一个类数组,包含了哪些信息可供我们使用。

属性

属性值

0

[object Touch]

length

1

item

function item(){[native code]}

上述列表中,是给出的当一个手指时的touches中,所包含的属性值,当然,从这里,也证明了TouchList对象时一个类数组。而item方法,也跟我们在NodeList,HTMLCollection对象中的一样吧,获取第n个对象,只是我们大多数仍然会喜欢使用数组的方式进行操作吧。

那么我们继续看看,如果是多个点进行操作呢,如果是多个点的话,会不会有什么区别呢?首先,先把滑动区域进行限制,此时的滑动区域值限制在页面中有边框的区域。

这个时候,touches是没有什么问题的,有几根手指在操作,就会保存几个touch对象,而changedTouches和targetTouches却感觉是不灵敏的样子,尤其是changedTouches,因为当把touch事件的触发区域固定在一个小区域之后,changedTouches不论几根手指触发,都是只保存了一个touch对象,changedTouches这个类数组的长度一直只有一个元素。不理解(Android手机测试)。

如果不进行固定呢,把touchstart的监听绑定到document对象上面呢。

更让我疑惑的是,此时,这三个属性,都能正确的获取到触点的个数,有几根手指,就能有在各个属性中,就会保存多少个touch对象。

属性

属性值

touches

保存当前一个触摸个数的列表。只读属性(a list of Touches for every point of contact currently touching the surface.)

targetTouches

保存当前一个触摸个数的列表。只读属性,该列表中包含的元素需要发生在当前事件所触发的元素之上(A list of Touches for every point of contact that is touching the surface and started on the element that is the target of the current event.)

changedTouches

该属性是依据事件在触发的,并且获取到的一个列表。我的理解就是,只有事件变化时,才会更改该属性的取值。只读属性(For the touchstart event this must be a list of the touch points that just became active with the current event. For the touchmove event this must be a list of the touch points that have moved since the last event. For the touchend and touchcancel events this must be a list of the touch points that have just been removed from the surface. )

touches属性的解释,很明显,就是当每一个事件被触发时,就会获取此时每个触点的相关信息,并保存到touches对象中去。

targetTouches属性,却是要只有在被绑定的元素本身之上触发,才会被保存到targetTouches属性中去,因为之前的demo里,是把touchstart事件绑定到了document对象上,所以导致该属性获取的值域touches基本相同(按理说,这个属性获取到的touch的list列表,在一定的情况下,是和touches获取到的有区别的,但是我还是不能确定,这个区别到底是由什么因素引起,可以参考touch-14中的示例,但是并不能理解是什么原因导致的)。

changedTouches属性,按我的理解,就是在事件变化时,才会出现这个属性,对于这个确切的说法,还是先看下面的这个例子之后,才能更好的理解了。

这个时候,有一个有趣的问题就是,如果你以一个触点(一根手指)来操作的话,touches和targetTouches两个属性,当这个触点(这根手指)离开屏幕,触发touchend事件时,这两个属性中,是不包含任何对象的,就是说,这两个类数组的长度是0。而changedTouches却可以获取到这个触点的touch对象,为什么?

验证这个可以通过很简单的方法,用两个触点(两根手指),其中一个触点一直按着屏幕,而另外一个触点,触发touchend事件,可以看到这个时候,touches和targetTouches的属性中,数组长度为1,是那个一直接触者屏幕的触点的属性值。

从上面的两点,也可以理解到,这三个属性之间的差距,就拿touchend事件时的情况来说明吧(这个比较好理解),当touchend被触发时,手指时离开屏幕了,所以此时的touches和targetTouches属性的属性值,是不会再包含这个刚离开的触点的信息的,所以,当一个触点触发touchend事件时,touches和targetTouches属性值中的touch对象个数就是0了,表示当前一个触点也没有。

而changedTouches的属性值,却是包含了触发touchend事件之前,所有的触点,比如一个触点时,这个时候,changedTouches的属性值中,就包含了一个touch对象的信息,代表刚触发touchend事件的这个触点的相关信息。

不知是否可以理解我之前的这些个说明。如有问题,请指出,非常感谢。

明白了touhes,targetTouches和changedTouches三者的这一个小小的差别,也就能理解,为何在我们判断滑动方向时,使用changedTouches获取触点的在touchstart和touchend时的位置信息的原因了吧,关于这个,在下一篇文章中进行测试说明。

OK,暂时关于touch事件的属性方面,只想到了这么多,欢迎指教。

参考文章:http://www.zhangyunling.com/235.html(touch事件

简要的探讨一下移动端 touch 事件处理几个坑,以及相应的简单处理方法。

click 穿透

假设有个弹出层,上面有个关闭的按钮支持 touchend 触发后关闭,若正好下方有个元素支持 click 事件,在弹出层关闭后将会在下方元素触发 click 事件。这种效果肯定不是我们需要的,而且我们无法确定合适会在上方出现一个支持 touch 的弹出层,所以我认为最好的处理方式是禁用所有元素的 click 事件,相比 click 需要长达 1s 的触发时间,使用 touchend 可以获得更好的体验。

tap 事件的判定

一个正确的 tap 事件应当满足一下条件:

  1. 用户手指从屏幕移开时触发
  2. 不能在用户移动手指时触发(防止和滚动、拖拽事件的冲突)
  3. 多个手指同时触摸屏幕时不能触发
  4. 不应该触发 click 事件

具体实现代码可以参考 tap-event。

使用原生的滚动事件

Android 4.0 以下是不支持原生的 webview 滚动的,所以只能使用 iscroll 之类的工具来模拟元素滚动。它的缺点就是有些过于的复杂,所以我还是会在条件允许的情况下使用原生的滚动。

启用原生滚动只需要给外层元素加上样式 -webkit-overflow-scrolling: touch; 即可,如果你的监听函数比较占用资源我们可以通过一个简单的 buffer 函数来限制它的触发间隔,例如:

代码语言:javascript复制
function buffer(fn, ms) {
  var timeout;
  return function () {
    if (timeout) return;
    var args = arguments;
    timeout = setTimeout(function () {
      timeout = null;
      fn.apply(null, args);
    }, ms);
  }
}
document.querySelector('.scrollable').onscroll = buffer(onScroll, 100);

另外的建议就是不要在可滚动元素上使用阴影样式(text-shadow 和 box-shadow),因为它们非常影响性能,而且看上去也不怎么美观。

还有需要注意的是如果你需要启用apple-mobile-web-app-capable, 注意将apple-mobile-web-app-status-bar-style设置为black-translucent,否则会出现还差 22 像素滚动不到头的坑爹 bug。

禁用页面整体拖动

IOS下默认情况下用户的拖动操作在scroll滚到头以后会导致整体页面的滚动,一种方式是禁用掉 document 的 touchmove 原生触发

代码语言:javascript复制
document.addEventListener('touchmove', function(e) {
  e.preventDefault();
});

此时原生的滚动是无法工作的,解决办法就是禁用滚动元素的 touchmove 事件冒泡

代码语言:javascript复制
scrollable.addEventListener('touchmove', function (e) {
   e.stopPropagation();
});

另一种方式是判定滚动元素滚到头之后禁用掉默认的处理

代码语言:javascript复制
var el = document.querySelector('.scrollable');var sy = 0;
events.bind(el, 'touchstart', function (e) {
  sy = e.pageY;
})

events.bind(el, 'touchmove', function (e) { 
 var down = (e.pageY - sy > 0);  //top
  if (down && el.scrollTop = el.scrollHeight - el.clientHeight) {
    e.preventDefault();
  }
})

我个人倾向于第二种方案,因为如果单纯的禁用 document 的 touchmove 监听,会导致一些处理的失效,比如说上面提到的 tap-event 模块。

转载本站文章《移动端的touch事件处理》, 请注明出处:https://www.zhoulujun.cn/html/webfront/SGML/html5/2017_0216_7950.html

0 人点赞