mobiforge.com/design-deve…javascript
(本翻譯未徹底按照原文進行,由於老外太多廢話!)
Pointer Events API 是Hmtl5的事件規範之一,它主要目的是用來將鼠標(Mouse)、觸摸(touch)和觸控筆(pen)三種事件整合爲統一的API。css
相比Touch Events API,雖然目前除了Apple的 Safari瀏覽器,其餘瀏覽器都在實現對該事件類型的支持,可是狀況並非很好。
本篇文章忽略瀏覽器的兼容問題,只討論其基本使用方法。更多內容能夠參考:Pointer Events 背景資料。html
和 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 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的目標元素是位於觸點下方的元素,離開該元素區域,目標元素就會改變。數組
Poiter API的強大之處在於它對Mouse Event的兼容,使得基於Mouse Event的站點能夠很好的運行。固然這是有意爲之,爲了達到這個目的,當Pointer Event被觸發以後,會再次觸發一個對應的Mouse Event。固然只有被認定爲主Pointer(primary Pointer)的Pointer纔會繼續觸發Mouse Event。某種程度上,你能夠認爲在同一時間只有一個鼠標輸入。基於Mouse Event 的網站,原有的處理邏輯無需改動,只須要添加新的針對Touch Event的處理邏輯便可。
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 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,單擊鼠標,就能夠在頁面看到輸出的座標值了。
和使用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」,更多前端開發知識