鸿蒙炫酷的动画库——ohos-vap

​​​​​地址: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

源码下载

  1. 本项目依赖 json 库,通过git submodule引入,下载代码时需加上--recursive参数。

    git clone --recursive https://gitcode.com/openharmony-tpc/openharmony_tpc_samples.git

  2. 开始编译项目。

快速使用

  1. api模式 可参考示例代码 api模式
  2. 组件模式 可参考示例代码 组件模式,使用更便捷

头文件引入

在使用文件中进行头文件的引入

复制代码
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 ,当 stateRENDERSTART 返回 AnimConfig 对象

  • 参数 ret ,当 stateFAILED 反应当前的错误码

  • 参数 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 按钮,测试动画效果,再次点击进入循环播放

相关推荐
游戏技术分享8 小时前
【鸿蒙游戏技术分享 第75期】AGC后台批量导入商品失败,提示“参数错误”
游戏·华为·harmonyos
No Silver Bullet9 小时前
HarmonyOS NEXT开发进阶(十七):WebView 拉起 H5 页面
华为·harmonyos
liuhaikang9 小时前
【鸿蒙HarmonyOS Next App实战开发】口语小搭档——应用技术实践
harmonyos
北方的流星11 小时前
华为交换机MSTP和VRRP综合应用配置
运维·网络·华为
C雨后彩虹12 小时前
简易内存池
java·数据结构·算法·华为·面试
liuhaikang12 小时前
鸿蒙VR视频播放库——md360player
音视频·vr·harmonyos
北方的流星14 小时前
华为交换机IPv6静态路由、默认路由、RIPng和OSPFv3路由配置
运维·网络·华为
飞露14 小时前
鸿蒙Preview预览文件失败原因
华为·harmonyos
夏小鱼的blog15 小时前
【HarmonyOS应用开发入门】第五期:状态管理V2入门 - 1
harmonyos·状态管理
小雨青年15 小时前
鸿蒙 HarmonyOS 6 | ArkUI (04):数据展示 List 列表容器 LazyForEach 懒加载机制
华为·list·harmonyos