Signatur
Beschreibung
Die schreibgeschützte Eigenschaft gripSpace eines XRInputSource-Objekts gibt ein XRSpace zurück, dessen Ursprung und Ausrichtung der Handfläche bzw. dem Griff eines Controllers folgen. Mit diesem Raum rendert man virtuelle Objekte so, als würde der Nutzer sie in der Hand halten, zum Beispiel ein Controller-Modell, ein Schwert, eine Taschenlampe oder ein Werkzeug.
Laut Spezifikation liegt der Ursprung in der Mitte der geschlossenen Faust. Die −Z-Achse zeigt dabei nach vorn durch die Faust, also in die Richtung, in die ein gehaltener Stab zeigen würde. Die +X-Achse steht senkrecht zum Handrücken, bei der rechten Hand zeigt sie also nach außen und bei der linken Hand nach innen. Die +Y-Achse zeigt nach oben, grob in Richtung Daumen. Damit unterscheidet sich gripSpace von targetRaySpace: Dieser beschreibt den Zielstrahl zum Auswählen und Zeigen, gripSpace dagegen die Lage der Hand.
Für die konkrete Pose übergibt man den Raum in jedem Frame an XRFrame.getPose(space, baseSpace), meist zusammen mit dem Referenzraum der Sitzung. Der Rückgabewert ist ein XRPose mit einer transform-Matrix, oder null, wenn das Tracking gerade verloren ist.
Bei Eingabequellen, die man nicht halten kann, ist die Eigenschaft null. Das betrifft blickgesteuerte Eingaben (targetRayMode === "gaze") und Bildschirm-Tippen ("screen") auf Smartphones.
Rückgabewert
XRSpace, der die Griffpose der Eingabequelle beschreibt, oder null, wenn das Gerät nicht gehalten werden kann (z. B. bei Blick- oder Bildschirmeingabe).Beispiele
Controller-Modell in der Hand rendern
const session = await navigator.xr.requestSession('immersive-vr');
const refSpace = await session.requestReferenceSpace('local-floor');
const onXRFrame = (time, frame) => {
session.requestAnimationFrame(onXRFrame);
for (const source of session.inputSources) {
// Blick- oder Bildschirmeingaben haben keinen gripSpace
if (!source.gripSpace) continue;
const pose = frame.getPose(source.gripSpace, refSpace);
if (pose) {
// 4x4-Matrix (Float32Array) für die Render-Engine
drawControllerModel(source.handedness, pose.transform.matrix);
}
}
};
session.requestAnimationFrame(onXRFrame);
Griffposition und Zielstrahl vergleichen
const logInputPoses = (frame, refSpace) => {
for (const source of frame.session.inputSources) {
const grip = source.gripSpace
? frame.getPose(source.gripSpace, refSpace)
: null;
const ray = frame.getPose(source.targetRaySpace, refSpace);
if (grip && ray) {
const { x, y, z } = grip.transform.position;
console.log(`${source.handedness}: Griff bei (${x.toFixed(2)}, ${y.toFixed(2)}, ${z.toFixed(2)})`);
console.log('Strahl-Ursprung:', ray.transform.position);
}
}
};
Neue Eingabequellen auf gripSpace prüfen
session.addEventListener('inputsourceschange', (event) => {
for (const source of event.added) {
const holdable = source.gripSpace !== null;
console.log(`${source.targetRayMode} (${source.handedness}) – greifbar: ${holdable}`);
}
});
// Wichtig · Fallstricke
- Immer auf
nullprüfen: BeitargetRayMode"gaze"und"screen"(AR auf dem Smartphone) gibt es keinengripSpace. Ein ungeprüfter Aufruf vonframe.getPose(null, …)wirft einenTypeError. - Pose kann
nullsein: Auch wenn eingripSpacevorhanden ist, liefertgetPose()null, sobald das Tracking abreißt, etwa wenn der Controller außer Sicht ist. Modelle sollte man dann ausblenden oder an der letzten bekannten Position einfrieren. - Nicht mit
targetRaySpaceverwechseln: Für Zeigestrahlen und Treffertests nutzt mantargetRaySpace, für gehaltene ObjektegripSpace. Beide Räume können deutlich gegeneinander verschoben und gedreht sein. - Handtracking: Bei optisch verfolgten Händen (
source.hand) wirdgripSpaceaus den Gelenkdaten abgeleitet. Für genaue Fingerposen nutzt manXRHandmitframe.getJointPose(). - Nur in jedem Frame gültig: Posen immer innerhalb des
requestAnimationFrame-Callbacks derXRSessionabfragen. Ein außerhalb davon verwendetesXRFrameist ungültig und wirft einen Fehler. - Kompatibilität: WebXR braucht einen sicheren Kontext (HTTPS). Unterstützt wird es vor allem in Chromium-Browsern und im Meta Quest Browser. Firefox (Desktop) und Safari (macOS) unterstützen WebXR nicht. Vorher mit
navigator.xr?.isSessionSupported()prüfen.