Signatur
Beschreibung
Die Eigenschaft inputSources einer XRSession liefert ein XRInputSourceArray. Darin steht für jedes Eingabegerät, das gerade mit dem XR-Gerät verbunden ist, ein XRInputSource-Objekt: Handcontroller, erkannte Hände (Hand-Tracking), Blicksteuerung (targetRayMode "gaze") oder Bildschirmberührungen in AR-Sitzungen auf dem Smartphone ("screen").
Das Array ist live. Es wird vom Browser laufend aktualisiert, sobald Geräte verbunden oder getrennt werden. Ein XRInputSourceArray ist array-ähnlich: Es hat length, einen Indexzugriff sowie forEach(), entries(), keys() und values() und lässt sich mit for...of durchlaufen. Methoden wie map() oder filter() fehlen jedoch. Für sie wandelt man es zuerst mit Array.from() um.
Typischerweise liest man inputSources in jedem Frame der Render-Schleife (requestAnimationFrame) aus. Dort ermittelt man mit frame.getPose(source.targetRaySpace, refSpace) die Zeigerichtung und mit source.gripSpace die Position der Hand. Knöpfe und Achsen fragt man über source.gamepad ab. Auf Änderungen der Geräteliste reagiert man über das Ereignis inputsourceschange.
Rückgabewert
XRInputSource pro verbundener Eingabequelle. Ist nichts verbunden, ist es leer (length === 0).Beispiele
Verbundene Eingabequellen nach Sitzungsstart auflisten
const startXR = async () => {
const session = await navigator.xr.requestSession('immersive-vr');
for (const source of session.inputSources) {
console.log(`Hand: ${source.handedness}, Modus: ${source.targetRayMode}, Profile: ${source.profiles.join(', ')}`);
}
console.log(`Anzahl: ${session.inputSources.length}`);
};
document.querySelector('#vr-button').addEventListener('click', startXR);
Zeigestrahl und Trigger in der Render-Schleife auswerten
const runLoop = async (session) => {
const refSpace = await session.requestReferenceSpace('local-floor');
const onFrame = (time, frame) => {
session.requestAnimationFrame(onFrame);
for (const source of frame.session.inputSources) {
const rayPose = frame.getPose(source.targetRaySpace, refSpace);
if (!rayPose) continue; // Tracking kurzzeitig verloren
const { x, y, z } = rayPose.transform.position;
const triggerPressed = source.gamepad?.buttons[0]?.pressed ?? false;
if (triggerPressed) {
console.log(`${source.handedness}: Trigger bei (${x.toFixed(2)}, ${y.toFixed(2)}, ${z.toFixed(2)})`);
}
}
};
session.requestAnimationFrame(onFrame);
};
Auf Geräteänderungen reagieren und nach Händigkeit filtern
const setupInputs = (session) => {
const controllers = new Map();
session.addEventListener('inputsourceschange', ({ added, removed }) => {
added.forEach((src) => controllers.set(src.handedness, src));
removed.forEach((src) => controllers.delete(src.handedness));
console.log('Aktiv:', [...controllers.keys()]);
});
// XRInputSourceArray hat kein filter() – vorher umwandeln
const handTracked = Array.from(session.inputSources).filter((s) => s.hand);
console.log(`Hand-Tracking-Quellen: ${handTracked.length}`);
};
// Wichtig · Fallstricke
- Live-Objekt: Wer den Inhalt einmalig in eine eigene Variable kopiert, hat danach veraltete Daten. Man sollte die Eigenschaft deshalb in jedem Frame neu lesen oder auf
inputsourceschangereagieren. - Kein echtes Array:
map(),filter()undfind()gibt es nicht. Vorher mitArray.from()oder dem Spread-Operator umwandeln. - Kurz nach dem Start oft leer: Direkt nach
requestSession()kann das Array noch leer sein, weil die Controller erst später gemeldet werden. Dafür istinputsourceschangezuverlässiger. - Instabile Objekte: Wird ein Gerät getrennt und wieder verbunden, entsteht ein neues
XRInputSource-Objekt. Referenzen auf das alte Objekt sollte man inremovedfreigeben. source.gamepadist bei Blick- oder Bildschirm-Eingaben oftnull. Deshalb optionale Verkettung (?.) verwenden. Für einfache Klicks eignen sich die Sitzungsereignisseselectundsqueezebesser als das Abfragen der Knöpfe in jedem Frame.- Kompatibilität: WebXR braucht einen sicheren Kontext (HTTPS). Unterstützt wird es vor allem von Chromium-Browsern und dem Meta Quest Browser. Firefox (Desktop) und Safari bieten keine bzw. nur eingeschränkte Unterstützung (visionOS). Vorher mit
navigator.xr?.isSessionSupported()prüfen. - Nach
session.end()sind die Eingabequellen nicht mehr gültig.