Signatur
Beschreibung
Das Ereignis gesturechange gehört zu den proprietären WebKit-Gestenereignissen. Es wird wiederholt auf einem Element ausgelöst, solange eine Mehrfinger-Geste läuft, also zwischen gesturestart und gestureend. Das übergebene GestureEvent enthält die Eigenschaften scale (Skalierungsfaktor relativ zum Beginn der Geste, 1 = unverändert) und rotation (Drehwinkel in Grad relativ zum Beginn, positiv = im Uhrzeigersinn).
Typische Einsatzzwecke sind Zoom per Pinch und das Drehen von Bildern, Karten oder Canvas-Inhalten. Ein Beispiel ist eine Bildergalerie, in der man mit zwei Fingern zoomt. Unter iOS kommen die Ereignisse vom Touchscreen, unter macOS Safari von Pinch- und Drehgesten auf dem Trackpad.
Du kannst den Handler per addEventListener("gesturechange", handler) registrieren oder über die Eigenschaft element.ongesturechange zuweisen. Soll der Browser dabei nicht selbst zoomen, rufst du event.preventDefault() auf. Der Listener muss dann mit { passive: false } registriert werden.
Weil die API nicht standardisiert ist, solltest du sie nur als Verbesserung für Safari nutzen. Für alle anderen Browser brauchst du eine Alternative mit Pointer Events oder Touch Events, bei der du zwei Kontaktpunkte selbst auswertest.
Parameter
| Name | Typ | Default | Beschreibung |
|---|---|---|---|
| $type Pflicht | string | Der Ereignisname "gesturechange". |
|
| $handler Pflicht | (event: GestureEvent) => void | Callback, das bei jeder Bewegung während der Geste aufgerufen wird. Wichtige Eigenschaften: event.scale (number), event.rotation (number, Grad), event.clientX/clientY (Mittelpunkt der Geste), dazu Modifikatortasten wie ctrlKey und shiftKey. |
|
| $options | boolean | AddEventListenerOptions | false | Optionen wie { passive: false }, damit preventDefault() den nativen Zoom verhindern kann. Weitere Optionen sind once, capture und signal. |
Rückgabewert
addEventListener gibt nichts zurück. Der Handler erhält ein GestureEvent (erbt von UIEvent).Beispiele
Bild per Pinch zoomen und drehen (Safari)
const img = document.querySelector('#foto');
let baseScale = 1;
let baseRotation = 0;
img.addEventListener('gesturestart', (e) => {
e.preventDefault();
}, { passive: false });
img.addEventListener('gesturechange', (e) => {
e.preventDefault(); // nativen Seitenzoom unterdrücken
const scale = baseScale * e.scale;
const rotation = baseRotation + e.rotation;
img.style.transform = `scale(${scale}) rotate(${rotation}deg)`;
console.log(`scale=${e.scale.toFixed(2)} rotation=${e.rotation.toFixed(1)}°`);
}, { passive: false });
img.addEventListener('gestureend', (e) => {
// Endwerte für die nächste Geste merken
baseScale *= e.scale;
baseRotation += e.rotation;
});
Handler per on-Property setzen und mit removeEventListener entfernen
const karte = document.querySelector('#karte');
const onGesture = (e) => {
e.preventDefault();
console.log('Zoomfaktor:', e.scale.toFixed(2));
};
// Variante 1: addEventListener
karte.addEventListener('gesturechange', onGesture, { passive: false });
// Später wieder abmelden, z. B. wenn der Zoom gesperrt wird
document.querySelector('#sperren').addEventListener('click', () => {
karte.removeEventListener('gesturechange', onGesture, { passive: false });
console.log('Gesten-Zoom deaktiviert');
});
// Variante 2: on-Property (ersetzt einen vorherigen Handler)
// karte.ongesturechange = onGesture;
// karte.ongesturechange = null; // entfernen
Funktionsprüfung mit Alternative für andere Browser
const flaeche = document.querySelector('#canvas-wrapper');
let zoom = 1;
const apply = () => { flaeche.style.transform = `scale(${zoom})`; };
if ('ongesturechange' in window) {
// Safari: native Gestenereignisse
let start = 1;
flaeche.addEventListener('gesturestart', (e) => { e.preventDefault(); start = zoom; }, { passive: false });
flaeche.addEventListener('gesturechange', (e) => {
e.preventDefault();
zoom = Math.min(Math.max(start * e.scale, 0.5), 4);
apply();
}, { passive: false });
} else {
// Chrome/Firefox/Edge: Trackpad-Pinch kommt als wheel mit ctrlKey
flaeche.addEventListener('wheel', (e) => {
if (!e.ctrlKey) return;
e.preventDefault();
zoom = Math.min(Math.max(zoom * Math.exp(-e.deltaY / 100), 0.5), 4);
apply();
}, { passive: false });
}
console.log('Gesten-API:', 'ongesturechange' in window ? 'WebKit' : 'Alternative');
// Wichtig · Fallstricke
- Nicht standardisiert: Nur Safari unter iOS/iPadOS und macOS unterstützt das Ereignis. Chrome, Firefox und Edge kennen es nicht, auch nicht unter Android. Prüfe die Unterstützung mit
'ongesturechange' in windowodertypeof GestureEvent !== 'undefined'. scaleundrotationbeziehen sich immer auf den Beginn der aktuellen Geste und sind keine schrittweisen Änderungen. Speichere die Endwerte ingestureend, sonst springt das Element bei der nächsten Geste auf den Ausgangszustand zurück.preventDefault()wirkt nur, wenn der Listener nicht passiv ist ({ passive: false }). Seit iOS 10 ignoriert Safari außerdemuser-scalable=noim Viewport-Meta-Tag. Das Abfangen der Gestenereignisse ist dann oft der einzige Weg, den Seitenzoom zu unterdrücken. Das schadet aber der Barrierefreiheit, also nur gezielt auf einzelnen Elementen einsetzen.- Unter iOS feuern parallel auch
touchstart,touchmoveundtouchend. Wer beide Ereignisarten verarbeitet, muss doppelte Reaktionen vermeiden. - Das Ereignis feuert sehr oft. Für aufwendige Neuzeichnungen die Werte zwischenspeichern und nur in
requestAnimationFrameanwenden. CSS-transformist günstiger als Layout-Änderungen.