Scanat 支持修改模型的缩放比例。比例变化后,模型尺寸会立即更新,同时相机也需要调整距离,保证用户仍然能看清模型。
PRD 对这件事的描述很简单:模型比例修改后,视角自动调整。
真正需要设计的是运动过程本身:模型和相机是同时变化,还是先后变化?相机什么时候开始移动?持续多久?使用什么缓动?这些问题很难通过静态设计稿确定,也没有一个可以直接套用的标准参数。
因此这次没有先把时间和缓动写死,而是先做一个可运行的 demo,把停顿时长和动画时长暴露成实时参数,用实际运动来比较不同方案。
模型缩放后的相机移动,本质上是一种被动补偿。
用户真正发起的操作是"修改模型比例"。模型尺寸变化是主要反馈,相机移动只是随后发生的视野修正。如果两者完全同步,用户很难分辨模型本身发生了什么变化;如果相机反应太晚,又会产生明显断层。
这和"重置视角"一类主动相机动画不同。后者来自用户明确的导航指令,可以立即开始移动;缩放后的相机补偿则需要先让模型变化被感知,再进入运镜。
| 维度 | 模型缩放后的相机补偿 | 重置视角 |
|---|---|---|
| 触发方式 | 模型状态变化后的被动反应 | 用户主动发起导航 |
| 开始前状态 | 通常静止 | 可能仍带有拖拽惯性 |
| 是否需要停顿 | 需要,让用户先看清模型变化 | 通常不需要 |
| 缓动重点 | 起点和终点都需要柔和 | 更强调接近终点时的减速 |
这里最重要的不是记住某条缓动曲线,而是先判断:这个相机运动为什么发生。 触发原因不同,时间关系和手感也应该不同。
最终采用三段式时序:模型立即缩放 → 停顿 0.4 秒 → 相机移动 1.5 秒。
停顿的作用是把两个变化拆开。用户先看到模型尺寸变化,再看到相机为新的尺寸重新调整距离。这样运动关系更容易理解,也减少了模型和视角同时变化带来的视觉混乱。
相机只沿当前观察方向调整与模型的距离,不改变观察角度。模型放大时相机后退,模型缩小时相机前移。整个过程保持为"同一视角下的推拉",而不是一次新的镜头切换。
缓动使用 easeInOutCubic。相机从静止状态开始,又在静止状态结束,因此起点和终点都需要平缓过渡。最初测试过 ease-out,但起步速度偏快,运动开始时会有明显的突兀感。
这次 demo 最有用的部分并不是复现最终动画,而是把原本写在代码里的数字变成了可以直接调整的参数。
停顿时长和相机动画时长都做成实时滑块。修改后可以立即重新触发动画,不需要反复改代码、刷新页面。
<input type="range" id="hold" min="0" max="2" step="0.1" value="0.4" />
<input type="range" id="dur" min="0.4" max="3" step="0.1" value="1.5" />0.4 秒和 1.5 秒都不是设计开始时就确定的数值,而是在连续比较不同组合后得到的结果。
对于这类依赖运动感受的参数,原型可以把讨论从"我觉得 0.5 秒可能比较合适"变成直接比较 0.3、0.4、0.5 秒的实际差异。参数没有脱离场景单独被决定,而是在完整运动里被判断。
相机目标位置根据模型缩放比例调整距离:
const ratio = newScale / oldScale;
const toPos = camera.position.clone().multiplyScalar(ratio);这里的重点是保持当前观察方向,只改变距离。比例变为 2x 时,相机沿原来的方向后退到相应距离;观察角度不发生变化。
这种路径在视觉上更稳定,也更符合"相机只是补偿模型尺寸变化"的角色。如果同时改变方向或重新计算观察角度,相机动画本身会变成新的视觉事件,反而削弱对模型变化的理解。
三段式时序。 Demo 中使用简单的延时和动画状态实现模型变化、停顿和相机移动:
setTimeout(() => {
pivot.scale.setScalar(pendingScale);
const fromPos = camera.position.clone();
const toPos = camera.position.clone().multiplyScalar(ratio);
setTimeout(() => {
anim = {
fromPos,
toPos,
start: performance.now(),
dur: durMs
};
}, holdMs);
}, loadMs);缓动。
function easeInOutCubic(t) {
return t < 0.5
? 4 * t * t * t
: 1 - Math.pow(-2 * t + 2, 3) / 2;
}这次 demo 没有发现新的交互 bug,它解决的是另一类问题:在真正看到动画之前,很难判断某一种运动是不是合适。
静态稿可以表达动画前后的状态,文字可以描述时长和曲线,但两者都不能完整传达运动本身。尤其是停顿、速度变化和前后两个动作之间的关系,只有运行起来以后才能判断。
因此在这类任务里,原型不只是展示方案,也是一种设计工具。它把难以描述的"手感"变成可以直接观察和比较的对象。