DEV Community

Cover image for HarmonyOS Development: Understanding Frame Animation
程序员一鸣
程序员一鸣

Posted on

HarmonyOS Development: Understanding Frame Animation

Foreword 

this article is based on Api12 

the so-called frame animation is similar to playing movies, playing frame by frame. Compared with attribute animation, we can set relevant attribute values for each frame, and has the advantages of pausing and continuing playback, and also has real-time response to events. It should be noted that the performance is far inferior to attribute animation, so if the scene can be realized by attribute animation, or the main push property animation. 

Implement frame animation 

A component moves frame by frame and can be paused. 

First of all, frame animation is to create a frame animation through gettext ().createAnimator, and through the onFrame method to receive the frame callback, and in this method to perform related animation operations. 

Simple example


 

@Entry
@Component
struct Index {
@State animatorOptions: AnimatorResult | undefined = undefined
animatorOption: AnimatorOptions = {
duration: 3000, //The duration of animation playback
delay: 0, //Animation delay playback duration
easing: 'linear', //Animation interpolation curve
iterations: 1, //Number of animation plays
fill: "forwards", //Is the animation restored to its initial state after execution
direction: 'normal', //Animation playback mode
begin: 0, //Animation interpolation starting point
End: 100//Animation interpolation endpoint
}
@State translateX: number = 0
@State translateY: number = 0
onPageShow(): void {
this.animatorOptions = this.getUIContext().createAnimator(this.animatorOption)
this.animatorOptions.onFrame = (progress: number) => {
//Callback upon receiving a frame
this.translateX = progress
}
}
onPageHide(): void {
this.animatorOptions = undefined
}
build() {
RelativeContainer() {
Text("1")
.width(30)
.height(30)
.textAlign(TextAlign.Center)
.backgroundColor(Color.Red)
.margin({ top: 100 })
.id("view1")
.alignRules({
top: { anchor: "__container__", align: VerticalAlign.Top },
middle: { anchor: "__container__", align: HorizontalAlign.Center }
})
.translate({ x: this.translateX, y: this.translateY })
Row() {
Button (Play)
.onClick(() => {
this.animatorOptions? .play()
})
Button (pause)
.margin({ left: 10 })
.onClick(() => {
this.animatorOptions? .pause()
})
Button (Reset)
.margin({ left: 10 })
.onClick(() => {
this.translateX = 0
this.translateY = 0
})
}
.margin({ top: 10 })
.alignRules({
center: { anchor: "__container__", align: VerticalAlign.Center },
middle: { anchor: "__container__", align: HorizontalAlign.Center }
})
}
.height('100%')
.width('100%')
}
}
Enter fullscreen mode Exit fullscreen mode

the createAnimator method creates a frame animation and returns an AnimatorResult object. Through AnimatorResult, we can implement many functions, such as play, pause, cancel, monitor status, etc. 

Play


 

this.animatorOptions?.play()
Enter fullscreen mode Exit fullscreen mode

pause


 

this.animatorOptions?.pause()
Enter fullscreen mode Exit fullscreen mode

cancel


 

this.animatorOptions?.cancel()
Enter fullscreen mode Exit fullscreen mode

reverse


 

this.animatorOptions?.reverse()
Enter fullscreen mode Exit fullscreen mode

complete


 

this.animatorOptions?.finish()
Enter fullscreen mode Exit fullscreen mode

animation Complete Callback


 


 this.animatorOptions!.onFinish = () => {

 }
Enter fullscreen mode Exit fullscreen mode

animation Cancel Callback


 


this.animatorOptions!.onCancel = () => {

}
Enter fullscreen mode Exit fullscreen mode

animation Repeat Callback


 

this.animatorOptions!.onRepeat = () => {
}
Enter fullscreen mode Exit fullscreen mode

set the desired frame rate range 

byThe setExpectedFrameRateRange method is used to set the Api, which must be 12 or more.

 

let expectedFrameRate: ExpectedFrameRateRange = {
  min: 0,
  max: 60,
  expected: 30
}
animatorResult.setExpectedFrameRateRange(expectedFrameRate)
Enter fullscreen mode Exit fullscreen mode

Picture Frame Animation 

frame animation, the most used place is the picture, such as loading prompt, a simple small animation, etc., the use frequency is quite high, the system provides the ImageAnimator component is convenient for us to implement. 

Simple example


 

@Entry
@Component
struct Index {
  @State state: AnimationStatus = AnimationStatus.Running
  private images: Array<ImageFrameInfo> = [
    { src: $r("app.media.loading001") },
    { src: $r("app.media.loading002") },
    { src: $r("app.media.loading003") },
    { src: $r("app.media.loading004") },
    { src: $r("app.media.loading005") },
    { src: $r("app.media.loading006") },
    { src: $r("app.media.loading007") },
    { src: $r("app.media.loading008") },
    { src: $r("app.media.loading009") },
    { src: $r("app.media.loading010") },
    { src: $r("app.media.loading011") },
    { src: $r("app.media.loading012") }
  ]

  build() {
    RelativeContainer() {

      Column() {

        ImageAnimator()
          .images(this.images)
          .fixedSize(true)
          .fillMode(FillMode.None)
          .iterations(-1)
          .state(this.state)
          .width(40)
          .height(40)

      }
      .id("view1")
      .width(120)
      .height(120)
      .borderRadius(10)
      .backgroundColor("#80000000")
      .justifyContent(FlexAlign.Center)
      .alignRules({
        top: { anchor: "__container__", align: VerticalAlign.Top },
        middle: { anchor: "__container__", align: HorizontalAlign.Center }
      })
      .margin({ top: 50 })


      Row() {
        Button("start")
          .onClick(() => {
            this.state = AnimationStatus.Running
          })
        Button("pouse")
          .margin({ left: 10 })
          .onClick(() => {

            this.state = AnimationStatus.Paused

          })
        Button("stop")
          .margin({ left: 10 })
          .onClick(() => {
            this.state = AnimationStatus.Stopped
          })
      }
      .margin({ top: 10 })
      .alignRules({
        center: { anchor: "__container__", align: VerticalAlign.Center },
        middle: { anchor: "__container__", align: HorizontalAlign.Center }
      })
    }
    .height('100%')
    .width('100%')
  }
}
Enter fullscreen mode Exit fullscreen mode

view the effect: 

except normal. Resource Resource play, also supported play PixelMap animation, the only difference is when setting the data.

 


  imagePixelMap: Array<PixelMap> = []
  @State images:Array<ImageFrameInfo> = []

  async aboutToAppear() {
    this.imagePixelMap.push(await this.getPixmapFromMedia($r('app.media.icon')))
    this.images.push({src:this.imagePixelMap[0]})
  }

private async getPixmapFromMedia(resource: Resource) {
    let unit8Array = await getContext(this)?.resourceManager?.getMediaContent({
      bundleName: resource.bundleName,
      moduleName: resource.moduleName,
      id: resource.id
    })
    let imageSource = image.createImageSource(unit8Array.buffer.slice(0, unit8Array.buffer.byteLength))
    let createPixelMap: image.PixelMap = await imageSource.createPixelMap({
      desiredPixelFormat: image.PixelMapFormat.RGBA_8888
    })
    await imageSource.release()
    return createPixelMap
  }
Enter fullscreen mode Exit fullscreen mode

ImageAnimator common properties 

property  type  overview 
images  Array set picture frame information set. Dynamic updates are not supported. 
state  AnimationStatus  control playback state 
duration  number  set playback duration 
reverse  boolean Set playback direction 
fixedSize  boolean  sets whether the image size is fixed to the component size 
fillMode  FillMode  sets the state before and after the animation starts under the current playback direction 
iterations  number  set the number of plays 

related Summary 

when setting the picture frame information set, dynamic update is not supported, which we need to know, and the most important point is that the performance is not as good as attribute animation, that is to say, it can be implemented with attribute animation, and try to use attribute animation.

Top comments (0)