ZWMAP 标注类型规范

1. 概述

标注类型用于描述视频画面上的交互区域和时间轴上的交互事件。协议遵循机制与策略分离原则:提供底层 UI 容器和原子动作,业务场景由用户自由组合。

标注数据包含两种元素:

  • 热区 (hotspots):在视频画面上显示的交互区域,占据一定位置和时间范围
  • 事件 (events):在时间轴上触发的交互面板,如测验、提示、表单等

hotspotsevents 至少提供一个。

2. ZWMAP 头部

{
  "zwp_protocol": "ZWMAP/1.0",
  "zwp_type": "annotation"
}

zwp_protocolzwp_type 为必填字段。不支持向后兼容,必须携带 ZWMAP 协议头。

3. 热区对象 (Hotspot)

热区描述视频画面上的一个交互区域。

字段 类型 必填 说明
id string 唯一标识符
time_range array 显示时间范围 [start, end](秒)
position object 位置与尺寸 { "x", "y", "w", "h" }(百分比 0-100)
action object 点击触发的动作(与 content 至少提供一个)
content object 显示的内容(文本、图片等)
style object 样式配置(边框、透明度、背景色、动画等)
hint string 悬停提示文字

4. 动作类型 (Actions)

所有 Action type 使用 UPPER_SNAKE_CASE。

4.1 媒体控制类

type 参数 说明
SEEK_TIME target(number), show_back_btn(bool) 时间轴跳转
LOAD_ITEM target(string) 切换播放列表项
PAUSE_MEDIA 强制暂停
PLAY_MEDIA 恢复播放

4.2 界面与路由类

type 参数 说明
OPEN_LINK url(string), target(_blank/_self), pause(bool) 打开外部链接
SET_VISIBILITY target_id(string), visible(bool), auto_hide(number) 控制热区显隐

4.3 数据与通信类

type 参数 说明
EMIT_MESSAGE payload(object) 向宿主环境发送 postMessage
SUBMIT_DATA api_url(string), method(POST/PUT/PATCH), body(object) 向外部 API 发送请求

4.4 状态管理类

type 参数 说明
SET_VARIABLE key(string), value(any) 设置会话变量

5. 事件类型 (Events)

所有 Event type 使用 UPPER_SNAKE_CASE,代表 5 种基础 UI 容器。

type 说明 核心字段
INFO_CARD 信息卡片/弹窗 title, content, media_url, buttons
CHOICE_BOARD 选择面板(统一 quiz/branch/questionnaire) prompt, options, is_evaluative, allow_multiple
INPUT_FORM 数据表单 title, fields, submit_url
TOAST_NOTICE 轻提示/气泡 text, position, duration
WEB_VIEW 外部视图容器 src, width, height

事件对象字段:

字段 类型 必填 说明
id string 唯一标识符
time number 触发时间点(秒)
type string 事件类型
interrupt boolean 是否中断播放(暂停视频)
data object 事件内容数据

6. 完整示例

{
  "zwp_protocol": "ZWMAP/1.0",
  "zwp_type": "annotation",
  "zwp_version": "1.0",
  "hotspots": [
    {
      "id": "hs_link_01",
      "time_range": [3.0, 15.0],
      "position": { "x": 10, "y": 20, "w": 25, "h": 15 },
      "action": {
        "type": "OPEN_LINK",
        "url": "https://www.example.com",
        "pause": true
      },
      "style": {
        "border_color": "#FF6B6B",
        "border_style": "solid",
        "border_width": 1,
        "animation": "pulse"
      },
      "hint": "点击查看详情"
    },
    {
      "id": "hs_msg_01",
      "time_range": [10.0, 30.0],
      "position": { "x": 70, "y": 5, "w": 15, "h": 8 },
      "content": {
        "type": "text",
        "text": "加入购物车 →",
        "style": { "color": "#fff", "font_size": 13 }
      },
      "action": {
        "type": "EMIT_MESSAGE",
        "payload": { "action": "ADD_TO_CART", "sku": "12345" }
      }
    }
  ],
  "events": [
    {
      "id": "quiz_01",
      "time": 60.0,
      "type": "CHOICE_BOARD",
      "interrupt": true,
      "data": {
        "prompt": "以下哪个是正确的?",
        "is_evaluative": true,
        "options": [
          { "text": "选项 A", "value": "A", "is_correct": false },
          { "text": "选项 B", "value": "B", "is_correct": true },
          { "text": "选项 C", "value": "C", "is_correct": false }
        ],
        "feedback": { "correct": "回答正确!", "wrong": "请重试" },
        "max_attempts": 2,
        "fallback_time": 10.0,
        "on_complete": { "type": "PLAY_MEDIA" }
      }
    },
    {
      "id": "toast_01",
      "time": 15.0,
      "type": "TOAST_NOTICE",
      "data": {
        "text": "精彩内容即将到来!",
        "position": "top_center",
        "duration": 3
      }
    }
  ]
}

7. 约束规则

  1. 每个 id 在整个文档范围内必须唯一(热区和事件之间不可冲突)
  2. position 的值为百分比(0-100),非像素值
  3. time_range 的第一个值必须小于第二个值
  4. actioncontent 至少提供一个
  5. SET_VISIBILITYtarget_id 必须指向已存在的热区 ID
  6. 事件按 time 值触发,播放器应按时间顺序处理
  7. 未知的 Action 或 Event type 必须静默忽略,不得导致播放器崩溃