Files
english/.opencode/skills/threejs/references/08-interaction.md
2026-04-12 01:06:31 +07:00

268 lines
6.6 KiB
Markdown

# Interaction & Picking
Handle user input and object interaction.
## Mouse/Touch Raycasting
Detect which object user clicked:
```javascript
const raycaster = new THREE.Raycaster();
const mouse = new THREE.Vector2();
const clickableObjects = []; // array of meshes
function onPointerMove(event) {
// Normalize mouse coordinates (-1 to +1)
mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
// Update raycaster
raycaster.setFromCamera(mouse, camera);
// Find intersections
const intersects = raycaster.intersectObjects(clickableObjects);
if (intersects.length > 0) {
// Hover effect
intersects[0].object.material.emissive.setHex(0xff0000);
}
}
function onClick(event) {
mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
raycaster.setFromCamera(mouse, camera);
const intersects = raycaster.intersectObjects(clickableObjects);
if (intersects.length > 0) {
const object = intersects[0].object;
console.log('Clicked:', object.name);
console.log('Point:', intersects[0].point);
}
}
renderer.domElement.addEventListener('pointermove', onPointerMove);
renderer.domElement.addEventListener('click', onClick);
```
## DragControls (Addon)
Drag objects with mouse:
```javascript
import { DragControls } from 'three/addons/controls/DragControls.js';
const controls = new DragControls(objectsArray, camera, renderer.domElement);
// Events
controls.addEventListener('dragstart', (event) => {
orbitControls.enabled = false; // disable camera controls during drag
event.object.material.emissive.set(0xaaaaaa);
});
controls.addEventListener('drag', (event) => {
console.log(event.object.position);
});
controls.addEventListener('dragend', (event) => {
orbitControls.enabled = true;
event.object.material.emissive.set(0x000000);
});
```
## TransformControls (Addon)
Interactive 3D gizmo for translate/rotate/scale:
```javascript
import { TransformControls } from 'three/addons/controls/TransformControls.js';
const transformControls = new TransformControls(camera, renderer.domElement);
scene.add(transformControls);
// Attach to object
transformControls.attach(mesh);
// Switch modes
transformControls.setMode('translate'); // or 'rotate', 'scale'
// Switch space
transformControls.setSpace('world'); // or 'local'
// Events
transformControls.addEventListener('change', () => {
renderer.render(scene, camera);
});
transformControls.addEventListener('dragging-changed', (event) => {
orbitControls.enabled = !event.value; // disable orbit during transform
});
// Keyboard shortcuts
window.addEventListener('keydown', (event) => {
switch (event.key) {
case 'g': transformControls.setMode('translate'); break;
case 'r': transformControls.setMode('rotate'); break;
case 's': transformControls.setMode('scale'); break;
case 'x': transformControls.showX = !transformControls.showX; break;
case 'Escape': transformControls.detach(); break;
}
});
```
## Selection Box (Addon)
Box selection for multiple objects:
```javascript
import { SelectionBox } from 'three/addons/interactive/SelectionBox.js';
import { SelectionHelper } from 'three/addons/interactive/SelectionHelper.js';
const selectionBox = new SelectionBox(camera, scene);
const helper = new SelectionHelper(renderer, 'selectBox');
let isSelecting = false;
renderer.domElement.addEventListener('pointerdown', (event) => {
isSelecting = true;
selectionBox.startPoint.set(
(event.clientX / window.innerWidth) * 2 - 1,
-(event.clientY / window.innerHeight) * 2 + 1,
0.5
);
});
renderer.domElement.addEventListener('pointermove', (event) => {
if (isSelecting) {
selectionBox.endPoint.set(
(event.clientX / window.innerWidth) * 2 - 1,
-(event.clientY / window.innerHeight) * 2 + 1,
0.5
);
const allSelected = selectionBox.select();
console.log('Selected:', allSelected.length);
}
});
renderer.domElement.addEventListener('pointerup', () => {
isSelecting = false;
});
```
## Keyboard Input
Handle keyboard controls:
```javascript
const keysPressed = {};
window.addEventListener('keydown', (event) => {
keysPressed[event.key] = true;
});
window.addEventListener('keyup', (event) => {
keysPressed[event.key] = false;
});
// In animation loop
function animate() {
const speed = 0.1;
if (keysPressed['w']) object.position.z -= speed;
if (keysPressed['s']) object.position.z += speed;
if (keysPressed['a']) object.position.x -= speed;
if (keysPressed['d']) object.position.x += speed;
renderer.render(scene, camera);
requestAnimationFrame(animate);
}
```
## Pointer Lock (First Person)
Lock pointer for FPS controls:
```javascript
import { PointerLockControls } from 'three/addons/controls/PointerLockControls.js';
const controls = new PointerLockControls(camera, renderer.domElement);
// Lock on click
renderer.domElement.addEventListener('click', () => {
controls.lock();
});
controls.addEventListener('lock', () => {
console.log('Pointer locked');
});
controls.addEventListener('unlock', () => {
console.log('Pointer unlocked');
});
// Movement
const velocity = new THREE.Vector3();
const direction = new THREE.Vector3();
function animate() {
if (controls.isLocked) {
// Apply movement
controls.moveForward(velocity.z);
controls.moveRight(velocity.x);
}
renderer.render(scene, camera);
requestAnimationFrame(animate);
}
```
## Object Highlighting
Visual feedback on hover/selection:
```javascript
let hoveredObject = null;
const originalEmissive = new THREE.Color();
function onPointerMove(event) {
mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
raycaster.setFromCamera(mouse, camera);
const intersects = raycaster.intersectObjects(scene.children, true);
// Reset previous
if (hoveredObject) {
hoveredObject.material.emissive.copy(originalEmissive);
hoveredObject = null;
}
// Highlight new
if (intersects.length > 0) {
hoveredObject = intersects[0].object;
originalEmissive.copy(hoveredObject.material.emissive);
hoveredObject.material.emissive.setHex(0x555555);
}
renderer.domElement.style.cursor = hoveredObject ? 'pointer' : 'default';
}
```
## Tooltips & UI Overlays
Show HTML tooltip at 3D position:
```javascript
function updateTooltip(object3D, text) {
const vector = object3D.position.clone();
vector.project(camera);
const x = (vector.x * 0.5 + 0.5) * window.innerWidth;
const y = (-vector.y * 0.5 + 0.5) * window.innerHeight;
tooltip.style.left = x + 'px';
tooltip.style.top = y + 'px';
tooltip.textContent = text;
}
```