Appearance
Viewer 入口
设计意图
Viewer 是 .fmbv 渲染引擎的唯一公开入口:它持有 WebGL 渲染管线与场景,把能力按域拆给 14 个只读 manager 属性——入口本体只剩生命周期(create / dispose)、事件订阅(on / off / once)与一个尺寸逃生口(resize)。这种「单一入口 + 域 manager」的形态让每个域的 API 各自内聚,也让公开面不暴露任何底层渲染实现的类型。
构造入口只有一个:Viewer.create(canvas, options?)(构造函数私有,不可 new)。它在给定 canvas 上建立渲染管线并开启渲染循环;若指定了 options.environment(HDRI),会等环境加载完成后才 resolve。事件订阅采用退订函数模式:on() / once() 返回取消订阅函数,组件卸载时调用即可,不必保存 handler 引用去 off。
何时用它:集成的第一步(创建实例 / 传构造选项)与最后一步(dispose 释放 GPU 资源),以及跨域的事件总线接入;具体能力操作都在各 manager,见下表。
manager 一览
| 属性 | 职责 |
|---|---|
viewer.model | 模型加载 / 卸载、结构树查询与节点级外观控制 |
viewer.camera | 相机位姿、标准视图、zoom-to-fit 与交互控制 |
viewer.selection | 点击 / 框选 / 程序化选中与 hover 高亮 |
viewer.edges | 全局边线显示开关、颜色与不透明度 |
viewer.renderMode | 全局与按节点的渲染模式(实色线框 / 线框 / 实色 / 消隐) |
viewer.lights | 场景光源的增删与强度调整 |
viewer.stats | 加载与渲染统计快照(fps / 帧时 / chunk 进度等) |
viewer.navcube | 导航立方体的开关 / 停靠 / 尺寸 |
viewer.background | 画布背景(纯色 / 渐变 / 图片 / 透明) |
viewer.export | 矢量 SVG 工程图导出(消隐线条) |
viewer.explode | 零件爆炸(散开 / 锁定 / 动画) |
viewer.section | 剖切 section 与盖面 / gizmo 交互 |
viewer.quality | 渲染质量(抗锯齿 / AO / 色调映射 / 质量档) |
viewer.diagnostics | LOD 等调试诊断——实验性,不建议外部集成使用 |
构造选项(ViewerOptions)
全部可选,逐项一句话(精确类型见 ViewerOptions):
background— 初始背景:纯色或"transparent";不传用引擎默认浅灰垂直渐变。antialias— 硬件 MSAA,传给 WebGLRenderer,构造后不可变(默认true);运行时抗锯齿是另一回事,见viewer.quality。pixelRatio— 渲染像素比;默认取window.devicePixelRatio(非浏览器环境为 1)。workerPoolSize— 几何解码 worker 池大小;默认按 CPU 核数减一,钳制在 1–4。initialCamera— 初始相机位姿(位置 / 目标点);不传用引擎默认位姿。navCube— 导航立方体初始配置:enabled默认true、anchor默认"top-right"、size默认 185(CSS 像素)。debug— 调试开关;当前版本未接线,预留。quality— 渲染质量初值(抗锯齿 / AO / 色调映射 / 质量档),逐项默认值见QualityOptions。environment— HDRI 环境贴图 URL(.hdrequirect),用于 IBL;不传用内置程序化环境。
典型用法
完整生命周期(创建 → 加载 → 取景 → 销毁):
ts
import { Viewer } from "@modelcubes/viewer-core";
const viewer = await Viewer.create(canvas);
await viewer.model.load("/models/assembly.fmbv"); // 骨架就绪即 resolve
viewer.camera.fitView();
// ……不再使用时(如组件卸载)
viewer.dispose();事件订阅与退订(on 返回退订函数,不必保存 handler):
ts
const unsub = viewer.on("selection-changed", (e) => panel.show(e.current));
viewer.once("model-loaded", () => spinner.hide()); // 只触发一次
// 组件卸载时
unsub();按宿主环境定制构造(透明背景嵌入页面 + 关内建导航立方):
ts
const viewer = await Viewer.create(canvas, {
background: "transparent",
navCube: { enabled: false }, // 改用 <fmb-view-cube> 组件时关掉内建
quality: { tier: "high" },
});注意事项
Viewer.create是唯一构造入口,构造函数私有;指定options.environment时create会等 HDRI 加载完成后才 resolve,加载失败会 reject。antialias(MSAA)构造后不可变;运行时可开关的是 SMAA 后处理(viewer.quality.setAntialias),两者独立叠加。off需要与on时相同的 handler 引用——匿名箭头函数没法退订,优先用on/once返回的退订函数。- 画布尺寸变化引擎自动跟踪(ResizeObserver),宿主通常无需理会;
resize()是显式逃生口,供宿主在浏览器完成布局前就已知晓布局变化时立即同步渲染器与相机宽高比,已销毁时静默返回。 dispose()幂等(重复调用静默返回):停渲染循环、释放全部 GPU 资源与事件订阅;销毁后再调各 manager 的方法抛ViewerError(Disposed),on/once也抛,off静默返回。debug选项当前未接线,传了不生效。
完整签名与延伸
Viewer—create/on/off/once/resize/dispose的精确签名。ViewerOptions/ViewerEvents— 构造选项与全事件 payload 类型。- 指南:加载与流式 — 创建后第一件事(加载模型)的事件时序。
- 概念:架构总览(单一入口与分层)、事件系统(事件清单与订阅模式)。