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

前言

本文基于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
        })
    }
  }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

程序员一鸣

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值