deck-api.md 4.7 KB

deck-api · 播放器 API

实现:lib/player/deck.js。规则见 .claude/rules/player.md

页面接入

<script src="<相对>/lib/vendor/gsap/gsap.min.js"></script>
<script src="<相对>/lib/theme/theme.js"></script>
<script src="<相对>/lib/player/deck.js"></script>
<script>
  window.SCREEN_TIMELINES = {
    s2: function (ctx) {
      var tl = gsap.timeline();
      tl.from(ctx.q(".s-title"), { y: 20, opacity: 0, duration: .5, ease: "var(--ease-out)" });
      return tl;                 // 必须返回 timeline
    }
  };
  deck.init();
</script>

window.SCREEN_TIMELINES

  • 键 = 屏的 data-tl 属性值;值为 function(ctx) -> gsap.timeline
  • ctx{ el, q(sel), qAll(sel), count(sel, to, opts) }
    • q/qAll 只在屏内查询。
    • count 数字滚动,返回 gsap tween,tl.add(ctx.count(...), pos) 使用;opts: {to, duration, ease, dec, sep}

window.deck API

方法 说明
deck.init() 初始化:收集 .screen、建导航、绑键盘。页面必须在 SCREEN_TIMELINES 定义后调用
deck.init({ mode:'shell', planEl, planUrl }) 总控容器模式(见下「总控容器模式」)
deck.next() / deck.prev() 下一屏 / 上一屏(门控)
deck.jump(i) 跳第 i 屏
deck.nextChapter() / deck.prevChapter() 下一章 / 上一章(读 deck-config 的 next/prev)
deck.goFile(file, dir) 跳指定章节文件
deck.showPage(i) / deck.loadPage(i) 总控模式:切到第 i 页 / 预加载第 i 页(iframe)

屏与门控

  • 屏:<section class="screen" id="..." data-tl="key">。无 data-tl 的屏 = 无动画屏,↓ 即切。
  • 视频门控:屏内 <video data-gate src="...">data-gate-autoplay 进入即自动播;播放中再 ↓ 跳过。
  • 状态机:idle → playing → done 在 idle 播放动画/视频,playing 忽略(防连击),done 才切屏。
  • 进度条与章节 tab 由 deck 渲染,状态机驱动(不依赖 scroll)。
  • gsap 缺失时 data-tl 屏直接 done,不白屏。

总控容器模式(shell)

把多个单页拼成一场演示:总控页 iframe 加载各页 + 预加载下一页(隐藏)+ 翻页切换可见性 → 无白屏无缝衔接;iframe 不销毁,翻回保留状态;导航顺序由编排配置决定,单页零改动可复用

接入(总控页,如 course/deck-shell.html

<script type="application/json" class="deck-plan">
{ "id": "cbec-full", "title": "跨境电商 · 完整版",
  "pages": [
    { "file": "cases/cbec/demo/p0-prologue.html", "title": "序 · 三个时代同时存在" },
    { "file": "cases/cbec/demo/p1-west-board.html", "title": "01 · 欧美棋盘" }
  ] }
</script>
<script src="<相对>/lib/theme/theme.js"></script>
<script src="<相对>/lib/player/deck.js"></script>
<script> deck.init({ mode: 'shell', planEl: '.deck-plan' }); </script>

编排配置(plan.json)

  • pages[].file 相对总控页title 用于顶栏 tab。不同编排 = 不同 plan.json,单页不改。
  • 示例:course/plans/cbec-full.jsoncourse/plans/health-fmc-full.json
  • 改 plan.json 后运行 scripts/embed-deck-plan.js <plan.json> <总控页> --write 重新内嵌(file:// 零 fetch)。
  • http 下可 ?plan=plans/health-fmc-full.json 切换编排(file:// 自动回退内嵌)。

运行时行为

交互 行为
页内 //滚轮/触摸 页内屏级翻页(页自己的 deck 状态机)
最后一屏 / 页内 / plan 顺序切页(忽略页内 deck-config 的文件跳转)
总控顶栏 tab / 底部 ‹ › 按钮 直接切页
首次展示某页 重演入场动画(预加载已渲染好内容 → 无白屏)
翻回已看过的页 保留原状态(iframe 不销毁)
总控主题按钮 同步广播所有 iframe 页面

页侧兼容(单页模式不变)

  • 页面在 iframe 内被加载时:deck.js 自动进入容器子模式——不渲染自身顶栏/进度条(由总控提供), 翻章请求改发 postMessage 给总控;独立打开时行为与之前完全一致
  • 页内 deck-config 保留(单页独立导航仍然可用),总控场景下其 next/prev 仅作参考、不生效。

通信协议(postMessage,跨源/跨 file:// 均可用)

方向 type 载荷
页 → 总控 deck-progress { index, total, state } 屏级进度
页 → 总控 deck-go { file, dir } 翻章请求(dir>0 下页 / <0 上页)
总控 → 页 deck-register 确认容器模式(页收到后才接管翻章)
总控 → 页 deck-cmd `{ cmd: 'next'