更新记录

1.0.1(2026-09-15)

本次修的主要是只有付费加密版才会暴露的问题:源码版开发时一切正常、买家侧才复现。 建议 1.0.0 用户尽快升级。

① 修 H5 白屏:does not provide an export named 'VapScaleType'(P0,加密版必现)

  • 根因(三轮才定死):加密插件的平台入口在 H5 端只提供「运行期导出」,而类型在运行期没有任何绑定 组件原本写 import { VapPlayer, VapMixData, VapScaleType } from "@/uni_modules/axen-vap-x"。 付费插件发布后平台入口是密文.web/.encrypt/**/index.module.js 在磁盘上就是密文,构建期解密后 才交给浏览器),而浏览器只能按运行期导出解析具名 import —— 纯类型永远不会有运行期绑定,于是直接抛 SyntaxError: The requested module '...' does not provide an export named 'VapScaleType',组件不渲染。 编译期零报错,只在加密版(买家侧)出现。对照:App 三端入口在编译期解密、类型照常解析; mp-weixin 产物是整模块 require() + .VapPlayer 取值,从不按名字索要类型 —— 只有 H5 的 ESM 具名 import 会暴露这个坑
  • 修复:类型声明按平台分流(组件与示例页都这么写):
    • WEB || MP-WEIXIN:在文件内本地声明(JS 端类型是结构化匹配,不存在"两个类"的问题)
    • APP-ANDROID || APP-IOS || APP-HARMONY:仍从插件入口 import(必须共用同一份声明, 否则触发 compiler-known-issues error17)
  • 三条已验证的死路(别再试): ① import type { ... } from "@/uni_modules/axen-vap-x" —— 编译产物原样保留该语句,web 管线不认这个前缀; ② 从 "@/uni_modules/axen-vap-x/utssdk/interface.uts" 引 —— 解析不了(TS2307: Cannot find module '.../interface'); ③ 在 interface.uts 里给类型名配同名运行时值 —— UTS 报 invalid redeclaration (类型名与常量同名会冲突,不像 TS 那样分属两个命名空间)。
  • 业务侧提醒:H5 / 小程序端不要从插件入口 import 本插件的类型;readme 已给出可直接复制的分流写法。

② H5 融合图片加载失败不再静默(新增播放前预检)

跨域图片被浏览器拦掉后,旧版是静默失败:动画照常播完、只有该融合元素不显示, 控制台里只有一行 blocked by CORS policy,不看控制台根本发现不了。 现在播放前会用与引擎完全相同的方式crossOrigin=anonymous)预检所有 http(s) 融合图片:

  • 失败 → 报 code=2(素材下载失败),并在控制台打印可执行的修复提示 (图片服务器需返回 Access-Control-Allow-Origin,或改用同源图片 / 带 CORS 的 CDN);
  • 播放不中断 —— 缺的只是一个元素,动画本身没问题;
  • 同源 / 本地路径(如 /static/user.png)不探测;App / 小程序端走原生下载,完全不受影响。

③ 其他

  • 组件 5 条 'player' is possibly 'null' 编译告警清理(改用 player?.,与本文件其它调用一致)。
  • 文档:readme 补「类型导入按平台分流」的可复制写法、以及「打 iOS 模拟器包会链接失败」的报错与自救步骤; PLATFORMS 补 H5 的图片/视频必须可跨域(含实测响应头对照)与本次预检行为。
  • 示例工程:融合头像从跨域的 q1.qlogo.cn 换成同源 /static/user.png,避免示例自踩文档里的 H5 跨域约束。

1.0.0(2026-09-15)

  • 跨端 VAP 视频动画特效组件(uni-app x:Android / iOS / HarmonyOS / H5 / 微信小程序)。
  • 功能:
    • 融合元素:头像 / 昵称 / 图片 / 文字动态合成(VAP 招牌能力)。
    • 网络地址自动下载缓存后播放。
    • 属性:src / loop / autoplay / mute / scaleType / mixData
    • 事件:@started / @finish / @frame / @error / @click
    • 方法:play() / stop() / pause() / resume() / setMixData()
  • 微信小程序:VideoDecoder mode 0 + MediaAudioPlayer 系统级音画同步;像素计算走 WASM(static/vap-pixel.wasm 随插件分发):YUV→RGBA / alpha 蒙版 / RGB+alpha 预合成 / 融合元素全帧合成(compose_full:纹理双线性缩放 + 蒙版裁剪 + source-over 都在堆内完成,有融合元素时每帧 canvas 调用从 ~12 次降到 2 次;wasm 不可用或纹理未就绪时自动回退 canvas 逐元素路径,视觉语义一致)。
  • 插件加密:uni_modules.encrypt 覆盖全部实现文件 —— 各端 utssdk/*/index.utsutssdk/shared.utsutssdk/interface.utsutssdk/app-harmony/builder.ets、 小程序与 web 的辅助实现,以及 组件本体 components/axen-vap-x/axen-vap-x.uvue。 仅文档、许可证、插件配置、原生二进制依赖(aar/har/framework)与引擎文件不加密。 说明:static/vap-pixel.wasm 为自研引擎产物,当前列入 encrypt —— 微信小程序只对 .js 做运行时解密、不解密 .wasm,加密后存在加载失败风险, 故保持明文分发(如需加密请先在小程序端实测加载是否正常)。
  • 底层:Android(腾讯 VAP animplayer + VapBridge)、iOS(QGVAPlayer)、HarmonyOS(OHOS-VAP)。
  • 合规:MIT(Tencent VAP)/ Apache-2.0(OHOS-VAP),保留第三方版权声明。

兼容性修正

  • 五端统一暴露 clearCanvas():Android/Harmony 为空实现、web 透传引擎;组件卸载时统一「先清屏再销毁」,修复微信端动画结束后的残影。
  • Android 真机跑通(HBuilderX 5.07 + 自编基座),期间修掉四个会导致编译/运行失败的问题:
    1. 导出类不再实现 AAR 里的 VapBridge.Listener(uvue 编译类路径没有 AAR,会报 Cannot access 'VapBridge.Listener' which is a supertype of 'VapPlayer');AAR 相关代码全部收敛进内部 VapBridgeAdapter
    2. 组件 → 插件改为传普通对象(UTSJSONObject)+ JSON 字符串(mixDataJson / configJson), 插件侧由新增的 utssdk/shared.uts 解析,解决 uni.UNIxxxx.VapOptionsuts.sdk.modules.uniVapX.VapOptions 两个同名类导致的「参数类型不匹配」(compiler-known-issues error17)。
    3. 共享类型(VapScaleType/VapMixData/VapOptions/VapEvents)统一只在 utssdk/interface.uts 声明,各平台 import 复用。
    4. 组件内 player.on(...) 回调形参补类型标注;onUnmounted 里五端都不存在的 clear() 改为 clearCanvas()
  • 文档补充:事件回调形参固定为 UTSJSONObject(用 getNumber/getString 取值);ref 调子组件方法的正确类型写法。

融合素材(vapx)播放卡死修复

  • 现象:含融合元素的素材(vapx.mp4)点击后无任何反应——不显示画面、不触发 started/error,系统日志里也不创建视频解码器;而无融合元素的 demo.mp4 一切正常。
  • 根因VapBridge(AAR 内 Java 桥)的 fetchImage / fetchText 只在成功取到资源时才回调 result, 违反 VAP SDK 官方契约——官方 demo 明确注释「无论图片是否获取成功都必须回调 result,否则会无限等待资源」。 于是有融合元素的素材在播放前卡在资源等待,解码器不会被创建,事件也不会回调。
  • 修复:两处回调改为无条件 result.invoke(...)(取不到时传 null),重新编译 animplayer-release.aar 并替换到 utssdk/app-android/libs/
  • 排查附注vapx.mp4 文件本身完好(设备侧 md5 = f981e0f094ead842ad5ae99f1ffaa1a1,与腾讯官方 demo 登记值一致),排除素材损坏/编码不兼容。
  • ⚠️ AAR 源码工程 android-player/ 未纳入版本控制,重编译 AAR 前请先对照上述修改点,避免改动丢失。

无音轨素材丢尾帧修复(微信小程序)

  • 现象demo.mp4(80 帧 @25fps 无音轨)在微信端稳定报 78~79 帧,日志 end early: 78 / 80vapx.mp4(240 帧有音轨)始终精确。
  • 根因VideoDecoder 在流尾(EOS)会吞掉硬解管道里最后 1~2 帧;有音轨素材走 mode 0 + MediaAudioPlayer 时音频时钟会把尾帧全量 flush 出来,纯视频轨没有这条时钟。 是否丢帧与文件本身有关(疑与编码器写入的重排缓冲参数有关):vapx.mp4 去掉音轨后仍不丢, demo.mp4 则 mode 0 / mode 1 都丢,无法靠播放侧策略规避。
  • 修复:素材落盘后、播放前原地 remux 加一条静音 AAC 轨utssdk/mp-weixin/mp4-remux.uts, 纯字节数学无依赖:解析 moov/stbl 索引 → 视频样本原样拷贝 → 追加 44.1kHz mono 静音轨, 产物为 原路径 + ".sil.mp4" 伴生文件,faststart 布局),之后与有音轨素材完全同链路 (mode 0 + MediaAudioPlayer)。静音帧/esds 为常量字节(AAC 帧间无预测可任意重复), 提取自 ffmpeg anullsrc 产物。remux 失败(非 mp4 / 已有音轨 / co64 / 写盘异常)回退 mode 1 + fps 门控兜底;探测失败一律按"有音轨"处理,不引入新风险。 1.5MB 素材 remux 仅几 ms,一次性开销;结果按路径缓存,重播直接播伴生文件。
  • 被否决的中间方案(记录防回退):无音轨分流 mode 1 + fps 门控在 Node 状态机里 8 例全绿,但真机仍 78/80 —— mock 全绿 ≠ 真机正确,一切结论以真机日志为准。
  • 连带修掉loop > 1 时每轮只播 1 帧 —— frameIndex 在 seek 重播后没归零, 第二轮第一帧就满足"帧数达标即收尾"。现每轮归零,finish 上报的即素材总帧数。
  • 验证:Node 状态机(伪造 VideoDecoder/MediaAudioPlayer,按真实文件字节判音轨)8 例全绿; remux 产物字节级校验(视频样本与源逐一相等 / vapc 原样保留)+ ffprobe 全量解码零错误; 真机回归:demo.mp4 80/80、vapx.mp4 240/240、大文件去音轨 vapx remux 后 240/240、 包内素材不重复 remux。

已知限制(详见文档)

  • Android 引擎不支持 pause/resume(与官方 SDK 一致)。
  • HarmonyOS 端暂未开放静音控制(依赖 @ohos/vap 能力)。
  • 微信小程序 pause/resume:素材统一走 mode 0(无音轨的已 remux 加静音轨),实时解码无真暂停能力, pause 期间进度继续,音画仍同步。(仅 remux 失败的极少数素材回退 mode 1,pause 即真正停在当前帧。)
  • iOS 在 Intel Mac 的 x86_64 模拟器上会被 QGVAPlayer 拦截(Apple Silicon 模拟器正常,组件会自动切 OpenGL); scaleType 暂未生效;未实现 viewDidFailPlayMP4 回调,播放失败以 @error 之外的下载/超时路径上报。

规划

  • uni-app(vue) 老式原生插件版(覆盖非 uni-app x 存量用户)。
  • 遮罩 / 事件帧更精细回调。

平台兼容性

uni-app(3.8.4)

Vue2 Vue2插件版本 Vue3 Vue3插件版本 Chrome Chrome插件版本 Safari Safari插件版本 app-vue app-vue插件版本 app-nvue app-nvue插件版本 Android Android插件版本 iOS iOS插件版本 鸿蒙 鸿蒙插件版本
1.0.0 1.0.0 1.0.0 1.0.0 1.0.0 1.0.0 5.0 1.0.0 12 1.0.0 1.0.0
微信小程序 微信小程序插件版本 支付宝小程序 抖音小程序 百度小程序 快手小程序 京东小程序 鸿蒙元服务 QQ小程序 飞书小程序 小红书小程序 快应用-华为 快应用-联盟
1.0.0 - - - - - - - - - - -

uni-app x(4.0)

Chrome Chrome插件版本 Safari Safari插件版本 Android Android插件版本 iOS iOS插件版本 鸿蒙 鸿蒙插件版本 微信小程序 微信小程序插件版本
1.0.0 1.0.0 5.0 1.0.0 12 1.0.0 1.0.0 1.0.0

axen-vap-x 跨端 VAP 视频动画特效组件

基于 Tencent VAP(MIT)、OHOS-VAP(Apache-2.0)与自研微信引擎封装的 uni-app x UTS 组件,五端一套代码:Android / iOS / HarmonyOS(原生渲染)+ H5 + 微信小程序。

用于直播间进房动效、礼物特效、活动弹窗等需要透明度与融合元素的场景。

✨ 特性

  • 🖥️ 五端一套组件:同一份 UTS 组件 —— App 三端原生渲染(硬解 + Metal / OpenGL),H5 用 vap-web(WebGL),微信小程序用自研 VideoDecoder + Canvas2D 引擎
  • 👤 融合元素:动画内预留 tag,运行时注入头像 / 昵称 / 图片 / 文字(VAP 招牌能力)
  • 🔊 音效支持:音效 / 背景音内嵌在 VAP mp4 里,播放即有声、音画天然同步(素材无需单独挂音频文件)—— Android / iOS / 微信小程序端支持:微信端为 VideoDecoder + MediaAudioPlayer 系统管线级音画同步,iOS 含静音开关下的音频会话处理(避免"扬声器没声、插耳机才有声");HarmonyOS / H5 暂为无声渲染(见平台能力矩阵)
  • 网络地址:直接传 http(s),组件内部自动下载缓存后播放
  • 🎛️ 完整事件started / frame / finish / error / click,帧序号与总帧数五端语义已对齐
  • 📐 缩放与循环scaleType(aspectFit / aspectFill / scaleToFill)、loopmute
  • 🪶 低内存:视频硬解 + 只缓存一帧,适配「同屏多礼物、大粒子」场景

⚠️ 本插件适用于 uni-app x 项目。传统 uni-app(vue) 项目请使用配套免费组件 axen-vap(H5 + 微信小程序)。 支付宝等其余小程序平台因无逐帧视频解码 API 不支持(详见 PLATFORMS.md 的 11 平台矩阵)。

📦 安装

插件市场搜索 axen-vap-x,或直接将 uni_modules/axen-vap-x 导入项目。

  • HBuilderX 4.61+ 且为 uni-app x 工程
  • 五端均已实测跑通(App 三端真机 / 模拟器、H5 浏览器、微信真机),验证证据见 PLATFORMS.md,排查记录见 TROUBLESHOOTING.md

原生二进制(AAR / Framework / HAR)的集成与编译步骤见 uni-vap-x-demo/README.md。 使用前请先读 各端使用前提 —— 它会直接影响能否直接跑通。

🚀 快速开始

<template>
    <view>
        <axen-vap-x
            ref="vap"
            :src="'/static/demo.mp4'"
            :loop="1"
            :autoplay="true"
            :scaleType="'aspectFit'"
            :mixData="mixList"
            @started="onStarted"
            @frame="onFrame"
            @finish="onFinish"
            @error="onError"
            @click="onClick"
        />
    </view>
</template>

<script setup lang="uts">
    // 类型:H5/小程序端本地声明(加密入口在运行期不提供类型导出),App 三端从插件入口引
    // #ifdef WEB || MP-WEIXIN
    type VapMixData = { tag: string; imgUri?: string; txt?: string; color?: string; fontSize?: number; textAlign?: string; fontWeight?: string }
    // #endif
    // #ifdef APP-ANDROID || APP-IOS || APP-HARMONY
    import { VapMixData } from "@/uni_modules/axen-vap-x"
    // #endif

    const mixList: Array<VapMixData> = [
        { tag: "[imgUser]", imgUri: "https://.../avatar.png" },   // 融合头像
        { tag: "[textUser]", txt: "星河Momo", color: "#FFF1AE", fontSize: 28, textAlign: "center", fontWeight: "bold" }
    ]

    function onStarted() { console.log("started") }

    // ⚠️ 事件回调形参必须写 UTSJSONObject(不能写 any,也不能省略类型):
    //    - 写 `e: any`  → UTS 的 Any 没有动态属性,`e.totalFrameCount` 报「找不到名称」
    //    - 不写类型     → 报「Cannot infer type for this parameter」
    //    取值用 getString / getNumber / getBoolean。
    function onFrame(e: UTSJSONObject) { console.log("frame", e.getNumber("index")) }
    function onFinish(e: UTSJSONObject) { console.log("finish", e.getNumber("totalFrameCount")) }
    function onError(e: UTSJSONObject) { console.log("error", e.getString("message")) }
    function onClick(e: UTSJSONObject) { console.log("click", e.getString("tag")) }
</script>

📖 API 文档

属性

属性 类型 默认 说明
src string mp4 地址:本地路径或 http(s) 网络地址(网络地址自动下载并缓存)
mixData Array\<VapMixData> [] 融合元素,见下
loop number 1 循环播放次数:1 播一次;2 播两次;<=0 无限循环
autoplay boolean false 挂载后自动播放
mute boolean false 静音(Android / iOS / H5 / 微信支持;HarmonyOS 不支持,上报 code=5
scaleType string aspectFit aspectFit / aspectFill / scaleToFill
config any null vapc 配置。App 三端可留空(原生引擎自己解析 mp4 的 vapc box);H5 留空时组件会自动 fetch 素材并提取微信端建议显式传入 —— 留空时自研引擎按「RGB 在上 / alpha 在下」的常见拼版假定渲染,遇到别的拼版会裁剪错误
bypassProcess boolean false 仅微信小程序:跳过素材预处理(包内素材可用)

事件

事件 参数 触发
started 播放开始
frame { index } 每渲染一帧(index 为原生帧序号,基准各端不同
finish { totalFrameCount } 播放完成,totalFrameCount = 总帧数(不是「最后一帧序号」)
error { code, message } 失败上报,五端统一错误码(见下表)
click { tag } 点击到融合元素(iOS / Android / HarmonyOS 支持;H5 / 微信暂不支持,见「平台能力矩阵」)

回调形参类型固定为 UTSJSONObject(见上例),用 getNumber("totalFrameCount") / getString("tag") 取值。

@error 错误码(五端统一,业务侧按此分支)

code 含义 触发场景
1 引擎不可用 小程序基础库不支持 createVideoDecoder;H5 引擎脚本加载/初始化失败
2 素材下载失败 网络 src 或网络融合图片下载/落盘失败
3 解码或播放失败 解码器启动失败、播放中途报错、原生引擎抛错
4 参数/素材非法 src 为空或非法;H5 素材里找不到 vapc 配置
5 动作不被支持 Android 的 pause/resume、鸿蒙的 setMute、微信在已结束时 resume、H5 运行期 setMixData/setLoop/setMute

定义在 utssdk/interface.utsVapErrorCode能力缺失一律报 5,不再静默空实现 —— 例如在 Android 上调 pause() 会收到 code=5,请据此把暂停按钮置灰或改走 stop()

totalFrameCount 的来源按平台:iOS 为原生真实总帧数;Android / 微信 / H5 / 鸿蒙为引擎帧计数。 实测校准:demo.mp4 → 80 帧、vapx.mp4 → 240 帧,与 mp4 stsz.sample_count / vapc info.f 一致。 做进度条请统一用 index / totalFrameCount,不要假设 index 从 0 开始。

方法(ref 调用)

组件已 defineExpose 以下方法(示例为 uvue / App 端写法):

<axen-vap-x ref="vapRef" :src="src" />
import { UniVapXComponentPublicInstance } from "@/uni_modules/axen-vap-x/components/axen-vap-x/axen-vap-x.uvue"
const vapRef = ref<UniVapXComponentPublicInstance | null>(null)

vapRef.value?.play()          // 播放 / 重播
vapRef.value?.stop()          // 停止
vapRef.value?.pause()         // 暂停:iOS / HarmonyOS 支持;H5 支持;微信为"伪暂停"(时间轴继续走,resume 后续播);
                              //       **Android 不支持**(会上报 code=5,animplayer 无暂停能力)
vapRef.value?.resume()        // 恢复:同上;微信在解码器已结束时调用会收到 code=5,需重新 play()
vapRef.value?.setMixData(arr) // 动态更新融合元素后重播
vapRef.value?.clearCanvas()   // 清空画布(微信端防残影;组件卸载时已自动调用)

组件在 onUnmounted 里会先 clearCanvas()destroy(),所以「切场景 = 换 props + v-if 重挂载」这种声明式用法无需手动调方法(演示工程即如此)。

VapMixData 融合元素

type VapMixData = {
    tag: string        // 素材内占位 tag,如 [imgUser]
    imgUri?: string    // 图片地址(本地或网络,用于头像/图片填充)
    txt?: string       // 文字内容
    color?: string     // 文字颜色 #RRGGBB
    fontSize?: number  // 字号
    textAlign?: string // left / center / right
    fontWeight?: string// normal / bold
}

tag 必须与素材里的占位符完全一致(含方括号):素材里是 [imgUser],代码里也要写 [imgUser]。 文字对齐与颜色以素材 vapc 为准(与官方 Android 行为一致:对齐固定居中,颜色取 vapc 记录值)。

调试日志

插件的过程日志默认 静默,不会往控制台刷屏。排查时显式打开:

import { setVapDebug } from "@/uni_modules/axen-vap-x"
setVapDebug(true)     // 打开后输出 [vap-x] 前缀的过程日志

错误日志([vap-x] 的 error 级)始终输出,不受该开关影响。

🎨 素材制作

VAP 素材 = 普通 mp4 + 内嵌 vapc 配置(JSON)。使用官方 VapTool 生成:

  1. PS / AE 导出带 alpha 的视频帧
  2. 在 VapTool 中标记融合元素(矩形区域 + tag),导出 xxx.mp4
  3. 将 mp4 放入应用 static 或服务器

imgUri 可以写网络地址,插件会自动处理:Android 底层(腾讯 VAP 的 animplayer AAR)只接受本地文件路径 (其 decodeBitmapFile(path).exists() ? decodeFile(path) : null,且取不到图时不会回调, 会让整段播放卡在等资源上)。因此本插件在 Android 端播放前会把所有网络 imgUri 先下载成本地文件; 万一下载失败,该融合元素会被自动剔除 —— 宁可少一个头像,也不会让整段动效放不出来。

📁 插件目录约定(重要)

uni_modules/axen-vap-x/
├── components/axen-vap-x/axen-vap-x.uvue   # 组件本体
├── utssdk/
│   ├── interface.uts                     # 公共类型(VapPlayer / VapMixData / ...)
│   ├── shared.uts                        # 参数解析 + 日志开关(各端共用)
│   ├── app-android/  app-ios/  app-harmony/    # 原生三端
│   ├── mp-weixin/                        # 微信小程序自研引擎
│   └── web/                              # H5(vap-web)
└── static/
    ├── vap.min.js                        # H5 引擎(官方 vap-web 产物)
    └── vap-pixel.wasm                    # 微信端像素计算引擎

两条必须遵守的约定:

  1. UTS 插件不要建根 index.uts —— 入口由编译器按平台解析到 utssdk/<平台>/index.utsinterface.utsutssdk/ 下(与官方 uts-api 模板一致)。 若在根 index.uts 里用条件编译手动 re-export 各平台文件,JS 打包阶段会把 iOS 源码当普通模块解析而报 Rollup failed to resolve import "UIKit"。 业务侧照官方文档引用即可:import { VapPlayer } from "@/uni_modules/axen-vap-x" —— ⚠️ 但类型(VapMixData / VapScaleType / VapOptions / VapEvents)要按平台分流H5 / 微信小程序端不能从插件入口 import 类型(付费插件发布后入口是加密二进制,浏览器只能按 运行期导出解析具名 import,而纯类型在运行期没有任何绑定)→ H5 端会直接抛 The requested module ... does not provide an export named 'VapScaleType'编译期零报错。 正确写法(本项目组件与示例页就是这么写的):

    // H5 / 小程序:本地声明(JS 端类型是结构化匹配,不存在"两个类"的问题)
    // #ifdef WEB || MP-WEIXIN
    type VapMixData = { tag: string; imgUri?: string; txt?: string; color?: string; fontSize?: number; textAlign?: string; fontWeight?: string }
    // #endif
    // App 三端:从入口引(必须与插件共用同一份声明,否则触发 compiler-known-issues error17)
    // #ifdef APP-ANDROID || APP-IOS || APP-HARMONY
    import { VapMixData } from "@/uni_modules/axen-vap-x"
    // #endif

    两条已验证的死路(别再试):import type { ... } from "@/uni_modules/axen-vap-x"(编译产物原样保留、 web 管线不认);从 "@/uni_modules/axen-vap-x/utssdk/interface.uts" 引(解析不了,TS2307)。 如果只是用 :mixData 传数组、不做类型标注,则不涉及这个问题。

  2. 二进制与 js 资源必须放 static/ —— static/ 会被原样拷进产物,而 utssdk/ 只编译 .uts.js, 放 utssdk/ 下的 .wasm / .js / .min.js 不会进产物(会导致运行时 404,本插件踩过这个坑)。

⚙️ 各端使用前提(重要)

平台 前提
H5 图片素材(融合头像等)必须可跨域 —— 浏览器对 Image 做 CORS 校验,图片服务器不返回 Access-Control-Allow-Origin 时头像不显示,且是静默失败:动画照常播完、只有控制台报 blocked by CORS policy(2026-09-15 实测)。实测对照:gcore.jsdelivr.netACAO: *q1.qlogo.cn 不带。解决:同源图片或带 CORS 的 CDN(示例现已改用同源 /static/user.png)。App / 小程序端为原生下载,不受此限制。跨域加载 mp4 时同理
HarmonyOS 依赖 utssdk/app-harmony/libs/libvap.har。若从仓库拉取后缺失,需先执行 bash scripts/build-harmony-har.sh(从 vendor/ohos-tpc/vap 生成)。另:HBuilderX CLI 不支持鸿蒙发布,报「运行包制作失败」时可用 hdc install 直接安装产出的 hap
微信小程序 像素计算依赖随插件分发的 static/vap-pixel.wasm;不可用时自动回退纯 JS 路径(较慢,功能一致);动画只能在真机运行(真机预览 / 真机调试验证播放与音效),开发者工具仅用于页面布局与「不校验合法域名」等配置检查
iOS 需真机或 Apple Silicon 模拟器,详见下节

🔧 平台特定说明

iOS

  • 只在真机验证动画(重要):组件里没有"模拟器自动切 OpenGL"这类逻辑, 实测模拟器上 VAP 画面不出来(Metal 图层抓不到 / 渲染层不刷新), iOS 端动画一律用真机验证,模拟器只用来验证页面布局与样式。
  • framework 按平台存放(Apple Silicon 上真机 arm64 与模拟器 arm64 无法 lipo 合并, 而 HBuilderX 不会嵌入 .xcframework 动态库,所以只能切换):

    目录 架构 用途
    utssdk/app-ios/Frameworks/ 真机 arm64 默认发布版本,唯一能真正播放
    utssdk/app-ios/Frameworks-alt/device/ 真机 arm64 备份
    utssdk/app-ios/Frameworks-alt/simulator/ 模拟器 x86_64+arm64 仅本地编译校验

    切换命令:bash scripts/use-ios-framework.sh device|simulator|status(会同步到 demo 工程)

  • ⚠️ 打 iOS 模拟器包会链接失败(别误判成代码问题)Frameworks/ 里默认是真机切片, 所以选择「iOS Appstore(模拟器)」或运行到 iOS 模拟器 时,UTS 插件会在链接阶段报:

    Undefined symbols for architecture x86_64:
    "_OBJC_CLASS_$_QGVAPWrapView", referenced from: objc-class-ref in index.o

    这就是缺模拟器切片,跟代码、证书、描述文件都无关。另外:真机切片与模拟器切片 不可能同时就位(见上表),所以真机包与模拟器包不要一次同时提交 —— 同时提交时必有一个用错切片。 确实需要模拟器时(仅验证页面布局/编译,动画在模拟器上不出画面),手动换用模拟器切片 (插件包内没有 scripts/,按目录替换即可):

    cd uni_modules/axen-vap-x/utssdk/app-ios
    rm -rf Frameworks/QGVAPlayer.framework
    cp -R Frameworks-alt/simulator/QGVAPlayer.framework Frameworks/
    # 用完换回真机切片:把上面 simulator 换成 device
  • 重新编译 frameworkbash scripts/build-ios-framework.sh(从 vendor/tencent-vap/iOS 源码编译真机 + 模拟器两个切片)
  • 本地校验 UTS 是否可编译bash scripts/check-ios-uts.sh(用 HBuilderX 自带的 UTS→Swift 编译器生成 Swift,再对真实 framework 做 swiftc -typecheck,无需打开 HBuilderX)
  • 网络素材(src 为 http(s))会先下载到 NSTemporaryDirectory() 下同名文件再播放(同名复用即一级缓存); 融合图片通过 loadVapImageWithURL 异步下载,本地绝对路径则直接读取
  • scaleType 三档均已支持(通过 contentModeaspectFitaspectFitaspectFillaspectFillscaleToFillscaleToFill); 但只认构造时的取值,运行期改 scaleTypev-if 重挂载(各端一致)
  • 音频会话(iOS 特有行为):播放前若当前会话是 ambient/soloAmbient(系统默认), 插件会把它切到 playbacksetActive(true) —— 否则音效遵循机身静音开关,表现为 "扬声器没声、插耳机才有声"mute=true 时不会改宿主会话; 宿主自己配置过会话(如直播间 playAndRecord 收麦)时插件保持不动。

Android

  • 依赖 utssdk/app-android/libs/animplayer-release.aar(腾讯 VAP 官方 + 本插件的 VapBridge 桥接类)
  • 融合图片只接受本地路径,网络地址由插件预下载(见「素材制作」一节)

微信小程序

  • 只能在真机运行(重要):动画的解码与音画同步管线以真机为准 —— 请用真机预览 / 真机调试验证播放、融合与音效; 开发者工具无法完整播放动画,仅可用于页面布局与「不校验合法域名」等配置检查

⚖️ 平台能力矩阵(含不支持项)

能力 Android iOS HarmonyOS H5 微信小程序
播放 / 停止 / 重播
pause / resume ❌ 上报 code=5 ✅(resume 为重新 play) ⚠️ 伪暂停(时间轴继续走)
mute 属性 ❌ 上报 code=5 ✅(构造期)
scaleType ✅(仅渲染分支)
@click
@frame 帧序号基准 1 基 1 基 1 基 1 基 0 基
运行期 setMixData/setLoop/setMute ⚠️ 下次播放生效 ❌ 上报 code=5
同页多实例 ❌ 不保证(容器 id 固定)
网络素材缓存 进程内按 URL 按 URL 末段名 引擎内部 浏览器缓存 沙箱 vap-cache(不自动清理)

其他已知限制(如实告知,避免售后争议):

  • Androidloop 语义为"播放次数",<=0 只播一次(与 iOS/微信的"无限"不同); setLoop 只在下次 play() 完全生效。
  • iOS:网络素材按 URL 末段名缓存到临时目录 —— 同名不同源的素材会互相覆盖(请避免用同名文件); 布局未就绪(父容器 0 尺寸)超过 5 秒会放弃本次播放并上报 code=5
  • 微信小程序:未传 config 时按常见拼版假定渲染;素材需 faststart(moov 在前); 无音轨素材会原地生成 .sil.mp4(磁盘占用翻倍);vap-cache 与临时文件不会自动清理, 长会话请自行定期清理用户目录;WASM 不可用时回退纯 JS 逐像素路径(较慢)。
  • H5:引擎 vap.min.js 必须随 static/ 分发;play() 需等引擎就绪(首次会动态加载引擎); 运行期不支持 setMixData/setLoop/setMute(上报 code=5)。
  • 全部端src/scaleType 等 props 变更不会自动重建播放器 —— 换素材请 v-if 重挂载(演示工程即如此)。

💰 付费授权与试用(买前必读)

  • 本插件含 UTS 原生代码 + 原生二进制(AAR / framework / har),只能用「自定义基座」或云打包运行; 官方标准基座、离线打包不支持付费插件 —— 这是插件市场规则,不是插件限制。
  • 试用机制:市场提供试用版,但试用基座无法脱离 HBuilderX 独立安装,且每次启动会弹一次测试 toast; 正式购买后使用自己的自定义基座即无 toast。
  • 购买前请用「试用 + 自定义基座」跑通你的素材与场景(融合元素、音效、@click 等), 平台能力差异见上表(尤其 Android 无 pause/resume、鸿蒙无 mute、H5/微信无 @click)。

📌 性能定位(VAP 强场景)

VAP 内核 = MP4 视频流 + 逐帧遮罩合成,与 PAG / SVGA(矢量 / 位图数据容器)本质不同。它的性能价值在特定强场景最突出:

  • 🎁 同屏多特效 / 直播间多礼物:视频硬解 + 只缓存一帧,内存省、CPU 省
  • 💥 大粒子 / 爆场动画:真视频级粒子特效,远超矢量方案的表达上限

定位一句话:主打「直播间多礼物、大粒子、低内存」这类视频特效场景;通用动效请搭配 PAG / SVGA —— 不是 VAP 差,而是它的性能价值在特效礼物场景最突出

🔐 隐私与安全

本插件不采集任何数据,不使用 IDFA / 定位 / 相机 / 麦克风 / 相册 / 通讯录,也不上报任何日志或统计。

  • 播放素材仅使用应用内本地路径,或由插件下载到应用沙箱后再播放;下载仅请求调用方传入的 src / imgUri 地址
  • 不主动申请任何敏感系统权限
  • 融合元素内容(昵称、头像等)由调用方提供,请自行确认已获得用户授权
  • 网络请求说明:播放网络素材 / 网络融合图片时,会向调用方传入的 URL 发起请求(对端可见 IP 与 UA), 插件本身不向任何第三方服务器上报数据
  • iOS 音频会话说明:播放(且非静音)时会把系统音频会话切到 playback 并激活, 以便静音开关打开时仍有声音;mute=true 或宿主已自行配置会话时不改动(详见「iOS」一节)
  • 插件实现文件已通过 uni_modules.encrypt 加密分发(详见 changelog.md

📄 许可证

  • Android / iOS / H5 部分封装自 Tencent VAPMIT)— 见 LICENSE-tencent-vap.txt
  • HarmonyOS 部分封装自 OHOS-VAPApache-2.0)— 见 LICENSE-ohos-vap.txt
  • 插件本体授权以插件市场页面 / 购买协议为准

隐私、权限声明

1. 本插件需要申请的系统权限列表:

本插件不主动申请敏感系统权限。

2. 本插件采集的数据、发送的服务器地址、以及数据用途说明:

插件不采集任何数据。播放资源仅使用应用内本地路径或下载到应用沙箱后再播放。

3. 本插件是否包含广告,如包含需详细说明广告表达方式、展示频率: