多點觸控互動
Pointer events 擴充套件了 DOM 輸入事件,以支援各種指標輸入裝置,例如筆/觸控筆和觸控式螢幕,以及滑鼠。指標是一種與硬體無關的裝置,可以指向特定的螢幕座標集。擁有統一的指標事件模型可以簡化網站和應用程式的建立,並提供良好的使用者體驗,無論使用者使用何種硬體。
Pointer events 與 mouse events 有許多相似之處,但它們支援多個同時出現的指標,例如觸控式螢幕上的多根手指。此附加功能可用於提供更豐富的使用者互動模型,但代價是多點觸控互動處理的複雜性增加。本文件透過示例程式碼,演示了指標事件與不同多點觸控互動的使用。
示例
此示例演示瞭如何使用指標事件的各種事件型別(pointerdown、pointermove、pointerup、pointercancel 等)來實現不同的多點觸控互動。
定義觸控目標
應用程式使用 <div> 來定義三個不同的觸控目標區域。
div {
margin: 0em;
padding: 2em;
}
#target1 {
background: white;
border: 1px solid black;
}
#target2 {
background: white;
border: 1px solid black;
}
#target3 {
background: white;
border: 1px solid black;
}
全域性狀態
為了支援多點觸控互動,需要在各種事件階段保留指標的事件狀態。此應用程式使用三個陣列來快取事件狀態,每個快取對應一個目標元素。
// Log events flag
const logEvents = false;
// Event caches, one per touch target
const evCache1 = [];
const evCache2 = [];
const evCache3 = [];
註冊事件處理程式
為以下指標事件註冊了事件處理程式:pointerdown、pointermove 和 pointerup。 pointerup 的處理程式用於 pointercancel、pointerout 和 pointerleave 事件,因為這四個事件在此應用程式中具有相同的語義。
function setHandlers(name) {
// Install event handlers for the given element
const el = document.getElementById(name);
el.onpointerdown = pointerdownHandler;
el.onpointermove = pointermoveHandler;
// Use same handler for pointer{up,cancel,out,leave} events since
// the semantics for these events - in this app - are the same.
el.onpointerup = pointerupHandler;
el.onpointercancel = pointerupHandler;
el.onpointerout = pointerupHandler;
el.onpointerleave = pointerupHandler;
}
setHandlers("target1");
setHandlers("target2");
setHandlers("target3");
指標按下
當指標(滑鼠、筆/觸控筆或觸控式螢幕上的觸控點)與接觸表面接觸時,會觸發 pointerdown 事件。必須快取事件的狀態,以防此按下事件是多點觸控互動的一部分。
在此應用程式中,當指標按下某個元素時,該元素的背景顏色會根據該元素活動的觸控點數量而改變。有關顏色變化的更多詳細資訊,請參閱 update_background 函式。
function pointerdownHandler(ev) {
// The pointerdown event signals the start of a touch interaction.
// Save this event for later processing (this could be part of a
// multi-touch interaction) and update the background color
pushEvent(ev);
if (logEvents) {
log(`pointerDown: name = ${ev.target.id}`, ev);
}
updateBackground(ev);
}
指標移動
當指標移動時,會呼叫 pointermove 處理程式。在觸發其他事件型別之前,它可能會被呼叫多次(例如,如果使用者移動指標)。
在此應用程式中,指標移動透過將目標的邊框設定為 dashed 來表示,以提供清晰的視覺指示,表明該元素已接收到此事件。
function pointermoveHandler(ev) {
// Note: if the user makes more than one "simultaneous" touch, most browsers
// fire at least one pointermove event and some will fire several pointermove events.
//
// This function sets the target element's border to "dashed" to visually
// indicate the target received a move event.
if (logEvents) {
log("pointerMove", ev);
}
updateBackground(ev);
ev.target.style.border = "dashed";
}
指標抬起
當指標從接觸表面抬起時,會觸發 pointerup 事件。發生這種情況時,事件將從關聯的事件快取中移除。
在此應用程式中,此處理程式也用於 pointercancel、pointerleave 和 pointerout 事件。
function pointerupHandler(ev) {
if (logEvents) {
log(ev.type, ev);
}
// Remove this touch point from the cache and reset the target's
// background and border
removeEvent(ev);
updateBackground(ev);
ev.target.style.border = "1px solid black";
}
應用程式 UI
應用程式使用 <div> 元素作為觸控區域,並提供按鈕來啟用日誌記錄和清除日誌。
為防止瀏覽器預設的觸控行為覆蓋此應用程式的指標處理,touch-action 屬性已應用於 <body> 元素。
<div id="target1">Tap, Hold or Swipe me 1</div>
<div id="target2">Tap, Hold or Swipe me 2</div>
<div id="target3">Tap, Hold or Swipe me 3</div>
<!-- UI for logging/debugging -->
<button id="log">Start/Stop event logging</button>
<button id="clear-log">Clear the log</button>
<p></p>
<output></output>
body {
touch-action: none; /* Prevent default touch behavior */
}
雜項函式
這些函式支援應用程式,但與事件流沒有直接關係。
快取管理
這些函式管理全域性事件快取 evCache1、evCache2 和 evCache3。
function getCache(ev) {
// Return the cache for this event's target element
switch (ev.target.id) {
case "target1":
return evCache1;
case "target2":
return evCache2;
case "target3":
return evCache3;
default:
log("Error with cache handling", ev);
}
}
function pushEvent(ev) {
// Save this event in the target's cache
const evCache = getCache(ev);
evCache.push(ev);
}
function removeEvent(ev) {
// Remove this event from the target's cache
const evCache = getCache(ev);
const index = evCache.findIndex(
(cachedEv) => cachedEv.pointerId === ev.pointerId,
);
evCache.splice(index, 1);
}
更新背景顏色
觸控區域的背景顏色將如下變化:無活動觸控為 white;一個活動觸控為 yellow;兩個同時觸控為 pink;三個或更多同時觸控為 lightblue。
function updateBackground(ev) {
// Change background color based on the number of simultaneous touches/pointers
// currently down:
// white - target element has no touch points i.e. no pointers down
// yellow - one pointer down
// pink - two pointers down
// lightblue - three or more pointers down
const evCache = getCache(ev);
switch (evCache.length) {
case 0:
// Target element has no touch points
ev.target.style.background = "white";
break;
case 1:
// Single touch point
ev.target.style.background = "yellow";
break;
case 2:
// Two simultaneous touch points
ev.target.style.background = "pink";
break;
default:
// Three or more simultaneous touches
ev.target.style.background = "lightblue";
}
}
事件日誌記錄
這些函式用於將事件活動傳送到應用程式視窗(以支援除錯和了解事件流)。
// Log events flag
let logEvents = false;
document.getElementById("log").addEventListener("click", enableLog);
document.getElementById("clear-log").addEventListener("click", clearLog);
function enableLog(ev) {
logEvents = !logEvents;
}
function log(name, ev) {
const o = document.getElementsByTagName("output")[0];
o.innerText += `${name}:
pointerID = ${ev.pointerId}
pointerType = ${ev.pointerType}
isPrimary = ${ev.isPrimary}
`;
}
function clearLog(event) {
const o = document.getElementsByTagName("output")[0];
o.textContent = "";
}