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

5.8 KiB

Camera Controls (Addons)

Interactive camera navigation systems.

OrbitControls (Most Common)

Orbit camera around a target:

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):

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:

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:

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:

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):

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:

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

// 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
  });
}