更新记录

1.1.2(2026-09-23)

维护位版本:1.1.1 已于同日上架,本版暂无代码改动(1.1.1 之后的工作为文档一致性维护与 构建可复现性修复,不影响运行行为)。后续改动将追加到本条。

1.1.1(2026-09-18)

本版重点:iOS 打包彻底免切换 + 模拟器不再偶发崩溃。

模拟器统一走软解(消除苹果 VT/CoreVideo 偶发崩溃)

模拟器上 VT 软解每帧都要经过 CoreVideo 像素池,该池在模拟器下存在已知缺陷 (x86_64/Rosetta 必崩、arm64 偶发崩,栈为 CVPixelBuffer::setDefaultAttachments)。 本版把所有模拟器统一路由到自研 AVAssetReader 顺序软解解码器(独立分配路径,绕开该池), 真机不受影响(仍是原生 VT 硬解)。实测 arm64 与 x86_64 模拟器连续多轮播放均稳定、画面与声音正常。

iOS 依赖形态:单一静态 xcframework(打包自动选架构,无需切换)

iOS 端由"真机/模拟器分目录 + 手动切切片"改为单一 QGVAPlayer.xcframework (内含 ios-arm64 与 ios-arm64_x86_64-simulator 两个变体,静态库): 真机与模拟器打包均由 Xcode 自动选切片 —— 此前选「iOS Appstore(模拟器)」会报 Undefined symbols ... _OBJC_CLASS_$_QGVAPWrapView,现在两种打包类型都直接通过。 静态库无需嵌入(HBuilderX 对 .xcframework 不做 embed,动态库形态会 dyld 失败), 且其 UTS 插件模板自带 -ObjC,静态库中的分类不会被剥离。

1.1.0(2026-09-18)

本版重点:iOS 模拟器从"不可用"变为"可用",首播更快,鸿蒙融合头像修复。

iOS 模拟器现在可以播放动画(此前官方源码层面不支持)

腾讯 VAP 源码里原本有两道"模拟器不支持":p_playHWDMP4 的禁播守卫(按 hw.machine 为 x86_64/i386 时直接返回)、以及 Metal 渲染链在 #if TARGET_OS_SIMULATOR 下被整块挖空。 本版按架构编译期隔离地全部解决(真机零影响):

  • 取消 Metal 挖空,模拟器与真机共用同一 Metal 链路(含 vapx 融合合成);
  • 渲染器增加 IOSurface 兜底拷贝(模拟器软解输出无 IOSurface 背板,CVMetalTextureCache 会失败);
  • 移除禁播守卫;
  • x86_64(Rosetta)另拆四处闸门:AVAudioPlayer 初始化间歇挂起(改为后台异步,避免阻塞)、 kCVPixelBufferMetalCompatibilityKey 在 x86_64 有害(撤销)、 CoreVideo 像素池在 Rosetta 下损坏(该架构改用自研 AVAssetReader 顺序软解解码器绕开)。

验收(2026-09-18):iPhone 真机 total=240/12.07s(20fps 满帧)+ total=80; arm64 模拟器首播 0.5s、x86_64 模拟器首播 1.0s,均为满帧且有声、融合画面正常。

首播更快(预取 + 管线预热)

  • 新增 preloadVap(src):素材预取,页面创建即下载到与播放同一份缓存(幂等、失败静默), 消除首播时"点了要等下载"的 1-2 秒;其它平台为空操作,API 一致。
  • 框架侧 Metal 管线状态预热:把原本"首个 draw 才编译"的管线编译提前到视图初始化, 消除首播头几帧的编译卡顿。

HarmonyOS 融合头像修复

本地静态图(/static/user.png)在鸿蒙上不显示(动画正常、无报错)。两个机制坑: ① autoplay 由 builder.ets 直接播放,不经过 UTS 的 play();② XComponent 的 ctx 不是完整 Context(resourceDir 为 undefined),ArkTS 侧无法自行拼路径。 现改为在 UTS 构造函数 / setMixData 里统一用 UTSHarmony.getResourcePath() 解析真实路径, 两条播放路径共用同一份 params.mixJson。

其它

  • 修 SW 软解解码器的一个潜在死锁:显示循环抖动导致的非连续帧请求原本被静默丢弃 (缓冲永不回填 → 播放中途冻住),改为"领先则追赶、落后则忽略"。
  • 平台声明:传统 uni-app 段全部标不支持(同 1.0.2 口径,本版起随版本号一并生效)。
查看更多

平台兼容性

uni-app(3.8.4)

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

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 三端原生渲染(真机硬解,iOS 模拟器走自研软解;Metal / OpenGL),H5 用 vap-web(WebGL),微信小程序用自研 VideoDecoder + Canvas2D 引擎
  • 👤 融合元素:动画内预留 tag,运行时注入头像 / 昵称 / 图片 / 文字(VAP 招牌能力)
  • 🔊 音效支持(五端均出声):音效 / 背景音内嵌在 VAP mp4 里,播放即有声、音画天然同步(素材无需单独挂音频文件)。 各端出声路径:Android/iOS 走原生解码 + 系统音频会话(iOS 含静音开关下的会话处理,避免"扬声器没声、插耳机才有声"); 微信小程序为 VideoDecoder + MediaAudioPlayer 管线级音画同步;H5 由 web 引擎的 video 元素出声; HarmonyOS 由 OHOS-VAP 原生播放器的 InitAudioPlayer 出声。 ⚠️ 与"出声"是两件事:mute 属性在各端支持度不同 —— 鸿蒙侧 @ohos/vap 无 mute API, mute 属性被静默忽略(不生效、也不报错;只有直接调播放器的 setMute() 才会收到 code=5), H5 仅构造期生效。详见下方平台能力矩阵。
  • ⚡ 网络地址:直接传 http(s),组件内部自动下载缓存后播放
  • 🎛️ 完整事件:started / frame / finish / error / click,帧序号与总帧数五端语义已对齐
  • 📐 缩放与循环:scaleType(aspectFit / aspectFill / scaleToFill)、loop、mute
  • 🪶 低内存:视频硬解 + 只缓存一帧,适配「同屏多礼物、大粒子」场景

为什么鸿蒙只在 uni-app x 里支持(机制说明)

本插件的 App 三端统一走「原生视图」:组件里放 <native-view>,UTS 侧通过 element.bindHarmonyWrappedBuilder(wrapBuilder(buildVap)) 把 ArkTS @Builder 构造的原生视图 (鸿蒙为 XComponent + OHOS-VAP 的 libvap.har;Android 为 animplayer-release.aar)挂到该节点上, 尺寸与位置由 native-view 在页面布局中的盒子决定(因此内联小尺寸同样可用,不只是全屏覆盖层)。

关键点:这条路径只在 uni-app x 中存在 ——

  • 传统 uni-app 的「兼容模式」UTS 组件:官方明确不支持鸿蒙,且依赖 nvue(已不再维护);
  • renderjs:只支持 app-vue 与 web(依赖 webview 视图层),鸿蒙 Next 真机上 renderjs 内部函数不会触发;
  • 「标准模式」组件:仅限 uni-app x。

⇒ 所以"传统 uni-app 项目 + 鸿蒙 + 原生组件"在官方机制上没有路径,鸿蒙能力请使用本插件(uni-app x)。 (另注:市场上部分传统 uni-app 插件在平台表里标注"鸿蒙 √",按上述机制其无法在传统鸿蒙工程中提供 原生组件能力,选购时建议自行验证。)

⚠️ 本插件仅适用于 uni-app x 项目(交付物是 axen-vap-x.uvue,传统 uni-app 无法使用 vue 之外的后缀)。 传统 uni-app(vue) 项目请使用配套免费组件 axen-vap —— H5 + 微信小程序, Vue2 / Vue3 工程均已真机验证通过(2026-09-16)。 平台声明已按此如实标注(1.0.2 起,此前误标了传统 uni-app 支持)。 支付宝等其余小程序平台因无逐帧视频解码 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 上该属性不生效,且不会因此上报事件)
scaleType string aspectFit aspectFit / aspectFill / scaleToFill
config any null vapc 配置。App 三端可留空(原生引擎自己解析 mp4 的 vapc box);H5 留空时组件会自动 fetch 素材并提取;微信端建议显式传入 —— 留空时自研引擎按「RGB 在上 / alpha 在下」的常见拼版假定渲染,遇到别的拼版会裁剪错误
bypassProcess boolean false 仅微信小程序:跳过素材预处理(包内素材可用)

模块级函数(从插件入口 import,不是组件 ref 上的方法)

函数 说明
preloadVap(src) 素材预取(推荐调用):在页面初始化阶段就把素材下载到与播放同一份缓存,消除首播时"点了要等下载"的 1-2 秒。幂等、失败静默。iOS 已实现;其它平台为空操作(API 保持一致,便于跨端书写)。用法:import { preloadVap } from "@/uni_modules/axen-vap-x" 后直接调用(不是 vapRef.value?.preloadVap())

事件

事件 参数 触发
ready — 组件侧播放器封装对象已创建、事件回调已接好(native-view / canvas 就绪)。App 三端此时调 play() 最稳妥;鸿蒙原生播放器另有初始化时序,未就绪时 play() 会上报 code=5
started — 播放开始
frame { index } 每渲染一帧。index 五端统一 1 基(第 1 帧为 1,1.1.1 起对齐;1.1.0 时 iOS 与微信曾为 0 基)
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(直接调播放器时)与"播放器未就绪时 play()"、微信在已结束时 resume、H5 运行期 setMixData/setLoop/setMute

定义在 utssdk/interface.uts 的 VapErrorCode。能力缺失一律报 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 { AxenVapXComponentPublicInstance } 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?.clear()         // 清空画布(ref 上暴露的名字是 clear(),内部转调 player.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)只接受本地文件路径 (其 decodeBitmap 是 File(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.uts,interface.uts 放 utssdk/ 下(与官方 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.net 带 ACAO: *、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 原生 arm64 / x86_64 Rosetta 都已验证),详见下节

🔧 平台特定说明

iOS

  • iOS 模拟器 ✅ 可播放动画(2026-09-18 起;此前版本的本节曾写"模拟器无法播放",该结论已推翻): 腾讯源码原先有两道"模拟器不支持"——p_playHWDMP4 的禁播守卫(hw.machine == x86_64/i386 时 直接 not allowed in Simulator! 返回)+ Metal 渲染链在 #if TARGET_OS_SIMULATOR 下被整块挖空。 本插件已按 arch 编译期隔离地全部解决(真机零影响):
    1. 取消 Metal 挖空,模拟器与真机共用同一 Metal 链;
    2. 渲染器加 IOSurface 兜底拷贝(模拟器软解输出无 IOSurface 背板,CVMetalTextureCache 会失败);
    3. 移除禁播守卫;
    4. 模拟器统一改用自研 AVAssetReader 顺序软解解码器:模拟器上 VT 软解每帧都要经过 CoreVideo 像素池,该池在模拟器下存在已知缺陷(x86_64/Rosetta 必崩、arm64 偶发崩, 栈为 CVPixelBuffer::setDefaultAttachments)。软解走独立分配路径、彻底绕开该池。 真机不受影响(仍是原生 VT 硬解)。
    5. 另有四处闸门(与上一条"模拟器统一软解"分开计):① 禁播守卫(撤);② AVAudioPlayer 初始化间歇挂起 → 后台异步;③ kCVPixelBufferMetalCompatibilityKey 有害 → 撤销(保持腾讯原版解码属性); ④ 模拟器 CoreVideo 像素池缺陷 → 由软解路径绕开。 实测(2026-09-18):arm64 与 x86_64 模拟器均 total=240(12s,20fps 满帧)+ total=80, 连续多轮稳定、有画面有声音、融合画面正常。 ⇒ 使用建议:模拟器可直接验证动画/融合/事件;真机仍是发布前的推荐验收口径(性能与音效更真实)。
  • iOS 真机打包前置(付费款必须):本插件含原生 framework,必须用自定义基座, 云打包/自定义基座在 iOS 上要求手动创建的描述文件(Xcode 托管描述文件会被 DCloud 云打包拒绝; 免费个人开发者账号也拿不到 App Store 类型的描述文件)。前置条件与步骤见仓库 SHIPPING.md B·补 节。 (免费版 iOS 走 WebGL 软路径,不需要自定义基座 —— 这也是拿它当试用入口的原因之一。)
  • iOS framework 为单一 .xcframework(静态库双变体),打包时自动选切片、无需切换(2026-09-18 起): utssdk/app-ios/Frameworks/QGVAPlayer.xcframework 内含 ios-arm64(真机)与 ios-arm64_x86_64-simulator(模拟器)两个变体,Xcode/HBuilderX 会按打包目标自动选择。
    • 为什么是静态库:动态库形态下真机 arm64 与模拟器 arm64 同架构不同平台无法合并为单个 .framework;而 HBuilderX 的插件 framework 处理只对 .framework 判定"是否动态库", .xcframework 一律 embed=false ⇒ 动态库 xcframework 运行期会 dyld 失败。静态库不需要 embed, 因此 xcframework 可用(HBuilderX 的 UTS 插件模板自带 -ObjC,静态库里的分类不会被剥离)。
    • metallib 处理:静态 framework 的资源不会被复制进 App,故按平台把 default.metallib 编译成字节 内嵌进二进制(构建脚本生成 QGVAPEmbeddedMetalLib.h),加载器优先读 bundle 资源、其次落临时文件加载。
    • 对比:旧版是"真机/模拟器分目录 + 手动切切片",打模拟器包会报 Undefined symbols for architecture x86_64: _OBJC_CLASS_$_QGVAPWrapView。现已不再需要任何切换。
    • 如需自行从源码重建:bash scripts/build-ios-framework.sh(会重新生成 xcframework; 切片备份放在仓库 .ios-build/xcf-slices/{device,simulator}/,不再放插件目录内, 避免被构建工具当成"插件依赖框架"误捡)。
  • 本地校验 UTS 是否可编译:bash scripts/check-ios-uts.sh(用 HBuilderX 自带的 UTS→Swift 编译器生成 Swift,再对真实 framework 做 swiftc -typecheck,无需打开 HBuilderX)
  • 网络素材(src 为 http(s))会先下载到 NSTemporaryDirectory() 下同名文件再播放(同名复用即一级缓存); 融合图片通过 loadVapImageWithURL 异步下载,本地绝对路径则直接读取
  • scaleType 三档均已支持(通过 contentMode:aspectFit→aspectFit、aspectFill→aspectFill、scaleToFill→scaleToFill); 但只认构造时的取值,运行期改 scaleType 需 v-if 重挂载(各端一致)
  • 音频会话(iOS 特有行为):播放前若当前会话是 ambient/soloAmbient(系统默认), 插件会把它切到 playback 并 setActive(true) —— 否则音效遵循机身静音开关,表现为 "扬声器没声、插耳机才有声"。mute=true 时不会改宿主会话; 宿主自己配置过会话(如直播间 playAndRecord 收麦)时插件保持不动。

Android

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

微信小程序

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

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

能力 Android iOS HarmonyOS H5 微信小程序
播放 / 停止 / 重播 ✅ ✅ ✅ ✅ ✅
pause / resume ❌ 上报 code=5 ✅ ✅(resume 为重新 play) ✅ ⚠️ 伪暂停(时间轴继续走)
内嵌音轨出声(播放即有音效) ✅ ✅ ✅(原生 InitAudioPlayer) ✅(web video 元素) ✅(MediaAudioPlayer)
mute 属性 ✅ ✅ ❌ 不生效且不报错(@ohos/vap 无 mute API;只有直接调 setMute() 才上报 code=5) ✅(构造期) ✅
scaleType ✅ ✅ ✅ ✅ ✅(仅渲染分支)
@click ✅ ✅ ✅ ❌ ❌
@frame 帧序号基准 1 基(五端统一,1.1.1 起) ← ← ← ←
运行期 setMixData/setLoop/setMute ✅ ⚠️ 下次播放生效 setMixData/setLoop ✅;setMute ❌ 上报 code=5 ❌ 上报 code=5 ✅
同页多实例 ✅ ✅ ✅ ❌ 不保证(容器 id 固定) ✅
网络素材缓存 进程内按 URL 按 URL 末段名 引擎内部 浏览器缓存 沙箱 vap-cache(不自动清理)

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

  • Android:loop 语义为"播放次数",<=0 只播一次(与 iOS/微信的"无限"不同); setLoop 只在下次 play() 完全生效。
  • iOS:网络素材按 URL 末段名缓存到临时目录 —— 同名不同源的素材会互相覆盖(请避免用同名文件); 布局未就绪(父容器 0 尺寸)超过 5 秒会放弃本次播放 —— 不上报事件(仅在 setVapDebug(true) 时打印一行日志),需要感知请自行加超时兜底。
  • 微信小程序:未传 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 VAP(MIT)— 见 LICENSE-tencent-vap.txt
  • HarmonyOS 部分封装自 OHOS-VAP(Apache-2.0)— 见 LICENSE-ohos-vap.txt
  • 插件本体授权以插件市场页面 / 购买协议为准

隐私、权限声明

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

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

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

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

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

无