多点触控交互
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 = "";
}