Skip to content

viewer.views — ViewsManager

设计意图

viewer.views视图——一个可复现的场景结果态。它做两件事:把当前场景读成一份可序列化的 ViewState(capture),以及把这样一份状态重新套回场景(apply)。一份视图涵盖相机位姿与投影、显示模式、隐藏集、逐件变换覆盖、逐件与逐面外观(颜色 / 金属度粗糙度 / 材质预设)、序号标签及其样式——也就是「上次看到的那一眼」需要的全部信息。

在此之上,本域还持有一份视图信封(.fmbview.json,类型 ViewDocument)做视图集导航:list / activate / next / prev 把多个视图串成一条可翻页的序列,isDirty 告诉宿主「用户已经把当前视图看歪了」,resetToCurrent 一步复位。信封通常在 model.load() 的 view-on-load 里自动装载(见加载时应用视图),也可以由宿主经 loadDocument() 手动喂(拖入 sidecar 文件)。

apply 走的是不记录 undo 的批量还原原语——切视图是导航,不是编辑,不会污染 viewer.history 的撤销栈。

何时用它:分享链接里带的初始视角、装配讲解的分步视图翻页、「回到这一步」的复位按钮。逐条捕获 / 应用的语义细节见视图集导航

典型用法

捕获当前场景并存盘,之后原样复现:

ts
const state = viewer.views.capture(); // 纯 JSON,可直接 JSON.stringify
localStorage.setItem("last-view", JSON.stringify(state));

// ……下次加载完模型后
viewer.views.apply(JSON.parse(localStorage.getItem("last-view")!), { animate: false });

视图集翻页(Share 形态的视图导航器就是这么接的):

ts
const views = viewer.views.list(); // readonly ViewDocumentView[]
nextBtn.onclick = () => viewer.views.next(); // 到尾不循环,返回 false
prevBtn.onclick = () => viewer.views.prev();
viewer.views.activate("爆炸图"); // 按 id、name 或整数索引切

订阅 view-changed 驱动导航器 UI(切视图、装载 / 清空信封、偏离翻转都发这一个事件):

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();

宿主手动装载 sidecar(拖入 .fmbview.json):

ts
const ok = viewer.views.loadDocument(await file.text()); // 解析/签名/空信封失败返回 false
if (!ok) toast("这份视图文件与当前模型不匹配");

注意事项

  • apply 默认带动画({ animate: true }):相机 400ms 补间,外观走材质渐变,变换走位置补间。首帧复现视图时传 { animate: false },否则会看到一段「飞入」。
  • apply 不入撤销栈,capture / apply 都不产生 history 步;但 apply 会发 visibility-changed / appearance-changed / transform-changed 等状态事件,订阅方按常规刷新即可。
  • isDirty 只看四个通道:相机(位移超过场景半径的 0.5%)、可见性、变换覆盖、爆炸导致的包围球外扩。改颜色不翻 dirty。
  • activate / loadDocument 失败返回 false 而不抛:未命中 selector、信封为空、签名(nodeCount)与当前模型不符,都是「不改变现状 + 返回 false」。
  • 换模型自动清空信封:model-disposedclearDocument() 自动执行,hasDocument 转 false、currentIndex 回 -1。
  • displayMode"xray" 是 UI 概念,引擎没有对应渲染档——apply 遇到 "xray" 保持当前显示模式不变;capture 不传 opts.displayMode 时由引擎 renderMode 反推,永远不会产出 "xray"
  • ViewState 是纯 JSON(裸 number 节点 id、{x,y,z} 相机),可以直接落盘 / 走网络,不含任何引擎对象引用。

完整签名与延伸