原文链接
https://mobiforge.com/design-development/html5-pointer-events-api-combining-touch-mouse-and-pen
(本翻译未完全按照原文进行,因为老外太多废话!)
Pointer Events API 是Hmtl5的事件规范之一,它主要目的是用来将鼠标(Mouse)、触摸(touch)和触控笔(pen)三种事件整合为统一的API。
相比Touch Events API,虽然目前除了Apple的 Safari浏览器,其他浏览器都在实现对该事件类型的支持,但是情况并不是很好。
本篇文章忽略浏览器的兼容问题,只讨论其基本使用方法。更多内容可以参考:Pointer Events 背景资料。
Pointer Events
和 Touch Events API 对应于触摸事件类似,Pointer Events API则对应于Pointer事件,那么什么是Pointer呢?
Pointer 是指可以在屏幕上反馈一个指定坐标的输入设备。
Pointer Events继承并扩展了Mouse Event,所以它拥有Mouse Event的常用属性,比如 clientX, clientY等等,同时也增加了一些新的属性,比如tiltX, tiltY, 和 pressure等等。我们对Pointer的如下属性更感兴趣:
t1.png
这里有几点需要注意的地方:
. <b>pointerId</b>:代表每一个独立的Pointer。根据id,我们可以很轻松的实现多点触控应用。
. <b>width/height</b>:Mouse Event 在屏幕上只能覆盖一个点的位置,但是一个Pointer可能覆盖一个更大的区域。
. <b>isPrimary</b>:当有多个Pointer被检测到的时候(比如多点触摸),对每一种类型的Pointer会存在一个Primary Poiter。只有Primary Poiter会产生与之对应的Mouse Event。稍后会讨论更多与此有关的内容。
. <b>pressure/tilt/width/height: </b>:这些特性,使程序支持更复杂的操作成为可能。
下面是PointerEvent Api 定义的核心事件:
t2.png
Mouse events, pointer events, 和touch events 对照表
t3.png
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的对比。
t4.png
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 ="init()">...</body>}
现在可以在浏览器打开页面了,如果你的浏览器支持pointer event,单击鼠标,就可以在页面看到输出的坐标值了。
pointermove event
和使用touch api的<code>touchmove</code>事件一样,我们可以使用<code>pointermove</code>事件来处理移动事件。
下面我们设计我们的demo:当捕获一个pointerdown 事件的时候,我们开始追踪pointer的移动轨迹。所以我们首先要监听<code>pointerdown</code>事件,然后在<code>pointerdown</code>事件的处理函数中添加对<code>pointermove</code>事件的监听。
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路径结束的时候——用户移开了手指或者笔尖,松开了鼠标按钮——我们需要停止绘图。所以我们需要监听<code>pointerup</code>事件,并添加一个<code>endPointer</code>处理函数。
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 ="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 arrayvar multiLastPt=new Object(); ...// Get the id of the pointer associated with the eventvar id = e.pointerId; ...// Store coordsmultiLastPt[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 ="init()"> <canvas id="mycanvas" width="500" height="500"> Canvas element not supported. </canvas> </body></html>
小结
本文只是简单介绍了Pointer Event的使用,虽然目前浏览器的支持情况并不完美,但是作为w3c的标准,会被支持的越来越好。
如果你在开发中使用Pointer Event Api,一定要注意它和touch事件的区别,处理touch相关操作的时候要谨慎。
作者:玄魂
链接:https://www.jianshu.com/p/e8feac7035c6
共同学习,写下你的评论
评论加载中...
作者其他优质文章