经纬志
首页 / three.js / 使用示例

模型旋转

代码拼出一栋楼,拖拽时转的是模型本身,转盘与地面保持不动。

three.js 0.160.0 · 更新 2026-10-28
源码与运行效果
运行效果

说明

建筑由十几个立方体与圆柱拼成,全部挂在一个 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 是三维渲染库,不是地图库。它负责把几何画到屏幕上, 方位、比例尺与坐标系都由项目自己定义。