其他控制器
本教程共 40 篇 · 第 22 篇 · 更新于 2026-08-14 · 约 9 分钟阅读
本节目标:认识 OrbitControls 之外的五种官方控制器,知道每种控制器的适用场景和基本用法。学完你能按需求挑选合适的控制器,并组合出”拖拽 + 观察”的编辑体验。
控制器全家桶
OrbitControls 适合观察,但很多应用需要别的交互。官方 addons 的 controls 目录下,还有这些控制器:
| 控制器 | 用途 | 交互方式 |
|---|---|---|
| TransformControls | 在场景里拖拽物体 | 鼠标拖拽操作杆 |
| PointerLockControls | 第一人称视角(FPS) | 鼠标锁定 + 键盘移动 |
| FlyControls | 飞行漫游,无重力约束 | 键盘 + 鼠标 |
| TrackballControls | 轨迹球,可任意翻转 | 鼠标三键 |
| ArcballControls | 无万向锁的轨道旋转 | 鼠标 + 触屏 |
它们都在 three/addons/controls/ 目录下,引入方式和 OrbitControls 一样。
更新机制不一样
学控制器之前,先搞懂一个区别:有的控制器需要每帧更新,有的不需要。用错就”控制器没反应”。
| 控制器 | 每帧 update() | 说明 |
|---|---|---|
| OrbitControls | 阻尼 / 自动旋转时需要 | 其他情况可不调 |
| TransformControls | 不需要 | 事件驱动,拖拽即生效 |
| PointerLockControls | 不需要 | 事件驱动,移动自己算 |
| FlyControls | 需要,传 delta(秒) | 不调不动 |
| TrackballControls | 需要 | 不调不动 |
| ArcballControls | 需要 | 不调不动 |
TransformControls 和 PointerLockControls 是事件驱动的,操作立刻生效。其余几个每帧都要调 update(),FlyControls 还要把帧间隔时间传进去。记住这张表,后面每个都不踩坑。
TransformControls:拖拽物体
编辑类应用里,需要直接拖动场景中的物体。TransformControls 在物体上显示一个操作杆(gizmo),拖拽即可改变位置、旋转或缩放:
import { TransformControls } from 'three/addons/controls/TransformControls.js';
const transform = new TransformControls(camera, renderer.domElement);
transform.attach(cube); // 绑定要操作的物体
scene.add(transform.getHelper()); // 操作杆本体要加进场景
三种模式,对应快捷键 W / E / R:
transform.setMode('translate'); // W 键:移动
transform.setMode('rotate'); // E 键:旋转
transform.setMode('scale'); // R 键:缩放
世界坐标、局部坐标切换,以及吸附对齐也都内置:
transform.setSpace('local'); // 按物体自身坐标轴
transform.setTranslationSnap(1); // 移动吸附到整数单位
transform.setRotationSnap(Math.PI / 12); // 旋转每 15° 一格
transform.setScaleSnap(0.25); // 缩放按 0.25 步进
它和 OrbitControls 同时使用时有个经典坑:拖操作杆时,视角也会跟着转。解决方法是监听 dragging-changed 事件,拖拽期间禁用轨道控制器。完整示例:
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<title>TransformControls + OrbitControls</title>
<style>
* { margin: 0; padding: 0; }
html, body { height: 100%; }
canvas { display: block; }
</style>
<script type="importmap">
{
"imports": {
"three": "https://cdn.jsdelivr.net/npm/three@0.185.0/build/three.module.js",
"three/addons/": "https://cdn.jsdelivr.net/npm/three@0.185.0/examples/jsm/"
}
}
</script>
</head>
<body>
<script type="module">
import * as THREE from 'three';
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
import { TransformControls } from 'three/addons/controls/TransformControls.js';
const scene = new THREE.Scene();
scene.background = new THREE.Color(0x1a1a2e);
scene.add(new THREE.GridHelper(10, 10));
const camera = new THREE.PerspectiveCamera(
60, window.innerWidth / window.innerHeight, 0.1, 100
);
camera.position.set(4, 3, 6);
const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
document.body.appendChild(renderer.domElement);
const light = new THREE.DirectionalLight(0xffffff, 3);
light.position.set(5, 10, 5);
scene.add(light);
// 观察用:轨道控制器
const orbit = new OrbitControls(camera, renderer.domElement);
orbit.enableDamping = true;
// 编辑用:变换控制器
const cube = new THREE.Mesh(
new THREE.BoxGeometry(1.5, 1.5, 1.5),
new THREE.MeshStandardMaterial({ color: 0x00d4ff })
);
scene.add(cube);
const transform = new TransformControls(camera, renderer.domElement);
transform.attach(cube);
scene.add(transform.getHelper());
// 拖操作杆时暂停轨道控制
transform.addEventListener('dragging-changed', (event) => {
orbit.enabled = !event.value;
});
// W / E / R 切换模式
window.addEventListener('keydown', (event) => {
if (event.key === 'w') transform.setMode('translate');
if (event.key === 'e') transform.setMode('rotate');
if (event.key === 'r') transform.setMode('scale');
});
function animate() {
requestAnimationFrame(animate);
orbit.update();
renderer.render(scene, camera);
}
animate();
</script>
</body>
</html>
Note操作杆(getHelper 返回的对象)必须加入场景,否则看不见也拖不动。官方 misc_controls_transform 示例就是”TransformControls + OrbitControls”的组合。
PointerLockControls:第一人称
PointerLockControls 基于浏览器的 Pointer Lock API:点击后鼠标指针被锁定,移动鼠标转动视角,适合 FPS 类游戏:
import { PointerLockControls } from 'three/addons/controls/PointerLockControls.js';
const controls = new PointerLockControls(camera, document.body);
scene.add(controls.object); // 相机作为场景子对象,方便整体移动
// 点击后锁定鼠标
document.addEventListener('click', () => controls.lock());
controls.addEventListener('lock', () => console.log('已锁定'));
controls.addEventListener('unlock', () => console.log('已解锁'));
它只负责转动视角,移动要靠键盘。官方 pointerlock 示例里,用 WASD 记录方向,每帧调用移动方法:
let moveForward = false;
let moveBackward = false;
let moveLeft = false;
let moveRight = false;
// keydown / keyup 里按 event.code 设置四个布尔值
const velocity = new THREE.Vector3();
const clock = new THREE.Clock();
function animate() {
requestAnimationFrame(animate);
const delta = clock.getDelta();
// 前进后退
velocity.z = (moveForward ? 1 : 0) - (moveBackward ? 1 : 0);
controls.moveForward(-velocity.z * 50 * delta);
// 左右平移
velocity.x = (moveRight ? 1 : 0) - (moveLeft ? 1 : 0);
controls.moveRight(velocity.x * 50 * delta);
renderer.render(scene, camera);
}
animate();
距离由速度乘以帧间隔时间算出,这样不同帧率下移动速度一致。moveForward 的负号是因为它按”前进方向”为正,算出来的速度向量方向相反。
Note指针锁定后,普通 mousemove 拿不到位移,必须靠控制器处理视角。进入游戏前的遮罩提示、按 Esc 退出后的恢复,都通过 lock / unlock 事件处理。完整套路参考官方 misc_controls_pointerlock 示例。
FlyControls:飞行漫游
FlyControls 模拟飞行器操作:WASD 前后左右移动,R/F 上升下降,Q/E 翻滚,方向键控制俯仰和偏航。没有”地面”概念,可以倒着飞,适合城市漫游、太空场景:
import { FlyControls } from 'three/addons/controls/FlyControls.js';
const controls = new FlyControls(camera, renderer.domElement);
controls.movementSpeed = 50; // 移动速度
controls.rollSpeed = 0.5; // 翻滚速度
controls.dragToLook = true; // 按住鼠标拖动才转视角
注意它的 update() 需要传时间间隔(秒),用 Clock 计算:
const clock = new THREE.Clock();
function animate() {
requestAnimationFrame(animate);
const delta = clock.getDelta();
controls.update(delta);
renderer.render(scene, camera);
}
animate();
autoForward 设为 true 时,不按键也会缓慢前进,像开了自动驾驶。
TrackballControls:轨迹球
TrackballControls 和 OrbitControls 很像,但不保持相机的”上”方向。你可以把视角转到天翻地覆,没有约束。左键旋转、中键缩放、右键平移,还支持 A / S / D 键:
import { TrackballControls } from 'three/addons/controls/TrackballControls.js';
const controls = new TrackballControls(camera, renderer.domElement);
controls.rotateSpeed = 1;
controls.zoomSpeed = 1.2;
controls.panSpeed = 0.8;
它每帧都要调用 update()。两个常用配置:
controls.staticMoving = false; // false 时带阻尼
controls.dynamicDampingFactor = 0.2; // 阻尼强度
Tip大多数场景用 OrbitControls 就够。TrackballControls 的优势是完全自由的视角,代价是用户容易转晕。需要”上下不颠倒”的场合别用它。
ArcballControls:无万向锁的轨道
ArcballControls 是较新的轨道控制器,内部用四元数计算旋转,没有万向锁问题。单指旋转、双指缩放平移,旋转带平滑惯性,手感接近建模软件。构造时多接收一个 scene 参数:
import { ArcballControls } from 'three/addons/controls/ArcballControls.js';
const controls = new ArcballControls(camera, renderer.domElement, scene);
controls.enableGrid = false; // 关闭辅助网格
controls.dampingFactor = 8; // 惯性强度
controls.minDistance = 1;
controls.maxDistance = 20;
几个特色方法:
controls.focus(cube); // 平滑聚焦到某个物体
controls.copyState(); // 复制当前视角状态
controls.pasteState(); // 粘贴刚才复制的视角
controls.reset(); // 复位
controls.setGizmosVisible(false); // 隐藏屏幕上的操作提示圆环
开启 enableAnimations(默认开启)时,旋转、缩放都带平滑过渡。enableFocus 开启时,双击物体可以直接聚焦过去。触屏上它的手势比 OrbitControls 更细腻,平板设备上体验好。
还有两个小控制器
DragControls 让物体直接跟着鼠标走,不需要操作杆,适合”拖拽摆放”这类简单需求。构造参数是物体数组:
import { DragControls } from 'three/addons/controls/DragControls.js';
const drag = new DragControls(objects, camera, renderer.domElement);
drag.addEventListener('dragstart', () => (orbit.enabled = false));
drag.addEventListener('dragend', () => (orbit.enabled = true));
事件有 dragstart / drag / dragend,拖拽时同样要暂停 OrbitControls,不然视角和物体一起乱动。
FirstPersonControls 是早期的第一人称方案,鼠标拖动控制视角、WASD 移动。不需要指针锁定,但交互偏旧,新项目建议直接用 PointerLockControls,这里不展开。
键盘操作一览
控制器大多内置键盘绑定,写操作说明时对照这张表:
| 控制器 | 按键 |
|---|---|
| OrbitControls | 方向键平移(需 listenToKeyEvents) |
| TransformControls | W / E / R 切换模式,Q 切换坐标系,X / Y / Z 锁轴,Shift 吸附 |
| PointerLockControls | 无内置移动键,移动代码自己写 |
| FlyControls | WASD 移动,R / F 升降,Q / E 翻滚,方向键俯仰偏航 |
| TrackballControls | A 旋转,S 缩放,D 平移 |
表里没有的按键行为,多半要自己监听 keydown 实现,控制器不会替你处理。
怎么选
一句话总结选择思路:
- 观察展示 → OrbitControls(上一章)
- 拖拽编辑物体 → TransformControls
- FPS 游戏 → PointerLockControls
- 自由飞行漫游 → FlyControls
- 完全自由的旋转 → TrackballControls 或 ArcballControls
控制器可以组合。TransformControls 配 OrbitControls 是最常见的编辑方案;PointerLockControls 的场景里一般不需要第二个控制器。
Note所有控制器都在监听 DOM 事件。页面销毁、切换场景时记得清理:OrbitControls、TransformControls、PointerLockControls、FlyControls 都有 dispose() 方法,调用后监听器会被移除。ArcballControls 与 TrackballControls 也都有 dispose(),页面卸载时统一调用。