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

简介: 问题的原因,第一个,由于键值发生了变化,造成了组件重新创建,第二个,由于组件重新创建,动画时机过早,造成属性未生效。

前言


本文基于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
        })
    }
  }
}
相关文章
|
3月前
|
容器
HarmonyOS NEXT仓颉开发语言实战案例:外卖App
仓颉语言实战分享,教你如何用仓颉开发外卖App界面。内容包括页面布局、导航栏自定义、搜索框实现、列表模块构建等,附完整代码示例。轻松掌握Scroll、List等组件使用技巧,提升HarmonyOS应用开发能力。
|
3月前
|
存储 IDE 定位技术
【HarmonyOS 5】鸿蒙组件&模板服务详解 - 助力高效开发的利器
在移动应用开发领域,效率与质量始终是开发者追求的核心目标。鸿蒙系统作为新兴的操作系统,为开发者提供了丰富且强大的开发资源,其中鸿蒙组件&模板服务更是成为开发者快速构建高质量应用的得力助手。
120 0
|
3月前
|
容器
HarmonyOS NEXT仓颉开发语言实战案例:健身App
本期分享一个健身App首页的布局实现,顶部采用Stack容器实现重叠背景与偏移效果,列表部分使用List结合Scroll实现可滚动内容。代码结构清晰,适合学习HarmonyOS布局技巧。
|
3月前
|
容器
HarmonyOS NEXT仓颉开发语言实战案例:银行App
仓颉语言银行App项目分享,页面布局采用List容器,实现沉浸式体验与模块化设计。顶部资产模块结合Stack与Row布局,背景图与内容分离,代码清晰易懂;功能按钮部分通过负边距实现上移效果,圆角仅保留顶部;热门推荐使用header组件,结构更规范。整体代码风格与ArkTS相似,但细节更灵活,适合金融类应用开发。
|
2月前
|
存储 SQL 前端开发
跟老卫学HarmonyOS开发:ArkTS关系型数据库开发
本节以“账本”为例,使用关系型数据库接口实现账单的增、删、改、查操作。通过创建ArkTSRdb应用,演示如何操作RdbStore进行数据管理,并结合界面按钮实现交互功能。
90 0
跟老卫学HarmonyOS开发:ArkTS关系型数据库开发
|
3月前
|
编译器 程序员 开发者
详解HarmonyOS NEXT系统中ArkTS和仓颉的混合开发
本文介绍了鸿蒙系统中ArkTs与仓颉语言的混合开发方法,讲解了如何通过DevEco Studio创建混合项目、目录结构特点及组件调用方式,强调编译器自动化处理大幅简化开发流程,提升了开发体验。
HarmonyOS NEXT仓颉开发语言实战案例:图片预览器
本文介绍了如何使用仓颉语言实现图片放大预览器。通过弹窗组件`CustomDialogController`与`Swiper`容器结合,实现全屏图片浏览效果,支持多图切换与点击关闭功能,适配动态广场场景下的图片预览需求。
|
3月前
|
容器
HarmonyOS NEXT仓颉开发语言实战案例:动态广场
本文介绍了使用仓颉语言开发类似朋友圈的动态广场页面,包含导航栏和状态列表。通过Column、Row、List等组件实现页面结构,并使用Grid适配图片展示。结合数据绑定与组件化思想,完成动态内容展示。
|
3月前
|
前端开发
HarmonyOS NEXT仓颉开发语言实现画板案例
本文介绍了使用仓颉开发语言实现画板功能的案例,通过Canvas组件实现画布绘制,并利用贝塞尔曲线使画笔跟随触摸轨迹生成平滑曲线。内容包含画布初始化、触控事件处理及清空功能,展示了仓颉语言在图形绘制中的应用技巧。
详解HarmonyOS NEXT仓颉开发语言中的全局弹窗
仓颉语言提供全局弹窗模块prompt_action,支持无需页面依赖的弹窗功能。包含三种预设弹窗:文字提示、对话框及菜单弹窗,也可自定义内容并动态控制关闭。适用于各类交互场景,提升开发效率。