鸿蒙开发:平移动画时间为啥没了?

程序员一鸣
• 阅读 10

前言

本文基于Api13

这两天在搞有关动画相关的,有一个很简单的平移动画,却卡了我一段时间,由于事情曲折,各位友友听我娓娓道来;说的是,有一个组件,设置了translate属性,让其从左到右平移,为了能够平缓的平移,需要加上平移时间,代码如下:

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

  build() {
    Column() {

      Text("动画" )
        .width(50)
        .height(50)
        .backgroundColor(Color.Pink)
        .textAlign(TextAlign.Center)
        .margin({ top: 10 })
        .fontColor(Color.White)
        .translate({ x: this.translateX })
        .animation({ duration: 500 })

      Button("点击")
        .margin({ top: 10 })
        .onClick(() => {
          this.translateX = 200
        })
    }
  }
}

以上呢,就是一段很简单的代码,点击按钮之后,让组件由左向右平移200,持续动画时间为500毫秒。

我们看下实际的运行效果:

鸿蒙开发:平移动画时间为啥没了?

可以看到,上述的代码,一切都没问题的,都按照正常的功能执行,但是,我不是移动一个组件,而是多个组件,于是,我又创建了一个组件。

Column() {

      Text("动画1")
        .width(50)
        .height(50)
        .backgroundColor(Color.Pink)
        .textAlign(TextAlign.Center)
        .margin({ top: 10 })
        .fontColor(Color.White)
        .translate({ x: this.translateX })
        .animation({ duration: 500 })

      Text("动画2")
        .width(50)
        .height(50)
        .backgroundColor(Color.Pink)
        .textAlign(TextAlign.Center)
        .margin({ top: 10 })
        .fontColor(Color.White)
        .translate({ x: this.translateX })
        .animation({ duration: 500 })

      Button("点击")
        .margin({ top: 10 })
        .onClick(() => {
          this.translateX = 200
        })
    }

以上的代码运行之后,也是没有问题,如果有N个组件呢,一个一个复制也不是办法,于是,我就使用ForEach来遍历,这样直接控制数据源就行了,代码又改为了如下:

Column() {

      ForEach([1, 2], (item: number, index: number) => {
        Text("动画"+index)
          .width(50)
          .height(50)
          .backgroundColor(Color.Pink)
          .textAlign(TextAlign.Center)
          .margin({ top: 10 })
          .fontColor(Color.White)
          .translate({ x: this.translateX })
          .animation({ duration: 500 })
      })

      Button("点击")
        .margin({ top: 10 })
        .onClick(() => {
          this.translateX = 200
        })
    }

效果和之前的一样,点击按钮之后,两个组件也能同步的进行平移,有的友友就说了,你的问题在哪?tell me why?looking my eyes!

各位友友稍安勿躁,问题马上来了。

因为牵扯的不仅仅是多组件移动的问题,后续的功能,还有每个组件的移动距离是不一样的,于是,我又重新定义的数据源,而移动距离则从数据源中获取:

ForEach(this.translateArray, (item: number,index:number) => {
        Text("动画"+index)
          .width(50)
          .height(50)
          .backgroundColor(Color.Pink)
          .textAlign(TextAlign.Center)
          .margin({ top: 10 })
          .fontColor(Color.White)
          .translate({ x: item })
          .animation({ duration: 500 })
      })

以上的代码,就能实现每个组件的平移距离动态设置,想让那个组件移动,就让哪个组件移动,只需要控制数据源即可,貌似代码非常完美,当时我也是这么觉得,于是就运行了程序。

让第一个组件平移200,让第二个组件平移300。

Button("点击")
        .margin({ top: 10 })
        .onClick(() => {
          this.translateArray[0] = 200
          this.translateArray[1] = 300
        })

运行之后看下效果:

鸿蒙开发:平移动画时间为啥没了?

移动过去了吗?哎,确实移动过去了,但是,平移动画的时间没了!只是很生硬的一下平移过去了,一个ForEach把动画时间干没了,此时的我,大脑早已一串问号。

追寻问题

从前言中,可以看到,一开始使用ForEach,只是数据源固定的,使用共同的平移属性,那时的代码运行之后,还一切正常,直到数据源切换为了动态的数据源,此时的动画时间就不生效了;针对这个问题,我们再次做下验证,让数据源和改变的平移数据区分开来,我们再次看下实际的效果。

@Entry
@Component
struct Index {
  @State dataArray: number[] = [0, 0]
  @State translateArray: number[] = [0, 0]

  build() {
    Column() {

      ForEach(this.dataArray, (_: number,index:number) => {
        Text("动画"+index)
          .width(50)
          .height(50)
          .backgroundColor(Color.Pink)
          .textAlign(TextAlign.Center)
          .margin({ top: 10 })
          .fontColor(Color.White)
          .translate({ x: this.translateArray[index] })
          .animation({ duration: 500 })
      })

      Button("点击")
        .margin({ top: 10 })
        .onClick(() => {
          this.translateArray[0] = 200
          this.translateArray[1] = 300
        })
    }
  }
}

代码还是无比的简单,定义了两个数据源,一个用于数据加载,一个用于平移距离设置,运行之后,我们看下效果:

鸿蒙开发:平移动画时间为啥没了?

我们可以发现,动画时间又生效了,基本上,我们就可以暂时得出结论了,在ForEach中的属性动画,如果数据源发生改变,那么动画时间是不生效的。

这是为什么呢?

数据源发生变化,和动画时间有什么关系?这简直就是风牛马不相及,于是,我就查看了官网的ForEach介绍,看到了这样的一段话:

鸿蒙开发:平移动画时间为啥没了?

似乎恍然大悟,ForEach中当键值变化时,ArkUI框架将视为该数组元素已被替换或修改,并会基于新的键值创建一个新的组件。

查看了之前的代码,由于没有设置,都是统一的使用的默认的键值,也就是(item: Object, index: number) => { return index + '__' + JSON.stringify(item); },打印了一下日志,确实发生了变化。

鸿蒙开发:平移动画时间为啥没了?

不过有一事,仍然不明,你组件创建创建呗,和我动画时间有毛关系,接着又查看了官方对animation的相关介绍。

如果内部组件还未创建,动画时机过早,动画属性没有初值无法对组件产生动画。

通过以上,我们就能很直观的明白了问题的原因,第一个,由于键值发生了变化,造成了组件重新创建,第二个,由于组件重新创建,动画时机过早,造成属性未生效。

相关总结

这是一个由属性动画造成的一系列问题,折射出了ForEach的键值知识点,所以啊,友友们,遇到问题,莫慌,还是要以官方为主,不过此问题,还是给自己上了一课,基础知识不牢,是最大的痛点!

有的友友说了,虽然,采用两个数据源,解决了以上的问题,可我就想使用一个呢,这个,在实际的开发中,可以使用对象数组的形式,更改需要变化的属性即可,避免重新创建新的组件。

具体案例如下:

@Observed
class TranslateBean {
  name?: string
  translate?: number

  constructor(name: string, translate: number) {
    this.name = name
    this.translate = translate
  }
}

@Component
struct TextView {
  @ObjectLink item: TranslateBean

  build() {
    Text(this.item.name)
      .width(50)
      .height(50)
      .backgroundColor(Color.Pink)
      .textAlign(TextAlign.Center)
      .margin({ top: 10 })
      .fontColor(Color.White)
      .translate({ x: this.item.translate })
      .animation({ duration: 500 })
  }
}


@Entry
@Component
struct Index {
  @State dataArray: TranslateBean[] = [new TranslateBean("动画一", 0), new TranslateBean("动画二", 0)]
  @State translateArray: number[] = [200, 200]

  build() {
    Column() {

      ForEach(this.dataArray, (item: TranslateBean, index: number) => {
        TextView({ item: item })
      })

      Button("点击")
        .margin({ top: 10 })
        .onClick(() => {
          this.dataArray[0].translate = 200
          this.dataArray[1].translate = 300
        })
    }
  }
}
点赞
收藏
评论区
推荐文章
Stella981 Stella981
3年前
Android Property Animation
属性动画系统是一个健壮的框架,它使你几乎能够将任何东西做成动画。你可以定义一个动画来随着时间改变任何对象属性,而不管它是否会被绘制到屏幕上。一个属性动画在一个特定的时间长度中改变一个属性的值(一个对象中的一个成员)。当这个属性会影响到屏幕上绘制的组件时,也就产生了我们看得到的那类动画了。要动画化一些东西,你可以指定你想要动画化的对象属性,比如一个对象在屏幕上
Wesley13 Wesley13
3年前
Unity 2D游戏开发教程之使用脚本实现游戏逻辑
Unity2D游戏开发教程之使用脚本实现游戏逻辑使用脚本实现游戏逻辑通过上一节的操作,我们不仅创建了精灵的动画,还设置了动画的过渡条件,最终使得精灵得以按照我们的意愿,进入我们所指定的动画状态。但是这其中还有一些问题。例如,我们无法使用键盘控制精灵当前要进入的动画状态,而且精
美凌格栋栋酱 美凌格栋栋酱
5个月前
Oracle 分组与拼接字符串同时使用
SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(
陈杨 陈杨
3个月前
鸿蒙原生绘图API:从基础到高阶的绘制之旅(基础版)
theme:hydrogen大家好,欢迎来到莓创IT技术分享频道,我是陈杨。由于经常有小伙伴一直给我反馈说莓创图表(mccharts)数据多的时候经常卡顿,很无奈之前做动画的时候没考虑ArkTs的性能瓶颈,导致现在又要重构开发。于是我重新翻阅文档,看看有没
陈杨 陈杨
2天前
鸿蒙5横向柱状图series属性解析
大家好,欢迎回来鸿蒙5莓创图表组件的专场,我们这一期来讲解McHorBarChart(横向柱状图)组件中series属性的详细用法。series属性是控制柱状图数据系列的核心配置项,掌握好这些属性可以让你轻松创建各种样式的柱状图效果。1.name属性作用:
陈杨 陈杨
2天前
鸿蒙5莓创图表柱状图组件series属性全解析:打造高定制化柱状图
大家好,欢迎回来鸿蒙5莓创图表组件的专场,我们这一期来深入讲解柱状图(BarChart)的series属性配置。通过掌握这些属性,你可以灵活控制柱状图的显示、样式、动画及交互效果。本文将逐一拆解所有属性,结合代码案例帮助大家快速上手!1.show作用:控制
陈杨 陈杨
2天前
鸿蒙5莓创图表组合图组件深度讲解:基础属性篇
大家好,欢迎回来鸿蒙5莓创图表组件的专场,我们这一期来讲解McLineBarChart组合图组件的基础属性详细用法。本文将通过分点拆解每个属性的核心功能和使用方式,帮助开发者快速掌握该组件的定制能力。一、grid(绘图网格)作用:控制图表主体与容器边界的间
GeorgeGcs GeorgeGcs
6小时前
【HarmonyOS 5】桌面快捷方式功能实现详解
鸿蒙开发能力HarmonyOSSDK应用服务鸿蒙金融类应用(金融理财一、前言在移动应用开发中,如何让用户快速触达核心功能,是目前很常见的功能之一。鸿蒙系统提供的桌面快捷方式(Shortcuts)功能,允许开发者为应用内常用功能创建直达入口,用户通过长按应用
GeorgeGcs GeorgeGcs
5小时前
【HarmonyOS 5】如何开启DevEco Studio热更新调试应用模式
鸿蒙开发能力HarmonyOSSDK应用服务鸿蒙金融类应用(金融理财一、AttributeModifier和AttributeUpdater的定义和作用1.AttributeModifier是ArkUI组件的动态属性,提供属性设置功能。开发者可使用attr