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

前言

本文基于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毫秒。

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

91001.gif

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

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
        })

运行之后看下效果:

90002.gif

移动过去了吗?哎,确实移动过去了,但是,平移动画的时间没了!只是很生硬的一下平移过去了,一个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
        })
    }
  }
}

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

90003.gif

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

这是为什么呢?

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

image.png

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

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

image.png

不过有一事,仍然不明,你组件创建创建呗,和我动画时间有毛关系,接着又查看了官方对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
        })
    }
  }
}
#鸿蒙开发#
鸿蒙点石成金 文章被收录于专栏

简单的也好,复杂的也罢,关于HarmonyOS,总要深入浅出,总要步步为赢。

全部评论

相关推荐

写下这篇文章的时候,我正坐在从学校飞往北京的飞机上。就在今天,我的秋招终于算是有了结论,一共60场面试,拿到了字节百度美团等10+大厂offers,最终确认了腾讯给的机会。同时给我的这三个月,这三年以及从今天往前的所有人生做了个结。这句话写的真好,为什么这么说呢?本来挺久之前我就想写点什么,有特别多想记录的,从选择这个专业到选择这个岗位,从科研的疲惫到未来生活的期待,但总感觉这样写没个纲,乱成一团。直到我今天正式在系统中点击了三方的确认,我才突然发现这种感觉就是“不可逃避的结束”在向我走来,于是纲便有了。首先是这三个月的结果吧,或者换句话说,其实是秋招的结果。从我硕士选择了强化学习的研究方向,我就知道并不会有太多的岗位。从试错中学习,这听起来很符合人类的学习方式,但实际场景中哪来那么多试错的成本?除了游戏产业和机器人行业,我想不到特别对口的赛道,而这两个行业国内又只有寡头,让我望而生畏。整个秋招,我没法像学后端开发的同学一样投递大量的简历,我没法像学大模型的同学一样是时代的香饽饽,我只能盯着那几家公司去投,或者想方设法的在别的不太相关的算法岗上沾沾边。方向是大于努力的,但努力一定不是不重要的。秋招整体对我来说还算顺利,前文就自然变成了只有我自己懂的无病呻吟,不再赘述。从结果来说,我的秋招是非常成功的,至少我自己是满意的。命运给了我很大的惊喜,我从未想过能够在这次有多个远超期待的offer,所以我如今是心满意足。虽说很多事都是焉知非福吧,但对口的工作内容,熟悉的工作环境,我一定不会后悔。我就是这样,毕竟让我在做一百次选择也不会变,那为什么要在不可预测的未来后悔。然后是三年,三年即将过去,我的硕士生涯来到了最后一章。回想过往,我在其中反复感受井底之蛙的狭隘。从我在二十多个四点睡的凌晨产出的论文初稿开始,链式反应就这样发生了。把论文投出去,我发了一篇很长的朋友圈,那时候觉得压力真的好大,尽管其实根本没人要求我什么。那时,我第一次觉得我比本科毕业时的自己进步了太多,可以独当一面了。然后去了北京自所交流,尽管大多的时间都在修改那篇返稿的文章,但也在不一样的平台中见识了人外有人的世界。回来后,我第二次觉得自己有了很大的进步,而鄙夷去北京前的自己是如此短浅。那是11月,我开始纠结到底未来该从事开发岗还是算法岗,但时间并没有给我机会。我偷懒了,两个月根本没有做任何开发岗的准备,于是只能硬闯算法。期间只有那篇论文中了让我稍微有些自信,毕竟只有两周的理论准备时间让我心里太虚了,这甚至还算上了刷题的时间。第一面就是最想去的公司,我甚至紧张到大脑一片空白。好在后面算是有惊无险,拿到了腾讯给我的实习机会。去腾讯工作的时间是幸福的,组里氛围也很好,在公司获得的提升我觉得甚至超过了我在学校一年的量。毕竟做算法,思维的敏捷度和见识广度都是如此重要。看着同事前辈们的工作能力,和工业级的项目架构,我又一次不由得感叹曾经自己的狭隘。于是每天我只睡五小时,忙完工作忙学校,每每想到这里,我也不觉得我的成功是侥幸了。我真的建议大家离开自己舒适的环境到外面看看,鸡头或许真的不如凤尾。硕士是一个连锁反应最直接,最有力的时期。高考失利或许还能补救,考研没上岸还有第二次机会,但就业前这一年,努力就是会有回报,就一定会体现在结果中,没有侥幸。最后,也是我最想聊的。十九年的学生生涯终于快要画下句号,我其实一直觉得非常梦幻。我能回忆起每一个瞬间,有小学六年级遇到的很有个性的数学老师,有考上重点中学的快乐,有中考和提前高考而大失败的难受,有本科比赛的每个通宵的焦虑,有保研出现差错的绝望,有刚读研高压之下的崩溃。但这篇长文不会再有更多的剧情了,每个故事都让我无限回味,成为了我一生中最宝贵的财富。这些瞬间组成了我。我父亲说我是一个总抓不住机会的人,确实有很多别人没有的机会摆在我面前,我都错过了。但我心中的热爱始终没有错过,我觉得这对我来说是幸运且幸福的。我非常爱打游戏,从初中开始学编程,第一个目的就是做出属于自己的游戏,做了很多小游戏发在班级群里,被人厌烦。高中自己买了unity的书,想做自己的游戏,无奈连网络的基本知识都不懂,无功而返。到了大学,我又被强化学习吸引,我想知道能不能让人工智能来帮我打游戏呢?这一整条线我没有放弃过,拿到了游戏算法offer,我真的特别特别开心。人不是一直成功的,我经历过的失败远超过成功10倍,但那让我知道成功来之不易,让我知道失败是生活常态,让我知道真正的怯懦不是不敢失败,而是不敢尝试。言尽于此,这些都“不可逃避的结束”了。追风赶月莫停留,平芜尽处是春山。
肖先生~:追风赶月莫停留,平芜尽处是春山,passion!
我的秋招日记
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务