Signatur
Beschreibung
Das Ereignis squeezeend gehört zur WebXR Device API. Es wird an eine XRSession gesendet, wenn eine Eingabequelle ihre primäre Greifaktion (engl. primary squeeze action) beendet. Typisch ist das Loslassen der Grip-Taste an einem VR-Controller oder das Öffnen der Faust beim Hand-Tracking. Die MDN-Kurzbeschreibung spricht von der „primären Aktion“. Gemeint ist aber die Greifaktion. Für die primäre Aktion (z. B. den Trigger) gibt es das eigene Ereignis selectend.
Das Ereignis wird immer ausgelöst, wenn eine Greifaktion endet, egal wie. Das gilt, wenn die Aktion normal abgeschlossen wurde (dann kommt vorher ein squeeze-Ereignis), wenn sie abgebrochen wurde, oder wenn der Controller während der Aktion getrennt wurde. Damit eignet sich squeezeend gut zum Aufräumen: Ein gegriffenes Objekt wird losgelassen, eine Hervorhebung wird entfernt, ein Zustand wird zurückgesetzt.
Die Reihenfolge ist: squeezestart → (optional) squeeze → squeezeend. Das Ereignisobjekt ist ein XRInputSourceEvent. Seine Eigenschaft inputSource nennt den betroffenen Controller bzw. die Hand, frame liefert einen XRFrame, mit dem man die Pose zum Zeitpunkt des Ereignisses abfragen kann.
Den Handler registriert man mit addEventListener("squeezeend", …) oder über die Eigenschaft onsqueezeend der Sitzung.
Parameter
| Name | Typ | Default | Beschreibung |
|---|---|---|---|
| $type Pflicht | string | Der Ereignisname "squeezeend". |
|
| $handler Pflicht | (event: XRInputSourceEvent) => void | Callback, der ein XRInputSourceEvent mit inputSource (XRInputSource) und frame (XRFrame) erhält. |
|
| $options | boolean | AddEventListenerOptions | false | Übliche Listener-Optionen wie once oder signal. |
Rückgabewert
addEventListener gibt nichts zurück. Der Handler erhält ein XRInputSourceEvent.Beispiele
Gegriffenes Objekt beim Loslassen freigeben
const session = await navigator.xr.requestSession('immersive-vr');
const refSpace = await session.requestReferenceSpace('local');
const heldObjects = new Map(); // XRInputSource -> Objekt
session.addEventListener('squeezestart', (event) => {
// Stellvertretend: Objekt in der Nähe des Controllers merken
heldObjects.set(event.inputSource, { name: 'Würfel' });
});
session.addEventListener('squeezeend', (event) => {
const obj = heldObjects.get(event.inputSource);
if (!obj) return;
// Pose zum Zeitpunkt des Loslassens bestimmen
const pose = event.frame.getPose(event.inputSource.gripSpace, refSpace);
if (pose) {
const { x, y, z } = pose.transform.position;
console.log(`${obj.name} losgelassen bei`, x.toFixed(2), y.toFixed(2), z.toFixed(2));
}
heldObjects.delete(event.inputSource);
});
Abgebrochene von abgeschlossenen Greifaktionen unterscheiden
const session = await navigator.xr.requestSession('immersive-vr');
const completed = new WeakSet();
session.addEventListener('squeeze', (event) => {
completed.add(event.inputSource); // Aktion erfolgreich abgeschlossen
});
session.onsqueezeend = (event) => {
const hand = event.inputSource.handedness; // 'left' | 'right' | 'none'
if (completed.has(event.inputSource)) {
console.log(`Greifen (${hand}) abgeschlossen`);
completed.delete(event.inputSource);
} else {
console.log(`Greifen (${hand}) abgebrochen oder Controller getrennt`);
}
};
Listener beim Sitzungsende entfernen
const session = await navigator.xr.requestSession('immersive-vr');
const onSqueezeEnd = (event) => {
console.log('squeezeend von', event.inputSource.handedness);
};
session.addEventListener('squeezeend', onSqueezeEnd);
session.addEventListener('end', () => {
session.removeEventListener('squeezeend', onSqueezeEnd);
// Alternativ bei on-Property: session.onsqueezeend = null;
console.log('Sitzung beendet, Listener entfernt');
}, { once: true });
// Wichtig · Fallstricke
- Greifaktion, nicht primäre Aktion:
squeezeendgehört zur Grip-Taste bzw. Greifgeste. Für Trigger oder Tippen sindselectstart,selectundselectendzuständig. - Nicht jede Eingabequelle kann greifen: Blickgesteuerte Eingaben, Bildschirm-Taps oder einfache Controller haben oft keine Greifaktion. Dann wird
squeezeendnie ausgelöst. - Immer aufräumen: Setze den Zustand in
squeezeendzurück, nicht insqueeze. Sonst bleiben Objekte nach einem Abbruch oder einer Trennung „kleben“. - Das
framegilt nur kurz:event.frameist nur innerhalb des Handlers aktiv. Speichere es nicht, um es später zu verwenden, denn dann wirftgetPose()einen Fehler.getPose()kann außerdemnullliefern, wenn das Tracking verloren ging. - Kompatibilität: WebXR braucht einen sicheren Kontext (HTTPS). Unterstützt wird es in Chromium-Browsern und im Meta Quest Browser, nicht in Firefox und Safari (Desktop). Prüfe die Unterstützung mit
navigator.xr?.isSessionSupported().