鸿蒙应用开发-属性动画animation、显式动画animateTo和帧动画组件ImageAnimator

2,410 阅读5分钟

一、 属性动画animation

1. 基本使用

组件的某些通用属性变化时,可以通过属性动画实现渐变过渡效果,提升用户体验。支持的属性包括width、height、backgroundColor、opacity(透明度)、scale(缩放)、rotate(旋转)、translate(平移)等。

使用动画的核心步骤如下:

  1. 声明相关状态变量
  2. 将状态变量设置到相关可动画属性接口
  3. 通过属性动画接口开启属性动画(在属性动画上面的属性会应用动画)
  4. 通过状态变量改变UI界面
@Entry
@Component
struct Index {
  @State w:number = 50  //宽
  @State h:number = 50  //高
  @State Opacity:number = 1  //透明度
  @State bkc:ResourceColor = Color.Blue  //背景色
  @State ag:number = 0  //旋转角度
  @State y:number = 1   //缩放
  @State x:number = 1   //缩放
  @State sy:number = 0  //平移
  build() {
    Column() {
      Column() {}
      .width(this.w)
      .height(this.h)
      .backgroundColor(this.bkc)//背景色
      .opacity(this.Opacity)//透明度
      .scale({x:this.x,y:this.y})//缩放
      .rotate({angle:this.ag})//旋转角度
      .translate({x:0,y:this.sy})//平移
      .animation({})
      .onClick(()=>{
       // 三元表达式:  判断语句 ?(true) :(false)
        this.w = this.w == 50 ?150 :50
        this.h = this.h == 50 ?150 :50
        this.Opacity = this.Opacity == 1 ?0.5 :1
        this.ag = this.ag == 0 ?360 :0
        this.sy = this.sy == 0 ?200 :0
        this.y = this.y == 1 ?2 :1
        this.x = this.x == 1 ?2 :1
        this.bkc = this.bkc == Color.Blue ?Color.Red :Color.Blue
      })
    }
    .width('100%')
    .height('100%')
    .backgroundColor(Color.Pink)
  }
}
动画基本使用.gif

2.常用属性

名称参数类型必填描述
durationnumber动画时长,单位为毫秒。默认值:1000
curvestring、 Curve、ICurve设置动画曲线。默认值:Curve.EaseInOut
delaynumber动画延迟播放时间。单位为毫秒,默认不延时播放。默认值:0取值范围:(-∞, +∞)
iterationsnumber动画播放次数。默认值:1 说明: 设置为-1时表示无限次播放。设置为0时表示无动画效果。
playModePlayMode动画播放模式,默认播放完成后重头开始播放。默认值:PlayMode.Normal
onFinish() => void结束回调,动画播放完成时触发。从API version 9开始,该接口支持在ArkTS卡片中使用。
(1)动画枚举 Curve的取值含义
名称描述
Linear表示动画从头到尾的速度都是相同的。
Ease表示动画以低速开始,然后加快,在结束前变慢,CubicBezier(0.25, 0.1, 0.25, 1.0)。
EaseIn表示动画以低速开始,CubicBezier(0.42, 0.0, 1.0, 1.0)。
EaseOut表示动画以低速结束,CubicBezier(0.0, 0.0, 0.58, 1.0)。
EaseInOut表示动画以低速开始和结束,CubicBezier(0.42, 0.0, 0.58, 1.0)。
FastOutSlowIn标准曲线,CubicBezier(0.4, 0.0, 0.2, 1.0)。
LinearOutSlowIn减速曲线,CubicBezier(0.0, 0.0, 0.2, 1.0)。
FastOutLinearIn加速曲线,CubicBezier(0.4, 0.0, 1.0, 1.0)。
ExtremeDeceleration急缓曲线,CubicBezier(0.0, 0.0, 0.0, 1.0)。
Sharp锐利曲线,CubicBezier(0.33, 0.0, 0.67, 1.0)。
Rhythm节奏曲线,CubicBezier(0.7, 0.0, 0.2, 1.0)。
Smooth平滑曲线,CubicBezier(0.4, 0.0, 0.4, 1.0)。
Friction阻尼曲线,CubicBezier(0.2, 0.0, 0.2, 1.0)。
自定义:import curves from '@ohos.curves'curves.cubicBezierCurve(0.29,-0.33,.83,0.67)
(2)playMode 取值
名称描述
Normal动画正向播放。(默认值)
Reverse动画反向播放。
Alternate动画在奇数次(1、3、5...)正向播放,在偶数次(2、4、6...)反向播放。需配合属性iterations设置为-1无限播放使用。
AlternateReverse动画在奇数次(1、3、5...)反向播放,在偶数次(2、4、6...)正向播放。需配合属性iterations设置为-1无限播放使用。
@Entry
@Component
struct Index {
  @State w:number = 50
  build() {
    Column() {
      Column(){}
      .height(50)
      .width(this.w)
      .backgroundColor(Color.Blue)
      .animation({
        // duration:1000,   //播放时长
        curve:Curve.Linear, //动画曲线
        delay:3000,    //延迟3秒播放
        iterations:-1,   // 播放次数 -1为无限播放
        playMode:PlayMode.Alternate  //动画在奇数次(1、3、5...)正向播放,在偶数次(2、4、6...)反向播放。
      })
      .onClick(()=>{
        this.w = 200
      })
    }
    .width('100%')
    .height('100%')
    .backgroundColor(Color.Pink)
  }
}
@Entry
@Component
struct Index {
  @State w:number = 50
  build() {
    Column() {
      Column(){}
      .height(50)
      .width(this.w)
      .backgroundColor(Color.Blue)
      .animation({
        curve:Curve.Linear, //动画曲线
        delay:2000,    //**延迟2秒播放**
        iterations:2,
        onFinish:()=>{
          AlertDialog.show({message:'动画完成了'})
        }
      })
      .onClick(()=>{
        this.w = 200
      })
    }
    .width('100%')
    .height('100%')
    .backgroundColor(Color.Pink)
  }
}
动画无限播放.gif 动画完成了.gif

3.组件加载自动触发

上面学的动画效果都是我们点击后才开始的,下面我们可以通过挂载事件(通用事件)让动画效果自动触发。

名称支持冒泡功能描述
onAppear(event: () => void)组件挂载显示时触发此回调。从API version 9开始,该接口支持在ArkTS卡片中使用。
onDisAppear(event: () => void)组件卸载消失时触发此回调。从API version 9开始,该接口支持在ArkTS卡片中使用。
    @Entry
    @Component
    struct Index {
      @State sc:number = 1   // 缩放
      @State isShow:boolean = false  // 控制文本是否出现
      build() {
        Column({space:50}) {
          Button('显示和隐藏')
            .onClick(()=>{
              this.isShow = !this.isShow
            })
          if(this.isShow){
            Text('全场低至一分购')
              .fontSize(30)
              .fontWeight(800)
              .fontColor(Color.Red)
              .backgroundColor(Color.Orange)
              .padding(10)
              .margin({top:50})
              .borderRadius(20)
              .scale({
                x:this.sc,
                y:this.sc
              })
              .animation({
                iterations:-1,
                playMode:PlayMode.Alternate
              })
              .onAppear(()=>{
                this.sc = 1.3
                AlertDialog.show({message:'文本出来了'})
              })
              .onDisAppear(()=>{
                AlertDialog.show({message:'文本销毁了'})
              })
          }
        }
        .width('100%')
        .height('100%')
        .backgroundColor(Color.Pink)
      }
    }
动画自动.gif

二、显式动画animateTo

1.了解

属性动画 animation是作为属性使用,而animateTo显示动画是一个系统的内置函数,可以直接调用,一般事件触发时一起使用,比如点击事件,滚动事件等等。基本用法:

// 参数 1 动画选项 和 animation 相同
// 参数 2 箭头函数,状态变量的修改写在里面
animateTo({参数1}, () => {
  状态变量的改变
})

2.案例-返回顶部

使用了Tabs组件和自定义tabBar,

    @Entry 
    @Component
    struct Index {
      //顶部按钮平移
      @State translateY:number = 80
      //Tabs的首页导航自定义样式
      @Builder
      firstTabBarBuilder() {
        Column() {
          Image($r('app.media.mjyp_tabbar_icon_0_1_selected'))
            .width(25)
          Text('首页')
            .fontColor(Color.Orange)
        }
      }
      //Tabs的其他导航自定义样式
      @Builder
      otherTabBarBuilder(icon: string, text: string) {
        Column() {
          Image($r(icon))
            .width(25)
          Text(text)
        }
      }

      scroller = new Scroller()

      build() {
        Column() {
          Tabs() {
            TabContent() {
              Stack({ alignContent: Alignment.BottomEnd }) {
                // 3. 绑定控制器对象
                Scroll(this.scroller) {
                  Column() {
                  }
                  .height(2000)
                  .width('100%')
                  .backgroundColor('#0094ff')
                  .linearGradient({angle:180,colors:[['#0094ff',0],['#9400ff',0.5],['#00ff94',1]]})
                }
                // 4. 滚动触发事件中
                .onWillScroll(() => {
                  // 4.1 如果y偏移量大于200,则
                  if(this.scroller.currentOffset().yOffset > 200){
                    animateTo({duration:500},()=>{
                    this.translateY = -50
                    })
                  }else {
                    animateTo({duration:500},()=>{
                      this.translateY = 80
                    })
                  }
                })
                // 顶部按钮
                Column() {
                  Image($r('app.media.ic_public_arrow_up_0'))
                    .fillColor('rgba(0,0,0,0.5)')
                    .width(45)

                  Text('顶部')
                }
                .backgroundColor(Color.White)
                .height(80)
                .width(80)
                .borderRadius(40)
                // 1. 通用属性平移-> y方向向上平移
                .translate({
                  x:-20,
                  y: this.translateY
                })
                .onClick(()=>{
                  this.scroller.scrollEdge(Edge.Top)
                })
              }

            }.tabBar(this.firstTabBarBuilder())

            TabContent() {

            }.tabBar(this.otherTabBarBuilder('app.media.mjyp_tabbar_icon_1', '推荐'))

            TabContent() {

            }.tabBar(this.otherTabBarBuilder('app.media.mjyp_tabbar_icon_1', '我的'))

            TabContent() {

            }.tabBar(this.otherTabBarBuilder('app.media.mjyp_tabbar_icon_1', '我的'))
          }
          .barPosition(BarPosition.End)

        }
        .height('100%')
        .width('100%')
      }
    }
显示动画.gif

三、ImageAnimator 帧动画组件

1.了解

ImageAnimator不是容器组件,也不需要传递参数,只需要设置属性即可。用法:

ImageAnimator()
  .属性()

2.常用属性

参数名称参数类型参数描述
imagesArray<[ImageFrameInfo]设置图片帧信息集合。 说明: 不支持动态更新。
stateAnimationStatus默认为初始状态,用于控制播放状态。
durationnumber单位为毫秒,默认时长为1000ms;duration为0时,不播放图片;值的改变只会在下一次循环开始时生效;当images中任意一帧图片设置了单独的duration后,该属性设置无效。 默认值:1000 从API version 10开始,该接口支持在ArkTS卡片中使用。
iterationsnumber默认播放一次,设置为-1时表示无限次播放。 默认值:1

!!! 注意点:

  1. ImageAnimator组件必须设置宽和高,否则看不见
  2. 如果图片数量大,并且单个图片的占用空间大,会影响性能,使用时尽量对图片进行压缩处理

使用如下:

@Entry
@Component
struct Index {
  @State state:AnimationStatus = AnimationStatus.Initial
  build() {
    Column() {
      ImageAnimator()
        .images([
          { src: $r('app.media.loading_dog_0') },
          { src: $r('app.media.loading_dog_1') },
          { src: $r('app.media.loading_dog_2') },
          { src: $r('app.media.loading_dog_3') },])
        .state(this.state)  // 动画状态
        .duration(500)   // 持续时间
        .iterations(-1) // 播放次数
        .width(300)
        .height(200)
      Row() {
        Button('启动')
          .onClick(()=>{
            this.state = AnimationStatus.Running  //播放
          })
        Button('暂停')
          .onClick(()=>{
            this.state = AnimationStatus.Paused  //暂停:停在触发的瞬间
          })
        Button('停止')
          .onClick(()=>{
            this.state = AnimationStatus.Stopped //停止:直接到最后一张图
          })
      }
    }
    .width('100%')
    .height('100%')
  }
}

动画帧.gif