[翻譯]整合鼠標、觸摸 和觸控筆事件的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」,更多前端開發知識

相關文章
相關標籤/搜索