转载请注明出处: http://qiudeqing.com/html5/2015/06/21/touch-event.html

事件类型

事件名称 备注
touchstart 触点与触摸屏接触时触发
touchmove 触点在触摸屏上移动时触发
touchend 触点离开触摸屏时在该触点接触触摸屏时的元素上触发
touchcancel fired when a touch point has been disrupted in an implementation-specific manner(如触点太多)

Touch对象

Touch对象用于描述触点与触摸屏的一个接触点,具有以下属性:

TouchEvent对象

TouchEvent对象用描述touch事件,包含以下属性:

touches, changedTouch, targetTouches在线demo

与鼠标事件的关系

用户代理可能同时发送touch事件和鼠标事件。如果用户代理同时发送两种事件,touchstart事件必须在所有鼠标事件之前发送。在touchstarttouchmove监听器中如果调用了preventDefault方法,用户代理不应该再发送任何的鼠标事件。

如果用户代理将touch事件解析为鼠标事件,那它必须在touchend位置按顺序发送mousemove,mousedown,mouseup,click事件。

最佳实践

阻止缩放

在viewpoint设置中阻止缩放

<meta name="viewport"
  content="width=device-width, initial-scale=1.0, user-scalable=no">

阻止滚动

document.body.addEventListener('touchmove', function (e) {
  e.preventDefault();
}, false);

合理渲染

touch事件经常需要处理多个触点, 渲染时使用requestAnimationFrame分别处理每一个触点能提高用户体验.

var touches = [];
canvas.addEventListener('touchmove', function (e) {
  touches = e.touches;

  requestAnimationFrame(render);
}, false);

function render() {
  if (touches.length) {
    // do some work
    touches.pop();
    requestAnimationFrame(render);
  }
}

合理使用targetTouches和changedTouches

event.touches是所有与屏幕接触的触点.

单指touch拖拽元素

<div id="d2">
  <style>
  #d2 .target {
    display: inline-block;
    padding: 20px;
    background: #ddd;
    border: 1px solid #000;
  }
  #d2 .fixed {
    position: fixed;
  }
  </style>
  <span class="target">手指touch, 然后移动, 元素将跟随</span>
  <script>
  var target = document.querySelector('#d2 .target');
  target.addEventListener('touchmove', function (e) {
    if (event.targetTouches.length == 1) {
      e.preventDefault();
      var touch = event.targetTouches[0];
      target.style.left = (touch.pageX - 50) + 'px';
      target.style.top = (touch.pageY - 50)+ 'px';
      target.classList.add('fixed');
    }
  }, false);
  </script>
</div>

在线demo

如何处理手指在触摸屏上的滑动(swipe)事件

需要监听touchmove事件动态判断水平还是竖直滑动。

var touchStartClientX,
  touchStartClientY;

document.addEventListener('touchstart', function (event) {
  if (event.targetTouches.length > 1) {
    return; // 忽略多个手指触摸
  }

  touchStartClientX = event.touches[0].clientX;
  touchStartClientY = event.touches[0].clientY;

  event.preventDefault();
}, false);

document.addEventListener('touchmove', function (event) {
  event.preventDefault();
}, false);

document.addEventListener('touchend', function (event) {
  if (event.targetTouches > 0) {
    return; // 忽略多个触点
  }

  var touchEndClientX = event.touches[0].clientX,
    touchEndClientY = event.touches[0].clientY;

  var dx = touchEndClientX - touchStartClientX;
  var absDx = Math.abs(dx);

  var dy = touchEndClientY - touchStartClientY;
  var absDy = Math.abs(dy);

  // 如果移动距离大于10px,认为它是滑动
  if (Math.max(absDx, absDy) > 10) {
    var dir = absDx > absDy ? (dx > 0 ? 'left' : 'right') :
      (dy > 0 ? 'down' : 'up');
  }
}, false);

300ms延迟

UC浏览器的问题

UC等很多Android浏览器只触发一次touchmove, 解决方法是在touchstart中调用preventDefault解决..

这样会取消掉后续的click事件, 造成click链接失效, click监听器不执行等. 可以在touchstart事件监听器检查元素. 进行特殊检查

经与UC的开发讨论,得出如下结果~~

  1. UC在内核不响应touchend事件的时候会补发一个touchcancel事件,开发者需要同时监听end和cancel事件,专门为UC做一下优化
  2. 在UC的move事件并不是连续触发多次,而是只触发一次。根据x,y的值可以来判断改变距离。

参考