模型旋转
代码拼出一栋楼,拖拽时转的是模型本身,转盘与地面保持不动。
说明
建筑由十几个立方体与圆柱拼成,全部挂在一个 Group 下。
指针按下后记录起点,移动时把横纵位移累加到
group.rotation.y 与 rotation.x,
俯仰限制在 −0.22 到 0.68 弧度之间,避免翻到楼底。
旋转中心就是 Group 的原点,而楼体在建模时都以地面为基准向上叠,
因此旋转看起来像在转盘上转。滚轮只改变相机到目标的距离,
与旋转互不干扰。
| 设置 | 数值 | 说明 |
|---|---|---|
| 楼体高度 | 约 20 个单位 | 主体 12、屋顶 0.8、天线 6 |
| 横向灵敏度 | 0.008 弧度 / 像素 | 拖拽 100 像素约转 46 度 |
| 俯仰范围 | −0.22 – 0.68 弧度 | 约 −13 到 39 度 |
| 自动旋转 | 0.0045 弧度 / 帧 | 拖拽期间自动暂停 |
| 相机距离 | 18 – 85 | 滚轮缩放时限制范围 |
| 阴影 | PCFSoft,1024 贴图 | 阴影相机范围 ±26 |
转模型还是转相机
两种做法视觉上都像「环绕观察」,含义完全不同。
转相机(OrbitControls)保持物体世界坐标不变,
适合查看场景整体;转模型则改变物体的变换矩阵,
会影响它的阴影方向、与地面的相对关系,以及拾取后的世界坐标。
展示单体建筑、导出旋转动画、对齐模型朝向时应当转模型; 浏览大范围三维场景时应当转相机。示例把相机固定, 正是为了让旋转的影响面更清楚。
拖拽实现要点
canvas.addEventListener('pointerdown', function (event) {
dragging = true;
lastX = event.clientX;
lastY = event.clientY;
canvas.setPointerCapture(event.pointerId);
});
canvas.addEventListener('pointermove', function (event) {
if (!dragging) return;
const dx = event.clientX - lastX;
const dy = event.clientY - lastY;
lastX = event.clientX;
lastY = event.clientY;
model.rotation.y += dx * 0.008;
model.rotation.x = THREE.MathUtils.clamp(
model.rotation.x + dy * 0.004, -0.22, 0.68
);
});
setPointerCapture 让指针移出画布后仍持续接收事件,
否则快速拖拽到窗口外会中断旋转。移动端需要禁止页面滚动,
通常再给画布加 touch-action: none。
旋转的层级选择
如果模型内部已经有动画在改某个节点的旋转,把整个模型再套进一个外层
Group 做交互旋转,两者互不覆盖。
直接改同一节点的 rotation 会让动画与交互互相打架,
表现为姿态突然跳变。
旋转不改变地理朝向。rotation.y 是场景内部的姿态,
与正北方向没有绑定关系。要让建筑按真实方位角摆放,
需要在载入时把方位角换算成绕 Y 轴的弧度。
three.js 是三维渲染库,不是地图库。它负责把几何画到屏幕上, 方位、比例尺与坐标系都由项目自己定义。