文件最后提交记录最后更新时间
30 天前
1 年前
21 天前
21 天前
1 年前
1 年前
1 个月前
1 年前
1 年前
1 年前
1 年前
1 年前
21 天前
README.md

OHOS-VAP

English | 简体中文

License

In the era of digital entertainment and online interaction, the quality of visual effects directly impacts user experience. OHOS-VAP is a powerful animation particle effect rendering component built on OpenHarmony using OpenGL technology and specialized algorithms. Not only does it provide stunning animation effects for applications, but it also creates an immersive visual experience for users.

Key Features

  • Compared to Webp and Apng animation schemes, it offers high compression rates (smaller assets) and hardware decoding (faster decoding) advantages.
  • Compared to Lottie, it can achieve more complex animation effects (like particle effects).
  • High-performance rendering: With the powerful capabilities of OpenGL, OHOS-VAP achieves efficient particle effect rendering, ensuring a smooth user experience across various devices.
  • Easy integration: OHOS-VAP has a simple design, making it easy to integrate with existing projects, helping developers quickly implement stunning animation effects and enhance application appeal.
  • Multi-platform support: Compatible with multiple devices, whether on phones, tablets, or computers, OHOS-VAP can provide consistent visual effects, facilitating cross-terminal application development.

Application Scenarios

  • Live broadcast effects: On major short video platforms such as Douyin, Kuaishou, and others, use OHOS-VAP to add cool gift effects to live broadcasts, enhancing audience interaction and increasing the fun of the broadcast.
  • E-commerce promotional activities: In gaming and e-commerce platform events, use OHOS-VAP to achieve stunning product display effects, attract users' attention, and drive sales conversions.
  • Game experience enhancement: Add particle effects to game scenes to enhance overall gaming experience, immersing players in a more vivid virtual world. apps

Build Dependencies

  • IDE Version: DevEco Studio 5.0.1.403
  • SDK Version: ohos_sdk_public 5.0.0 (API Version 12 Release)
  • Developers can call the this.xComponentContext.play() interface to implement custom video parameter paths (supports network URLs).

C/C++ Layer Directory Structure

├─include				# Mask, Mix, Renderer, Utility class header files storage
│  ├─mask
│  ├─mix
│  ├─render
│  └─util
├─manager 				# xcomponent life cycle management
├─mask 					# Implementation of masking
├─mix 					# Implementation of mixing
├─napi					# Napi layer function encapsulation
├─render				# Implementation of the renderer
├─types   				# Interface declarations
│  └─libvap	# so file interface declarations
└─util					# Implementation of utility classes

How to Install

ohpm install @ohos/vap

For details about the OpenHarmony ohpm environment configuration, see OpenHarmony HAR.

Source code download

  1. This project relies on json library, which is introduced by git submodule, and --recursive parameter should be added when downloading code.
git clone --recursive https://gitcode.com/CPF-ApplicationTPC/openharmony_tpc_samples.git
  1. Start compiling the project.

Quick Start

  1. For API mode, refer to the example code API Mode
  2. For component mode, refer to the example code Component Mode, for easier use.

Importing Header Files

Import header files in the usage file.

import { VAPPlayer,MixData } from '@ohos/vap';

Define VAPPlayer Component

private vapPlayer: VAPPlayer | undefined = undefined;
@State buttonEnabled: boolean = true; // This state controls whether the button can be clicked
@State src: string = "/storage/Users/currentUser/Documents/1.mp4"; // This path can be a network path

Configure Network Resource Download Path

// For specific usage, refer to the example code
// Get sandbox path
let context : Context = getContext(this) as Context
let dir = context.filesDir

Interface

private xComponentId: string = 'xcomponentId_' + util.generateRandomUUID()
XComponent({
  id: this.xComponentId, // Unique identifier
  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 // Set storage path
    }
  })
  .backgroundColor(Color.Transparent)
  .height('100%')
  .visibility(this.buttonEnabled ? Visibility.Hidden: Visibility.Visible)
  .width('80%')

Set Video Alignment Mode

Set the video alignment mode through the setFitType interface (supports FIT_XY, FIT_CENTER, CENTER_CROP)

This interface needs to be used before play.

this.vapPlayer?.setFitType(fitType)

Loop playback (setLoop / loops)

Controls how many times the currently loaded video animation repeats after play() is called. Configure setLoop(loops) or (component mode only) the loops property before calling play(), same as setFitType / setVideoMode.

Class API Parameter Default Description
VAPPlayer setLoop(loops: number): void loops: number 1 Sets the loop count passed to native when play() runs. Use Infinity for infinite repeat until stop() / stopAsync() or teardown.
VAPComponent setLoop(loops: number): void loops: number 1 Same semantics as VAPPlayer.
VAPComponent loops: number (public field) 1 May assign directly, e.g. this.vapCmp.loops = 3; play() still applies this.loops to native. Prefer setLoop() if you need to sync the value to native as soon as XComponent context exists.

Notes

  • After a normal playback complete callback or stop(), the implementation resets the internal loop counter to 1; call setLoop() again before the next play() if you need a loop count other than the default value of 1.
  • Infinity means “no fixed upper bound”; stopping is still done explicitly via stop / lifecycle, as in the sample pages.
// Play the file 3 times, then fire the play completion callback
this.vapPlayer?.setLoop(3);
this.vapPlayer?.play(uri, opts, () => { this.buttonEnabled = true; });

// Infinite loop until stop()
this.vapPlayer?.setLoop(Infinity);
this.vapPlayer?.play(uri, opts, () => { /* may not run until stopped */ });

Usage

Using the Play Interface

To customize the order of merged animation information, you need to specify a tag. The tag is defined during video production. You can obtain local video information through the this.vapPlayer.getVideoInfo(uri) interface, and online video information through this.vapPlayer.getVideoInfoAsync(uri).

When the merged information is text, you can configure the text alignment, color, and size.

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;
});

When the merged information is an image, you can use a local png file or an image.PixelMap. If using image.PixelMap, only the pixel format image.PixelMapFormat.RGBA_8888 is currently supported.

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;
});

Using Pause

this.vapPlayer?.pause()

Using Stop

this.vapPlayer?.stop()

Using StopAsync (Asynchronous Stop)

// Asynchronous stop with timeout (recommended for new applications)
this.vapPlayer?.stopAsync('unique_id', 3000).then(() => {
  console.log('Stop completed successfully');
}).catch((error) => {
  console.error('Stop failed:', error);
});

// Set stop completion callback (for error handling)
this.vapPlayer?.setStopCompleteCallback(() => {
  console.log('Stop operation completed successfully');
});

Listening for Gestures

  • During animation playback, if the clickable area is tapped and a merged animation resource is clicked, a callback will return that resource (string).
  • This interface needs to be used before play.
this.vapPlayer?.on('click', (state)=>{
  if(state) {
    console.log('js get onClick: ' + state)
  }
})

Listening for Playback Lifecycle Changes

This interface needs to be used before 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))
  }
})
  • Callback parameter state reflects the current playback status.
enum VapState {
  UNKNOWN,
  READY,
  START,
  RENDER,
  COMPLETE,
  DESTROY,
  FAILED
}
  • Parameter ret, when state is RENDER or START, returns the AnimConfig object.
  • Parameter ret, when state is FAILED, reflects the current error code.
  • Parameter ret, other statuses will be undefined.

Application Exit Background

  onPageHide(): void {
    console.log('[LIFECYCLE-Page] onPageHide');
    this.vapPlayer?.stop()
  }

You can call the onPageHide method in the page lifecycle.

Compatibility Mode for Legacy Videos (alphaplayer symmetrical videos)

this.vapPlayer?.setVideoMode(VideoMode.VIDEO_MODE_SPLIT_HORIZONTAL)

For older videos, it is recommended to call this interface. This interface needs to be used before play.

Constraints and Limitations

Passes in the following versions:

  • DevEco Studio 5.0(5.0.3.810), SDK: API12(5.0.0.60)

Permissions Setup

  • No configuration required if the video file is confirmed to be in the sandbox.
  • Add permissions in the application module's module.json5, for example: entry\src\main\module.json5
  • READ_MEDIA to read files in the user's directory (like documents); WRITE_MEDIA (to download to the user's directory); INTERNET to download network files.
"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"
}
]

Compile Build

  • After creating the project successfully, to build run Build -> Build Hap(s)/APP(s) -> build App(s) option.
  • The /entry/build/default/outputs will generate a hap package.
  • Sign and install the generated hap package.

Test Demo

Click the Play button to test animation effects, and click again to enter loop playback.