Signatur
Beschreibung
Das Ereignis squeeze gehört zu einer Gruppe von Ereignissen, mit denen WebXR die primäre Squeeze-Aktion einer Eingabequelle meldet. Auf den meisten Controllern ist das der seitliche Greiftaster („Grip“). Bei Hand-Tracking kann es auch eine Greifgeste sein. Der Ablauf ist immer derselbe: Zuerst kommt squeezestart, wenn die Aktion beginnt. Wurde sie erfolgreich abgeschlossen, folgt squeeze und direkt danach squeezeend. Wird die Aktion abgebrochen, etwa weil der Controller die Verbindung verliert, kommt nur squeezeend und kein squeeze.
Der Handler erhält ein XRInputSourceEvent. Es enthält die auslösende Eingabequelle (inputSource) und einen XRFrame (frame). Mit diesem Frame lässt sich die Position des Controllers zum Zeitpunkt des Ereignisses abfragen. Er gilt aber nur während des Handler-Aufrufs.
Nutzen Sie squeeze für einzelne, abgeschlossene Aktionen wie „Menü öffnen“ oder „Objekt einmal greifen bzw. ablegen“. Wenn etwas solange passieren soll, wie der Taster gedrückt ist (z. B. ein Objekt festhalten und mitbewegen), eignet sich die Kombination aus squeezestart und squeezeend besser.
Sie können den Handler mit addEventListener("squeeze", …) registrieren oder alternativ über die Eigenschaft XRSession.onsqueeze setzen.
Parameter
| Name | Typ | Default | Beschreibung |
|---|---|---|---|
| $type Pflicht | string | Der Ereignisname "squeeze". |
|
| $handler Pflicht | (event: XRInputSourceEvent) => void | Callback-Funktion, die ein XRInputSourceEvent erhält. Darin stehen event.inputSource (die auslösende XRInputSource) und event.frame (ein XRFrame, der nur während des Aufrufs gültig ist). |
|
| $options | boolean|AddEventListenerOptions | false | Die üblichen Listener-Optionen wie once oder signal. |
Rückgabewert
addEventListener gibt nichts zurück. Der Rückgabewert des Handlers wird ignoriert.Beispiele
Squeeze-Aktion mit addEventListener auswerten
const startXR = async () => {
if (!navigator.xr || !(await navigator.xr.isSessionSupported('immersive-vr'))) {
console.log('immersive-vr wird nicht unterstützt');
return;
}
const session = await navigator.xr.requestSession('immersive-vr');
const refSpace = await session.requestReferenceSpace('local');
session.addEventListener('squeeze', (event) => {
const { inputSource, frame } = event;
// Die Pose ist nur innerhalb des Handlers abrufbar
const pose = frame.getPose(inputSource.gripSpace ?? inputSource.targetRaySpace, refSpace);
console.log(`Squeeze an Hand: ${inputSource.handedness}`);
if (pose) {
const { x, y, z } = pose.transform.position;
console.log(`Position: ${x.toFixed(2)}, ${y.toFixed(2)}, ${z.toFixed(2)}`);
}
});
};
// requestSession braucht eine Nutzeraktion
document.querySelector('#enter-vr').addEventListener('click', startXR);
Menü per onsqueeze umschalten und Handler wieder entfernen
let menuVisible = false;
const toggleMenu = (event) => {
// Nur auf den linken Controller reagieren
if (event.inputSource.handedness !== 'left') return;
menuVisible = !menuVisible;
console.log(`Menü ${menuVisible ? 'geöffnet' : 'geschlossen'}`);
};
const setupSession = (session) => {
// Variante über die on-Property
session.onsqueeze = toggleMenu;
session.addEventListener('end', () => {
session.onsqueeze = null; // Handler entfernen
console.log('Sitzung beendet, Handler entfernt');
});
};
Vollständigen Ablauf squeezestart → squeeze → squeezeend protokollieren
const logSqueezeLifecycle = (session) => {
const controller = new AbortController();
const { signal } = controller;
const pressed = new Set();
session.addEventListener('squeezestart', (e) => {
pressed.add(e.inputSource);
console.log('squeezestart');
}, { signal });
session.addEventListener('squeeze', () => {
console.log('squeeze (erfolgreich abgeschlossen)');
}, { signal });
session.addEventListener('squeezeend', (e) => {
pressed.delete(e.inputSource);
console.log('squeezeend');
}, { signal });
// Alle drei Listener auf einmal entfernen
session.addEventListener('end', () => controller.abort(), { once: true });
};
// Wichtig · Fallstricke
- Nur für abgeschlossene Aktionen: Wird die Aktion abgebrochen, bleibt
squeezeaus. Aufräumarbeiten (z. B. ein festgehaltenes Objekt loslassen) gehören deshalb insqueezeend, nicht insqueeze. - Frame nur kurz gültig:
event.frameist nur während des Handler-Aufrufs aktiv. Ein späterer Aufruf vonframe.getPose(), etwa in einemsetTimeoutoder nachawait, wirft einenInvalidStateError. - Nicht jedes Gerät hat eine Squeeze-Aktion: Einfache Controller, Blick- oder Bildschirmeingabe (z. B. Handheld-AR) bieten oft keine. Planen Sie deshalb eine Alternative über
selectein. - Auch in transienten Sitzungen: Das Ereignis kann ebenso in
immersive-ar-Sitzungen auftreten. - Kompatibilität: WebXR steht nur in sicheren Kontexten (HTTPS) zur Verfügung und funktioniert vor allem in Chromium-Browsern und im Meta Quest Browser. Firefox und Safari (Desktop) unterstützen es nicht, Safari auf der Apple Vision Pro nur eingeschränkt. Prüfen Sie vorher mit
navigator.xr?.isSessionSupported(). - Für Tastenwerte, die nicht zur primären Squeeze-Aktion gehören, fragen Sie im Render-Loop
inputSource.gamepad.buttonsab.