HarmonyOS鸿蒙Next中什么是属性动画?在开发中如何使用呢?

HarmonyOS鸿蒙Next中什么是属性动画?在开发中如何使用呢? 鸿蒙当中的可执行动画属性有布局属性,比如位置,大小,内外边距,对齐方式,背景,仿射变换等,那么在实际的开发中,如何触发其动画执行呢?在实际的开发中又如何来使用呢?

4 回复

属性接口包含尺寸属性、布局属性、位置属性等多种类型,用于控制组件的行为。针对当前界面上的组件,其部分属性(如位置属性)的变化会引起UI的变化。添加动画可以让属性值从起点逐渐变化到终点,从而产生连续的动画效果。为保障动画起点和终点的正确性,属性动画会将当前在标脏队列内的所有节点进行刷新。如果发现当前动画时长较长时,需要确认当前是否有额外的节点刷新。根据变化时是否能够添加动画,可以将属性分为可动画属性和不可动画属性。判断一种属性是否适合作为可动画属性主要有两个标准:

  1. 属性变化能够引起UI的变化。例如,enabled属性用于控制组件是否可以响应点击、触摸等事件,但enabled属性的变化不会引起UI的变化,因此不适合作为可动画属性。
  2. 属性在变化时适合添加动画作为过渡。例如,focusable属性决定当前组件是否可以获得焦点,当focusable属性发生变化时,应立即切换到终点值以响应用户行为,不应该加入动画效果,因此不适合作为可动画属性。

属性接口分类说明:

  • 可动画属性:
    • 系统可动画属性:
分类 说明
布局属性 位置、大小、内边距、外边距、对齐方式、权重等。
仿射变换 平移、旋转、缩放、锚点等。
背景 背景颜色、背景模糊等。
内容 文字大小、文字颜色,图片对齐方式、模糊等。
前景 前景颜色等。
Overlay Overlay属性等。
外观 透明度、圆角、边框、阴影等。
*   自定义可动画属性:通过自定义属性动画机制抽象出的可动画属性。
  • 不可动画属性:zIndex、focusable等。

通常,可动画属性的参数数据类型必须具备连续性,即可以通过插值方法来填补数据点之间的空隙,达到视觉上的连续效果。但属性的参数数据类型是否能够进行插值并非决定属性是否可动画的关键因素。例如,对于设置元素水平方向布局的direction属性,其参数数据类型是枚举值。但是,由于位置属性是可动画属性,ArkUI同样支持在其属性值改变引起组件位置变化时添加动画。

对于可动画属性,系统不仅提供通用属性,还支持自定义可动画属性。

  • 系统可动画属性:组件自带的支持改变UI界面的属性接口,如位置、缩放、模糊等。
  • 自定义可动画属性:ArkUI提供@AnimatableExtend装饰器用于自定义可动画属性。开发者可从自定义绘制的内容中抽象出可动画属性,用于控制每帧绘制的内容,如自定义绘制音量图标。通过自定义可动画属性,可以为ArkUI中部分原本不支持动画的属性添加动画。

接口文档:https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/arkts-attribute-animation-overview

更多关于HarmonyOS鸿蒙Next中什么是属性动画?在开发中如何使用呢?的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html


什么是属性动画呢?字面之义就是让属性产生动画,产生某些可执行的动作,使其和原有的UI形态发生了根本的变化,当然了,其本身也类似这层意思;属性动画中,我们需要知道,并不是所有的属性都可以执行动画操作,比如一个组件,设置焦点控制,禁用控制,改变的只是动作状态,而本身的UI形态并没有发生变化,所以并不能执行动画,也就不属于动画属性。

是否定义为一个可执行动画属性,有两个标准,第一个是改变它能够使其UI发生变化,比如宽高,内边距,外边距属性,第二就是,改变它,可以添加动画作为过渡,不做动画,又何谈属性动画呢,是吧。

常见的可执行动画属性,如下:

分类 说明
布局属性 位置、大小、内边距、外边距、对齐方式、权重等。
仿射变换 平移、旋转、缩放、锚点等。
背景 背景颜色、背景模糊等。
内容 文字大小、文字颜色,图片对齐方式、模糊等。
前景 前景颜色等。
Overlay Overlay属性等。
外观 透明度、圆角、边框、阴影等。

实现属性动画

属性动画最注重的就是两点,一是必须使其UI发生改变,二是能够有动画产生连续效果,目前在鸿蒙开发中,系统提供了两个可以实现属性动画的方式,使用animateTo和animation来实现。

简单举例:

使用animateTo让组件平移100,动画持续时间为1秒。

@Entry
@Component
struct Index {
  @State translateX: number = 0

  build() {
    Column() {
      Text("1")
        .width(50)
        .height(50)
        .textAlign(TextAlign.Center)
        .backgroundColor(Color.Red)
        .translate({ x: this.translateX })

      Button("点击")
        .margin({ top: 10 })
        .onClick(() => {
          this.getUIContext()?.animateTo({ duration: 1000 }, () => {
            this.translateX = 100 //x轴移动100
          })
        })
    }
    .height('100%')
      .width('100%')
  }
}

使用animation让组件平移100,动画持续时间为1秒。

@Entry
@Component
struct Index {
  @State translateX: number = 0

  build() {
    Column() {
      Text("1")
        .width(50)
        .height(50)
        .textAlign(TextAlign.Center)
        .backgroundColor(Color.Red)
        .translate({ x: this.translateX })
        .animation({duration:1000})

      Button("点击")
        .margin({ top: 10 })
        .onClick(() => {
          this.translateX = 100 //x轴移动100
        })
    }
    .height('100%')
      .width('100%')
  }
}

可以看到实际效果是一样的。

当然了,除了平移属性能够实现这种效果,其实你使用margin也能实现平移的效果。

.margin({ left: this.translateX })
.animation({ duration: 1000 })

说明

需要说明的是,实现一个动画,无论是使用animation还是animateTo都是触发的动作,真正展示视觉效果,还得是可执行的属性,无论平移,缩放还是旋转。

平移动画

让一个组件产生一个平移动画,设置translate属性即可,当然,你设置offset或者margin也是可以实现的,只不过**,一个是组件偏移,一个是位置的坐标偏移,我理解是大差不差的,但是还是建议使用官方的translate。**

其类型参数是TranslateOptions,接收三个参数,x,y,z,分别表示在对应轴移动的距离,值为正时表示向对应轴的正向移动,值为负时表示向对应轴的反向移动。

如果你仅仅是平移,只需要XY两个值即可,因为Z值有接近观察点放大和远离观察点缩小效果,一般未有缩放效果,Z值我们可以不传递。

例如,向X轴方向平移100,向Y轴方向平移100。

@Entry
@Component
struct Index {
  @State translateX: number = 0
  @State translateY: number = 0

  build() {
    RelativeContainer() {

      Text("1")
        .width(50)
        .height(50)
        .textAlign(TextAlign.Center)
        .backgroundColor(Color.Red)
        .translate({ x: this.translateX, y: this.translateY })
        .animation({ duration: 1000 })

      Button("点击")
        .onClick(() => {
          this.translateX = 100
          this.translateY = 100
        })
        .alignRules({
          center: { anchor: "__container__", align: VerticalAlign.Center },
          middle: { anchor: "__container__", align: HorizontalAlign.Center }
        })
    }
    .height('100%')
      .width('100%')
  }
}

效果:

如果你要加上Z值,比如也设置100,我们就会看到明显的缩放动画。

缩放动画

缩放动画,可以使用scale属性进行实现,类型参数为ScaleOptions,有五个参数,分别为X,Y,Z,centerX和centerY,X轴、Y轴、Z轴为缩放比例,默认值为1,centerX和centerY为设置缩放的中心点。

简单举例,原有基础之上放大2倍。

@Entry
@Component
struct Index {
  @State translateX: number = 1
  @State translateY: number = 1
  @State translateZ: number = 1

  build() {
    RelativeContainer() {

      Text("1")
        .width(50)
        .height(50)
        .textAlign(TextAlign.Center)
        .backgroundColor(Color.Red)
        .scale({ x: this.translateX, y: this.translateY, z: this.translateZ })
        .animation({ duration: 1000 })
        .margin({ top: 100 })
        .alignRules({
          top: { anchor: "__container__", align: VerticalAlign.Top },
          middle: { anchor: "__container__", align: HorizontalAlign.Center }
        })

      Button("点击")
        .margin({ top: 10 })
        .onClick(() => {
          this.translateX = 2
          this.translateY = 2
          this.translateZ = 2
        })
        .alignRules({
          center: { anchor: "__container__", align: VerticalAlign.Center },
          middle: { anchor: "__container__", align: HorizontalAlign.Center }
        })
    }
    .height('100%')
      .width('100%')
  }
}

效果查看:

默认系数为1,放大就比1大,缩小就比1小,centerX和centerY就是中心点,默认是组件的中点,如下图,放大两倍后,也是以中点放大两倍。

如果你更改了centerX和centerY的值,再放大时就是以更改的值作为中心点进行放大,可以看到是有明显的区别的。

旋转动画

旋转动画,可以使用rotate属性进行实现,类型参数为RotateOptions,总共有8个参数,详细介绍如下:

名称 类型 必填 说明
x number 旋转轴向量x坐标。
y number 旋转轴向量y坐标。
z number 旋转轴向量z坐标。
angle number | string 旋转角度。取值为正时相对于旋转轴方向顺时针转动,取值为负时相对于旋转轴方向逆时针转动。取值可为string类型,如’90deg’。
centerX number | string 变换中心点x轴坐标。表示组件变换中心点(即锚点)的x方向坐标。单位:vp
centerY number | string 变换中心点y轴坐标。表示组件变换中心点(即锚点)的y方向坐标。单位:vp
centerZ10+ number z轴锚点,即3D旋转中心点的z轴分量。
perspective10+ number 视距,即视点到z=0平面的距离。旋转轴和旋转中心点都基于坐标系设定,组件发生位移时,坐标系不会随之移动。

正常的旋转,我们只改变角度即可,也就是按照中心点旋转,比如旋转180度。

@Entry
@Component
struct Index {
  @State angle: number = 0

  build() {
    RelativeContainer() {

      Text("1")
        .width(50)
        .height(50)
        .textAlign(TextAlign.Center)
        .backgroundColor(Color.Red)
        .rotate({ angle: this.angle})
        .animation({ duration: 1000 })
        .margin({ top: 100 })
        .alignRules({
          top: { anchor: "__container__", align: VerticalAlign.Top },
          middle: { anchor: "__container__", align: HorizontalAlign.Center }
        })

      Button("点击")
        .margin({ top: 10 })
        .onClick(() => {
          this.angle = 180
        })
        .alignRules({
          center: { anchor: "__container__", align: VerticalAlign.Center },
          middle: { anchor: "__container__", align: HorizontalAlign.Center }
        })
    }
    .height('100%')
      .width('100%')
  }
}

效果查看:

当然了,你也可以通过改变中心点,或者坐标点,实现想要的旋转效果。

组合动画

单一动画,我们设置单个可执行属性即可,如果是多个动画,显而易见,就是设置多个属性,比如实现一个,X轴移动100,并且旋转180度,放大1.5倍。

@Entry
@Component
struct Index {
  @State angle: number = 0
  @State translateX: number = 0
  @State scaleX: number = 1
  @State scaleY: number = 1
  @State scaleZ: number = 1

  build() {
    RelativeContainer() {

      Text("1")
        .width(50)
        .height(50)
        .textAlign(TextAlign.Center)
        .backgroundColor(Color.Red)
        .rotate({ angle: this.angle })
        .translate({ x: this.translateX })
        .scale({ x: this.scaleX, y: this.scaleY, z: this.scaleZ })
        .animation({ duration: 1000 })
        .margin({ top: 100 })
        .alignRules({
          top: { anchor: "__container__", align: VerticalAlign.Top },
          middle: { anchor: "__container__", align: HorizontalAlign.Center }
        })

      Button("点击")
        .margin({ top: 10 })
        .onClick(() => {
          this.translateX = 100
          this.angle = 180
          this.scaleX = 1.5
          this.scaleY = 1.5
          this.scaleY = 1.5
        })
        .alignRules({
          center: { anchor: "__container__", align: VerticalAlign.Center },
          middle: { anchor: "__container__", align: HorizontalAlign.Center }
        })
    }
    .height('100%')
      .width('100%')
  }
}

效果查看

两种属性动画介绍

无论是是使用animateTo还是animation,其实最终要改变的都是组件的可执行属性,最终的效果是一致的,animateTo是闭包内改变属性引起的界面变化,一般作用于出现消失转场,而animation则是组件通过属性接口绑定的属性变化引起的界面变化,一般使用场景为,animateTo适用对多个可动画属性配置相同动画参数的动画,需要嵌套使用动画的场景;animation适用于对多个可动画属性配置不同参数动画的场景。

相关总结

如果你要执行动画的组件,是始终存在的,那么这种情况下,是推荐使用的,如果是那种将要出现或者将要消失的组件,这里是不推荐的。

还有就是,要合理的使用属性动画,避免一些轮询机制中使用,否则会影响其它页面的组件展示效果。

属性动画是HarmonyOS中用于实现UI元素属性值平滑过渡的动画系统。它通过改变对象的属性值(如位置、透明度、缩放)并逐帧刷新来实现动画效果。

在开发中使用时,首先导入@ohos.animator模块。然后创建Animator对象,设置目标属性、起始值、结束值、时长和曲线。通过onFrame回调更新UI属性,最后调用start()启动动画。关键类是AnimatorPropertyAnimator

在HarmonyOS Next中,属性动画(Property Animation)是指通过改变组件的某个属性值(如位置、大小、透明度、旋转角度等),并让这个变化过程在一段时间内平滑过渡,从而形成视觉动画效果。

核心机制与使用方式:

  1. 动画属性:正如您所列举的,几乎所有影响组件外观和布局的属性都可以进行动画化,包括:

    • 布局属性positionwidthheightmarginpaddingconstraintSize等。
    • 变换属性translatescalerotate
    • 外观属性backgroundColoropacityblur等。
  2. 触发与执行:属性动画的核心是状态管理。通常通过改变与组件属性绑定的状态变量(@State, @Prop, @Link等)的值来触发动画。系统会监测到状态变化,并自动计算从前一个值到新值的平滑过渡。

基本使用步骤(以ArkTS为例):

// 1. 定义动画参数和状态变量
@State private animateWidth: number = 100
@State private animateOpacity: number = 1.0
private animationSettings: AnimateParam = {
  duration: 500, // 动画时长(毫秒)
  curve: Curve.EaseInOut // 动画曲线(缓动函数)
}

// 2. 在UI描述中,将组件的属性与状态变量绑定,并应用动画参数
Column() {
  // 当animateWidth或animateOpacity变化时,宽和不透明度会以动画形式过渡到新值
  Rectangle()
    .width(this.animateWidth)
    .opacity(this.animateOpacity)
    .animation(this.animationSettings) // 应用动画配置
}
.onClick(() => {
  // 3. 在事件(如点击)中改变状态值,触发动画
  this.animateWidth = (this.animateWidth === 100) ? 200 : 100
  this.animateOpacity = (this.animateOpacity === 1.0) ? 0.5 : 1.0
})

关键点:

  • .animation()修饰器:将其附加到组件上,并传入AnimateParam对象来配置该组件属性变化的动画行为(时长、曲线、延迟、迭代次数等)。
  • 隐式动画:这是最常用的方式。当状态变量驱动属性变化时,只需声明.animation(),系统会自动生成补间动画。
  • 显式动画:对于更复杂的控制(如并行、顺序动画),可使用animateTo函数显式地定义动画闭包,闭包内所有属性变化都会以动画执行。
  • 动画曲线Curve枚举(如LinearEaseSpring)定义了变化速率,对视觉效果影响很大。

总结:在HarmonyOS Next开发中,使用属性动画的本质是声明式地绑定状态与属性,并通过.animation()修饰器或animateTo函数声明动画配置。状态变化即触发动画,开发者无需手动计算每一帧的值。

回到顶部