- name
- r3f-interaction
- description
- React Three Fiber interaction - pointer events, controls, gestures, selection. Use when handling user input, implementing click detection, adding camera controls, or creating interactive 3D experiences.
# React Three Fiber Interaction
## Quick Start
```tsx
import { Canvas } from '@react-three/fiber'
import { OrbitControls } from '@react-three/drei'
function InteractiveMesh() {
return (
<mesh
onClick={(e) => console.log('Clicked!', e.point)}
onPointerOver={(e) => console.log('Hover')}
onPointerOut={(e) => console.log('Unhover')}
>
<boxGeometry />
<meshStandardMaterial color="hotpink" />
</mesh>
)
}
export default function App() {
return (
<Canvas>
<ambientLight />
<InteractiveMesh />
<OrbitControls />
</Canvas>
)
}
```
## Pointer Events
R3F provides built-in pointer events on mesh elements.
### Available Events
```tsx
<mesh
// Click events
onClick={(e) => {}} // Click (pointerdown + pointerup on same object)
onDoubleClick={(e) => {}} // Double click
onContextMenu={(e) => {}} // Right click
// Pointer events
onPointerDown={(e) => {}} // Pointer pressed
onPointerUp={(e) => {}} // Pointer released
onPointerMove={(e) => {}} // Pointer moved while over object
onPointerOver={(e) => {}} // Pointer enters object
onPointerOut={(e) => {}} // Pointer leaves object
onPointerEnter={(e) => {}} // Pointer enters object (no bubbling)
onPointerLeave={(e) => {}} // Pointer leaves object (no bubbling)
onPointerMissed={(e) => {}} // Click that missed all objects
// Wheel
onWheel={(e) => {}} // Mouse wheel
// Touch
onPointerCancel={(e) => {}} // Touch cancelled
>
<boxGeometry />
<meshStandardMaterial />
</mesh>
```
### Event Object
```tsx
function InteractiveMesh() {
const handleClick = (event) => {
// Stop propagation to parent objects
event.stopPropagation()
// Event properties
console.log({
object: event.object, // The mesh that was clicked
point: event.point, // World coordinates of intersection
distance: event.distance, // Distance from camera
face: event.face, // Intersected face
faceIndex: event.faceIndex, // Face index
uv: event.uv, // UV coordinates at intersection
normal: event.normal, // Face normal
camera: event.camera, // Current camera
ray: event.ray, // Ray used for intersection
intersections: event.intersections, // All intersections
nativeEvent: event.nativeEvent, // Original DOM event
delta: event.delta, // Click distance (useful for drag detection)
})
}
return (
<mesh onClick={handleClick}>
<boxGeometry />
<meshStandardMaterial />
</mesh>
)
}
```
### Hover Effects
```tsx
import { useState } from 'react'
function HoverableMesh() {
const [hovered, setHovered] = useState(false)
return (
<mesh
onPointerOver={(e) => {
e.stopPropagation()
setHovered(true)
document.body.style.cursor = 'pointer'
}}
onPointerOut={(e) => {
setHovered(false)
document.body.style.cursor = 'default'
}}
scale={hovered ? 1.2 : 1}
>
<boxGeometry />
<meshStandardMaterial color={hovered ? 'hotpink' : 'orange'} />
</mesh>
)
}
```
### Selective Raycasting
```tsx
// Disable raycasting for specific objects
<mesh raycast={() => null}>
<boxGeometry />
<meshStandardMaterial />
</mesh>
// Or use layers
<mesh
layers={1} // Only raycast against layer 1
onClick={() => console.log('clicked')}
>
<boxGeometry />
<meshStandardMaterial />
</mesh>
```
## Camera Controls
### OrbitControls
```tsx
import { OrbitControls } from '@react-three/drei'
function Scene() {
return (
<>
<mesh>
<boxGeometry />
<meshStandardMaterial />
</mesh>
<OrbitControls
makeDefault // Use as default controls
enableDamping // Smooth movement
dampingFactor={0.05}
enableZoom={true}
enablePan={true}
enableRotate={true}
autoRotate={false}
autoRotateSpeed={2}
minDistance={2}
maxDistance={50}
minPolarAngle={0} // Top limit
maxPolarAngle={Math.PI / 2} // Horizon limit
minAzimuthAngle={-Math.PI / 4} // Left limit
maxAzimuthAngle={Math.PI / 4} // Right limit
target={[0, 1, 0]} // Look-at point
/>
</>
)
}
```
### OrbitControls with Ref
```tsx
import { OrbitControls } from '@react-three/drei'
import { useRef, useEffect } from 'react'
function Scene() {
const controlsRef = useRef()
useEffect(() => {
// Access controls methods
if (controlsRef.current) {
controlsRef.current.reset()
controlsRef.current.target.set(0, 1, 0)
controlsRef.current.update()
}
}, [])
return <OrbitControls ref={controlsRef} />
}
```
### MapControls
Top-down map-style controls.
```tsx
import { MapControls } from '@react-three/drei'
<MapControls
enableDamping
dampingFactor={0.05}
screenSpacePanning={false} // Pan in world space
maxPolarAngle={Math.PI / 2}
/>
```
### FlyControls
Free-flying camera controls.
```tsx
import { FlyControls } from '@react-three/drei'
<FlyControls
movementSpeed={10}
rollSpeed={Math.PI / 24}
dragToLook
/>
```
### FirstPersonControls
FPS-style controls.
```tsx
import { FirstPersonControls } from '@react-three/drei'
<FirstPersonControls
movementSpeed={10}
lookSpeed={0.1}
lookVertical
/>
```
### PointerLockControls
Lock pointer for FPS games.
```tsx
import { PointerLockControls } from '@react-three/drei'
import { useRef } from 'react'
function Scene() {
const controlsRef = useRef()
return (
<>
<PointerLockControls ref={controlsRef} />
{/* Click to lock pointer */}
<mesh onClick={() => controlsRef.current?.lock()}>
<planeGeometry args={[10, 10]} />
<meshBasicMaterial color="green" />
</mesh>
</>
)
}
```
### CameraControls
Advanced camera controls with smooth transitions.
```tsx
import { CameraControls } from '@react-three/drei'
import { useRef } from 'react'
function Scene() {
const controlsRef = useRef()
const focusOnObject = async () => {
// Smooth transition to target
await controlsRef.current?.setLookAt(
5, 3, 5, // Camera position
0, 0, 0, // Look-at target
true // Enable transition
)
}
return (
<>
<CameraControls ref={controlsRef} />
<mesh onClick={focusOnObject}>
<boxGeometry />
<meshStandardMaterial color="red" />
</mesh>
</>
)
}
```
### TrackballControls
Unconstrained rotation controls.
```tsx
import { TrackballControls } from '@react-three/drei'
<TrackballControls
rotateSpeed={2.0}
zoomSpeed={1.2}
panSpeed={0.8}
staticMoving={true}
/>
```
### ArcballControls
Arc-based rotation controls.
```tsx
import { ArcballControls } from '@react-three/drei'
<ArcballControls
enableAnimations
dampingFactor={25}
/>
```
## Transform Controls
Gizmo for moving/rotating/scaling objects.
```tsx
import { TransformControls, OrbitControls } from '@react-three/drei'
import { useRef, useState } from 'react'
function Scene() {
const meshRef = useRef()
const [mode, setMode] = useState('translate')
const orbitRef = useRef()
return (
<>
<OrbitControls ref={orbitRef} makeDefault />
<TransformControls
object={meshRef}
mode={mode} // 'translate' | 'rotate' | 'scale'
space="local" // 'local' | 'world'
onMouseDown={() => {
// Disable orbit while transforming
if (orbitRef.current) orbitRef.current.enabled = false
}}
onMouseUp={() => {
if (orbitRef.current) orbitRef.current.enabled = true
}}
/>
<mesh ref={meshRef}>
<boxGeometry />
<meshStandardMaterial color="orange" />
</mesh>
{/* Mode switching buttons in HTML */}
<div className="controls">
<button onClick={() => setMode('translate')}>Move</button>
<button onClick={() => setMode('rotate')}>Rotate</button>
<button onClick={() => setMode('scale')}>Scale</button>
</div>
</>
)
}
```
### PivotControls
Alternative transform gizmo with pivot point.
```tsx
import { PivotControls } from '@react-three/drei'
function Scene() {
return (
<PivotControls
anchor={[0, 0, 0]} // Anchor point
depthTest={false} // Always visible
lineWidth={2} // Axis line width
axisColors={['red', 'green', 'blue']}
scale={1} // Gizmo scale
fixed={false} // Fixed screen size
>
<mesh>
<boxGeometry />
<meshStandardMaterial color="orange" />
</mesh>
</PivotControls>
)
}
```
## Drag Controls
### useDrag from @use-gesture/react
```bash
npm install @use-gesture/react
```
```tsx
import { useDrag } from '@use-gesture/react'
import { useSpring, animated } from '@react-spring/three'
import { useThree } from '@react-three/fiber'
function DraggableMesh() {
const { size, viewport } = useThree()
const aspect = size.width / viewport.width
const [spring, api] = useSpring(() => ({
position: [0, 0, 0],
config: { mass: 1, tension: 280, friction: 60 }
}))
const bind = useDrag(({ movement: [mx, my], down }) => {
api.start({
position: down ? [mx / aspect, -my / aspect, 0] : [0, 0, 0]
})
})
return (
<animated.mesh {...bind()} position={spring.position}>
<boxGeometry />
<meshStandardMaterial color="hotpink" />
</animated.mesh>
)
}
```
### DragControls (Drei)
```tsx
import { DragControls, OrbitControls } from '@react-three/drei'
import { useRef } from 'react'
function Scene() {
const meshRef = useRef()
const orbitRef = useRef()
return (
<>
<OrbitControls ref={orbitRef} makeDefault />
<DragControls
onDragStart={() => {
if (orbitRef.current) orbitRef.current.enabled = false
}}
onDragEnd={() => {
if (orbitRef.current) orbitRef.current.enabled = true
}}
>
Ver no GitHub