开篇提要
摘要: Day 1 我们搭好了金三角,立方体自己转了起来。但有个硬伤——你只能从一个角度看它,像被钉在墙上的监控画面。
今天给相机“解绑”,让立方体跟着你的鼠标转:
- 鼠标光标交互——鼠标指哪,相机看哪,最直观的“眼神操控”
- 轨道控制器——拖拽、旋转、缩放,像玩手办一样自由
- 阻尼效果——带惯性的拖拽,手感丝滑不卡顿
- 坐标轴辅助器——红X绿Y蓝Z,转晕了也不怕迷路
1.鼠标光标交互
1.const cursor = { x: 0, y: 0 }
这里我们创建一个全局对象cursor,用于保存处理后的鼠标位置,初始值为(0,0),代表鼠标位于屏幕正中心。
2.window.addEventListener('mousemove', (e) => { console.log(e.clientX); })
给整个浏览器窗口添加mousemove鼠标时间监听器,这个监听器可以监听当鼠标发生移动的时候,回调函数会被出发,事件对象e中包含了鼠标当前的位置信息。
在函数中打印console.log(e.clientX);,表示打印x轴的实时坐标。
我们可以看到目前我们设置的画布宽度为800
// sizes const sizes = { width: 800, height: 600 }
鼠标越往左越趋近于0,越往右越趋近800,鼠标移出画布比800还大。但是我们通常不这样做。
我们只需要用公式:当前x坐标的像素位置 / 画布总宽度。
结果范围:
当鼠标在最左侧时:0 / 800 = 0
当鼠标在最右侧时:800 / 800 = 1
当鼠标在中间时:400 / 800 = 0.5
为什么这么做?
因为在 Three.js 或 WebGL 开发中,直接使用像素坐标(如 0~800)通常不方便进行数学计算或映射到 3D 空间。因为我们需要进行数学计算让鼠标的坐标规范在0到1之间。这样我们就不用考虑分辨率,无论画布大小如何变化,(比如改成1920*1080),cursor.x始终保持在0到1之间,逻辑通用。
注意: 这里不再赘述y轴的坐标获取,只需要注意在y轴坐标的时候我们需要给公式取反
cursor.y = -(e.clientY / sizes.height - 0.5);
因为在屏幕坐标系中y轴向下为正,而three.js世界中是向上为正,如果没有没有给结果取反,这意味着当你鼠标向上移动(clientY 变小),cursor.y 会变小(负数方向);而在 Three.js 中,通常希望鼠标向上时物体或相机向上移动(正数方向),如果 cursor.y 为负,相机就会向下移动。这与直觉可能相反(通常鼠标上移,视角上移)。
这样我们就获得了坐标范围,接下来我们可以在tick函数中做点什么: 我们在tick函数中更新相机的位置,大家可以来猜想一下,我们应该移动哪个轴可以观察立方体?这很容易知道,是x轴和y轴,我们可以通过移动移动相机在x轴和y轴上的位置观察立方体,给相应的值乘3,来增加移动的幅度。
` // 更新相机位置
camera.position.x = cursor.x * 3;
camera.position.y = cursor.y * 3;
`
当我们更新相机后,需要让我们的相机注视着立方体,确保我们的相机以立方体为中心。只需要调用
// 注视立方体 camera.lookAt(mesh.position)
mesh.position 获取的是立方体在 3D 空间中的坐标向量,默认是(0, 0, 0)。
可以看到效果很好。
虽然效果很好,但是我们看不到立方体的后面和上面以及下面,因此我们需要引入今天的第二个知识:
2.轨道控制器
2.1我们如何实例化轨道控制器呢?
实际上没那么简单,因为轨道控制器OrbitControls 不能像核心库(如 THREE.Scene, THREE.Mesh)那样直接从 'three'包中引入,主要原因是,核心库和扩展库分离:
- 核心库 (
three) : 只包含渲染 3D 场景最基础、最核心的功能(如场景、相机、几何体、材质、渲染器等)。保持核心库轻便是为了性能和通用性。 - 扩展库 (
three/examples/jsm/...) :OrbitControls属于“控件”或“辅助工具”,它不是渲染 3D 图形所必需的。因此,Three.js 团队将其放在examples目录下,作为附加模块提供。
我们项目中使用的是Three.js r150+ 版本推荐的新方式:
import { OrbitControls } from 'three/examples/jsm/Addons.js';
旧版本:
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js'
2.2创建轨道控制器
// 创建控制器 const controls = new THREE.OrbitControls(camera, canvas);
你需要给这个类2个值,一个是你的相机,因为轨道控制器要更新相机,另一个是DOM元素,是指页面中某个要作为参考的元素,以便我们把鼠标事件放在上面,就像你刷手机——手指必须在屏幕上滑动,屏幕才会跟着动。滑到手机边框上?没反应。轨道控制器的第二个参数 canvas 就是这个作用:告诉控制器“只有用户在这个画布范围内拖拽,相机才响应”,然而我们canvas,就是我们当前的画布。
此时我们可以进行移动鼠标让立方体旋转,按鼠标右键移动立方体位置,用滚轮来放大缩小。
3.阻尼效果
虽然我们已经可以随意玩转我们的立方体了,但是在上图可以看到,我鼠标移动很快,它转的也很快,停止也很快,完全是鼠标移动方式,我们可以添加阻尼,让他看起来更加平滑自然。
小贴士: 阻尼就是给相机加上“惯性”。
就像你在超市推购物车——猛推一把然后松手:
- 没有阻尼:购物车瞬间停下,像撞上了一堵墙(立方体转起来会很“愣”)
- 有阻尼:购物车会继续滑一小段,慢慢停下(手感丝滑,有过度感)
加入阻尼很简单:
// 加入阻尼并开启 controls.enableDamping = true
并且在tick函数中更新控制器:
controls.update()
我们会得到这样的效果:
⚠️ 大坑提醒:开启阻尼后,必须在动画循环里每帧调用 controls.update() ,否则阻尼不生效。
4.坐标轴辅助器
3d世界的指南针,一句话解释:
坐标轴辅助器就是帮你分清“东西南北”的工具——红X、绿Y、蓝Z,一眼看懂三维方向。
- 红色 = X轴(左/右)
- 绿色 = Y轴(上/下)
- 蓝色 = Z轴(前/后)
我们添加它看看效果:
// 创建坐标轴辅助器 //实例化坐标辅助器,单位为2,代表坐标轴长度为2个单位,2倍的立方体宽度 const axesHelper = new THREE.AxesHelper(2) //将创建好的坐标轴辅助器添加到当前场景 scene 中。 scene.add(axesHelper)
这就是坐标轴辅助器,简直是为 3D 新手量身定制的「指南针」——红 X、绿 Y、蓝 Z,一眼就能分清上下左右前后,再也不会转着转着就迷路啦。
Day 1 的立方体像挂在墙上的监控画面——你只能从一个角度看它。
今天的四步,给你的相机解了绑:
- 鼠标滑动,视角跟随
- 拖拽旋转,任意角度
- 惯性阻尼,手感丝滑
- 坐标辅助,永不迷路
现在,你的立方体从“监控录像”变成了“可触摸的展品”。