地址:https://ohpm.openharmony.cn/#/cn/detail/@ohos%2Fvap
代码地址:
https://gitcode.com/openharmony-tpc/openharmony_tpc_samples/tree/master/vap
OHOS-VAP
在数字娱乐和在线互动的时代,视觉效果的精美程度直接影响用户体验。OHOS-VAP 是一个基于 OpenHarmony 运用 OpenGL 技术和特殊算法打造的强大动画粒子特效渲染组件。它不仅能够为应用程序提供令人惊叹的动画效果,更为用户创造了一种身临其境的视觉享受。

主要特征
- 相比Webp, Apng动图方案,具有高压缩率(素材更小)、硬件解码(解码更快)的优点.
- 相比Lottie,能实现更复杂的动画效果(比如粒子特效)
- 高性能渲染:凭借 OpenGL 的强大能力,OHOS-VAP 实现了高效的粒子特效渲染,确保流畅的用户体验,适用于各种设备。
- 易于集成:OHOS-VAP 设计简洁,易于与现有项目集成,帮助开发者快速实现酷炫的动画效果,提升应用的吸引力。
- 多平台支持:兼容多个设备,无论是手机、平板还是电脑,OHOS-VAP 都能提供一致的视觉效果,助力跨终端应用开发。
应用场景
- 直播间特效:在各大短视频平台中如抖音,快手,得物,企鹅电竞,利用 OHOS-VAP 为直播间增添炫酷的礼物特效,提升观众的互动体验,增加直播的趣味性。
- 电商活动推广:在游戏领域及电商平台的活动中,使用 OHOS-VAP 实现令人惊叹的产品展示效果,吸引用户眼球,推动销售转化。
- 游戏体验提升:为游戏场景增添粒子特效,提升整体游戏体验,让玩家沉浸在更为生动的虚拟世界中。

构建依赖
- 对于开发人员可以调用
this.xComponentContext.play()接口来实现自定义视频传参路径(支持网路URL)
C/C++层目录结构
├─include # 遮罩 融合 渲染器 工具类头文件存放
│ ├─mask
│ ├─mix
│ ├─render
│ └─util
├─manager # xcomponent 生命周期管理
├─mask # 遮罩的实现
├─mix # 融合实现
├─napi # Napi 层功能的封装
├─render # 渲染器的实现
├─types # 接口声明
│ └─libvap # so文件接口声明
└─util # 工具类的实现0
源码下载
-
本项目依赖 json 库,通过
git submodule引入,下载代码时需加上--recursive参数。git clone --recursive https://gitcode.com/openharmony-tpc/openharmony_tpc_samples.git
-
开始编译项目。
快速使用
头文件引入
在使用文件中进行头文件的引入
import { VAPPlayer,MixData } from '@ohos/vap';
定义 VAPPlayer 组件
private vapPlayer: VAPPlayer | undefined = undefined;
@State buttonEnabled: boolean = true; // 该状态为控制按钮是否可以点击
@State src: string = "/storage/Users/currentUser/Documents/1.mp4"; // 该路径可为网络路径
配置网络资源下载路径
// 具体使用可参考示例代码
// 获取沙箱路径
let context : Context = getContext(this) as Context
let dir = context.filesDir
界面
private xComponentId: string = 'xcomponentId_' + util.generateRandomUUID()
XComponent({
id: this.xComponentId, // 唯一标识
type: 'surface',
libraryname: 'vap'
})
.onLoad((xComponentContext?: object | Record<string, () => void>) => {
if (xComponentContext) {
this.vapPlayer = new VAPPlayer(this.xComponentId)
this.vapPlayer.setContext(xComponentContext)
this.vapPlayer.sandDir = dir // 设置存储路径
}
})
.backgroundColor(Color.Transparent)
.height('100%')
.visibility(this.buttonEnabled ? Visibility.Hidden: Visibility.Visible)
.width('80%')
设置视频对齐方式
通过setFitType这个接口设置视频对齐方式(支持FIT_XY,FIT_CENTER,CENTER_CROP)

接口需要在play之前使用
this.vapPlayer?.setFitType(fitType)
使用
播放接口 Play 的使用
融合动画信息顺序自定义,需要指定 tag, tag 为视频制作时指定,该信息可通过this.vapPlayer.getVideoInfo(uri)接口获取本地视频信息, 通过this.vapPlayer.getVideoInfoAsync(uri)获取在线视频信息。
当融合信息为字体时,可配置字体的对齐,颜色,大小
let opts: Array<MixData> = [{
tag: 'sImg1',
imgUri: getContext(this).filesDir + '/head1.png'
}, {
tag: 'abc',
txt: "星河Harmony NEXT",
imgUri: getContext(this).filesDir + '/head1.png'
}, {
tag: 'sTxt1',
txt: "星河Harmony NEXT",
textAlign: this.textAlign,
fontWeight: this.fontWeight,
color: this.color
}];
this.buttonEnabled = false;
this.vapPlayer?.play(getContext(this).filesDir + "/vapx.mp4", opts, () => {
this.buttonEnabled = true;
});
当融合信息是图片时,可以使用本地的png图片或image.PixelMap,若使用image.PixelMap,当前仅支持像素格式为image.PixelMapFormat.RGBA_8888数据。
let info = await this.vapPlayer?.getVideoInfoAsync("https://static.mszmapp.com/files/20250530/8d7032c2665096d51ca1736c74753998.mp4")
console.log('getVideoInfo info ' + JSON.stringify(info))
let color = new ArrayBuffer(16);
let colorView = new Int8Array(color);
colorView.set([
// 第一行
0, 255, 0, 255, // 左 (绿色)
255, 255, 0, 255, // 右 (黄色)
// 第二行
0, 255, 0, 255, // 左 (绿色)
255, 255, 0, 255 // 右 (黄色)
])
let pixelMap = image.createPixelMapSync({size: {height:2, width: 2}, pixelFormat: image.PixelMapFormat.RGBA_8888})
pixelMap.writeBufferToPixelsSync(color)
let opts: Array<MixData> = []
if (info?.srcInfos !== undefined) {
for (let s of info?.srcInfos) {
if (s.type === SrcType.IMG) {
opts.push({
tag: s.tag,
imgUri: pixelMap
})
} else if (s.type === SrcType.TXT) {
opts.push({
tag: s.tag,
txt: "星河Harmony NEXT 星河Harmony NEXT",
})
}
}
}
this.buttonEnabled = false;
this.vapPlayer?.play("https://static.mszmapp.com/files/20250530/8d7032c2665096d51ca1736c74753998.mp4", opts, () => {
LogUtil.info("js get callback")
this.buttonEnabled = true;
});
暂停的使用
this.vapPlayer?.pause()
停止的使用
this.vapPlayer?.stop()
异步停止的使用 (StopAsync)
this.vapPlayer?.stopAsync('unique_id', 3000).then(() => {
console.log('停止操作完成');
}).catch((error) => {
console.error('停止失败:', error);
});
this.vapPlayer?.setStopCompleteCallback(() => {
console.log('停止操作已成功完成');
});
监听手势
-
在动画播放过程中点击播放区域,如果点击到融合动画资源,回调会返回该资源(字符串)
-
接口需要在
play之前使用this.vapPlayer?.on('click', (state)=>{
if(state) {
console.log('js get onClick: ' + state)
}
})
监听播放生命周期变化
接口需要在play之前使用
this.vapPlayer?.on('stateChange', (state, ret)=>{
if(state) {
console.log('js get on: ' + state)
if(ret)
console.log('js get on frame: ' + JSON.stringify(ret))
}
})
-
回调参数
state反应当前播放的状态enum VapState {
UNKNOWN,
READY,
START,
RENDER,
COMPLETE,
DESTROY,
FAILED
} -
参数
ret,当state为RENDER或START返回AnimConfig对象 -
参数
ret,当state为FAILED反应当前的错误码 -
参数
ret,其余状态为undefined
应用退出后台
onPageHide(): void {
console.log('[LIFECYCLE-Page] onPageHide');
this.vapPlayer?.stop()
}
可在页面的生命周期中调用onPageHide方法
兼容老视频(alphaplayer 对称的视频)
this.vapPlayer?.setVideoMode(VideoMode.VIDEO_MODE_SPLIT_HORIZONTAL)
对于老视频推荐调用这个接口,接口需要在play之前使用
约束与限制
在下述版本通过
- DevEco Studio 5.0(5.0.3.810), SDK: API12(5.0.0.60)
权限设置
-
如果确定视频文件在沙箱中则不必配置
-
在应用模块的
module.json5中添加权限, 例如:entry\src\main\module.json5 -
READ_MEDIA读取用户目录下的文件(比如 文档);WRITE_MEDIA(下载到用户目录下);INTERNET下载网络文件"requestPermissions": [
{
"name": 'ohos.permission.READ_MEDIA',
"reason": 'string:read_file', "usedScene": { "abilities": [ "EntryAbility" ], "when": "always" } }, { "name": 'ohos.permission.WRITE_MEDIA', "reason": 'string:read_file',
"usedScene": {
"abilities": [
"EntryAbility"
],
"when": "always"
}
},
{
"name": "ohos.permission.INTERNET"
}
]
编译构建
- 工程创建成功后,构建请运行
Build -> Build Hap(s)/APP(s) -> build App(s)选项 /entry/build/default/outputs生成hap包- 签名安装生成的
hap包
测试demo
点击 Play 按钮,测试动画效果,再次点击进入循环播放