Signatur
Beschreibung
Die Eigenschaft jointName gehört zum Interface XRJointSpace aus dem WebXR Hand Input Module. Jeder XRJointSpace steht für genau eines der 25 Gelenke einer verfolgten Hand. jointName gibt an, um welches Gelenk es sich handelt. Der Wert ist ein String aus der Aufzählung XRHandJoint.
Mögliche Werte sind "wrist" sowie für den Daumen "thumb-metacarpal", "thumb-phalanx-proximal", "thumb-phalanx-distal" und "thumb-tip". Für jeden weiteren Finger (index-finger, middle-finger, ring-finger, pinky-finger) gibt es jeweils die Endungen -metacarpal, -phalanx-proximal, -phalanx-intermediate, -phalanx-distal und -tip.
Nützlich ist die Eigenschaft vor allem, wenn du über alle Gelenke eines XRHand-Objekts iterierst und je nach Gelenk verschieden reagieren willst. Beispiele: Nur die Fingerspitzen als Kugeln darstellen, Gesten wie ein „Pinch“ (Daumen- und Zeigefingerspitze berühren sich) erkennen oder Gelenke gezielt einem Knochen in einem 3D-Handmodell zuordnen.
Der Name wird beim Erzeugen des Space festgelegt und ändert sich danach nicht. Die eigentliche Position und Ausrichtung des Gelenks erhältst du nicht über jointName. Dafür brauchst du in jedem Frame XRFrame.getJointPose() oder XRFrame.fillPoses().
Rückgabewert
XRHandJoint, z. B. "wrist", "thumb-tip" oder "pinky-finger-phalanx-distal".Beispiele
Alle Gelenknamen einer Hand ausgeben
// Voraussetzung: Die Session wurde mit optionalFeatures: ['hand-tracking'] angefordert
const session = await navigator.xr.requestSession('immersive-vr', {
optionalFeatures: ['hand-tracking']
});
session.addEventListener('inputsourceschange', (event) => {
for (const source of event.added) {
if (!source.hand) continue; // kein Hand-Tracking für diese Eingabequelle
// XRHand verhält sich wie eine Map: Gelenkname -> XRJointSpace
for (const jointSpace of source.hand.values()) {
console.log(source.handedness, jointSpace.jointName);
}
}
});
Nur Fingerspitzen pro Frame darstellen
const onXRFrame = (time, frame) => {
const session = frame.session;
for (const source of session.inputSources) {
if (!source.hand) continue;
for (const jointSpace of source.hand.values()) {
// Über jointName nur die Spitzen herausfiltern
if (!jointSpace.jointName.endsWith('-tip')) continue;
const pose = frame.getJointPose(jointSpace, refSpace);
if (pose) {
const { x, y, z } = pose.transform.position;
drawSphere(jointSpace.jointName, x, y, z, pose.radius);
}
}
}
session.requestAnimationFrame(onXRFrame);
};
const drawSphere = (name, x, y, z, radius) => {
// Platzhalter für den eigenen Renderer
console.log(`${name}: (${x.toFixed(3)}, ${y.toFixed(3)}, ${z.toFixed(3)}) r=${radius}`);
};
Pinch-Geste erkennen
const PINCH_DISTANCE = 0.015; // 1,5 cm
const isPinching = (frame, hand, refSpace) => {
// Direkter Zugriff per Gelenkname über XRHand.get()
const thumb = hand.get('thumb-tip');
const index = hand.get('index-finger-tip');
console.assert(thumb.jointName === 'thumb-tip');
const thumbPose = frame.getJointPose(thumb, refSpace);
const indexPose = frame.getJointPose(index, refSpace);
if (!thumbPose || !indexPose) return false;
const a = thumbPose.transform.position;
const b = indexPose.transform.position;
const distance = Math.hypot(a.x - b.x, a.y - b.y, a.z - b.z);
return distance < PINCH_DISTANCE;
};
// Wichtig · Fallstricke
- Experimentell: Das Hand Input Module wird bisher nur von wenigen Browsern unterstützt, etwa dem Meta Quest Browser, Chrome mit WebXR-Flag oder OpenXR-Runtime und Safari auf visionOS. Desktop-Firefox und die meisten mobilen Browser unterstützen es nicht. Prüfe vorher mit
navigator.xr?.isSessionSupported()undsource.hand, ob es verfügbar ist. - Die API steht nur in einem sicheren Kontext (HTTPS) zur Verfügung. Außerdem muss die Session das Feature
'hand-tracking'angefordert haben, sonst istinputSource.handnull. jointNameist schreibgeschützt. Eine Zuweisung wird still ignoriert, im Strict Mode löst sie einenTypeErroraus.- Der Name sagt nichts darüber aus, ob das Gelenk gerade verfolgt wird.
getJointPose()kann trotzdemundefinedliefern, z. B. wenn die Hand verdeckt ist. - Die Namen sind feste Kleinbuchstaben-Strings mit Bindestrich. Tippfehler wie
"index-tip"führen beihand.get()zuundefined. Nutze am besten Konstanten. - Performance: Wenn du viele Gelenke pro Frame abfragst, ist
XRFrame.fillPoses()effizienter als einzelnegetJointPose()-Aufrufe.