Appearance
加载时应用视图(view-on-load)
把模型装入场景时,常常希望一并恢复上次保存的「视图」——相机角度、隐藏了哪些零件、有没有爆炸/平移、染了什么色、什么显示模式。如果等模型几何全部加载完再去套用,用户会先看到一帧默认 fit 视图、随后「啪」地跳到目标视图。viewer.model.load(source, opts) 的 view-on-load 在骨架就绪(model-skeleton-ready)阶段就把视图套好,消除这次闪烁。
API
ts
interface ModelLoadOptions {
/** 加载后(骨架就绪时)应用一个视图。 */
view?: ViewOnLoad;
/** 应用时是否动画飞入(默认 false:首帧直接就位,不飞入)。 */
animateView?: boolean;
}
type ViewOnLoad =
| { state: ViewState } // ① 直接给视图态
| { document: ViewDocument | string; select?: string | number } // ② 文档对象 / JSON 字符串
| { fetch: true; url?: string; select?: string | number }; // ③ 自动 fetch sidecar① { state } —— 直接给一个 ViewState
适合在内存里已经拿到一个 ViewState(例如先 viewer.views.capture() 存下、重载时还原):
ts
const snap = viewer.views.capture(); // { cam, projection, displayMode, hidden, transforms, colors, ... }
await viewer.model.load(bytes, { view: { state: snap } });② { document } —— 一个 ViewDocument(或其 JSON 字符串)
.fmbview.json 的内容就是一个 ViewDocument。可以把解析好的对象、或原始 JSON 字符串直接交给 load,并用 select 指定应用其中哪一个视图:
ts
const jsonText = await readSidecarSomehow(); // 字符串
await viewer.model.load(bytes, { view: { document: jsonText, select: "Camera 1" } });select 可为视图 id、name 或整数索引;缺省时取文档的 activeId,再退化到第一个视图。
③ { fetch: true } —— 自动拉取同源 sidecar
模型来自 URL 时,可让引擎自动推导并 fetch sidecar(默认 <modelUrl>.fmbview.json,去掉 query/hash 后追加后缀);也可用 url 显式指定:
ts
// 自动推导:models/part.fmbv → models/part.fmbv.fmbview.json
await viewer.model.load("https://cdn.example/models/part.fmbv", { view: { fetch: true, select: 0 } });
// 或显式指定 sidecar URL
await viewer.model.load(modelUrl, { view: { fetch: true, url: viewsUrl } });.fmbview.json 文档格式(ViewDocument v2)
ts
interface ViewDocument {
version: 2;
signature: { nodeCount: number }; // 陈旧守卫:与当前模型节点数比对
activeId: string; // 默认选中视图的 id
views: { id: string; name: string; snap: ViewState }[];
}signature.nodeCount守卫:若文档记录的节点数与当前模型不一致(模型被改过),整份文档视为不兼容,跳过应用——不会把陈旧的隐藏/变换套到对不上的节点。- 版本:仅接受
version: 2;其它版本解析返回null→ 跳过。 - 文档解析、兼容性判断、选视图都是纯函数,可单独使用:
parseViewDocument(json)、isViewDocumentCompatible(doc, nodeCount)、selectView(doc, selector)、deriveSidecarUrl(modelUrl)。
view-applied 事件
每次 view-on-load 解析后,引擎发一个 view-applied 事件回报结果——无论成功还是被跳过:
ts
viewer.on("view-applied", (e) => {
if (!e.applied) viewer.camera.fitView(); // 没套上(无 sidecar / 不兼容 / 坏 JSON)→ 退回默认 fit
});e.reason 取值:
| reason | 含义 |
|---|---|
ok | 已成功应用 |
not-found | sidecar 不存在 / 文档里没有匹配的视图 |
parse-error | JSON 损坏或非法结构 |
signature-mismatch | nodeCount 不符,文档与当前模型对不上 |
empty | 文档存在但没有任何视图 |
view-on-load 失败只是 skip,绝不 reject load()——加载本身照常完成,你据 view-applied.applied 决定是否兜底 fitView()。
运行时应用:viewer.views
view-on-load 是「加载时」入口;运行期随时可用 viewer.views(ViewsManager)捕获/应用视图:
ts
const snap = viewer.views.capture({ displayMode: "shaded" }); // 捕获当前场景为 ViewState
viewer.views.apply(snap, { animate: true }); // 平滑过渡应用(相机/隐藏/变换/外观同帧驱动)上层应用接入现状
- desktop 编辑器:直开
.fmbv时,在源文件旁读<源>.fmbview.json并经{ document }在骨架就绪套用;若套用成功则跳过fitView,消除首帧闪烁。 - web 编辑器:同源
?model=<URL>深链时,按deriveSidecarUrl自动{ fetch: true };本地拖入的文件(无法访问同目录 sidecar)走普通加载。 - 独立阅读器
@fmb/viewer:支持?model=/?view=<id|name|index>/?views=<sidecarUrl>深链。
相关
- 加载与流式 —
load()的事件时序与失败处理。 ModelManager—load/ModelLoadOptions完整签名。ViewsManager—capture/apply签名。ViewerEvents—view-appliedpayload 类型。