交互规范
相机控制
使用 OrbitControls 时,必须完成以下配置:
- 启用阻尼:
controls.enableDamping = true,并在每帧调用controls.update()。 - 限制缩放距离:设置
minDistance与maxDistance。推荐取值范围为 1.2×R ~ 8×R(R 为模型外接半径),防止相机进入模型内部或远离至不可见。 - 限制俯仰角:设置
maxPolarAngle略小于 π(推荐 0.95π),避免相机翻转至模型下方。 - 初始取景:相机距离约为 3.2×R,采用小幅俯视角度,保证默认视角下模型完整可见且具有立体感。
滚轮事件处理
如自行接管缩放逻辑,必须使用原生非被动事件监听:
element.addEventListener('wheel', handler, { passive: false })
并在处理函数中调用 preventDefault()。框架层的滚轮绑定通常是被动监听(React 的 onWheel、Vue 未显式退出 passive 的 @wheel),无法阻止页面默认滚动行为,会导致用户在 3D 区域缩放时整个页面随之滚动。OrbitControls 自带的滚轮处理已满足此要求。
环境动效
- 推荐实现闲置漂浮动效:使用互为非整数倍频率的 Lissajous 摆动,避免出现可感知的循环点;幅度应保持轻微。
- 推荐为拖拽交互接入欠阻尼弹簧模型,避免指针位移与旋转 1:1 直接映射。以效果为准而不是以数值为准:松手后有一次看得见的回弹过冲,约 0.3 秒停稳。(刚度 70 / 阻尼 5.5 是某一种逐帧积分写法下的取值——这两个数只有配上你用的公式才有意义,所以按效果调。)
- 环境动效的变换必须挂载在不随参数重建的持久父节点(rig)上(见参数变化与重建),确保参数变化时动效连续。
- 所有动效必须响应
prefers-reduced-motion媒体查询,在用户开启减弱动效时全部禁用。
帧循环
- 每帧更新(动效、弹簧、
controls.update())必须在 rAF 循环里直接改Object3D的变换;框架渲染仅由 Config 变化触发,不得在帧循环里触发框架状态更新(React 的setState、Vue 的响应式赋值、Svelte 的 store 写入——都会一秒重渲 60 次并掉帧)。 - 页签处于隐藏状态时(
document.visibilityState === 'hidden')rAF 会暂停,所有动效数值都冻结在最后一帧。任何要读这些数值的场景——自动化检查、调试——都得先把页签切到前台,否则读到的是旧值,会误判成动效坏了。
文本选择
3D 舞台容器必须设置 user-select: none,防止拖拽旋转时选中页面文本。