Signatur
Beschreibung
Die Eigenschaft frame eines XRInputSourceEvent enthält ein XRFrame-Objekt. Es gibt den Zustand der XR-Welt genau zu dem Zeitpunkt wieder, an dem die Benutzereingabe stattfand. So kannst du die Position und Ausrichtung des Controllers, der Hand oder des Blickstrahls zum Zeitpunkt der Aktion bestimmen und musst nicht auf den nächsten Animationsframe warten.
Normalerweise rufst du im Handler event.frame.getPose(event.inputSource.targetRaySpace, referenceSpace) auf. Damit erhältst du den Zielstrahl, mit dem du zielst, Objekte auswählst, schießt oder Hit-Tests auswertest. Für Greifgesten (squeeze) nimmst du meist gripSpace, damit ein Objekt an der Hand ausgerichtet wird.
Wichtig: Dieses Frame ist ein sogenanntes Ereignis-Frame und kein Animationsframe. Es ist nur während der synchronen Ausführung des Event-Handlers aktiv. Danach sind seine Methoden nicht mehr nutzbar. Außerdem darfst du auf einem Ereignis-Frame kein getViewerPose() aufrufen, denn das ist nur in Callbacks von requestAnimationFrame() erlaubt.
Rückgabewert
XRFrame, in dem das Eingabeereignis ausgelöst wurde. Es ist nur innerhalb des Event-Handlers aktiv.Beispiele
Zielstrahl beim select-Ereignis auslesen
const session = await navigator.xr.requestSession('immersive-vr');
const refSpace = await session.requestReferenceSpace('local');
session.addEventListener('select', (event) => {
// Pose des Zielstrahls genau zum Zeitpunkt der Eingabe
const pose = event.frame.getPose(event.inputSource.targetRaySpace, refSpace);
if (!pose) return; // Tracking kann verloren sein
const { position, orientation } = pose.transform;
console.log('Strahl-Ursprung:', position.x.toFixed(2), position.y.toFixed(2), position.z.toFixed(2));
console.log('Hand:', event.inputSource.handedness);
});
Objekt beim Greifen (squeezestart) an die Hand heften
let grabbed = null;
session.addEventListener('squeezestart', (event) => {
const { inputSource, frame } = event;
if (!inputSource.gripSpace) return; // z. B. Blickeingabe ohne Grip
const gripPose = frame.getPose(inputSource.gripSpace, refSpace);
if (gripPose) {
grabbed = { source: inputSource, matrix: gripPose.transform.matrix };
console.log('Objekt gegriffen mit', inputSource.handedness);
}
});
session.addEventListener('squeezeend', () => {
grabbed = null;
console.log('Objekt losgelassen');
});
Fallstrick: Frame außerhalb des Handlers verwenden
let savedFrame = null;
session.addEventListener('select', (event) => {
savedFrame = event.frame; // Referenz speichern ...
// Richtig: Pose sofort auslesen und die Daten speichern
const pose = event.frame.getPose(event.inputSource.targetRaySpace, refSpace);
console.log('Sofort ausgelesen:', pose !== null);
setTimeout(() => {
try {
// ... aber hier ist das Frame nicht mehr aktiv
savedFrame.getPose(event.inputSource.targetRaySpace, refSpace);
} catch (err) {
console.log(err.name);
}
}, 0);
});
// Wichtig · Fallstricke
- Nur synchron gültig: Das Frame ist nur während der Ausführung des Handlers aktiv. Wenn du es später oder nach einem
awaitverwendest, werfen Methoden wiegetPose()einenInvalidStateError. Lies die benötigten Daten deshalb sofort aus und speichere sie, nicht das Frame selbst. - Kein
getViewerPose(): Auf Ereignis-Frames wirft der Aufruf einenInvalidStateError. Die Pose des Betrachters bekommst du nur im Callback vonXRSession.requestAnimationFrame(). getPose()kannnullzurückgeben, wenn das Tracking gerade verloren ist. Prüfe den Wert deshalb immer.- Nicht jede Eingabequelle hat einen
gripSpace, z. B. Blick- oder Bildschirmeingaben (targetRayMode'gaze'bzw.'screen'). - Kompatibilität: WebXR funktioniert nur in sicheren Kontexten (HTTPS). Unterstützt wird es in Chromium-Browsern (Chrome, Edge, Samsung Internet), im Meta Quest Browser und in Safari auf visionOS, aber nicht in Firefox. Prüfe die Unterstützung vorher mit
navigator.xr?.isSessionSupported().