Skip to content

75. Three.js 的各种控制器 Controls

Published:

前面我们都是用轨道控制器 OrbitControls

2025-03-14 21.40.04.gif

那还有没有其他的控制器呢?

有的。

Three.js 文档里可以看到:

image.png

有这么多。

这节我们就把这些控制器都过一遍:

npx create-vite all-controls

image.png

进入项目,安装依赖:

npm install
npm install --save three
npm install --save-dev @types/three

改下 src/main.js

import './style.css';
import * as THREE from 'three';
import {
    OrbitControls
} from 'three/addons/controls/OrbitControls.js';
import mesh from './mesh.js';

const scene = new THREE.Scene();
scene.add(mesh);

const directionLight = new THREE.DirectionalLight(0xffffff, 2);
directionLight.position.set(500, 400, 300);
scene.add(directionLight);

const ambientLight = new THREE.AmbientLight();
scene.add(ambientLight);

const width = window.innerWidth;
const height = window.innerHeight;

const helper = new THREE.AxesHelper(500);
// scene.add(helper);

const camera = new THREE.PerspectiveCamera(60, width / height, 0.1, 10000);
camera.position.set(500, 500, 500);
camera.lookAt(0, 0, 0);

const renderer = new THREE.WebGLRenderer({
  antialias: true
});
renderer.setSize(width, height)

function render() {
    renderer.render(scene, camera);
    requestAnimationFrame(render);
}

render();

document.body.append(renderer.domElement);

const controls = new OrbitControls(camera, renderer.domElement);

创建 Scene、Light、Camera、Renderer。

改下 style.css

body {
  margin: 0;
}

创建 mesh.js

import * as THREE from 'three';

const planeGeometry = new THREE.PlaneGeometry(1000, 1000);
const planeMaterial = new THREE.MeshLambertMaterial({
    color: new THREE.Color('skyblue')
});

const plane = new THREE.Mesh(planeGeometry, planeMaterial);
plane.rotateX(- Math.PI / 2);
plane.position.y = -50;

const boxGeometry = new THREE.BoxGeometry(100, 100, 100);
const boxMaterial = new THREE.MeshLambertMaterial({
    color: new THREE.Color('orange')
});
const box = new THREE.Mesh(boxGeometry, boxMaterial);
box.name = 'box';

const box2 = box.clone();
box2.position.x = 200;
box2.name = 'box2';

const  group = new THREE.Group();
group.add(plane);
group.add(box);
group.add(box2);

export default group;

这个场景是我们前面画过的,一个平面,两个立方体。

给两个立方体加一个 name,后面查找用。

跑起来看一下:

npm run dev

image.png

2025-04-21 10.57.36.gif

可以用 OrbitControls 做相机的旋转、放缩,物体的平移。

我们换别的控制器试一下:

DragControls

DragControls 是在 3D 场景内实现拖动交互的控制器。

用一下:

image.png

const box1 = scene.getObjectByName('box');
const box2 = scene.getObjectByName('box2');

const controls = new DragControls([box1, box2], camera, renderer.domElement);

controls.addEventListener( 'dragstart', function(event) {
	event.object.material.color.set('lightgreen');
});

controls.addEventListener( 'dragend', function(event) {
	event.object.material.color.set('orange');
});

2025-04-21 11.28.40.gif

现在就可以拖动这俩立方体了。

不过现在它俩材质,会相互影响。

创建的时候 clone 一下:

image.png

box2.material = box.material.clone();

2025-04-21 11.32.29.gif

这样就好了。

此外,它还实现了 hover 事件:

image.png

controls.addEventListener('hoveron', (event) => {
  event.object.material.wireframe = true;
});

controls.addEventListener('hoveroff', (event) => {
  event.object.material.wireframe = false;
});

2025-04-21 12.27.55.gif

FlyControls

飞行控制器,相机有一种飞行的效果,可以通过键盘上下左右键控制往哪边转,然后按住左键前进,按住右键后退。

image.png

const controls = new FlyControls(camera, renderer.domElement);
controls.movementSpeed = 100;
controls.rollSpeed = Math.PI / 10;

const clock = new THREE.Clock();
function render() {
    controls.update(clock.getDelta());

    renderer.render(scene, camera);
    requestAnimationFrame(render);
}

movementSpeed 是调节移动速度,rollSpeed 是方向旋转速度。

在渲染循环里调用下 update,传入每帧渲染间隔 clock.getDelta

试下效果:

2025-04-21 13.03.44.gif

操作方式是这样的:

有一种飞行的感觉。

FirstPersonControls

FirstPersonControls 和 FlyControls 差不多,都是通过鼠标键盘控制前进、旋转。

但 FlyControls 是飞行,所以可以旋转超过 90 度。

而 FirstPersonControls 是行走,所以向上向下不能旋转超过 90 度。

其余的差不多。

换成 FirstPersonControls:

image.png

const controls = new FirstPersonControls(camera, renderer.domElement);
controls.movementSpeed = 100;

2025-04-21 15.59.22.gif

TransformControls

变换控制器,可以用来改变物体的 position、scale、rotation。

image.png

const box1 = scene.getObjectByName('box');
const box2 = scene.getObjectByName('box2');

const controls = new TransformControls(camera, renderer.domElement);
controls.attach(box1);
scene.add(controls.getHelper());

试下效果:

2025-04-21 14.14.25.gif

按住颜色方向的箭头,可以在 x(红)、y(绿)、z(蓝) 方向移动。

当然,也可以禁用掉某个方向的箭头:

image.png

controls.showX = false;

比如禁用掉 x 轴的箭头。

2025-04-21 14.17.30.gif

这样就只能在 YZ 平面移动了

大家可能注意到这里有个方块:

image.png

按住这个方块就是在 YZ 平面移动的意思。

2025-04-21 14.19.50.gif

此外,他还可以修改物体的 scale 和 rotation

image.png

controls.setMode('scale')

默认 mode 是 translate,分别修改为 rotate 和 scale 试试:

2025-05-14 09.04.56.gif

2025-05-14 09.05.23.gif

这样就是修改旋转角度和缩放了

MapControls

这个和 OrbitControls 一样,只不过默认操作方式不一样。

image.png

它默认是左键平移,右键旋转,和 OrbitControls 正好相反。

为啥叫 MapControls 呢,因为看地图的时候,基本都是这种交互。

我们用一下:

image.png

const controls = new MapControls(camera, renderer.domElement);

2025-04-21 16.03.59.gif

只是左键右键和 OrbitControls 有区别,其余一样。

案例代码上传了小册仓库

总结

这节我们过了一遍各种控制器:

不同场景下需要不同的交互方式,需要不同的控制器。

评论