From 8cc2fb87aac384e398d120fb0ba9370ebfa8f8e7 Mon Sep 17 00:00:00 2001 From: hzj <1304805162@qq.com> Date: Thu, 30 Apr 2026 11:29:25 +0800 Subject: [PATCH] =?UTF-8?q?feat(=E6=96=B0=E7=BB=84=E4=BB=B6):=20VAP?= =?UTF-8?q?=E8=A7=86=E9=A2=91=E6=92=AD=E6=94=BE=E7=BB=84=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/vap播放说明.md | 286 ++++++++++++++++++++++++++++++++ src/components/VapMp4Player.vue | 107 ++++++++++-- src/config/imagePaths.js | 6 + 3 files changed, 381 insertions(+), 18 deletions(-) create mode 100644 docs/vap播放说明.md diff --git a/docs/vap播放说明.md b/docs/vap播放说明.md new file mode 100644 index 0000000..228e0da --- /dev/null +++ b/docs/vap播放说明.md @@ -0,0 +1,286 @@ +# VAP 播放接入说明 + +本文档整理当前项目中 VAP mp4 播放相关的组件、工具函数和活动页用法,方便后续复用、排查和扩展。 + +## 相关文件 + +| 文件 | 作用 | +| --- | --- | +| `src/components/VapMp4Player.vue` | 通用 VAP 播放组件,支持全屏弹层和页面内嵌播放 | +| `src/utils/vapMp4Config.js` | 从 mp4 文件中解析 `vapc` 配置并缓存 | +| `src/views/Activities/ActiveGame/vapBg.vue` | ActiveGame 顶部动图背景封装 | +| `src/views/Activities/ActiveGame/index.vue` | ActiveGame 中背景 VAP、奖励全屏 VAP 和预加载示例 | +| `src/config/imagePaths.js` | OSS 图片/视频 URL 生成,包含 `getMp4Url` | +| `src/utils/protectedAssets.js` | 受保护资源地址的编码和还原 | + +依赖包在 `package.json` 中: + +```json +"video-animation-player": "^1.0.5" +``` + +## 播放组件 + +`VapMp4Player.vue` 是项目里的通用播放器,内部使用 `video-animation-player`。 + +基础用法: + +```vue + +``` + +主要参数: + +| 参数 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| `modelValue` | Boolean | `false` | 控制播放组件显示,支持 `v-model` | +| `src` | String | `''` | mp4 地址 | +| `mode` | String | `'overlay'` | `overlay` 为全屏弹层,`inline` 为页面内嵌 | +| `fit` | String | `'contain'` | `contain`、`cover`、`fill` | +| `loop` | Boolean | `false` | 是否循环播放 | +| `mute` | Boolean | `false` | 是否静音 | +| `accurate` | Boolean | `true` | 传给 VAP 播放器的精准模式 | +| `autoClose` | Boolean | `true` | 非循环播放结束后是否自动关闭 | +| `showClose` | Boolean | `true` | 全屏弹层是否展示关闭按钮 | +| `showStatus` | Boolean | `true` | 是否展示加载/错误状态文案 | +| `vapData` | Object | `{}` | 额外透传给 `new Vap().play()` 的参数 | + +事件: + +| 事件 | 说明 | +| --- | --- | +| `update:modelValue` | 更新显示状态 | +| `loaded` | VAP 进入 playing 后触发 | +| `ended` | 播放结束 | +| `error` | 加载或播放失败 | +| `close` | 调用关闭逻辑后触发 | + +注意: + +- `overlay` 模式会通过 `Teleport` 挂到 `body`,默认 `z-index: 3000`。 +- `inline` 模式是 `position: absolute; inset: 0`,父容器需要有明确尺寸和定位。 +- 组件内部会先调用 `resolveProtectedAssetUrl(src)`,因此可以直接传 `getMp4Url` 生成的受保护地址。 + +## VAP 配置解析 + +`src/utils/vapMp4Config.js` 负责从 mp4 中读取名为 `vapc` 的 box,并解析其中的 JSON 配置。 + +导出方法: + +```js +getVapConfigFromMp4(mp4Url) +preloadVapConfig(mp4Url) +clearVapConfigCache(mp4Url) +``` + +说明: + +- `getVapConfigFromMp4(mp4Url)` 会 `fetch` 整个 mp4,读取 ArrayBuffer 后查找 `vapc`。 +- 解析结果以 `mp4Url` 为 key 缓存在 `configCache` 中,重复播放同一个地址不会重复解析。 +- `preloadVapConfig(mp4Url)` 适合页面预加载阶段使用,失败会吞掉错误并返回 `null`。 +- `clearVapConfigCache(mp4Url)` 可清除某个地址或全部缓存。 + +常见错误: + +| 错误 | 可能原因 | +| --- | --- | +| `vapc box not found` | mp4 不是 VAP 格式,或没有内嵌 VAP 配置 | +| `mp4 request failed: xxx` | 地址不可访问、跨域、鉴权、OSS 路径错误 | +| `vapc box does not contain json` | `vapc` 存在但内容不是合法 JSON | + +## mp4 地址约定 + +`src/config/imagePaths.js` 中提供了 mp4 URL 方法: + +```js +getRawMp4Url(imagePath, filename) +getMp4Url(imagePath, filename) +``` + +示例: + +```js +import { getMp4Url } from '@/config/imagePaths.js' + +const mp4Url = (filename) => getMp4Url('Activities/ActiveGame/', filename) +const bgVapUrl = mp4Url('bg') +``` + +生成的线上地址形如: + +```text +https://tkm-likei.oss-ap-southeast-1.aliyuncs.com/h5/Azizi/Activities/ActiveGame/bg.mp4 +``` + +开发环境会走: + +```text +/oss/h5/Azizi/Activities/ActiveGame/bg.mp4 +``` + +如果资源路径属于受保护范围,`getMp4Url` 会返回保护后的地址。播放器或预加载工具在真正 `fetch` 前需要用 `resolveProtectedAssetUrl` 还原。`VapMp4Player.vue` 内部已经处理播放时的还原;页面手动预加载时也要处理: + +```js +import { preloadVapConfig } from '@/utils/vapMp4Config.js' +import { resolveProtectedAssetUrl } from '@/utils/protectedAssets.js' + +await preloadVapConfig(resolveProtectedAssetUrl(bgVapUrl)) +``` + +## ActiveGame 当前用法 + +### 顶部背景 VAP + +`src/views/Activities/ActiveGame/vapBg.vue` 用于顶部背景: + +```vue + +``` + +内部实际使用: + +```vue + +``` + +页面样式中给背景 VAP 固定区域: + +```scss +.vap-background-layer { + width: 100vw; + height: 150vw; + position: absolute; + top: 0; + left: 0; + z-index: 1; + pointer-events: none; +} +``` + +### 奖励全屏 VAP + +点击奖励展示的左侧奖励图时,展示全屏居中的 VAP: + +```vue + +``` + +当前临时地址: + +```js +const rewardVapUrl = + 'https://tkm-likei.oss-ap-southeast-1.aliyuncs.com/svgasource/manager-d824e59b-1a88-4d4f-aed3-01768ba20542.mp4' +``` + +## 预加载建议 + +如果页面打开后要尽快展示 VAP,建议同时做两件事: + +1. 在页面预加载阶段调用 `preloadVapConfig`。 +2. 不要等 loading 结束后才挂载 VAP 组件,可以让页面在 loading 遮罩下先挂载,VAP 提前初始化。 + +ActiveGame 当前预加载示例: + +```js +await Promise.all([ + preloadImages(criticalImages), + preloadVapConfig(resolveProtectedAssetUrl(bgVapUrl)), + preloadVapConfig(rewardVapUrl), +]) +``` + +说明: + +- `bgVapUrl` 来自 `getMp4Url`,需要先 `resolveProtectedAssetUrl`。 +- `rewardVapUrl` 是普通完整 OSS 地址,可以直接传入。 +- `preloadVapConfig` 主要预热 VAP 配置解析和浏览器请求缓存,但最终播放仍由 `VapMp4Player` 创建 canvas 和播放器实例。 + +## 接入新页面步骤 + +1. 准备带 `vapc` 配置的 VAP mp4 文件。 +2. 如果放在活动 OSS 目录,使用 `getMp4Url('Activities/xxx/', 'filename')` 生成地址。 +3. 页面中引入 `VapMp4Player.vue`。 +4. 全屏播放用 `mode="overlay"`,页面背景或局部播放用 `mode="inline"`。 +5. 页面预加载阶段调用 `preloadVapConfig(resolveProtectedAssetUrl(mp4Url))`。 +6. 对 `@error` 做兜底提示或日志,避免静默失败。 + +全屏弹层模板: + +```vue + +``` + +页面背景模板: + +```vue +
+ +
+``` + +```scss +.vap-bg { + position: absolute; + width: 100vw; + height: 150vw; + overflow: hidden; + pointer-events: none; +} +``` + +## 排查清单 + +- 页面空白或不播放:确认组件 `modelValue` 为 `true`,`src` 非空。 +- 只有 loading 不结束:确认页面预加载流程有 `finally` 关闭 loading。 +- 提示找不到 VAP 配置:确认 mp4 内有 `vapc` box。 +- 本地能播线上不能播:检查 OSS 路径、跨域、资源保护地址是否还原。 +- inline 不显示:确认父容器有 `position: relative/absolute` 和明确宽高。 +- overlay 被遮挡:检查是否有更高的 z-index;默认播放器弹层是 `3000`。 +- 背景遮挡内容:检查页面层级,ActiveGame 当前是页面背景 `z-index: 0`、VAP 背景 `z-index: 1`、页面内容 `z-index: 2`。 + diff --git a/src/components/VapMp4Player.vue b/src/components/VapMp4Player.vue index 431bb69..93e0fb8 100644 --- a/src/components/VapMp4Player.vue +++ b/src/components/VapMp4Player.vue @@ -1,22 +1,40 @@