Skip to content

<fmb-load-card>

渐进加载卡。大装配不是「加载中 → 完成」两态,而是骨架 → 几何分块 → 边线 → 收敛四段流入的,而且骨架就绪后模型已经可以转动了。这张卡的职责就是诚实地说出这件事,而不是转一个圈假装什么都没发生。

组件是纯受控的:它不订阅引擎事件,只按 stage / progress 两个属性渲染。把引擎事件翻成这两个数是宿主的事——下面给了完整接法。

最小用法

html
<div class="viewport" style="position: relative">
  <canvas id="canvas"></canvas>
  <fmb-load-card id="load" hidden></fmb-load-card>
</div>
ts
import { FmbLoadCard } from "@modelcubes/viewer-ui";
void [FmbLoadCard];

const card = document.getElementById("load") as FmbLoadCard;
card.stage = 1;
card.progress = 0.4;
card.hidden = false;

属性

名称类型property / attribute默认值说明
stagenumberproperty + attribute0当前阶段序号 0..3,对应 LOAD_STAGES 四项
progressnumberproperty + attribute0总进度 0..1
labelstringproperty + attribute""模型名;plain 档不显示
vocabulary"plain" | "engineering"property + attribute"plain"plain 只给一条进度 + 一句可交互提示;engineering 列出四阶段清单
centerbooleanproperty + attribute(反射)false居中显示(首屏加载)而不是贴在工具条上方

LOAD_STAGES 是导出的常量,四项分别是「骨架 / 几何分块 / 边线 / 收敛」及其副标题,需要自定义展示时可直接读它。

事件

无。

把引擎事件翻成 stage / progress

这是 @fmb/viewer 的真实接法,要点都在注释里:

ts
/** 各阶段的进度权重:骨架很快,几何最久,边线其次,收敛收尾。 */
const STAGE_FLOOR = [0, 0.12, 0.88, 1] as const;

viewer.on("model-loading", () => {
  card.stage = 0;
  card.progress = 0.04;
  card.hidden = false;
});

viewer.on("model-skeleton-ready", () => {
  card.stage = 1; // ★ 此刻模型已可交互
  card.progress = STAGE_FLOOR[1];
});

viewer.on("model-geom-progress", ({ ready, total }) => {
  const frac = total > 0 ? Math.min(1, ready / total) : 0;
  card.stage = 1;
  card.progress = STAGE_FLOOR[1] + (STAGE_FLOOR[2] - STAGE_FLOOR[1]) * frac;
});

viewer.on("model-loaded", () => {
  card.stage = 2;
  card.progress = STAGE_FLOOR[2];
});

viewer.on("model-edges-ready", () => {
  card.stage = 3;
  card.progress = 1;
  setTimeout(() => (card.hidden = true), 260); // 补满 100% 再收,让「完成」被看见
});

两个必须处理的坑

一、不要在 load() 的 Promise resolve 时收卡。 它在骨架就绪时就 resolve,几何与边线此后仍在后台流入——在那里收卡,进度会永远停在最后一个到达的事件上(实测卡在 96%)。收尾信号是 model-edges-ready

二、必须挂超时兜底。 在两种情况下 model-loaded / model-edges-ready 不保证到达:模型里有孤儿 mesh(没有节点引用、永不被请求),或相机是固定的局部视锥(离屏 mesh 永不被请求)。没有兜底,进度卡会永远挂在画面上。@fmb/viewer 用的是骨架后 20s、model-loaded 后 4s 两档超时,谁先到谁收卡。

行为细节

  • 纯受控:组件自身不碰 viewer,也不订阅任何事件——所有状态由宿主推入。
  • hidden 是标准 HTML 属性,:host([hidden]) { display: none } 已在组件内置,直接 card.hidden = true 即可。
  • 两种文案档:plain 面向分享场景的最终用户(一条进度条 + 「已经可以转动了」这类提示);engineering 面向工程审阅(展开四阶段清单,标出已完成 / 进行中)。

可定制点

卡片底色 / 描边 / 阴影走 --fmb-surface--fmb-border--fmb-shadow-lg;进度条填充走 --fmb-accent;文字走 --fmb-fg-1 / --fmb-fg-2。全表见主题定制

相关