Interactions
Build interactions that work across XR and non-XR web applications
@react-three/xr uses the same pointer events as @react-three/fiber, which allows building interactions that work on non-XR devices as well as XR devices. So, just like you'd expect from @react-three/fiber and everywhere else in react, interactions are built using
onPointerMove
onPointerCancel
onPointerDown
onPointerUp
onPointerEnter
onPointerLeave
onPointerOver
onPointerOut
onClick
onDblClick
onContextMenu
onWheel
The following example shows how to bind an onClick
handler to a mesh that gets executed when the mesh is clicked. This interaction will work in non-XR devices as well as in XR devices using @react-three/xr.
<mesh onClick={(event) => console.log("I've been clicked", event)}>
<boxGeometry />
</mesh>
The event
object provided to the onClick handler contains useful information, such as the intersection point
in world space.
The way pointer events are handled can be configured using the pointerEvents
, pointerEventsType
, and pointerEventsOrder
properties, which are available on all threejs objects.
The pointerEvents
property corresponds to the pointerEvents
property of CSS, which allows to completely disable pointer events for an element and its children. However, children can also re-enable pointer events by setting pointerEvents="auto"
.
The pointerEventsType
property allows to blacklist or whitelist pointer events for specific pointer types. For instance, setting pointerEventsType={{ deny: "grab" }}
prevents triggering pointer events from grabbing the object or any of its children.
The pointerEventsOrder
allows to overwrite the sorting order, similar to how renderOrder
allows to overwrite the rendering order in threejs. The default pointer events order is 0
. Setting it to a value greater than 0
will ensure it is intersected before anything with a lower pointer events order. Setting pointerEventsOrder
is helpful for building an interactive x-ray object that is always rendered above anything else and should, therefore, always be interacted with first. For instance, this can be used to build controls that are overlayed over the object that they control.
Pointer Capture
Another concept that @react-three/fiber leverages from the web is pointer captures. Pointer captures allow to force all consecutive events to be emitted to a specific object, even if that object is not intersected. This is useful for building dragging interactions without a complex global state. Typically, a pointer capture is set using object.setPointerCapture
in the event handler of onPointerDown
with the pointerId
of the pointer that pressed on the object.
.The following example illustrates how pointer events can be built to create a simple dragging implementation (that only works if the mesh is not inside a transformed group).
function DraggableCube() {
const isDraggingRef = useRef(false)
const meshRef = useRef<Mesh>(null)
return (
<mesh
ref={meshRef}
onPointerDown={(e) => {
if (isDraggingRef.current) {
return
}
isDraggingRef.current = true
meshRef.position.copy(e.point)
}}
onPointerMove={(e) => {
if (!isDraggingRef.current) {
return
}
meshRef.position.copy(e.point)
}}
onPointerUp={(e) => (isDraggingRef.current = false)}
>
<boxGeometry />
</mesh>
)
}