鸿蒙动画开发01——布局更新动画

 如果你也对鸿蒙开发感兴趣,加入“Harmony自习室”吧!扫描下方名片,关注公众号,公众号更新更快,同时也有更多学习资料和技术讨论群。

1、概述

从现在起,我们将开启一个新系列——鸿蒙动画开发系列,在这个系列中,我们将分别接触鸿蒙的各种动画效果。在开始之前,我们先对动画做一个简单的介绍。

1.1、什么是动画

动画的原理是在一个时间段内,多次改变UI外观,由于人眼会产生视觉暂留,所以最终看到的就是一个“连续”的动画。

UI的一次改变称为一个动画帧,对应一次屏幕刷新,而决定动画流畅度的一个重要指标就是帧率FPS(Frame Per Second),即每秒的动画帧数,帧率越高则动画就会越流畅。

ArkUI中,产生动画的方式是改变属性值且指定动画参数。动画参数包含了如动画时长、变化规律(即曲线)等参数。当属性值发生变化后,按照动画参数,从原来的状态过渡到新的状态,即形成一个动画。

1.2、鸿蒙动画的分类

鸿蒙的动画有两种分类方式:1. 页面分类的动画;2. 基础能力分类的动画。

👉🏻 页面分类的方式又可分为页面内的动画和页面间的动画。如下图所示,页面内的动画指在一个页面内即可发生的动画,页面间的动画指两个页面跳转时才会发生的动画。

图片

👉🏻 如果按照基础能力分,可分为属性动画、显式动画、转场动画三部分。如下图所示。

图片

下面,我们将逐步展开学习,第一步,先学习页面内的动画。

2、布局更新动画

显式动画(animateTo)和属性动画(animation)是ArkUI提供的最基础和常用的动画功能。

在布局属性(如尺寸属性、位置属性)发生变化时,可以通过属性动画或显式动画,按照动画参数过渡到新的布局参数状态。他们分别有如下特点:

👉🏻 显示动画

闭包内的变化均会触发动画,包括由数据变化引起的组件的增删、组件属性的变化等,可以做较为复杂的动画。

👉🏻 属性动画

动画设置简单,属性变化时自动触发动画(有css开发经验的朋友应该会比较熟悉)。

2.1、显示动画的使用

显式动画的接口如下:

animateTo(value: AnimateParam, event: () => void): void

第一个参数指定动画参数,第二个参数为动画的闭包函数。

其中AnimateParam是动画参数,包含有如下字段:

class AnimateParam {/*  动画持续时间,单位为毫秒。默认值:1000  从API version 9开始,该接口支持在ArkTS卡片中使用。  说明:  - 在ArkTS卡片上最大动画持续时间为1000毫秒,若超出则固定为1000毫秒。  - 设置浮点型类型的值时,向下取整。例如,设置值为1.2,按照1处理。*/duration: number;/*  动画的播放速度,值越大动画播放越快,值越小播放越慢,为0时无动画效果。  默认值:1.0*/tempo: number;/*动画曲线。默认值:Curve.EaseInOut*/curve: Curve | ICurve | string;/*  单位为ms(毫秒),默认不延时播放。默认值:0  说明:  - 设置浮点型类型的值时,向下取整。例如,设置值为1.2,按照1处理。*/delay: number;/*  默认播放一次,设置为-1时表示无限次播放。默认值:1*/iterations: number;/*  设置动画播放模式,默认播放完成后重头开始播放。默认值:PlayMode.Normal*/playMode: PlayMode;// 动效播放完成回调。onFinish: () => void;}

以下是使用显式动画产生布局更新动画的示例。示例中,当Column组件的alignItems属性改变后,其子组件的布局位置结果发生变化。只要该属性是在animateTo的闭包函数中修改的,那么由其引起的所有变化都会按照animateTo的动画参数执行动画过渡到终点值。​​​​​​​

@Entry@Componentstruct LayoutChange {  // 用于控制Column的alignItems属性  @State itemAlign: HorizontalAlign = HorizontalAlign.Start;  allAlign: HorizontalAlign[] = [HorizontalAlign.Start, HorizontalAlign.Center, HorizontalAlign.End];  alignIndex: number = 0;  build() {    Column() {      Column({ space: 10 }) {        Button("1").width(100).height(50)        Button("2").width(100).height(50)        Button("3").width(100).height(50)      }      .margin(20)      .alignItems(this.itemAlign)      .borderWidth(2)      .width("90%")      .height(200)      Button("click").onClick(() => {        // 动画时长为1000ms,曲线为EaseInOut        animateTo({ duration: 1000, curve: Curve.EaseInOut }, () => {          this.alignIndex = (this.alignIndex + 1) % this.allAlign.length;          // 在闭包函数中修改this.itemAlign参数,使Column容器内部孩子的布局方式变化,使用动画过渡到新位置          this.itemAlign = this.allAlign[this.alignIndex];        });      })    }    .width("100%")    .height("100%")  }}

图片

除直接改变布局方式外,也可直接修改组件的宽、高、位置。​​​​​​​

@Entry@Componentstruct LayoutChange2 {  @State myWidth: number = 100;  @State myHeight: number = 50;  // 标志位,true和false分别对应一组myWidth、myHeight值  @State flag: boolean = false;  build() {    Column({ space: 10 }) {      Button("text")        .type(ButtonType.Normal)        .width(this.myWidth)        .height(this.myHeight)        .margin(20)      Button("area: click me")        .fontSize(12)        .margin(20)        .onClick(() => {          animateTo({ duration: 1000, curve: Curve.Ease }, () => {            // 动画闭包中根据标志位改变控制第一个Button宽高的状态变量,使第一个Button做宽高动画            if (this.flag) {              this.myWidth = 100;              this.myHeight = 50;            } else {              this.myWidth = 200;              this.myHeight = 100;            }            this.flag = !this.flag;          });        })    }    .width("100%")    .height("100%")  }}

在第二个Button的点击事件中,使用animateTo函数,在闭包中修改this.myWidth和this.myHeight状态变量,而这两个状态变量分别为第一个Button的宽、高属性值,所以第一个Button做了宽高动画。效果如下图。

图片

与此同时,第二个Button也产生了一个位置动画。这是由于第一个Button的宽高变化后,引起了Column内部其他组件的布局结果也发生了变化,第二个Button的布局发生变化也是由于闭包内改变第一个Button的宽高造成的。

❓Q:如果我不想让第二个Button的位置被影响,那需要怎么做呢?

✅A:可以使用一些可以相互隔离的布局来实现,例如层叠布局,详情参考:ArkTs布局入门03——层叠布局(Stack)

2.2、属性动画的使用

显式动画把要执行动画的属性的修改放在闭包函数中触发动画,而属性动画则无需使用闭包,把animation属性加在要做属性动画的组件的属性后即可。

属性动画的接口为(AnimateParam参数的字段同上):

animation(value: AnimateParam)

其入参为动画参数。想要组件随某个属性值的变化而产生动画,此属性需要加在animation属性之前。有的属性变化不希望通过animation产生属性动画,可以放在animation之后。上面显式动画的示例很容易改为用属性动画实现。例如:​​​​​​​

@Entry@Componentstruct LayoutChange2 {  @State myWidth: number = 100;  @State myHeight: number = 50;  @State flag: boolean = false;  @State myColor: Color = Color.Blue;  build() {    Column({ space: 10 }) {      Button("text")        .type(ButtonType.Normal)        .width(this.myWidth)        .height(this.myHeight)        // animation只对其上面的type、width、height属性生效,时长为1000ms,曲线为Ease        .animation({ duration: 1000, curve: Curve.Ease })        // animation对下面的backgroundColor、margin属性不生效        .backgroundColor(this.myColor)        .margin(20)              Button("area: click me")        .fontSize(12)        .onClick(() => {          // 改变属性值,配置了属性动画的属性会进行动画过渡          if (this.flag) {            this.myWidth = 100;            this.myHeight = 50;            this.myColor = Color.Blue;          } else {            this.myWidth = 200;            this.myHeight = 100;            this.myColor = Color.Pink;          }          this.flag = !this.flag;        })    }  }}

上述示例中,第一个button上的animation属性,只对写在animation之前的type、width、height属性生效,而对写在animation之后的backgroundColor、margin属性无效。运行结果是width、height属性会按照animation的动画参数执行动画,而backgroundColor会直接跳变,不会产生动画。效果如下图:

图片


 

⭐️ tips:属性动画和显示动画在实际应用时有一些区别。

  1. 使用属性动画时,会按照指定的属性动画参数执行动画。每个组件可为自己的属性配置不同参数的属性动画。

  2. 显式动画会对动画闭包前后造成的所有界面差异执行动画,且使用同一动画参数,适用于统一执行的场景。此外,显式动画也可以用于一些非属性变量造成的动画,如if/else的条件,ForEach使用的数组元素的删减。

  3. 如果一个属性配置了属性动画,且在显式动画闭包中改变该属性值,属性动画优先生效,会使用属性动画的动画参数。

如果你还有什么问题,可以随时留言,或者入群讨论,获取群方式如下:

图片

  • 25
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值