Skip to content

开发约定

挂件运行在 iframe 内,合成到画布上,并随场景一起进入虚拟摄像头推流。为了保证三类窗口(编辑 / 预览 / 推流)行为一致,并配合客户端的合成 / 采集 / 持久化机制,挂件开发应遵循以下约定。

1. 透明背景与无滚动

挂件应完全透明,以便正确叠加到画布与推流画面上。入口页面建议:

css
html, body {
  margin: 0;
  background: transparent;
  overflow: hidden;
}

<iframe> 由宿主设 allowtransparencybackground: transparent,挂件页面只需保证自身背景透明。如果挂件需要不透明色块,应直接在内部 DOM 上设背景,而不要把 body 设成不透明。

2. 自适应尺寸

defaultSize 在 manifest 中声明的是初始尺寸,用户可以拖拽缩放。挂件应始终响应宿主下发的尺寸,而非写死宽高:

js
ZBJ.onResize(({ width, height }) => {
  document.body.style.fontSize = Math.max(12, width / 28) + "px";
});

如果布局对宽高比敏感,可在 manifest 中设置 keepAspectRatio: true,宿主会在缩放交互中保持比例。

3. CSS 变量桥

manifest 中 configtheme 的每个键,SDK 都会在 init 与每次变更后写入 :root 的 CSS 变量 --zbj-<key>,便于挂件用纯 CSS 响应配置变更——简单挂件可以完全不写 JS

字段值类型写入形式用法示例
string原样color: var(--zbj-accent);
numberString(n)font-size: calc(var(--zbj-fontSize) * 1px);
boolean"1" / "0"--zbj-showIcon: 1display: var(--display-when-1);
object / arraygiftPicker / list不注入仅 JS 可读
null / undefined不注入

configtheme 共用 --zbj- 前缀,因此两者的 key 必须全局唯一(manifest 校验会拒绝重复键,详见 manifest 规范 §6.2)。

参考实现:

js
function applyCssVars(obj) {
  const root = document.documentElement.style;
  for (const k of Object.keys(obj)) {
    const v = obj[k];
    if (v == null || typeof v === "object") continue;
    root.setProperty("--zbj-" + k, typeof v === "boolean" ? (v ? "1" : "0") : String(v));
  }
}

4. 资源路径

包内资源应使用相对路径

html
<link rel="stylesheet" href="./assets/style.css" />
<img src="./assets/logo.png" />
<script src="./assets/app.js"></script>

挂件被安装后由主播酱内嵌服务在 /widgets/<id>/ 下托管;相对路径在两种状态(已安装 / 开发者模式)下都能正确解析。

加载外部 CDN 资源(字体、图片)需要符合宿主下发的 CSP:放行 https:data:blob:。详见 架构设计 §6 安全模型

5. 非交互叠层

宿主对挂件 iframe 恒设 pointer-events: none,挂件无法接收鼠标 / 键盘事件。需要主播操作的玩法应:

  1. 在挂件内部用直播事件驱动(礼物触发、关键词触发等);
  2. 或在「样式」面板的 schema 中暴露开关 / 按钮形式的 config 字段,由主播通过宿主面板操作。

6. 性能自律

  • DOM 数量:长时间运行的滚动 / 列表类挂件需自行限制元素数量(推荐用 while (list.children.length > max) list.lastChild.remove();)。
  • 动画帧率:不要在 requestAnimationFrame 中跑高频粒子;如需高帧率动画,优先用 CSS animations 或 <canvas> 离屏渲染。
  • 省电模式:编辑端对不可见元素会显示占位,挂件 iframe 可能被销毁;挂件应在 unload 时清理 timer / WS 监听。

7. 生命周期

阶段触发条件推荐行为
加载iframe 加载完成SDK 同步读 URL hash 的 init 数据,立即 ready;挂件无需主动操作
ZBJ.readyinit 数据准备好读取 config / theme 初始化 UI;订阅事件
onConfigChange / onThemeChange用户改面板增量更新视觉,避免整页重建
onResize元素尺寸变化重排自适应布局
直播事件平台事件抵达按需更新 DOM / 触发动画
卸载元素被删除 / 切换场景iframe 自动销毁;不需要特殊处理

8. storage 持久化的边界

ZBJ.storage.set(key, value) 仅在 mode === "edit" 下落盘,随场景一起持久化;在 previewvirtualcam 模式下为会话内内存态

  • 预览窗拉取的是当时的快照,本地修改不回写;
  • 虚拟摄像头离屏窗用于推流,不应被挂件「写入」改变持久化数据。

这种设计保证了「编辑器是唯一写入源」的语义。挂件作者应避免在非 edit 模式下做严重依赖持久化结果的逻辑——例如计分类挂件可以读取已保存的分数渲染当前值,但只在 edit 模式下累加。

9. 版本与兼容策略

  • manifest.sdkVersionZBJ.sdkVersion 均为 主版本号。当前规范全部接口属于 v1。
  • 冻结承诺:v1 生命周期内,window.ZBJ 接口签名只增不改不删;新增方法对老挂件不可见、不影响。
  • 只有在出现无法向后兼容的破坏性变更时,才会升 sdkVersion2。届时客户端会同时支持 v1 / v2 一段时间,给老挂件迁移窗口。
  • 客户端在安装挂件时校验 manifest.sdkVersion ≤ 客户端支持上限;超出时拒绝安装并提示升级客户端。

详细的版本演进计划见 版本策略