Dieser Inhalt wurde automatisch aus dem Englischen übersetzt, und kann Fehler enthalten. Erfahre mehr über dieses Experiment.

View in English Always switch to English

Pinch-Zoom-Gesten

Das Hinzufügen von Gesten zu einer Anwendung kann die Benutzererfahrung erheblich verbessern. Es gibt viele Arten von Gesten: von der einfachen Wischgeste mit einem Berührungspunkt bis zur komplexeren Drehgeste mit mehreren Berührungspunkten, bei der sich die Berührungspunkte (auch Pointer genannt) in unterschiedliche Richtungen bewegen.

Dieses Beispiel zeigt, wie Sie eine Pinch-Zoom-Geste erkennen. Dabei wird mithilfe von Pointer Events festgestellt, ob die nutzende Person zwei Pointer aufeinander zu oder voneinander weg bewegt.

Eine Live-Version dieser Anwendung ist auf GitHub verfügbar. Der Quellcode ist auf GitHub verfügbar; Pull Requests und Fehlermeldungen sind willkommen.

Beispiel

In diesem Beispiel verwenden Sie Pointer Events, um zwei Zeigegeräte beliebiger Art gleichzeitig zu erkennen, darunter Finger, Mäuse und Stifte. Bei der Pinch-in-Geste (Verkleinern), bei der sich die beiden Pointer aufeinander zubewegen, ändert sich die Hintergrundfarbe des Zielelements zu lightblue. Bei der Pinch-out-Geste (Vergrößern), bei der sich die beiden Pointer voneinander entfernen, ändert sich die Hintergrundfarbe des Zielelements zu pink.

Berührungsziel definieren

Die Anwendung verwendet <div>, um die Zielbereiche für die Pointer zu definieren.

css
div {
  margin: 0em;
  padding: 2em;
}
#target {
  background: white;
  border: 1px solid black;
}

Globaler Zustand

Um eine Geste mit zwei Pointern zu unterstützen, muss der Ereigniszustand eines Pointers über verschiedene Ereignisphasen hinweg erhalten bleiben. Diese Anwendung verwendet zwei globale Variablen, um den Ereigniszustand zwischenzuspeichern.

js
// Global vars to cache event state
const evCache = [];
let prevDiff = -1;

Event-Handler registrieren

Event-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 Bedeutung haben.

js
// Install event handlers for the pointer target
const el = document.getElementById("target");
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;

Pointer-Kontakt beginnt

Das Ereignis pointerdown wird ausgelöst, wenn ein Pointer (Maus, Stift oder Berührungspunkt auf einem Touchscreen) mit der Kontaktfläche in Berührung kommt. In dieser Anwendung muss der Ereigniszustand zwischengespeichert werden, falls dieses Ereignis Teil einer Pinch-Zoom-Geste mit zwei Pointern ist.

js
function pointerdownHandler(ev) {
  // The pointerdown event signals the start of a touch interaction.
  // This event is cached to support 2-finger gestures
  evCache.push(ev);
  log("pointerDown", ev);
}

Pointer-Bewegung

Der Event-Handler für pointermove erkennt, ob eine Pinch-Zoom-Geste mit zwei Pointern ausgeführt wird. Wenn zwei Pointer die Kontaktfläche berühren und der Abstand zwischen ihnen zunimmt (Pinch-out oder Vergrößern), ändert sich die Hintergrundfarbe des Elements zu pink. Nimmt der Abstand ab (Pinch-in oder Verkleinern), ändert sich die Hintergrundfarbe zu lightblue. In einer komplexeren Anwendung könnte die Unterscheidung zwischen Pinch-in und Pinch-out für anwendungsspezifische Funktionen genutzt werden.

Bei der Verarbeitung dieses Ereignisses wird der Rahmen des Zielelements auf dashed gesetzt. So wird deutlich sichtbar, dass das Element ein Bewegungsereignis empfangen hat.

js
function pointermoveHandler(ev) {
  // This function implements a 2-pointer pinch/zoom gesture.
  //
  // If the distance between the two pointers has increased (zoom in),
  // the target element's background is changed to "pink" and if the
  // distance is decreasing (zoom out), the color is changed to "lightblue".
  //
  // This function sets the target element's border to "dashed" to visually
  // indicate the pointer's target received a move event.
  log("pointerMove", ev);
  ev.target.style.border = "dashed";

  // Find this event in the cache and update its record with this event
  const index = evCache.findIndex(
    (cachedEv) => cachedEv.pointerId === ev.pointerId,
  );
  evCache[index] = ev;

  // If two pointers are down, check for pinch gestures
  if (evCache.length === 2) {
    // Calculate the distance between the two pointers
    const curDiff = Math.hypot(
      evCache[0].clientX - evCache[1].clientX,
      evCache[0].clientY - evCache[1].clientY,
    );

    if (prevDiff > 0) {
      if (curDiff > prevDiff) {
        // The distance between the two pointers has increased
        log("Pinch moving OUT -> Zoom in", ev);
        ev.target.style.background = "pink";
      }
      if (curDiff < prevDiff) {
        // The distance between the two pointers has decreased
        log("Pinch moving IN -> Zoom out", ev);
        ev.target.style.background = "lightblue";
      }
    }

    // Cache the distance for the next move event
    prevDiff = curDiff;
  }
}

Pointer-Kontakt endet

Das Ereignis pointerup wird ausgelöst, wenn ein Pointer von der Kontaktfläche abgehoben wird. In diesem Fall wird das Ereignis aus dem Ereignis-Cache entfernt, und die Hintergrundfarbe sowie der Rahmen des Zielelements werden auf ihre ursprünglichen Werte zurückgesetzt.

In dieser Anwendung wird dieser Handler auch für die Ereignisse pointercancel, pointerleave und pointerout verwendet.

js
function pointerupHandler(ev) {
  log(ev.type, ev);
  // Remove this pointer from the cache and reset the target's
  // background and border
  removeEvent(ev);
  ev.target.style.background = "white";
  ev.target.style.border = "1px solid black";

  // If the number of pointers down is less than two then reset diff tracker
  if (evCache.length < 2) {
    prevDiff = -1;
  }
}

Benutzeroberfläche der Anwendung

Die Anwendung verwendet ein <div>-Element als Berührungsfläche und stellt Schaltflächen bereit, um die Protokollierung zu aktivieren und das Protokoll zu löschen.

Damit das standardmäßige Berührungsverhalten des Browsers die Pointer-Verarbeitung dieser Anwendung nicht übersteuert, wird die Eigenschaft touch-action auf das <body>-Element angewendet.

html
<div id="target">
  Touch and Hold with 2 pointers, then pinch in or out.<br />
  The background color will change to pink if the pinch is opening (Zoom In) or
  changes to lightblue if the pinch is closing (Zoom out).
</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>
css
body {
  touch-action: none; /* Prevent default touch behavior */
}

Weitere Funktionen

Diese Funktionen unterstützen die Anwendung, sind aber nicht direkt am Ereignisablauf beteiligt.

Cache-Verwaltung

Diese Funktion hilft bei der Verwaltung des globalen Ereignis-Caches evCache.

js
function removeEvent(ev) {
  // Remove this event from the target's cache
  const index = evCache.findIndex(
    (cachedEv) => cachedEv.pointerId === ev.pointerId,
  );
  evCache.splice(index, 1);
}

Ereignisprotokollierung

Diese Funktionen geben Informationen über Ereignisaktivitäten im Fenster der Anwendung aus, um das Debugging und das Verständnis des Ereignisablaufs zu unterstützen.

js
// Log events flag
let logEvents = false;

document.getElementById("log").addEventListener("click", enableLog);
document.getElementById("clear-log").addEventListener("click", clearLog);

// Logging/debugging functions
function enableLog(ev) {
  logEvents = !logEvents;
}

function log(prefix, ev) {
  if (!logEvents) return;
  const o = document.getElementsByTagName("output")[0];
  o.innerText += `${prefix}:
  pointerID   = ${ev.pointerId}
  pointerType = ${ev.pointerType}
  isPrimary   = ${ev.isPrimary}
`;
}

function clearLog(event) {
  const o = document.getElementsByTagName("output")[0];
  o.textContent = "";
}

Siehe auch