属性动画、转场动画、帧动画

1. 属性动画-animation

今日核心:
容器组件:属性动画、转场动画、ImageAnimator

属性值的变化,通常会引发 UI 的变化,结合动画可以让这个变化过程【更为流畅】,反之这个过程将在一瞬间完成,用户体验不好,观感突兀。这就是动画的作用:
在这里插入图片描述
HarmonyOS 中的动画主要分为:

  1. 属性动画
  2. 转场动画
  3. 组件动画
    这么三类,一些和动画相关的其他细节咱们

1.1. 基本使用

接下来看看如何让咱们的应用动起来
组件的某些通用属性变化时,可以通过属性动画实现渐变过渡效果,提升用户体验。支持的属性包括width、height、backgroundColor、opacity、scale、rotate、translate等。

// 最核心写法,相关动画属性后续展开
组件
  .属性1()
  .属性2()
  // .... animation 必须在需要动画的属性的后面 
  .animation({})

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

  1. 声明相关状态变量
  2. 将状态变量设置到相关可动画属性方法上
  3. 通过属性动画接口开启属性动画(在属性动画上面的属性会应用动画)
    通过状态变量改变UI界面

模板代码:

@Entry
@Component
struct Page01_animation {
  // 1. 声明相关状态变量
  @State translateY: number = 1
  @State bgColor: ResourceColor = Color.Pink
  @State fontWeight: number = 100

  build() {
    Column() {
      Text('C')
        .width(100)
        .height(100)
        .opacity(1)
        .textAlign(TextAlign.Center)
        .fontWeight(this.fontWeight)// 2.将状态变量设置到相关可动画属性接口
        .backgroundColor(this.bgColor)// 2.将状态变量设置到相关可动画属性接口
        .translate({ y: this.translateY }) // 2.将状态变量设置到相关可动画属性接口
      Button('修改状态变量')
        .onClick(() => {
          // 4. 通过状态变量改变UI界面
          this.bgColor = '#0094ff'
          this.translateY = 100
          this.fontWeight = 900
        })
    }
    .width('100%')
    .height('100%')
    .justifyContent(FlexAlign.SpaceAround)
  }
}

参考代码:

@Entry
@Component
struct Page01_animation1 {
  // 1. 声明相关状态变量
  @State translateY: number = 1
  @State bgColor: ResourceColor = Color.Pink
  @State fontWeight: number = 100

  build() {
    Column() {
      Text('C')
        .width(100)
        .height(100)
        .opacity(1)
        .textAlign(TextAlign.Center)
        .fontWeight(this.fontWeight)//  2.将状态变量设置到相关可动画属性接口
        .backgroundColor(this.bgColor)// 2.将状态变量设置到相关可动画属性接口
        .translate({ y: this.translateY })// 2.将状态变量设置到相关可动画属性接口
          // 💥💥 注意: 要将.animation写在属性后面
        .animation({
          duration: 3000, // 动画持续时间,单位为毫秒
          iterations: 3, // 动画循环次数
          // playMode: PlayMode.Normal, // 正常播放动画
          // playMode: PlayMode.Reverse, // 逆序播放动画
          // playMode: PlayMode.AlternateReverse, // 动画逆序交替播放
          // playMode: PlayMode.Alternate, // 动画正逆交替播放
          delay: 2000, // 动画开始前的延迟时间,单位为毫秒
          onFinish: () => { // 动画完成时的回调函数
            AlertDialog.show({ message: '动画结束', })
          }



        })
      Button('修改状态变量')
        .onClick(() => {
          // 4. 通过状态变量改变UI界面
          this.bgColor = '#0094ff'
          this.translateY = 100
          this.fontWeight = 900
        })
    }
    .width('100%')
    .height('100%')
    .justifyContent(FlexAlign.SpaceAround)
  }
}

1.2. 常用属性

可以通过动画参数(以对象的形式传递)来定制动画效果

名称参数类型必填描述
durationnumber动画时长,单位为毫秒。默认值:1000curve string、 Curve、ICurve否 设置动画曲线。默认值:Curve.EaseInOut
delaynumber动画延迟播放时间。单位为毫秒,默认不延时播放。默认值:0取值范围:(-∞, +∞)
iterationsnumber动画播放次数。默认值:1取值范围:[-1, +∞)说明:设置为-1时表示无限次播放。设置为0时表示无动画效果。
playModePlayMode动画播放模式,默认播放完成后重头开始播放。默认PlayMode.Normal
onFinish() => void结束回调,动画播放完成时触发。从API version 9开始,该接口支持在ArkTS卡片中使用。

1.3. 案例-折扣信息

在这里插入图片描述

需求:

  1. 动画效果:
    a. 元素大小切换
    b. 动画次数无限,元素【加载之后】开启动画
1.3.1. 点击按钮触发

咱们先考虑如何实现点击开启动画,元素加载之后涉及到一个还未学习的知识点

核心步骤:有布局,有逻辑

  1. 布局:用 Text 方便调整实现布局
  2. 动画:animation
    a. 次数无限次
    b. 动画线性:匀速
    c. 改变的是缩放
    ⅰ. 宽高,定义在@State
  3. 如何触发:
    a.点击触发
1.3.1.1. 模板代码:
@Entry
@Component
struct Page02_animationDemo1 {
  build() {
    Column({ space: 50 }) {
      Text('全场低至一分购')
        .fontSize(30)
        .fontWeight(900)
        .fontColor(Color.Red)
        .backgroundColor('#e8b66d')
        .padding(10)
        .borderRadius(20)
    }
    .width('100%')
    .height('100%')
    .padding(20)

  }

  @Styles
  fullSize() {
    .width('100%')
    .height('100%')
  }
}
1.3.1.2. 参考代码:
@Entry
@Component
struct Page04_animationDemo1_event {
  // 1. 声明相关状态变量
  @State scaleNum: number = 1

  build() {
    Column({ space: 50 }) {
      Text('全场低至一分购')
        .fontSize(30)
        .fontWeight(900)
        .fontColor(Color.Red)
        .backgroundColor('#e8b66d')
        .padding(10)
        .borderRadius(20)
        .scale({
          // 2.将状态变量设置到相关可动画属性接口
          x: this.scaleNum,
          y: this.scaleNum
        })
        .animation({
          // 3. 通过属性动画接口开启属性动画
          duration: 1000,
          curve: Curve.EaseInOut,
          playMode: PlayMode.Alternate,
          iterations: -1
        })
        .onClick(() => {
          // 4.通过状态变量改变UI界面
          this.scaleNum = 1.3
          this.scaleNum = 1.3
        })
    }
    .width('100%')
    .height('100%')
    .padding(20)

  }

  @Styles
  fullSize() {
    .width('100%')
    .height('100%')
  }
}
1.3.2. 组件加载自动触发事件

如果要实现元素加载的时候就开始动画,可以使用挂载事件来实现,这是一个通用事件

名称功能描述
onAppear(event: () => void)组件挂载显示时触发此回调。从API version 9开始,该接口支持在ArkTS卡片中使用。
onDisAppear(event: () => void)组件卸载消失时触发此回调。从API version 9开始,该接口支持在ArkTS卡片中使用。
1.3.2.1. 基础模板
// 掌握: 挂载和卸载事件


@Entry
@Component
struct Index {
  @State isShow: boolean = false

  build() {
    Column() {
      Button('切换显示')
        .onClick(() => {
          this.isShow = !this.isShow
        })

      // 💥 if语句条件渲染 会创建和销毁组件
      if (this.isShow) {
        Text('看看我显示没')
          
      }
    }
  }
}
1.3.2.2. 参考代码
// 掌握: 挂载和卸载事件

@Entry
@Component
struct Index {
  @State isShow: boolean = false

  build() {
    Column() {
      Button('切换显示')
        .onClick(() => {
          this.isShow = !this.isShow
        })

      // 💥 if语句条件渲染 会创建和销毁组件
      if (this.isShow) {
        Text('看看我显示没')
          .onAppear(() => {
            // 监听组件的创建(显示)事件
            AlertDialog.show({ message: '创建了', alignment: DialogAlignment.Center })
          })

          .onDisAppear(() => {
            // 监听组件的销毁(消失)事件
            AlertDialog.show({ message: '销毁了', alignment: DialogAlignment.Center })

          })
      }
    }
  }
}

一个新的语法,可以实现功能,如何整合?

  1. 折扣信息-功能
    a. 元素加载出来之后自动播放动画
    b. 原本是点击播放动画,改写为在 onAppear 写开启动画的逻辑
1.3.2.3. 案例调整代码:
@Entry
@Component
struct Page02_animationDemo1 {
  // 1. 定义状态变量
  @State scaleNum: number = 1;

  // 2. 使用scale属性


  build() {
    Column({ space: 50 }) {
      Text('全场低至一分购')
        .fontSize(30)
        .fontWeight(900)
        .fontColor(Color.Red)
        .backgroundColor('#e8b66d')
        .padding(10)
        .borderRadius(20)
        .scale({ x: this.scaleNum, y: this.scaleNum })
        .animation({ duration: 500, iterations: -1, playMode: PlayMode.Alternate }) // 4. 使用动画属性方法
        .onAppear(() => {
          // 3. 点击事件, 修改状态变量
          this.scaleNum = 1.3;


        })
    }
    .width('100%')
    .height('100%')
    .padding(20)

  }

  @Styles
  fullSize() {
    .width('100%')
    .height('100%')
  }
}

1.4. 案例-返回顶部

在这里插入图片描述

1.4.1. 基础模版
@Entry
@Component
struct Page05_toTop {
  build() {
    Column() {
      // 头部
      Image($r('app.media.ic_pdd_top'))
        .width('100%')
      // 滚动区域
      Stack({ alignContent: Alignment.BottomEnd }) {
        Scroll() {
          Column() {
            Image($r('app.media.ic_pdd_content_1'))
              .width('100%')
            Image($r('app.media.ic_pdd_content_2'))
              .width('100%')
            Image($r('app.media.ic_pdd_content_3'))
              .width('100%')
          }
        }
        .edgeEffect(EdgeEffect.Spring)

        // 返回顶部
        Column() {
          Image($r('app.media.ic_public_arrow_up'))
            .width(25)
          Text('顶部')
            .fontSize(14)
            .offset({ y: -5 })
        }
        .backgroundColor(Color.White)
        .width(40)
        .height(40)
        .borderRadius(20)
      }
      .layoutWeight(1)


      // 底部
      Image($r('app.media.ic_pdd_bottom'))
        .width('100%')
    }
    .width('100%')
    .height('100%')
  }
}
1.4.2. 动画效果

需求:

  1. 页面滚动距离超过 300 时
    a. 显示右下角的返回顶部
    b. 反之隐藏
  2. 动画效果:
    a. 透明度(0-1)
    b. 移动距离(从下往上)
    核心步骤:
  3. 如何获取滚动距离?
    a. 获取滚动的距离,控制器的一个方法currentOffset()
    ⅰ. 创建控制器,绑定控制器,调用方法获取值
  4. 动画逻辑
    a. 判断>300 显示
    ⅰ. 透明度-》 0-1
    ⅱ. 从下往上-》本来在【下方】,往上移动
    b. 判断<300 隐藏
    ⅰ. 透明度-》 1-0
    ⅱ. 从下往上-》本来在【上方】,往下移动
@Entry
@Component
struct Page05_toTop {
  // isShow
  @State isShow: boolean = false
  // 创建控制器对象
  scroller: Scroller = new Scroller()

  build() {
    Column() {
      // 头部
      Image($r('app.media.ic_pdd_top'))
        .width('100%')
      // 滚动区域
      Stack({ alignContent: Alignment.BottomEnd, }) {
        Scroll(this.scroller) {
          Column() {
            Image($r('app.media.ic_pdd_content_1'))
              .width('100%')
            Image($r('app.media.ic_pdd_content_2'))
              .width('100%')
            Image($r('app.media.ic_pdd_content_3'))
              .width('100%')
          }
        }
        .edgeEffect(EdgeEffect.Spring)
        .onWillScroll(() => {
          if (this.scroller.currentOffset().yOffset > 300) {
            this.isShow = true
          } else {
            this.isShow = false
          }
        })

        // 返回顶部
        this.renderBackTop()
      }
      .layoutWeight(1)


      // 底部
      Image($r('app.media.ic_pdd_bottom'))
        .width('100%')
    }
    .width('100%')
    .height('100%')
  }

  @Builder
  renderBackTop() {
    // 返回顶部
    Column() {
      Image($r('app.media.ic_public_arrow_up'))
        .width(25)
      Text('顶部')
        .fontSize(14)
        .offset({ y: -5 })
    }
    .backgroundColor(Color.White)
    .width(40)
    .height(40)
    .borderRadius(20)
    .opacity(this.isShow ? 1 : 0)
    .translate({ y: this.isShow ? -60 : 60 })
    .animation({})
  }
  
}

2. 属性动画-animateTo

属性动画 animation是作为属性使用,而animateTo显示动画是一个系统的内置函数,可以直接调用。功能上和 animation一致,适合于给多个元素应用同一套动画的场景

2.1. 核心用法

和 animation 的核心用法步骤类似,第三步的语法需要调整

  1. 声明相关状态变量
  2. 将状态变量设置到相关可动画属性接口
  3. 通过属性动画接口开启属性动画(在属性动画上面的属性会应用动画)
  4. 通过状态变量改变UI界面
// 参数 1 动画选项 和 animation 相同
// 参数 2 箭头函数,状态变量的修改写在里面
animateTo({}, () => {
  // 第 4 步写到这里
})
2.1.1. 基础模版:
@Entry
@Component
struct Page06_animateTo {
  // 1. 声明相关状态变量
  @State rotateAngle: number = 0

  build() {
    Column() {
      // 4个齿轮
      Flex({ wrap: FlexWrap.Wrap }) {
        Image($r('app.media.ic_gear1'))
          .width(150)
          .rotate({ angle: this.rotateAngle })
        Image($r('app.media.ic_gear2'))
          .width(150)
          .rotate({ angle: this.rotateAngle })
        Image($r('app.media.ic_gear3'))
          .width(150)
          .rotate({ angle: this.rotateAngle })
        Image($r('app.media.ic_gear4'))
          .width(150)
          .rotate({ angle: this.rotateAngle })
      }

      Button('点击旋转齿轮')
        .onClick(() => {
          this.rotateAngle = 360
        })
    }
    .width('100%')
    .height('100%')
    .backgroundColor(Color.Orange)
  }
}
2.1.2. 参考代码:
@Entry
@Component
struct Page06_animateTo {
  @State rotateAngle: number = 0

  build() {
    Column() {
      Flex({ wrap: FlexWrap.Wrap }) {
        Image($r('app.media.ic_gear1'))
          .width(150)
          .rotate({ angle: this.rotateAngle })
        Image($r('app.media.ic_gear2'))
          .width(150)
          .rotate({ angle: this.rotateAngle })
        Image($r('app.media.ic_gear3'))
          .width(150)
          .rotate({ angle: this.rotateAngle })
        Image($r('app.media.ic_gear4'))
          .width(150)
          .rotate({ angle: this.rotateAngle })

        Text('xxx')
          .rotate({ angle: this.rotateAngle })
      }

      Button('点击旋转齿轮')
        .onClick(() => {
          // 1. 通过属性动画接口开启属性动画(在属性动画上面的属性会应用动画)
          animateTo({ iterations: -1, curve: Curve.Linear }, () => {
            // 2. 通过状态变量改变UI界面
            this.rotateAngle = 360
          })
        })
    }
    .width('100%')
    .height('100%')
    .backgroundColor(Color.Orange)
  }
}

2.2. 案例-折扣信息

还是之前的那个折扣信息案例,咱们通过 animateTo 来实现一次,来看看是否可以用 animateTo 一样的效果
在这里插入图片描述

需求:

  1. 将原模版中的 animation 替换为 animateTo
    核心步骤:
  2. 将 animation 设置的动画属性,通过 animateTo 设置
  3. 调整状态变量的代码写到 animateTo 的回调函数中即可
2.2.1. 基础模版:
@Entry
@Component
struct Page07_animateToDemo {
  // 1. 声明相关状态变量
  @State scaleX: number = 1
  @State scaleY: number = 1

  build() {
    Column({ space: 50 }) {
      Text('全场低至一分购')
        .fontSize(30)
        .fontWeight(900)
        .fontColor(Color.Red)
        .backgroundColor('#e8b66d')
        .padding(10)
        .borderRadius(20)// 2.将状态变量设置到相关可动画属性接口
        .scale({
          x: this.scaleX,
          y: this.scaleY
        })// 3. 通过属性动画接口开启属性动画
        .animation({
          duration: 1000,
          curve: Curve.EaseInOut,
          playMode: PlayMode.Alternate,
          iterations: -1
        })
        .onAppear(() => {
          // 4.通过状态变量改变UI界面
          this.scaleX = 1.3
          this.scaleY = 1.3
        })
    }
    .width('100%')
    .height('100%')
    .padding(20)

  }

  @Styles
  fullSize() {
    .width('100%')
    .height('100%')
  }
}
2.2.2. 参考代码:
@Entry
@Component
struct Page07_animateToDemo {
  // 1. 声明相关状态变量
  @State scaleX: number = 1
  @State scaleY: number = 1

  build() {
    Column({ space: 50 }) {
      Text('全场低至一分购')
        .fontSize(30)
        .fontWeight(900)
        .fontColor(Color.Red)
        .backgroundColor('#e8b66d')
        .padding(10)
        .borderRadius(20)// 2.将状态变量设置到相关可动画属性接口
        .scale({
          x: this.scaleX,
          y: this.scaleY
        })
        .onAppear(() => {
          // 3. 通过属性动画接口开启属性动画
          animateTo({
            duration: 1000,
            curve: Curve.EaseInOut,
            playMode: PlayMode.Alternate,
            iterations: -1
          }, () => {
            // 4.通过状态变量改变UI界面
            this.scaleX = 1.3
            this.scaleY = 1.3
          })

        })
    }
    .width('100%')
    .height('100%')
    .padding(20)

  }

  @Styles
  fullSize() {
    .width('100%')
    .height('100%')
  }
}

3. 转场动画

转场动画是指对将要出现或消失的组件做动画,对始终出现的组件做动画应使用属性动画

转场动画主要有如下分类:

  1. 出现/消失转场
  2. 导航转场(和路由相关-路由阶段讲解)
  3. 模态转场(全模态、半模态弹框,还有一些其他的弹出菜单-链接)
  4. 共享元素转场(和路由相关-路由阶段讲解)

3.1. 出现消失转场-核心用法

比如点击关闭广告,添加或者删除元素。核心就是元素需要出现或者消失,都可以使用转场动画来添加过渡效果
在这里插入图片描述
在这里插入图片描述
创建TransitionEffect

// 出现时会是所有转场效果的出现效果叠加,消失时会是所有消失转场效果的叠加
// 目前是透明度转场,使用默认的动画参数
 effect: object = TransitionEffect.OPACITY.animation({}))

将转场效果通过transition接口设置到组件

Text('test')
  .transition(this.effect)

新增或者删除组件触发转场

@State isPresent: boolean = true;
...
if (this.isPresent) {
  Text('test')
    .transition(effect)
}
...
// 控制新增或者删除组件
// 方式一:将控制变量放到animateTo闭包内,未通过animation接口定义动画参数的TransitionEffect将跟随animateTo的动画参数
animateTo({ curve: curves.springMotion() }, () => {
  this.isPresent = false;
})

// 方式二:直接控制删除或者新增组件,动画参数由TransitionEffect的animation接口配置
this.isPresent = false;

3.1.1. 基础模版:
@Entry
@Component
struct Page08_transition {
  @State isShow: boolean = false

  build() {
    Column() {
      Button('显示或隐藏')
        .onClick(() => {
          this.isShow = !this.isShow
        })
      
      if (this.isShow) {
        Text('我是文本框')
          .fontSize(50)
          .fontWeight(FontWeight.Bold)
      }


    }
    .width('100%')
    .height('100%')
  }
}
3.1.2. 参考代码:
@Entry
@Component
struct Page08_transition {
  @State isShow: boolean = false
  //  1. 定义转场效果
  effect: object = TransitionEffect
    .OPACITY.animation({})

  build() {
    Column() {
      Button('切换元素')
        .onClick(() => {
          this.isShow = !this.isShow
        })
      // 3. 新增或者删除组件触发转场
      if (this.isShow) {
        Text('我是文本框')
          .fontSize(50)
          .fontWeight(FontWeight.Bold)
          // 2. 添加转场效果
          .transition(this.effect)
      }
      // 我是底部的元素
      Text('底部的文本框')
        .fontSize(40)


    }
    .width('100%')
    .height('100%')
  }
}

3.2. 出现消失转场-转场效果

转场效果可以通过下列语法随意进行设置,支持通过点语法链式调用

// 设置多个效果 出现和消失均有转场
effect: object = TransitionEffect
    .OPACITY.animation({})
    .combine(TransitionEffect.scale({ x: 0, y: 0 }))// 组合 缩放效果
    .combine(TransitionEffect.rotate({ angle: 90 })) // 组合 旋转效果
    .combine(TransitionEffect.move(TransitionEdge.END)) // 组合 移动效果

// 出现没有转场
// 消失透明度转场
effect2: object = TransitionEffect.asymmetric(
  TransitionEffect.IDENTITY, // 出现转场
  TransitionEffect.OPACITY.animation({ // 消失转场
    duration: 300
  }))
3.2.1. 参考代码
@Entry
@Component
struct Page09_transitionEffect {
  @State isShow: boolean = false
  //  1. 定义转场效果
  effect = TransitionEffect
    .OPACITY.animation({})
    .combine(TransitionEffect.scale({ x: 0, y: 0 }))
    .combine(TransitionEffect.rotate({ angle: 90 }))
    .combine(TransitionEffect.move(TransitionEdge.END))

  build() {
    Column() {
      Button('切换元素')
        .onClick(() => {
          this.isShow = !this.isShow
        })
      // 3. 新增或者删除组件触发转场
      if (this.isShow) {
        Text('我是文本框')
          .fontSize(50)
          .fontWeight(FontWeight.Bold)// 2. 添加转场效果
          .transition(this.effect)
      }
      // 我是底部的元素(
      // 元素是瞬间消失或出现的,所以这个输入框会瞬间上去或下去
      // 因为通过动画的方式让这一过程更为平滑,所以会出现 Text 上去了,但是消失、出现的动画还在播放
      // 如果要视觉效果更为美观,可以通过布局的方式让元素的出现或消失不影响布局
      Text('底部的文本框')
        .fontSize(40)


    }
    .width('100%')
    .height('100%')
  }
}

3.3. 案例-转转

在这里插入图片描述

3.3.1. 基础模版:
@Entry
@Component
struct Page11_zhuanzhuan {
  @State showAdd: boolean = true
  @State showGift: boolean = true

  build() {
    Stack() {
      // 底部的主体内容
      Image($r('app.media.ic_zhuanzhuan_content'))
        .width('100%')
        .height('100%')

      // 首页的礼物
      if (!this.showAdd && this.showGift) {
        Column() {
          // 礼物图片
          Image($r('app.media.ic_zhuanzhuan_gift'))
            .width(200)
          // 关闭按钮
          Image($r('app.media.ic_zhuanzhuan_close'))
            .width(30)
            .onClick(() => {
              this.showGift = false
            })
        }
      }

      // 开屏广告
      if (this.showAdd) {
        Image($r('app.media.ic_zhuanzhuan_ad'))
          .width('100%')
          .height('100%')
          .onClick(() => {
            this.showAdd = false
          })
      }


    }
    .width('100%')
    .height('100%')
  }
}
3.3.2. 参考代码:
@Entry
@Component
struct Page11_zhuanzhuan {
  @State showAdd: boolean = true
  @State showGift: boolean = true
  // 添加广告转场效果
  addEffect: object = TransitionEffect.asymmetric(
    TransitionEffect.IDENTITY, // 出现转场
    TransitionEffect.OPACITY.animation({
      duration: 300
    })
      .combine(TransitionEffect.scale({ x: 0, y: 0 })))
  
  // 添加礼物的转场效果
  giftEffect: object = TransitionEffect.OPACITY.animation({ duration: 300 })
    .combine(TransitionEffect.move(TransitionEdge.END))
    .combine(TransitionEffect.scale({ x: 0, y: 0 }))

  build() {
    Stack() {
      // 底部的主体内容
      Image($r('app.media.ic_zhuanzhuan_content'))
        .width('100%')
        .height('100%')

      // 首页的礼物
      if (!this.showAdd && this.showGift) {
        Column() {
          // 礼物图片
          Image($r('app.media.ic_zhuanzhuan_gift'))
            .width(200)
          // 关闭按钮
          Image($r('app.media.ic_zhuanzhuan_close'))
            .width(30)
            .onClick(() => {
              this.showGift = false
            })
        }
        .transition(this.giftEffect) //应用转场
      }

      // 开屏广告
      if (this.showAdd) {
        Image($r('app.media.ic_zhuanzhuan_ad'))
          .width('100%')
          .height('100%')
          .transition(this.addEffect) // 应用转场
          .onClick(() => {
            this.showAdd = false
          })
      }


    }
    .width('100%')
    .height('100%')
  }
}

4. ImageAnimator 帧动画组件

4.1. 核心用法:

提供逐帧播放图片的能力,可以方便的控制播放状态:播放,暂停,停止…

试一试:
基于ImageAnimator 组件实现如下效果
在这里插入图片描述
点击播放,暂停,停止切换动画状态

4.1.1. 基础模版:
@Entry
@Component
struct Page12_ImageAnimator {
  // 需要动画的图片
  images: ImageFrameInfo[] = []

  build() {
    Column({ space: 10 }) {
      // 动画组件
      ImageAnimator()
        .xxxStyles()
      

      // 按钮控制区域
      Row({ space: 20 }) {
        Button('启动')
          .onClick(() => {
          })
        Button('暂停')
          .onClick(() => {
          })
        Button('停止')
          .onClick(() => {
          })
      }
    }
    .width('100%')
    .height('100%')
    .padding(10)

  }

  // 抽取的通用属性
  @Styles
  xxxStyles() {
    .width(300)
    .backgroundColor(Color.Gray)
    .padding(10)
    .borderRadius(10)
    .height(150)
  }
}
4.1.2. 参考代码:
@Entry
@Component
struct Page12_ImageAnimator {
  // 需要动画的图片
  // 两套动画素材
  // ic_animator_coin1(1-6 硬币)
  // ic_animator_dog1(1-4 狗狗)
  images: ImageFrameInfo[] = [
    { src: $r('app.media.ic_animator_dog1') },
    { src: $r('app.media.ic_animator_dog2') },
    { src: $r('app.media.ic_animator_dog3') },
    { src: $r('app.media.ic_animator_dog4') },
  ]
  // 需要控制动画状态 定义状态变量 默认状态
  @State aniStatus: AnimationStatus = AnimationStatus.Initial

  build() {
    Column({ space: 10 }) {
      // 动画组件
      ImageAnimator()
        .xxxStyles()
        .iterations(-1)// 动画次数
        .duration(400)// 持续时间
        .images(this.images)// 动画图片
        .state(this.aniStatus) // 动画抓个状态

      // 按钮控制区域
      Row({ space: 20 }) {
        Button('启动')
          .onClick(() => {
            this.aniStatus = AnimationStatus.Running // 播放
          })
        Button('暂停')
          .onClick(() => {
            this.aniStatus = AnimationStatus.Paused // 暂停:停在触发的瞬间
          })
        Button('停止')
          .onClick(() => {
            this.aniStatus = AnimationStatus.Stopped // 停止:直接到最后一张图
          })
      }
    }
    .width('100%')
    .height('100%')
    .padding(10)

  }

  // 抽取的通用属性
  @Styles
  animatorFancy() {
    .width(300)
    .backgroundColor(Color.Gray)
    .padding(10)
    .borderRadius(10)
    .height(150)
  }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值