面向 AI Agent 辅助开发的上下文与行动计划。人工协作时同样可用于 Sprint 规划。
| 层面 | 技术 |
|---|---|
| 构建 | Vite 5 + TypeScript 5 |
| 渲染 | WebGL 2(无外部 GL 框架) |
| 资源加载 | 本地 assets/res/ 静态文件(fetch JSON/PNG) |
| 动效/完成态 | js-confetti、Canvas 粒子爆破(自研 Explosion) |
| 音效 | Web Audio API via HTMLAudioElement |
| 外部依赖 | js-confetti(jszip 已移除,playable 不需要) |
index.html # playable 广告主入口(填色玩法页)
share.html # 分享落地页(回放 + Download CTA)
src/
base/ # 渲染引擎层
Scene.ts # 场景根节点:图层管理、变换矩阵、手势、动画帧
Gesture.ts # 捏合/拖拽/点击手势抽象
Animator.ts # 值驱动动画(Interpolator 可替换)
m4.ts # 4×4 矩阵运算(纯 TS 实现)
BgLayer / BoxLayer / BorderLayer / FrameLayer / ImageLayer / TextureLayer
ImageShaders.ts # GLSL shader 常量池
glsl/ # 独立 GLSL 文件(Gaussian / Bicubic 等)
filler/ # 填色玩法层
index.ts # 广告主入口脚本(init、UI 事件、CSS 回调)
play.ts # 分享页脚本(回放逻辑)
FillerScene.ts # 继承 Scene,增加 focusToArea / hint
FillerData.ts # 状态核心(coloredPercent、当前 group、getArea 命中)
WorkLayer.ts # 互动层:tap 填色、progress=1 动画完成后切换 group
AnimatableMask.ts# 掩码 FBO:每个 area 以扩散圆动画绘制到纹理
LineArtLayer.ts # 线稿渲染
NumberLayer.ts # 数字标注(atlas sprite)
HintLayer.ts # 提示高亮层
Loader.ts # 原 zip 解压模块(playable 中不使用,线上网站保留)
FillerData.ts # 数据模型:Area、AreaGroup、Data、FillerResource
LoadingController.ts # Loading overlay 显示/隐藏
Audio.ts # 音效播放器
explosion.ts # 粒子爆破动画
utils/
random.ts
assets/res/<id>/ → fetch config.json + loadImage(page.png / map.png)
↓ loadResource()
FillerResource { config(JSON), page(png), map(png), numberImage, bg, special }
↓
FillerData (状态机,管理 area.colored / currentGroup / taskList)
↓
FillerScene.addLayer(...) // BgLayer → BoxLayer → HintLayer → NumberLayer
// → WorkLayer → LineArtLayer → BorderLayer
↓ requestAnimationFrame
AnimatableMask.flush() // 将动画进度写入 FBO
↓
WorkLayer.draw() // 读取 FBO texture + colored texture → 渲染
↓
CSS 回调 (cssAdjustProgress / cssColorDone / cssOnFinish)
↓
完成:confetti + resetToResult + replay()
?url/?raw + vite-plugin-singlefile,构建输出单文件 HTML)| 缺口 | 优先级 | 说明 |
|---|---|---|
| 广告平台规范适配 | P0 | 单文件内联 HTML、资源 < 5 MB、无外部依赖(applovin/google/unity/mintergal) |
| CTA 按钮与跳转 | P0 | 所有平台都要求显眼 CTA,含平台 API 跳转 store |
| 自动演示(引导手势) | P1 | 游戏前 3-5 秒内有动效/提示引导用户点击上色 |
| 体积优化 | P1 | 当前依赖 js-confetti(jszip 已移除),需图片压缩 + 音效压缩 |
✅ 已完成:Vite ?url 静态导入 + vite-plugin-singlefile 全量内联 |
||
| 广告生命周期 API | P1 | 各平台 SDK 回调(mraid / gameReady 等) |
| 横竖屏适配 | P2 | 目前 padding 硬编码,需响应式布局 |
| 埋点/事件上报 | P2 | click / cta_click / complete 事件 |
| 多题材模板 | P3 | 当前单张图,需参数化支持不同图包 |
参考 README 中列出的竞品与规范链接,综合核心约束:
| 规范 | 要求 |
|---|---|
| 文件格式 | 单个自包含 HTML(所有 JS/CSS/图片 Base64 内联) |
| 文件大小 | ≤ 5 MB(Google)/ ≤ 5 MB(Unity)/ ≤ 3 MB(Applovin 建议) |
| 外部请求 | 禁止(Google/Applovin:所有资源必须内联) |
| HTTPS 重定向 | CTA 点击必须跳转到 store 链接(mraid.open / window.open) |
| MRAID | Applovin/Unity 需支持 MRAID 2.0(mraid.js 注入) |
| 帧率 | 建议 60fps,不得阻塞主线程 |
| 可玩时长 | 通常 15-60 秒内有 CTA |
| 广告标识 | 需显示"Ad"或"Sponsored"字样(部分平台) |
目标:vite build 输出一个完全自包含的单文件 dist/ad.html,无外部依赖。
任务拆解:
| # | 任务 | 文件 | 状态 |
|---|---|---|---|
| 1.1 | 简化入口:移除 URL id 解析和 zipUrl 逻辑,始终调 loadResource() |
src/filler/index.ts |
✅ |
| 1.2 | 图片资源以 Vite ?url 静态导入(构建时自动内联) |
src/filler/index.ts |
✅ |
| 1.3 | 音效 mp3 以 Vite ?url 静态导入 |
src/filler/Audio.ts |
✅ |
| 1.4 | 配置 Vite 输出单文件(vite-plugin-singlefile + assetsInlineLimit) | vite.config.js |
✅ |
| 1.5 | 清理 @types/jszip devDependency |
package.json |
✅ |
| 1.6 | 验证输出 HTML 可独立在浏览器打开无网络错误 | — | 待验收 |
验收标准:
file:// 打开可完整运行,无 CORS 错误,无外部请求目标:符合各平台规范的 CTA 流程,点击跳转 App Store / Google Play。
任务拆解:
| # | 任务 | 文件 | 状态 |
|---|---|---|---|
| 2.1 | 新增 CTA 按钮 UI(常驻 + 完成后放大高亮) | index.html / assets/css/tools.css |
✅ |
| 2.2 | CTA 点击跳转逻辑 | src/filler/cta.ts |
✅ |
| 2.3 | MRAID 2.0 适配层 | src/filler/mraid.ts |
✅ |
| 2.4 | gameReady / 平台初始化事件 | src/filler/index.ts |
✅ |
| 2.5 | 完成流程强化 CTA(宣传界面出现后高亮) | src/filler/index.ts |
✅ |
| 2.6 | "Ad"标签(右上角半透明徽章) | index.html / assets/css/tools.css |
✅ |
验收标准:
实际方案:以 DOM overlay 实现 FingerHint 类代替原计划的 WebGL GuidanceLayer,效果等同且更轻量。
| # | 任务 | 文件 | 状态 |
|---|---|---|---|
| 3.1 | 引导手型图片 | assets/img/finger.png |
✅ |
| 3.2 | DOM 悬浮手指层 | src/filler/FingerHint.ts |
✅ |
| 3.3 | 800ms 后自动指向待填区域 | src/filler/index.ts |
✅ |
| 3.4 | 用户交互后重置/停止 | src/filler/FingerHint.ts |
✅ |
目标:广告在主流设备(iPhone/Android 各尺寸)横竖屏均有良好视觉。
任务拆解:
| # | 任务 | 文件 | 要点 |
|---|---|---|---|
| 4.1 | 底部 toolbar padding 自适应 | src/filler/index.ts |
Padding 参数根据 window.innerWidth/Height 动态计算 |
| 4.2 | 颜色按钮横屏时改为侧边栏 | assets/css/tools.css |
@media (orientation: landscape) 布局调整 |
| 4.3 | Canvas 尺寸 resize 处理 | src/filler/index.ts |
window.addEventListener('resize') 已有,需同步 canvas size |
| 4.4 | 安全区(iPhone 刘海/Home Bar) | index.html |
env(safe-area-inset-*) CSS 变量 |
验收标准:
目标:最终产出 HTML 体积 ≤ 5 MB,60fps 无卡顿。
任务拆解:
| # | 任务 | 状态 | 结果 |
|---|---|---|---|
| 5.1 | 图片压缩 | ✅ | 当前最大图 302 KB,总资源 660 KB,无需额外压缩 |
| 5.2 | 音效压缩 | ✅ | 3 个音效合计 51 KB,已达标 |
| 5.3 | 评估/替换 js-confetti | ✅ | 保留,js-confetti 正常 tree-shaking 引入,体积可接受 |
| 5.4 | Tree-shaking 验证 | ✅ | JS 81.7 KB,jszip 已排除,test() 已 tree-shaken |
| 5.5 | GPU 内存估算 | ✅ | page + map 两张纹理约 4 MB GPU 显存,可接受 |
验收结果:
目标:在各平台预览工具中验证通过。
任务拆解:
| # | 任务 | 平台 | 工具/入口 |
|---|---|---|---|
| 6.1 | Applovin 预览 | Applovin MAX | https://p.applov.in/playablePreview?create=1 |
| 6.2 | Unity Ads 规范自检 | Unity | https://docs.unity.com/zh-cn/grow/acquire/creatives/playable/specifications |
| 6.3 | Google AdMob HTML5 校验 | https://support.google.com/google-ads/answer/9981650 | |
| 6.4 | Mintergal/Playturbo 预览 | Mintergal | https://www.playturbo.com/review/doc |
| 6.5 | 真机测试 | iOS Safari / Android Chrome | 重点验证音效、手势、CTA 跳转 |
验收标准:
选择:Vite build 时通过 vite-plugin-singlefile 将所有 JS/CSS inline,图片/音效在 TS 模块中以 Base64 字符串形式维护(构建前由脚本生成 src/filler/assetData.ts)。
理由:各平台明确要求单文件自包含,Vite 原生 asset inline 在 HTML 中仍产生多个文件,plugin-singlefile 是最少侵入的方案。
选择:UI 控制继续沿用直接 DOM 操作。
理由:playable 广告极度追求体积最小化,框架运行时会带来 ≥ 30 KB overhead,且当前 DOM 量极小无必要。
选择:在 src/filler/mraid.ts 实现轻量适配层,detect window.mraid 存在再调用,否则 fallback window.open。
理由:广告可能运行在不同平台 WebView,统一一套代码,运行时 feature detect。
阶段 1(单文件打包)
新增: src/filler/assetData.ts ← Base64 内嵌图片/音效常量(由构建脚本生成)
新增: scripts/embed-assets.mjs ← 构建前预处理脚本(图片/音效→base64 TS常量)
修改: src/filler/index.ts ← 移除 URL 解析/zipUrl 逻辑,始终调用 loadResource()
修改: src/filler/Audio.ts ← 音效改用 base64 data URL
修改: vite.config.js ← 引入 vite-plugin-singlefile
修改: package.json ← 移除 @types/jszip devDependency
阶段 2(CTA)
新增: src/filler/cta.ts ← CTA 跳转逻辑
新增: src/filler/mraid.ts ← MRAID 2.0 适配层
修改: index.html ← 新增 CTA 按钮 DOM
修改: assets/css/tools.css ← CTA 按钮样式
修改: src/filler/index.ts ← cssOnFinish 后接入 CTA 展示
阶段 3(引导动画)
新增: src/filler/GuidanceLayer.ts ← 手势引导 Layer
新增: assets/img/hand_hint.svg ← 手型图标
修改: src/filler/WorkLayer.ts ← 首次 tap 事件通知
修改: src/filler/index.ts ← 启动引导动画时序
阶段 4(响应式)
修改: assets/css/tools.css ← 横屏 media query
修改: src/filler/index.ts ← 动态 Padding 计算
阶段 5(体积优化)
修改: src/filler/explosion.ts ← 移除 js-confetti 依赖,自研或保留
修改: vite.config.js ← 确认 tree-shaking
资源: assets/res/*/page.png 压缩
资源: assets/sound/*.mp3 压缩
| 风险 | 可能性 | 影响 | 缓解措施 |
|---|---|---|---|
| 图片+音效内嵌后体积超 5 MB | 高 | P0 | 提前压缩图片至 WebP,评估 map 缩小至 512px |
| WebGL 2 在低端 Android WebView 不支持 | 中 | P1 | 检测 getContext('webgl2') 失败时展示静态降级画面 |
| MRAID 版本差异导致 CTA 无效 | 中 | P1 | 实现多版本 detect + fallback |
| 音效在 iOS Safari 需用户手势触发 | 高(已知问题) | P2 | 首次点击上色时解锁 AudioContext |
| 各平台预览工具对 inline base64 大文件的处理差异 | 低 | P1 | 提交前各平台逐一测试 |
https://creative-ag-global.umcdn.cn/html/09/2d/44/092d441b80639f462fa667bb3f8964bf.html(Color asis)https://p.applov.in/playablePreview?create=1https://support.google.com/google-ads/answer/9981650?hl=en#_HTMLhttps://docs.unity.com/zh-cn/grow/acquire/creatives/playable/specificationshttps://www.playturbo.com/review/dochttps://gemini.google.com/share/8340c20dd2d1