Appearance
视图集导航
一个视图是可复现的场景结果态:相机位姿与投影、显示模式、隐藏了哪些零件、零件被挪到了哪里、染成了什么颜色、贴了哪些序号标签——「上次看到的那一眼」需要的全部信息。viewer.views 负责把它捕获下来(capture)和套回去(apply)。
多个视图串起来就是视图集,持久化形态是 .fmbview.json 信封。装配讲解常常就是一串视图:总览 → 拆开外壳 → 剖到内部 → 标注关键件,用户按「下一步」逐个翻过去。
捕获与应用
capture() 返回的是纯 JSON(裸 number 节点 id、{x,y,z} 相机),可以直接存盘或走网络:
ts
const state = viewer.views.capture();
await fetch("/api/views", { method: "POST", body: JSON.stringify(state) });apply() 把它套回当前场景。默认带动画——相机 400ms 补间、外观材质渐变、变换位置补间;首帧复现时应该关掉,否则用户会看到一段莫名其妙的「飞入」:
ts
viewer.views.apply(state, { animate: false }); // 首帧:即时
viewer.views.apply(state); // 用户点「回到这一步」:带动画更好理解应用顺序是固定的:显示模式 → 可见性 → 外观 → 变换 → 投影与相机。这个顺序保证了相机取景时场景形态已经就位。
切视图不是编辑
apply 走的是不记录 undo 的批量还原原语,不会进入撤销栈。用户翻了十个视图再按 ⌘Z,撤销的是他之前做的那次编辑,而不是「退回上一个视图」——这符合直觉:导航不是操作。
视图集翻页
信封通常在加载时自动装载(见加载时应用视图),也可以由宿主手动喂:
ts
const ok = viewer.views.loadDocument(await file.text());
if (!ok) toast("这份视图文件与当前模型不匹配"); // 解析失败 / 签名不符 / 空信封之后就是常规的翻页操作:
ts
viewer.views.list(); // readonly ViewDocumentView[]
viewer.views.current; // 当前视图,无信封时 null
viewer.views.currentIndex; // 无信封时 -1
viewer.views.next(); // 到尾不循环,没有可切的返回 false
viewer.views.prev();
viewer.views.activate("爆炸图"); // 按 id、name 或整数索引activate 与 loadDocument 在失败时返回 false 而不抛错:未命中 selector、信封为空、签名(节点数)与当前模型不符,统一是「不改变现状 + 返回 false」。这让宿主可以放心地把用户拖进来的任意文件丢给它。
「已偏离」与复位
用户翻到某个视图后转了转相机、隐藏了几个零件,这时他看到的已经不是那个视图了。isDirty 就是这个状态:
ts
viewer.on("view-changed", (e) => {
navLabel.textContent = e.count ? `${e.index + 1} / ${e.count} · ${e.name}` : "";
resetBtn.hidden = !e.dirty;
});
resetBtn.onclick = () => viewer.views.resetToCurrent();偏离的判据只看四个通道:相机(位移超过场景半径的 0.5%)、可见性、变换覆盖、爆炸导致的包围球外扩。改颜色不算偏离——它不影响「你在看哪里、看到了什么形态」。
apply 自身造成的变化不算偏离(包括 400ms 补间期间的相机变化),所以切完视图 isDirty 恒为 false。
与视图相关的两个事件
| 事件 | 什么时候来 |
|---|---|
view-applied | 一次 view-on-load 应用结束,带成功 / 跳过原因 |
view-changed | 切了视图、装载或清空了信封、isDirty 翻转 |
驱动导航器 UI 用 view-changed 一个就够——它把「第几个 / 共几个 / 叫什么 / 偏没偏离」都带上了。
注意事项
- 换模型自动清空信封:
model-disposed时clearDocument()自动执行,导航器应据view-changed的count: 0收起。 displayMode的"xray"是 UI 概念,引擎没有对应渲染档:apply遇到它保持当前显示模式;capture永远不会产出"xray"(除非你在opts.displayMode里显式传)。- 签名守卫按节点数比对:模型换了版本、节点数变了,旧信封会被整份拒绝,而不是错位应用到别的零件上。
相关
- API:viewer.views — 全部方法与选项。
- 指南:加载时应用视图 — 让首帧就落在正确视角,消除加载闪烁。
- 组件:
<fmb-view-nav>— 现成的视图导航器,接上viewer即可用。