init
This commit is contained in:
259
.opencode/skills/threejs/references/10-controls.md
Normal file
259
.opencode/skills/threejs/references/10-controls.md
Normal file
@@ -0,0 +1,259 @@
|
||||
# Camera Controls (Addons)
|
||||
|
||||
Interactive camera navigation systems.
|
||||
|
||||
## OrbitControls (Most Common)
|
||||
|
||||
Orbit camera around a target:
|
||||
|
||||
```javascript
|
||||
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
|
||||
|
||||
const controls = new OrbitControls(camera, renderer.domElement);
|
||||
|
||||
// Target point
|
||||
controls.target.set(0, 0, 0);
|
||||
|
||||
// Damping (smooth motion)
|
||||
controls.enableDamping = true;
|
||||
controls.dampingFactor = 0.05;
|
||||
|
||||
// Zoom limits
|
||||
controls.minDistance = 5;
|
||||
controls.maxDistance = 50;
|
||||
|
||||
// Rotation limits
|
||||
controls.minPolarAngle = 0; // radians
|
||||
controls.maxPolarAngle = Math.PI / 2; // prevent going below ground
|
||||
controls.minAzimuthAngle = -Math.PI / 4; // horizontal limit
|
||||
controls.maxAzimuthAngle = Math.PI / 4;
|
||||
|
||||
// Behavior
|
||||
controls.enablePan = true;
|
||||
controls.enableZoom = true;
|
||||
controls.enableRotate = true;
|
||||
controls.autoRotate = true;
|
||||
controls.autoRotateSpeed = 2.0;
|
||||
|
||||
// Mouse buttons
|
||||
controls.mouseButtons = {
|
||||
LEFT: THREE.MOUSE.ROTATE,
|
||||
MIDDLE: THREE.MOUSE.DOLLY,
|
||||
RIGHT: THREE.MOUSE.PAN
|
||||
};
|
||||
|
||||
// In animation loop (required if damping enabled)
|
||||
function animate() {
|
||||
controls.update();
|
||||
renderer.render(scene, camera);
|
||||
requestAnimationFrame(animate);
|
||||
}
|
||||
|
||||
// Events
|
||||
controls.addEventListener('change', () => {
|
||||
renderer.render(scene, camera);
|
||||
});
|
||||
```
|
||||
|
||||
## MapControls
|
||||
|
||||
Bird's-eye map navigation (like OrbitControls but different mouse behavior):
|
||||
|
||||
```javascript
|
||||
import { MapControls } from 'three/addons/controls/MapControls.js';
|
||||
|
||||
const controls = new MapControls(camera, renderer.domElement);
|
||||
controls.enableDamping = true;
|
||||
controls.screenSpacePanning = false;
|
||||
controls.maxPolarAngle = Math.PI / 2;
|
||||
|
||||
// Mouse buttons
|
||||
controls.mouseButtons = {
|
||||
LEFT: THREE.MOUSE.PAN,
|
||||
MIDDLE: THREE.MOUSE.DOLLY,
|
||||
RIGHT: THREE.MOUSE.ROTATE
|
||||
};
|
||||
```
|
||||
|
||||
## FirstPersonControls
|
||||
|
||||
FPS-style camera movement:
|
||||
|
||||
```javascript
|
||||
import { FirstPersonControls } from 'three/addons/controls/FirstPersonControls.js';
|
||||
|
||||
const controls = new FirstPersonControls(camera, renderer.domElement);
|
||||
|
||||
controls.movementSpeed = 10;
|
||||
controls.lookSpeed = 0.1;
|
||||
controls.lookVertical = true;
|
||||
controls.constrainVertical = true;
|
||||
controls.verticalMin = 1.0;
|
||||
controls.verticalMax = 2.0;
|
||||
|
||||
// Requires delta time
|
||||
const clock = new THREE.Clock();
|
||||
function animate() {
|
||||
const delta = clock.getDelta();
|
||||
controls.update(delta);
|
||||
renderer.render(scene, camera);
|
||||
requestAnimationFrame(animate);
|
||||
}
|
||||
```
|
||||
|
||||
## FlyControls
|
||||
|
||||
Free-form flying navigation:
|
||||
|
||||
```javascript
|
||||
import { FlyControls } from 'three/addons/controls/FlyControls.js';
|
||||
|
||||
const controls = new FlyControls(camera, renderer.domElement);
|
||||
|
||||
controls.movementSpeed = 10;
|
||||
controls.rollSpeed = Math.PI / 24;
|
||||
controls.autoForward = false;
|
||||
controls.dragToLook = false;
|
||||
|
||||
const clock = new THREE.Clock();
|
||||
function animate() {
|
||||
const delta = clock.getDelta();
|
||||
controls.update(delta);
|
||||
renderer.render(scene, camera);
|
||||
requestAnimationFrame(animate);
|
||||
}
|
||||
```
|
||||
|
||||
## PointerLockControls
|
||||
|
||||
Locked pointer FPS controls:
|
||||
|
||||
```javascript
|
||||
import { PointerLockControls } from 'three/addons/controls/PointerLockControls.js';
|
||||
|
||||
const controls = new PointerLockControls(camera, renderer.domElement);
|
||||
|
||||
// Lock pointer on click
|
||||
renderer.domElement.addEventListener('click', () => {
|
||||
controls.lock();
|
||||
});
|
||||
|
||||
controls.addEventListener('lock', () => {
|
||||
console.log('Locked');
|
||||
});
|
||||
|
||||
controls.addEventListener('unlock', () => {
|
||||
console.log('Unlocked');
|
||||
});
|
||||
|
||||
// Movement
|
||||
const velocity = new THREE.Vector3();
|
||||
const direction = new THREE.Vector3();
|
||||
|
||||
window.addEventListener('keydown', (event) => {
|
||||
switch (event.code) {
|
||||
case 'KeyW': moveForward = true; break;
|
||||
case 'KeyS': moveBackward = true; break;
|
||||
case 'KeyA': moveLeft = true; break;
|
||||
case 'KeyD': moveRight = true; break;
|
||||
}
|
||||
});
|
||||
|
||||
function animate() {
|
||||
if (controls.isLocked) {
|
||||
// Calculate movement
|
||||
direction.z = Number(moveForward) - Number(moveBackward);
|
||||
direction.x = Number(moveRight) - Number(moveLeft);
|
||||
direction.normalize();
|
||||
|
||||
controls.moveForward(direction.z * 10);
|
||||
controls.moveRight(direction.x * 10);
|
||||
}
|
||||
renderer.render(scene, camera);
|
||||
requestAnimationFrame(animate);
|
||||
}
|
||||
```
|
||||
|
||||
## TrackballControls
|
||||
|
||||
Intuitive rotation (no gimbal lock):
|
||||
|
||||
```javascript
|
||||
import { TrackballControls } from 'three/addons/controls/TrackballControls.js';
|
||||
|
||||
const controls = new TrackballControls(camera, renderer.domElement);
|
||||
|
||||
controls.rotateSpeed = 1.0;
|
||||
controls.zoomSpeed = 1.2;
|
||||
controls.panSpeed = 0.8;
|
||||
controls.staticMoving = true;
|
||||
controls.dynamicDampingFactor = 0.3;
|
||||
|
||||
function animate() {
|
||||
controls.update();
|
||||
renderer.render(scene, camera);
|
||||
requestAnimationFrame(animate);
|
||||
}
|
||||
```
|
||||
|
||||
## ArcballControls
|
||||
|
||||
3D rotation with virtual ball metaphor:
|
||||
|
||||
```javascript
|
||||
import { ArcballControls } from 'three/addons/controls/ArcballControls.js';
|
||||
|
||||
const controls = new ArcballControls(camera, renderer.domElement, scene);
|
||||
|
||||
controls.enablePan = true;
|
||||
controls.enableZoom = true;
|
||||
controls.enableRotate = true;
|
||||
controls.cursorZoom = true;
|
||||
|
||||
function animate() {
|
||||
controls.update();
|
||||
renderer.render(scene, camera);
|
||||
requestAnimationFrame(animate);
|
||||
}
|
||||
```
|
||||
|
||||
## Controls Comparison
|
||||
|
||||
**OrbitControls**: Product viewers, 3D models, general use
|
||||
**MapControls**: Top-down maps, strategy games
|
||||
**FirstPersonControls**: Architectural walkthroughs
|
||||
**FlyControls**: Space navigation, creative tools
|
||||
**PointerLockControls**: FPS games
|
||||
**TrackballControls**: CAD applications
|
||||
**ArcballControls**: Scientific visualization
|
||||
|
||||
## Common Patterns
|
||||
|
||||
```javascript
|
||||
// Disable controls during UI interaction
|
||||
transformControls.addEventListener('dragging-changed', (event) => {
|
||||
orbitControls.enabled = !event.value;
|
||||
});
|
||||
|
||||
// Reset camera position
|
||||
function resetCamera() {
|
||||
controls.reset();
|
||||
}
|
||||
|
||||
// Animate camera to position
|
||||
function moveCameraTo(position, target) {
|
||||
gsap.to(camera.position, {
|
||||
duration: 1,
|
||||
x: position.x,
|
||||
y: position.y,
|
||||
z: position.z,
|
||||
onUpdate: () => controls.update()
|
||||
});
|
||||
gsap.to(controls.target, {
|
||||
duration: 1,
|
||||
x: target.x,
|
||||
y: target.y,
|
||||
z: target.z
|
||||
});
|
||||
}
|
||||
```
|
||||
Reference in New Issue
Block a user