主题
开发约定
挂件运行在 iframe 内,合成到画布上,并随场景一起进入虚拟摄像头推流。为了保证三类窗口(编辑 / 预览 / 推流)行为一致,并配合客户端的合成 / 采集 / 持久化机制,挂件开发应遵循以下约定。
1. 透明背景与无滚动
挂件应完全透明,以便正确叠加到画布与推流画面上。入口页面建议:
css
html, body {
margin: 0;
background: transparent;
overflow: hidden;
}<iframe> 由宿主设 allowtransparency 与 background: 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 中 config 与 theme 的每个键,SDK 都会在 init 与每次变更后写入 :root 的 CSS 变量 --zbj-<key>,便于挂件用纯 CSS 响应配置变更——简单挂件可以完全不写 JS。
| 字段值类型 | 写入形式 | 用法示例 |
|---|---|---|
string | 原样 | color: var(--zbj-accent); |
number | String(n) | font-size: calc(var(--zbj-fontSize) * 1px); |
boolean | "1" / "0" | --zbj-showIcon: 1 → display: var(--display-when-1); |
object / array(giftPicker / list) | 不注入 | 仅 JS 可读 |
null / undefined | 不注入 | — |
config 与 theme 共用 --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,挂件无法接收鼠标 / 键盘事件。需要主播操作的玩法应:
- 在挂件内部用直播事件驱动(礼物触发、关键词触发等);
- 或在「样式」面板的 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.ready | init 数据准备好 | 读取 config / theme 初始化 UI;订阅事件 |
onConfigChange / onThemeChange | 用户改面板 | 增量更新视觉,避免整页重建 |
onResize | 元素尺寸变化 | 重排自适应布局 |
| 直播事件 | 平台事件抵达 | 按需更新 DOM / 触发动画 |
| 卸载 | 元素被删除 / 切换场景 | iframe 自动销毁;不需要特殊处理 |
8. storage 持久化的边界
ZBJ.storage.set(key, value) 仅在 mode === "edit" 下落盘,随场景一起持久化;在 preview 与 virtualcam 模式下为会话内内存态:
- 预览窗拉取的是当时的快照,本地修改不回写;
- 虚拟摄像头离屏窗用于推流,不应被挂件「写入」改变持久化数据。
这种设计保证了「编辑器是唯一写入源」的语义。挂件作者应避免在非 edit 模式下做严重依赖持久化结果的逻辑——例如计分类挂件可以读取已保存的分数渲染当前值,但只在 edit 模式下累加。
9. 版本与兼容策略
manifest.sdkVersion与ZBJ.sdkVersion均为 主版本号。当前规范全部接口属于 v1。- 冻结承诺:v1 生命周期内,
window.ZBJ接口签名只增不改不删;新增方法对老挂件不可见、不影响。 - 只有在出现无法向后兼容的破坏性变更时,才会升
sdkVersion至2。届时客户端会同时支持 v1 / v2 一段时间,给老挂件迁移窗口。 - 客户端在安装挂件时校验
manifest.sdkVersion ≤ 客户端支持上限;超出时拒绝安装并提示升级客户端。
详细的版本演进计划见 版本策略。