Multi-Touch-Interaktion
Pointer Events erweitern DOM-Eingabeereignisse, um verschiedene Eingabegeräte wie Stifte/Stylus und Touchscreens sowie Maus zu unterstützen. Der Pointer ist ein hardwareunabhängiges Gerät, das auf einen bestimmten Satz von Bildschirmkoordinaten zielen kann. Ein einheitliches Ereignismodell für Pointer kann die Erstellung von Websites und Anwendungen vereinfachen und unabhängig von der Hardware des Benutzers eine gute Benutzererfahrung bieten.
Pointer Events weisen viele Ähnlichkeiten mit Mausereignissen auf, unterstützen jedoch mehrere gleichzeitige Pointer wie mehrere Finger auf einem Touchscreen. Dieses zusätzliche Merkmal kann verwendet werden, um reichhaltigere Benutzerinteraktionsmodelle bereitzustellen, geht jedoch mit zusätzlicher Komplexität bei der Handhabung von Multi-Touch-Interaktionen einher. Dieses Dokument demonstriert anhand von Beispielcode die Verwendung von Pointer Events mit unterschiedlichen Multi-Touch-Interaktionen.
Eine Live-Version dieser Anwendung ist auf GitHub verfügbar. Der Quellcode ist auf GitHub verfügbar; Pull-Requests und Fehlermeldungen sind willkommen.
Beispiel
Dieses Beispiel demonstriert die Verwendung verschiedener Ereignistypen von Pointer Events (pointerdown
, pointermove
, pointerup
, pointercancel
, usw.) für verschiedene Multi-Touch-Interaktionen.
Touch-Ziele definieren
Die Anwendung nutzt <div>
, um drei verschiedene Touch-Zielbereiche zu definieren.
<style>
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;
}
</style>
Globaler Zustand
Um Multi-Touch-Interaktion zu unterstützen, muss der Ereigniszustand eines Pointers während verschiedener Ereignisphasen beibehalten werden. Diese Anwendung verwendet drei Arrays, um den Ereigniszustand zwischenzuspeichern, ein Cache pro Zielelement.
// Log events flag
const logEvents = false;
// Event caches, one per touch target
const evCache1 = [];
const evCache2 = [];
const evCache3 = [];
Ereignis-Handler registrieren
Ereignis-Handler werden für die folgenden Pointer Events registriert: pointerdown
, pointermove
und pointerup
. Der Handler für pointerup
wird auch für die Ereignisse pointercancel
, pointerout
und pointerleave
verwendet, da diese vier Ereignisse in dieser Anwendung dieselbe Semantik haben.
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;
}
function init() {
setHandlers("target1");
setHandlers("target2");
setHandlers("target3");
}
Pointer down
Das pointerdown
Ereignis wird ausgelöst, wenn ein Pointer (Maus, Stift/Stylus oder Berührungspunkt auf einem Touchscreen) Kontakt mit der Kontaktfläche herstellt. Der Zustand des Ereignisses muss zwischengespeichert werden, falls dieses Down-Ereignis Teil einer Multi-Touch-Interaktion ist.
In dieser Anwendung ändert sich die Hintergrundfarbe des Elements, wenn ein Pointer auf einem Element platziert wird, je nachdem, wie viele aktive Berührungspunkte das Element hat. Siehe die Funktion update_background
für weitere Details zu den Farbänderungen.
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);
}
Pointer move
Der pointermove
Handler wird aufgerufen, wenn der Pointer bewegt wird. Er kann mehrmals aufgerufen werden (zum Beispiel, wenn der Benutzer den Pointer bewegt), bevor ein anderes Ereignis ausgelöst wird.
In dieser Anwendung wird eine Pointer-Bewegung durch die Darstellung des Rahmens des Ziels als gestrichelt
dargestellt, um einen klaren visuellen Hinweis darauf zu geben, dass das Element dieses Ereignis empfangen hat.
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";
}
Pointer up
Das pointerup
Ereignis wird ausgelöst, wenn ein Pointer von der Kontaktfläche entfernt wird. Wenn dies geschieht, wird das Ereignis aus dem zugehörigen Ereignis-Cache entfernt.
In dieser Anwendung wird dieser Handler auch für die Ereignisse pointercancel
, pointerleave
und pointerout
verwendet.
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";
}
Anwendungs-UI
Die Anwendung verwendet <div>
-Elemente für die Berührungsbereiche und bietet Schaltflächen zum Aktivieren des Loggings und zum Löschen des Logs.
Um zu verhindern, dass das Standard-Touch-Verhalten des Browsers die Zeigerverarbeitung dieser Anwendung überschreibt, wird die touch-action
Eigenschaft auf das <body>
Element angewendet.
<body onload="init();" style="touch-action:none">
<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" onclick="enableLog(event);">Start/Stop event logging</button>
<button id="clearlog" onclick="clearLog(event);">Clear the log</button>
<p></p>
<output></output>
</body>
Verschiedene Funktionen
Diese Funktionen unterstützen die Anwendung, sind jedoch nicht direkt in den Ereignisfluss eingebunden.
Cache-Verwaltung
Diese Funktionen verwalten die globalen Ereignis-Caches evCache1
, evCache2
und 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);
}
Hintergrundfarbe aktualisieren
Die Hintergrundfarbe der Berührungsbereiche ändert sich wie folgt: keine aktiven Berührungen ist weiß
; eine aktive Berührung ist gelb
; zwei gleichzeitige Berührungen sind rosa
und drei oder mehr gleichzeitige Berührungen sind hellblau
.
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";
}
}
Ereignis-Logging
Diese Funktionen werden verwendet, um Ereignisaktivitäten an das Anwendungsfenster zu senden (zur Unterstützung von Debugging und zum Lernen über den Ereignisfluss).
// Log events flag
let logEvents = false;
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 = "";
}