[翻译]整合鼠标、触摸 和触控笔事件的Html5 Pointer Event Api

原文连接

mobiforge.com/design-deve…javascript


(本翻译未彻底按照原文进行,由于老外太多废话!)
Pointer Events API 是Hmtl5的事件规范之一,它主要目的是用来将鼠标(Mouse)、触摸(touch)和触控笔(pen)三种事件整合为统一的API。css

相比Touch Events API,虽然目前除了Apple的 Safari浏览器,其余浏览器都在实现对该事件类型的支持,可是状况并非很好。
本篇文章忽略浏览器的兼容问题,只讨论其基本使用方法。更多内容能够参考:Pointer Events 背景资料html

Pointer Events

和 Touch Events API 对应于触摸事件相似,Pointer Events API则对应于Pointer事件,那么什么是Pointer呢?前端

Pointer 是指能够在屏幕上反馈一个指定坐标的输入设备。html5

Pointer Events继承并扩展了Mouse Event,因此它拥有Mouse Event的经常使用属性,好比 clientX, clientY等等,同时也增长了一些新的属性,好比tiltX, tiltY, 和 pressure等等。咱们对Pointer的以下属性更感兴趣:java






























































属性 说明
pointerId 惟一数值类型标识符
screenX 相对于用户屏幕的x坐标
screenY 相对于用户屏幕的y坐标
clientX 相对于当前窗口的x坐标,不包含滚动条的滚动距离
clientY 相对当前窗口的y坐标,不包含滚动条的滚动距离
pageX 相对于页面的x坐标, 包含滚动条的滚动距离
pageY 相对于页面的y坐标, 包含滚动条的滚动距离
width pointer在屏幕上接触范围的宽度
height pointer在屏幕上接触范围的高度
tiltX 触控介质沿Z轴方向与x轴的偏转角度, x,y坐标轴构成的平面为屏幕表面
tiltY 触控介质沿Z轴方向与y轴的偏转角度, x,y坐标轴构成的平面为屏幕表面
pressure 按压值
pointerType Pointer 类别: mouse, pen, 或者touch
isPrimary 是不是主Pointer设备

这里有几点须要注意的地方:web

. pointerId:表明每个独立的Pointer。根据id,咱们能够很轻松的实现多点触控应用。\
. width/height:Mouse Event 在屏幕上只能覆盖一个点的位置,可是一个Pointer可能覆盖一个更大的区域。\
. isPrimary:当有多个Pointer被检测到的时候(好比多点触摸),对每一种类型的Pointer会存在一个Primary Poiter。只有Primary Poiter会产生与之对应的Mouse Event。稍后会讨论更多与此有关的内容。\
. pressure/tilt/width/height: :这些特性,使程序支持更复杂的操做成为可能。canvas


下面是PointerEvent Api 定义的核心事件:api














































事件类型 触发时机
pointerover pointer移动到一个元素所在区域的时候
pointerenter pointer 移动到一个元素或者其后代元素所在区域的时候.
pointerdown 激活按钮状态 被赋值为非0值: 对于触摸或触控笔是指和屏幕产生接触的时候; 对于鼠标是指一个按键被按下的时候
pointermove pointer 改变了所在坐标, 或者 按压, 倾斜时,或者触发了没有在规范中定义的其余类型事件
pointerup active buttons state 值为 left: 触控笔或者手指离开屏幕, 或者释放鼠标按键
pointercancel 检测到当前pointer结束操做的时候, 好比改变方向, 触控点太多等意外输入
pointerout pointer移出一个元素所在区域.在不支持hover的设备上当 pointerup 事件和pointercancel事件被触发后触发
pointerleave pointer 离开元素或者起后代元素后
gotpointercapture 当一个元素成为pointer的目标元素的时候
lostpointercapture 当元素不在成为pointer的目标元素的时候

Mouse events, pointer events, 和touch events 对照表










































Mouse event Touch event Pointer event
mousedown touchstart pointerdown
mouseenter pointerenter
mouseleave pointerleave
mousemove touchmove pointermove
mouseout pointerout
mouseover pointerover
mouseup touchend pointerup

Mouse Event 和Point Event作一个对等关系很容易,可是Touch Event就没那么乐观了。可是上面的表格只是一个粗略的对照关系,相对应的事件在具体实现和含义上并不彻底相同。这意味着你不能使用同一个处理函数来处理不一样类型的事件,除非你明确的知道你在干什么,由于这些事件的运做方式不一样。例如touchmove 事件的目标元素是touch began 时的元素,即便move的过程当中触点不在该元素区域内,touchemove的目标元素仍然不会改变;可是mousemove 和 pointermove的目标元素是位于触点下方的元素,离开该元素区域,目标元素就会改变。数组

Mouse 兼容事件

Poiter API的强大之处在于它对Mouse Event的兼容,使得基于Mouse Event的站点能够很好的运行。固然这是有意为之,为了达到这个目的,当Pointer Event被触发以后,会再次触发一个对应的Mouse Event。固然只有被认定为主Pointer(primary Pointer)的Pointer才会继续触发Mouse Event。某种程度上,你能够认为在同一时间只有一个鼠标输入。基于Mouse Event 的网站,原有的处理逻辑无需改动,只须要添加新的针对Touch Event的处理逻辑便可。

Pointer API 的好处

Poiter API 整合了鼠标、触摸和触控笔的输入,使得咱们无需对各类类型的事件区分对待。

目前不管是web仍是本地应用都被设计成跨终端(手机,平板,PC)应用,鼠标多数应用在桌面应用,触摸则出如今各类设备上。过去开发人员必须针对不一样的输入设备写不一样的代码,或者写一个polyfill 来封装不一样的逻辑。Pointer Events 改变了这种情况:

统一事件监听,不用再分别处理\
不用为获取不一样事件的坐标值写不一样的代码\
若是输入设备支持,能够获取压力、宽、高、倾斜角度等参数\
若是须要的话能够区别对待不一样是事件类型

下面是各类事件Api的对比。




















































  Mouse Events Touch Events Pointer Events
支持鼠标 Y P Y
支持单点触控 P Y Y
支持多点触控 N Y Y
支持 笔, Kinect, 其余输入设备 P N Y
提供对 over/out/enter/leave events 和 hover 的支持 Y N Y
异步 panning/zooming 硬件加速 N N Y
W3C 标准 Y Y Y

Pointer Events 示例

在本篇文章中,咱们只展现Pointer Event Api的基本使用。第一件要作的事情是检测浏览器是否支持Pointer Event。

浏览器支持校验

if (window.PointerEvent) { 
  // Pointer events are supported. 
}复制代码

事件监听

第一个demo,咱们建立Pointer Event的事件监听程序,打印输入点的坐标值。咱们建立两个div,一个用来捕获Pointer事件,另外一个用来展示坐标值。

<div id="coords"></div>
  <div id="pointerzone"></div>复制代码

接下来添加事件监听的代码:

function init() {
    // Get a reference to our pointer div
    var pointerzone = document.getElementById("pointerzone");
    // Add an event handler for the pointerdown event
    pointerzone.addEventListener("pointerdown", pointerHandler, false);
}复制代码

在pointerHandler函数中,获取并展示pointer事件的坐标值:

function pointerHandler(event) {
    // Get a reference to our coordinates div
    var coords = document.getElementById("coords");
    // Write the coordinates of the pointer to the div
    coords.innerHTML = 'x: ' + event.pageX + ', y: ' + event.pageY;
  }复制代码

咱们确保在页面加载完成后执行init函数。

<body onload="init()">
  ...
  </body>
  }复制代码

如今能够在浏览器打开页面了,若是你的浏览器支持pointer event,单击鼠标,就能够在页面看到输出的坐标值了。

pointermove event

和使用touch api的touchmove事件同样,咱们可使用pointermove事件来处理移动事件。

下面咱们设计咱们的demo:当捕获一个pointerdown 事件的时候,咱们开始追踪pointer的移动轨迹。因此咱们首先要监听pointerdown事件,而后在pointerdown事件的处理函数中添加对pointermove事件的监听。

canvas.addEventListener("pointerdown", function() {
    canvas.addEventListener("mousemove", drawpointermove, false);
  }
  , false);复制代码

在drawpointermove函数中,咱们根据先后两个点的坐标,来连续绘制轨迹。

function draw(e) {
  ctx.beginPath();
  // Start at previous point
  ctx.moveTo(lastPt.x, lastPt.y);
  // Line to latest point
  ctx.lineTo(e.pageX, e.pageY);
  // Draw it!
  ctx.stroke();

  //Store latest pointer
  lastPt = {x:e.pageX, y:e.pageY};
}复制代码

当pointer路径结束的时候——用户移开了手指或者笔尖,松开了鼠标按钮——咱们须要中止绘图。因此咱们须要监听pointerup事件,并添加一个endPointer处理函数。

canvas.addEventListener("pointerup", endPointer, false);

  function endPointer(e) {
    //Stop tracking the pointermove event
    canvas.removeEventListener("pointermove", drawpointermove, false); 

    //Set last point to null to end our pointer path
    lastPt = null;
  }复制代码

运行结果:

下面给出这个demo的完整代码:

<html>
      <head>
        <style> /* Disable intrinsic user agent touch behaviors (such as panning or zooming) */ canvas { touch-action: none; } </style>


      <script type='text/javascript'> var lastPt = null; var canvas; var ctx; function init() { canvas = document.getElementById("mycanvas"); ctx = canvas.getContext("2d"); var offset = getOffset(canvas); if(window.PointerEvent) { canvas.addEventListener("pointerdown", function() { canvas.addEventListener("pointermove", draw, false); } , false); canvas.addEventListener("pointerup", endPointer, false); } else { //Provide fallback for user agents that do not support Pointer Events canvas.addEventListener("mousedown", function() { canvas.addEventListener("mousemove", draw, false); } , false); canvas.addEventListener("mouseup", endPointer, false); } } // Event handler called for each pointerdown event: function draw(e) { if(lastPt!=null) { ctx.beginPath(); // Start at previous point ctx.moveTo(lastPt.x, lastPt.y); // Line to latest point ctx.lineTo(e.pageX, e.pageY); // Draw it! ctx.stroke(); } //Store latest pointer lastPt = {x:e.pageX, y:e.pageY}; } function getOffset(obj) { //... } function endPointer(e) { //Stop tracking the pointermove (and mousemove) events canvas.removeEventListener("pointermove", draw, false); canvas.removeEventListener("mousemove", draw, false); //Set last point to null to end our pointer path lastPt = null; } </script>
    </head>
    <body onload="init()">
      <canvas id="mycanvas" width="500" height="500" style="border:1px solid black;"></canvas>
    </body>
  </html>复制代码

多点触控

这个例子中,咱们扩展上面的pointmove事件的代码,来实现对多点触控的支持。

首先咱们初始一个多个颜色的数组,用来追踪不一样的pointer。

var colours = ['red', 'green', 'blue', 'yellow','black'];复制代码

画线的时候经过pointer的id来取色。

//Key the colour based on the id of the Pointer
  multitouchctx.strokeStyle = colours[id%5];
  multitouchctx.lineWidth = 3;复制代码

在这个demo中,咱们要追踪每个pointer,因此须要分别保存每个pointer的相关坐标点。这里咱们使用关联数组来存储数据,每一个数据使用pointerId作key,咱们使用一个Object对象做为关联数组,用以下方法添加数据:

// This will be our associative array
var multiLastPt=new Object();
...
// Get the id of the pointer associated with the event
var id = e.pointerId;
...
// Store coords
multiLastPt[id] = {x:e.pageX, y:e.pageY};复制代码

结束画线的时候,须要删除相关数据。

delete multiLastPt[id];复制代码

运行结果以下:

完整代码以下:

<!DOCTYPE html>
<html> <head> <title>HTML5 multi-touch</title> <style> canvas { touch-action: none; } </style> <script> var canvas; var ctx; var lastPt = new Object(); var colours = ['red', 'green', 'blue', 'yellow', 'black']; function init() { canvas = document.getElementById('mycanvas'); ctx = canvas.getContext("2d"); if(window.PointerEvent) { canvas.addEventListener("pointerdown", function() { canvas.addEventListener("pointermove", draw, false); } , false); canvas.addEventListener("pointerup", endPointer, false); } else { //Provide fallback for user agents that do not support Pointer Events canvas.addEventListener("mousedown", function() { canvas.addEventListener("mousemove", draw, false); } , false); canvas.addEventListener("mouseup", endPointer, false); } } function draw(e) { var id = e.pointerId; if(lastPt[id]) { ctx.beginPath(); ctx.moveTo(lastPt[id].x, lastPt[id].y); ctx.lineTo(e.pageX, e.pageY); ctx.strokeStyle = colours[id%5]; ctx.stroke(); } // Store last point lastPt[id] = {x:e.pageX, y:e.pageY}; } function endPointer(e) { var id = e.pointerId; canvas.removeEventListener("mousemove", draw, false); // Terminate this touch delete lastPt[id]; } </script> </head> <body onload="init()"> <canvas id="mycanvas" width="500" height="500"> Canvas element not supported. </canvas> </body> </html>复制代码

小结

本文只是简单介绍了Pointer Event的使用,虽然目前浏览器的支持状况并不完美,可是做为w3c的标准,会被支持的愈来愈好。

若是你在开发中使用Pointer Event Api,必定要注意它和touch事件的区别,处理touch相关操做的时候要谨慎。


欢迎关注玄魂工做室--订阅号回复“html5”,更多前端开发知识

相关文章
相关标签/搜索