Signatur
Beschreibung
Das Ereignis selectstart gehört zu den Eingabe-Ereignissen der WebXR Device API. Es wird ausgelöst, wenn eine Eingabequelle (XRInputSource) ihre Primäraktion beginnt. Das kann der Trigger eines VR-Controllers sein, eine Pinch-Geste beim Hand-Tracking, ein Blick mit Bestätigung oder ein Tippen auf den Bildschirm in einer AR-Sitzung auf dem Smartphone. Du kannst es mit addEventListener("selectstart", …) oder über die Eigenschaft XRSession.onselectstart abonnieren.
Zusammen mit selectend und select bildet es einen vollständigen Aktionszyklus: selectstart → (Aktion läuft) → select (nur bei erfolgreichem Abschluss) → selectend. selectstart eignet sich deshalb für Interaktionen, die beim Drücken beginnen und bis zum Loslassen andauern, etwa Greifen und Ziehen von Objekten, Laden eines Schusses oder Zeichnen im Raum.
Der Handler bekommt ein XRInputSourceEvent. Dessen Eigenschaft inputSource nennt die auslösende Eingabequelle, frame ist ein XRFrame, der nur während des Handlers gültig ist. Über frame.getPose(inputSource.targetRaySpace, referenceSpace) fragst du ab, wohin der Zeigestrahl im Moment des Drückens zeigt.
Parameter
| Name | Typ | Default | Beschreibung |
|---|---|---|---|
| $type Pflicht | string | Der Ereignistyp, hier immer "selectstart". |
|
| $handler Pflicht | (event: XRInputSourceEvent) => void | Callback, der ein XRInputSourceEvent mit den Eigenschaften inputSource (XRInputSource) und frame (XRFrame) erhält. |
|
| $options | boolean | AddEventListenerOptions | false | Übliche Listener-Optionen wie once, signal oder capture. |
Rückgabewert
addEventListener gibt nichts zurück. Der Handler wird mit einem XRInputSourceEvent aufgerufen.Beispiele
Primäraktion erkennen und Zeigestrahl auslesen
const session = await navigator.xr.requestSession('immersive-vr');
const refSpace = await session.requestReferenceSpace('local');
session.addEventListener('selectstart', (event) => {
const { inputSource, frame } = event;
// Pose des Zeigestrahls zum Zeitpunkt des Drückens
const pose = frame.getPose(inputSource.targetRaySpace, refSpace);
if (pose) {
const { x, y, z } = pose.transform.position;
console.log(`selectstart (${inputSource.handedness}) bei`, x.toFixed(2), y.toFixed(2), z.toFixed(2));
}
});
Greifen und Ziehen mit selectstart/selectend
const grabbing = new Map(); // inputSource -> Objekt
const onSelectStart = (event) => {
const target = findObjectUnderRay(event.inputSource, event.frame); // eigene Trefferprüfung
if (target) {
grabbing.set(event.inputSource, target);
target.highlight = true;
}
};
const onSelectEnd = (event) => {
const obj = grabbing.get(event.inputSource);
if (obj) {
obj.highlight = false;
grabbing.delete(event.inputSource);
}
};
session.addEventListener('selectstart', onSelectStart);
session.addEventListener('selectend', onSelectEnd);
// Im Render-Loop: gegriffene Objekte der Controller-Pose folgen lassen
const onFrame = (time, frame) => {
for (const [source, obj] of grabbing) {
const pose = frame.getPose(source.gripSpace ?? source.targetRaySpace, refSpace);
if (pose) obj.matrix = pose.transform.matrix;
}
session.requestAnimationFrame(onFrame);
};
session.requestAnimationFrame(onFrame);
on-Property verwenden und Listener wieder entfernen
// Variante über die Event-Handler-Eigenschaft
session.onselectstart = (event) => {
console.log('Aktion gestartet von', event.inputSource.targetRayMode);
};
// Variante mit AbortController zum sauberen Entfernen
const controller = new AbortController();
session.addEventListener('selectstart', (e) => console.log('Start'), { signal: controller.signal });
session.addEventListener('end', () => {
controller.abort(); // Listener entfernen
session.onselectstart = null; // Property-Handler entfernen
});
// Wichtig · Fallstricke
- Frame nur im Handler gültig:
event.frameist inaktiv, sobald der Handler endet. Speicherst du ihn und rufst spätergetPose()auf, wird einInvalidStateErrorgeworfen. Brauchst du die Pose später, lies sie sofort aus. - Nicht jedes selectstart endet mit select: Wird die Aktion abgebrochen, z. B. weil der Controller die Verbindung verliert oder die Sitzung endet, folgt nur
selectendohneselect. Räume Zustand wie gegriffene Objekte deshalb immer inselectendauf. - Mehrere Eingabequellen: Beide Hände oder Controller können gleichzeitig Aktionen auslösen. Ordne Zustände immer über
event.inputSourcezu, nie global. - Transiente Eingaben: Bei
targetRayMode === "screen"(AR-Touch) wird die Eingabequelle erst unmittelbar vorselectstarterzeugt und verschwindet nachselectendwieder. - Sekundäraktionen wie der Grip-Button lösen stattdessen
squeezestartaus. - Kompatibilität: WebXR braucht einen sicheren Kontext (HTTPS) und wird von Chromium-Browsern sowie dem Meta Quest Browser unterstützt. Firefox und Safari auf dem Desktop bieten keine Unterstützung, visionOS nur eingeschränkt. Prüfe vorher mit
navigator.xr?.isSessionSupported().