鸿蒙应用开发-带你游遍动画王国

415 阅读3分钟

鸿蒙应用开发-带你游遍动画王国

想必大家在孤独、无聊摸鱼、emo的时候,多少都会打开qq音乐和网易云来缓解情绪。今天我在学习的空余时间,打开qq音乐看见了这样的一幕:

recording

动画无处不在,让我们一起来了解它吧!!!

今天呢,我将给大家讲述3种动画,分别是属性动画、显示动画、帧动画

属性动画animation

属性动画总体而言算是最简单的动画了,通常在我们的UI设计搭配点击事件\textcolor{Red}{点击事件}.onClick(()=>{})

!!!这里不是所有属性都能可以使用,只有==某些通用属性==变化时,才有效果。支持的属性包括 widthheightbackgroundColoropacityscale(缩放非常常用)、rotate(旋转)translate(移动)等。

基础属性

  • duration 设置动画时长,默认为1000ms
  • curve 设置动画曲线,详情见developer.huawei.com/consumer/cn…
  • delay 设置动画延迟执行的时长,默认为0ms
  • iterations 设置动画的播放次数(默认值为1,==-1==表示无限次播放,次数为0时无动画效果)
  • playMode 动画播放模式,默认播放完成后重头开始播放(PlayMode.Normal),!!!如果想要来回变话可以用(PlayMode.Alternate)奇数次正放,偶数次倒放
  • onFinish 结束回调,动画播放完成时触发

核心步骤

  1. 声明相关状态变量\textcolor{Red}{状态变量}(例如图片是否显示isShow:boolean=true)
  2. 将状态变量设置到相关可可动画属性\textcolor{Red}{可动画属性}接口
  3. 通过属性动画接口开启属性动画
  4. 通过状态变量改变UI界面

IU案例

首先,我们这里确定采用scale放大实现效果,所以绑定了**状态变量\textcolor{Red}{状态变量}**scaleXY,需要增加的功能就是点击一下图片,弹出放大后的图片。

image-20240818083056099

在文章开头的动画中,因为图片在放大过程中有一个移动的效果,所以我加上了一个新的状态变量distance

最终效果:

recording

完整代码:

@Entry
@Component
struct Index {
  @State
  scaleXY:number=1//放大倍率
  @State
  distance:number=0//移动距离
  build() {
      Column(){
        Image($r('app.media.img_IU'))
          .width(80)
          .scale({x:this.scaleXY,y:this.scaleXY})
          .translate({y:this.distance})
          .onClick(()=>{
            this.scaleXY=4
            this.distance=300
          })
          .animation({
            duration:1000,
            curve:Curve.Linear
          })
      }
      .width("100%")
      .height("100%")
  }
}

显示动画animateTo

基本用法

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

!!!\textcolor{Red}{!!!}这里注意,上面的与事件一起使用和animation中,位置不一样,仔细观察比较下图

image-20240818203724193image-20240818203834693

image-20240818202822089

案例

animateTo核心步骤与animation一致

首先,第一步我们先声明相关状态变量

image-20240819100416447

然后,第二步我们将状态变量设置值到相关可动画属性接口

image-20240819100803567

接着,第三步我们需要编写animateTo动画,先写动画属性

image-20240819102521618

最后,第四步我们将对状态变量的逻辑操作写入箭头函数中

image-20240819103620998

最后效果(播放一次):

recording

(无限播放):

recording

!!!这边很明显的可以看见onFinish()回调并没有触发,原因很简单:只有当动画结束时,才会触发

帧动画animator

介绍

animator是一个动画对象,用于管理和控制动画的播放。它可以通过设置不同的参数来控制动画的行为,如延时播放、播放次数、插值曲线等。animator可以通过createAnimator函数创建,并通过disposeAnimator函数销毁。

文档developer.huawei.com/consumer/cn…

实现过程

  1. 由于animator是一个对象,对象定义都需要类型,官方文档中给的是接口AnimatorResult,所以这里需要导入。
import {AnimatorResult} from '@kit.ArkUI'

@Entry
@Component
struct Index {
    animator:AnimatorResult|undefined = undefined//没有创建动画为undefined
	build(){}
}
  1. 创建动画样式,这边同样也要导入类Animator,然后去使用他的create()方法,里面的动画属性在接口AnimatorOptions中,可以Ctrl+单击进去查看这8个属性缺一不可。有很多实例方法也存放接口AnimatorResult中,同样可以去查看。

    recording

import {Animator,AnimatorResult}  from '@kit.ArkUI'
@Entry
@Component
struct Index {
    animator:AnimatorResult|undefined = undefined//没有创建动画为undefined
    @State
 	angle: number = 0 //旋转角度
   	//创建动画
    create=()=>{
        this.animator=Animator.create({
            duration:10000,    //播放时长
            easing:'linear',   //动画曲线
            delay:0,           //延迟事件
            fill:'forwards'    //动画结束后是否回到开始状态,forwards是保留结束状态
            direction:'normal' //跟playMode一样,动画的播放方式
            iterations:-1      //动画次数
            begin:0            //动画插值起点
            end:360          //动画插值终点
            //这边举个例子,有张图片我要它实现每次转一圈,那么这里begin就是0,end就是360,然				后用一个状态变量储存angle,再旋转属性实现
        })
        this.animator.onFrame=(value)=>{
            this.angle=value
        }//接受到动画帧时回调,换句话说begin-->end就是0-->360的变化过程中,每变一次就赋值
        状态变量angle
    }
	build(){}
}
  1. 编写UI界面,并将调用animator对象
import {Animator,AnimatorResult}  from '@kit.ArkUI'
@Entry
@Component
struct Index {
    animator:AnimatorResult|undefined = undefined//没有创建动画为undefined
    @State
 	angle: number = 0 //旋转角度
    @State
  	num: number = 0 //这个次数是用来控制只有第一次生成动画
   	//创建动画
    create=()=>{
        this.animator=Animator.create({
            duration:10000,    //播放时长
            easing:'linear',   //动画曲线
            delay:0,           //延迟事件
            fill:'forwards'    //动画结束后是否回到开始状态,forwards是保留结束状态
            direction:'normal' //跟playMode一样,动画的播放方式
            iterations:-1      //动画次数
            begin:0            //动画插值起点
            end:360          //动画插值终点
            //这边举个例子,有张图片我要它实现每次转一圈,那么这里begin就是0,end就是360,然				后用一个状态变量储存angle,再旋转属性实现
        })
        this.animator.onFrame=(value)=>{
            this.angle=value
        }//接受到动画帧时回调,换句话说begin-->end就是0-->360的变化过程中,每变一次就赋值状            态变量angle
    }
	build(){
        Column() {
          Image($r('app.media.startIcon'))
            .width(200)
            .borderRadius(100)
            .rotate({
              angle: this.angle 
            })
            .onClick(() => {
              this.num += 1
              if (this.num == 1) {
                this.create()
              }
              if (this.animator) { //这边判断是否为undefined
                this.animator.play()
              }
            })
        }
        .width("100%")
        .height("100%")
    }
}

动画效果:

recording

试一试

recording

完整代码:

import { Animator, AnimatorResult } from '@kit.ArkUI'

@Entry
@Component
struct Index {
  @State
  angle1: number = 0
  @State
  isPause: boolean = true
  animator: AnimatorResult | undefined = undefined
  animator1: AnimatorResult | undefined = undefined
  @State
  angle: number = 0
  @State
  num: number = 0
  @State
  start: number = 0
  @State
  end: number = 30
  create1 = () => {
    this.animator = Animator.create({
      duration: 10000,
      easing: 'linear',
      delay: 0,
      fill: 'forwards',
      direction: 'normal',
      iterations: -1,
      begin: 0,
      end: 360
    })
    this.animator.onFrame = (value) => {
      this.angle = value
    }
  }
  create2 = () => {
    this.animator1 = Animator.create({
      duration: 1000,
      easing: 'linear',
      delay: 0,
      fill: 'forwards',
      direction: 'normal',
      iterations: 1,
      begin: this.start,
      end: this.end
    })
    this.animator1.onFrame = (value) => {
      this.angle1 = value
    }
  }

  build() {
    Column() {
      Stack() {
        Image($r('app.media.img_bell'))
          .width(200)
          .borderRadius(100)
          .rotate({
            angle: this.angle
          })

        Image(this.isPause == false ? $r('app.media.ic_suspend') : $r('app.media.ic_pause'))
          .width(40)
          .fillColor(Color.White)
          .onClick(() => {
            this.num += 1
            if (this.num == 1) {
              this.create1()
            }
            this.create2()
            if (this.isPause == true && this.animator && this.animator1) {
              this.isPause = false
              this.start = 30
              this.end = 0
              this.animator.play()
              this.animator1.play()
            } else if (this.isPause == false && this.animator && this.animator1) {
              this.isPause = true
              this.start = 0
              this.end = 30
              this.animator.pause()
              this.animator1.play()
            }
            console.log(this.start.toString())
            console.log(this.end.toString())
          })
        Image($r("app.media.ic_wheat"))
          .width(200)
          .position({ x: -100 })
          .rotate({
            centerX: 80,
            centerY: 20,
            angle: this.angle1
          })
      }
    }
    .backgroundColor(Color.Pink)
    .width("100%")
    .height("100%")
  }
}

这边,我是用了两个动画来写的,虽然有点烦但是何尝不是一种提升呢?大家可以使用更简单的方法(显示动画),但这种不可以不会哦!!!

不知不觉又要和大家说再见了,如果喜欢我的文章就请给我点个赞和关注吧!!!让我们下期见💕💕💕

我的歌单顺便也分享给大家:c6.y.qq.com/base/fcgi-b…

“本文正在参加华为鸿蒙有奖征文征文活动‘’