Touch events
Um hochwertige Unterstützung für berührungsbasierte Benutzeroberflächen bereitzustellen, bieten Touch-Events die Fähigkeit, die Aktivität von Fingern (oder Stiften) auf Touchscreens oder Trackpads zu interpretieren.
Die Touch-Events-Schnittstellen sind relativ niedrigstufige APIs, die verwendet werden können, um anwendungsspezifische Multi-Touch-Interaktionen wie eine Zwei-Finger-Geste zu unterstützen. Eine Multi-Touch-Interaktion beginnt, wenn ein Finger (oder Stift) erstmals die Kontaktfläche berührt. Andere Finger können anschließend die Oberfläche berühren und sich optional über die Touch-Oberfläche bewegen. Die Interaktion endet, wenn die Finger von der Oberfläche entfernt werden. Während dieser Interaktion empfängt eine Anwendung Touch-Events während der Start-, Bewegungs- und Endphasen.
Touch-Events sind den Maus-Events ähnlich, unterstützen jedoch gleichzeitige Berührungen an verschiedenen Positionen auf der Touch-Oberfläche. Die TouchEvent
-Schnittstelle kapselt alle aktuell aktiven Berührungspunkte. Die Touch
-Schnittstelle, die einen einzelnen Berührungspunkt darstellt, enthält Informationen wie die Position des Berührungspunkts relativ zur Browser-Ansicht.
Definitionen
- Oberfläche
-
Die berührungsempfindliche Oberfläche. Dies kann ein Bildschirm oder Trackpad sein.
- Berührungspunkt
-
Ein Kontaktpunkt mit der Oberfläche. Dies kann ein Finger (oder Ellbogen, Ohr, Nase, was auch immer, aber typischerweise ein Finger) oder ein Stift sein.
Schnittstellen
TouchEvent
-
Repräsentiert ein Ereignis, das auftritt, wenn sich der Zustand der Berührungen auf der Oberfläche ändert.
Touch
-
Repräsentiert einen einzigen Kontaktpunkt zwischen dem Benutzer und der Berührungsoberfläche.
TouchList
-
Repräsentiert eine Gruppe von Berührungen; dies wird verwendet, wenn der Benutzer beispielsweise mehrere Finger gleichzeitig auf der Oberfläche hat.
Beispiel
Dieses Beispiel verfolgt mehrere Berührungspunkte gleichzeitig und ermöglicht es dem Benutzer, mit mehr als einem Finger gleichzeitig in einem <canvas>
zu zeichnen. Es funktioniert nur in einem Browser, der Touch-Events unterstützt.
Hinweis: Der untenstehende Text verwendet den Begriff "Finger", wenn der Kontakt mit der Oberfläche beschrieben wird, aber es könnte natürlich auch ein Stift oder eine andere Kontaktmethode sein.
Erstellen eines Canvas
<canvas id="canvas" width="600" height="600" style="border:solid black 1px;">
Your browser does not support canvas element.
</canvas>
<br />
Log:
<pre id="log" style="border: 1px solid #ccc;"></pre>
#log {
height: 200px;
width: 600px;
overflow: scroll;
}
Einrichten der Ereignis-Handler
Wenn die Seite geladen wird, wird die unten gezeigte startup()
-Funktion aufgerufen.
Diese richtet alle Ereignis-Listener für unser <canvas>
-Element ein, damit wir die Touch-Events behandeln können, sobald sie auftreten.
function startup() {
const el = document.getElementById("canvas");
el.addEventListener("touchstart", handleStart);
el.addEventListener("touchend", handleEnd);
el.addEventListener("touchcancel", handleCancel);
el.addEventListener("touchmove", handleMove);
log("Initialized.");
}
document.addEventListener("DOMContentLoaded", startup);
Nachverfolgen neuer Berührungen
Wir behalten den Überblick über die laufenden Berührungen.
const ongoingTouches = [];
Wenn ein touchstart
-Ereignis auftritt, das anzeigt, dass eine neue Berührung auf der Oberfläche erfolgt ist, wird die folgende handleStart()
-Funktion aufgerufen.
function handleStart(evt) {
evt.preventDefault();
log("touchstart.");
const el = document.getElementById("canvas");
const ctx = el.getContext("2d");
const touches = evt.changedTouches;
for (let i = 0; i < touches.length; i++) {
log(`touchstart: ${i}.`);
ongoingTouches.push(copyTouch(touches[i]));
const color = colorForTouch(touches[i]);
log(`color of touch with id ${touches[i].identifier} = ${color}`);
ctx.beginPath();
ctx.arc(touches[i].pageX, touches[i].pageY, 4, 0, 2 * Math.PI, false); // a circle at the start
ctx.fillStyle = color;
ctx.fill();
}
}
Dies ruft event.preventDefault()
auf, um zu verhindern, dass der Browser das Touch-Event weiterhin verarbeitet (dies verhindert auch, dass ein Maus-Event ebenfalls ausgeliefert wird). Dann holen wir den Kontext und ziehen die Liste der geänderten Berührungspunkte aus der TouchEvent.changedTouches
-Eigenschaft des Ereignisses heraus.
Danach iterieren wir über alle Touch
-Objekte in der Liste, fügen sie zu einem Array aktiver Berührungspunkte hinzu und zeichnen den Startpunkt für das Zeichnen als kleinen Kreis; wir verwenden eine 4-Pixel breite Linie, also wird ein Kreis mit 4-Pixel-Radius sauber angezeigt.
Zeichnen, während sich die Berührungen bewegen
Jedes Mal, wenn sich ein oder mehrere Finger bewegen, wird ein touchmove
-Ereignis ausgeliefert, was dazu führt, dass unsere handleMove()
-Funktion aufgerufen wird. Ihre Verantwortung in diesem Beispiel ist es, die zwischengespeicherten Berührungsinformationen zu aktualisieren und eine Linie vom vorherigen zum aktuellen Standpunkt jeder Berührung zu zeichnen.
function handleMove(evt) {
evt.preventDefault();
const el = document.getElementById("canvas");
const ctx = el.getContext("2d");
const touches = evt.changedTouches;
for (let i = 0; i < touches.length; i++) {
const color = colorForTouch(touches[i]);
const idx = ongoingTouchIndexById(touches[i].identifier);
if (idx >= 0) {
log(`continuing touch ${idx}`);
ctx.beginPath();
log(
`ctx.moveTo( ${ongoingTouches[idx].pageX}, ${ongoingTouches[idx].pageY} );`,
);
ctx.moveTo(ongoingTouches[idx].pageX, ongoingTouches[idx].pageY);
log(`ctx.lineTo( ${touches[i].pageX}, ${touches[i].pageY} );`);
ctx.lineTo(touches[i].pageX, touches[i].pageY);
ctx.lineWidth = 4;
ctx.strokeStyle = color;
ctx.stroke();
ongoingTouches.splice(idx, 1, copyTouch(touches[i])); // swap in the new touch record
} else {
log("can't figure out which touch to continue");
}
}
}
Auch dies iteriert über die geänderten Berührungen, sucht jedoch in unserem zwischengespeicherten Berührungsinformationsarray nach den vorherigen Informationen zu jeder Berührung, um den Startpunkt jedes neuen Liniensegments der Berührung zu bestimmen. Dies geschieht, indem auf jede Berührungseigenschaft Touch.identifier
geschaut wird. Diese Eigenschaft ist eine eindeutige ganze Zahl für jede Berührung und bleibt während der Dauer des Kontakts jedes Fingers mit der Oberfläche konsistent.
Das ermöglicht es uns, die Koordinaten der vorherigen Position jeder Berührung zu erhalten und die entsprechenden Kontextmethoden zu verwenden, um ein Liniensegment zu zeichnen, das die beiden Positionen verbindet.
Nachdem die Linie gezeichnet wurde, rufen wir Array.splice()
auf, um die vorherigen Informationen über den Berührungspunkt mit den aktuellen Informationen im ongoingTouches
-Array zu ersetzen.
Umgang mit dem Ende einer Berührung
Wenn der Benutzer einen Finger von der Oberfläche hebt, wird ein touchend
-Ereignis gesendet. Wir behandeln dies, indem wir die nachfolgende handleEnd()
-Funktion aufrufen. Ihre Aufgabe ist es, das letzte Liniensegment für jede Berührung zu zeichnen, die beendet wurde, und den Berührungspunkt aus der laufenden Berührungsliste zu entfernen.
function handleEnd(evt) {
evt.preventDefault();
log("touchend");
const el = document.getElementById("canvas");
const ctx = el.getContext("2d");
const touches = evt.changedTouches;
for (let i = 0; i < touches.length; i++) {
const color = colorForTouch(touches[i]);
let idx = ongoingTouchIndexById(touches[i].identifier);
if (idx >= 0) {
ctx.lineWidth = 4;
ctx.fillStyle = color;
ctx.beginPath();
ctx.moveTo(ongoingTouches[idx].pageX, ongoingTouches[idx].pageY);
ctx.lineTo(touches[i].pageX, touches[i].pageY);
ctx.fillRect(touches[i].pageX - 4, touches[i].pageY - 4, 8, 8); // and a square at the end
ongoingTouches.splice(idx, 1); // remove it; we're done
} else {
log("can't figure out which touch to end");
}
}
}
Dies ist der vorherigen Funktion sehr ähnlich; die einzigen wirklichen Unterschiede sind, dass wir ein kleines Quadrat zeichnen, um das Ende zu markieren, und dass wir, wenn wir Array.splice()
aufrufen, den alten Eintrag aus der laufenden Berührungsliste entfernen, ohne die aktualisierten Informationen hinzuzufügen. Das Ergebnis ist, dass wir diesen Berührungspunkt nicht mehr verfolgen.
Umgang mit abgebrochenen Berührungen
Wenn der Finger des Benutzers in die Browser-Benutzeroberfläche wandert oder die Berührung anderweitig abgebrochen werden muss, wird das touchcancel
-Ereignis gesendet und wir rufen die nachfolgende handleCancel()
-Funktion auf.
function handleCancel(evt) {
evt.preventDefault();
log("touchcancel.");
const touches = evt.changedTouches;
for (let i = 0; i < touches.length; i++) {
let idx = ongoingTouchIndexById(touches[i].identifier);
ongoingTouches.splice(idx, 1); // remove it; we're done
}
}
Da die Idee darin besteht, die Berührung sofort abzubrechen, entfernen wir sie aus der laufenden Berührungsliste, ohne ein letztes Liniensegment zu zeichnen.
Hilfsfunktionen
Dieses Beispiel verwendet zwei Hilfsfunktionen, die kurz betrachtet werden sollten, um den restlichen Code verständlicher zu machen.
Auswählen einer Farbe für jede Berührung
Um das Zeichnen jeder Berührung unterschiedlich aussehen zu lassen, wird die Funktion colorForTouch()
verwendet, um basierend auf der eindeutigen Identifikationsnummer der Berührung eine Farbe auszuwählen.
Diese Kennung ist eine undurchsichtige Zahl, aber wir können zumindest darauf vertrauen, dass sie sich zwischen den derzeit aktiven Berührungen unterscheidet.
function colorForTouch(touch) {
let r = touch.identifier % 16;
let g = Math.floor(touch.identifier / 3) % 16;
let b = Math.floor(touch.identifier / 7) % 16;
r = r.toString(16); // make it a hex digit
g = g.toString(16); // make it a hex digit
b = b.toString(16); // make it a hex digit
const color = `#${r}${g}${b}`;
return color;
}
Das Ergebnis dieser Funktion ist ein String, der bei Aufrufen von <canvas>
-Funktionen verwendet werden kann, um Zeichnungsfarben festzulegen.
Zum Beispiel ergibt sich aus einem Touch.identifier
-Wert von 10 der resultierende String "#a31".
Kopieren eines Berührungsobjekts
Einige Browser (z.B. mobiles Safari) verwenden Berührungsobjekte zwischen Ereignissen erneut, daher ist es am besten, die Eigenschaften zu kopieren, die Sie interessieren, anstatt das gesamte Objekt zu referenzieren.
function copyTouch({ identifier, pageX, pageY }) {
return { identifier, pageX, pageY };
}
Finden einer laufenden Berührung
Die ongoingTouchIndexById()
-Funktion unten durchsucht das ongoingTouches
-Array, um die Berührung zu finden, die der gegebenen Kennung entspricht und gibt dann den Index dieser Berührung im Array zurück.
function ongoingTouchIndexById(idToFind) {
for (let i = 0; i < ongoingTouches.length; i++) {
const id = ongoingTouches[i].identifier;
if (id === idToFind) {
return i;
}
}
return -1; // not found
}
Zeigen, was vor sich geht
function log(msg) {
const container = document.getElementById("log");
container.textContent = `${msg} \n${container.textContent}`;
}
Ergebnis
Sie können dieses Beispiel auf mobilen Geräten testen, indem Sie das Feld unten berühren.
Hinweis: Allgemeiner funktioniert das Beispiel auf Plattformen, die Touch-Events bereitstellen. Sie können dies auf Desktop-Plattformen testen, die solche Ereignisse simulieren können:
- In Firefox aktivieren Sie "Touch-Simulation" im Responsive Design Mode (möglicherweise müssen Sie die Seite neu laden).
- In Chrome verwenden Sie den Gerätemodus und stellen den Gerätetyp auf einen ein, der Touch-Events sendet.
Zusätzliche Tipps
Dieser Abschnitt bietet zusätzliche Tipps zum Umgang mit Touch-Events in Ihrer Webanwendung.
Umgang mit Klicks
Da der Aufruf von preventDefault()
bei einem touchstart
- oder dem ersten touchmove
-Ereignis einer Serie die entsprechenden Mausereignisse daran hindert, ausgelöst zu werden, ist es üblich, preventDefault()
bei touchmove
statt touchstart
aufzurufen. Auf diese Weise können Mausereignisse weiterhin ausgelöst werden und Dinge wie Links funktionieren weiterhin. Alternativ haben einige Frameworks begonnen, Touch-Events als Mausereignisse für den gleichen Zweck neu auszulösen. (Dieses Beispiel ist übervereinfacht und kann zu merkwürdigem Verhalten führen. Es ist nur als Leitfaden gedacht.)
function onTouch(evt) {
evt.preventDefault();
if (
evt.touches.length > 1 ||
(evt.type === "touchend" && evt.touches.length > 0)
)
return;
const newEvt = document.createEvent("MouseEvents");
let type = null;
let touch = null;
switch (evt.type) {
case "touchstart":
type = "mousedown";
touch = evt.changedTouches[0];
break;
case "touchmove":
type = "mousemove";
touch = evt.changedTouches[0];
break;
case "touchend":
type = "mouseup";
touch = evt.changedTouches[0];
break;
}
newEvt.initMouseEvent(
type,
true,
true,
evt.originalTarget.ownerDocument.defaultView,
0,
touch.screenX,
touch.screenY,
touch.clientX,
touch.clientY,
evt.ctrlKey,
evt.altKey,
evt.shiftKey,
evt.metaKey,
0,
null,
);
evt.originalTarget.dispatchEvent(newEvt);
}
PreventDefault() nur bei einer zweiten Berührung aufrufen
Eine Technik, um Dinge wie pinchZoom
auf einer Seite zu verhindern, besteht darin, preventDefault()
bei der zweiten Berührung in einer Serie aufzurufen. Dieses Verhalten ist in der Spezifikation für Touch-Events nicht gut definiert und führt zu verschiedenen Verhalten bei verschiedenen Browsern (d.h. iOS verhindert das Zoomen, erlaubt aber weiterhin ein Scrollen mit beiden Fingern; Android erlaubt Zoomen, aber nicht Scrollen; Opera und Firefox verhindern derzeit jegliches Scrollen und Zoomen.) Derzeit wird nicht empfohlen, sich in diesem Fall auf ein bestimmtes Verhalten zu verlassen, sondern auf den Meta-Viewport, um das Zoomen zu verhindern.
Spezifikationen
Specification |
---|
Touch Events |
Browser-Kompatibilität
Touch-Events sind typischerweise auf Geräten mit Touchscreen verfügbar, aber viele Browser machen die Touch-Events-API auf allen Desktops nicht verfügbar, selbst auf denen mit Touchscreens.
Der Grund dafür ist, dass einige Websites die Verfügbarkeit von Teilen der Touch-Events-API als Indikator nutzen, dass der Browser auf einem mobilen Gerät läuft. Wenn die Touch-Events-API verfügbar ist, gehen diese Websites davon aus, dass es sich um ein mobiles Gerät handelt und liefern mobile-optimierten Inhalt. Dies kann dann ein schlechtes Erlebnis für Benutzer von Desktops mit Touchscreens verursachen.
Um sowohl Touch als auch Maus auf allen Gerätetypen zu unterstützen, verwenden Sie stattdessen Pointer Events.
api.Touch
BCD tables only load in the browser
api.TouchEvent
BCD tables only load in the browser
api.TouchList
BCD tables only load in the browser