首页 最新 热门 推荐

  • 首页
  • 最新
  • 热门
  • 推荐

HarmonyOS( Beta5版)鸿蒙开发:组件转场-应用程序动效能力实践

  • 25-03-03 06:43
  • 4568
  • 6725
blog.csdn.net

概述

本文介绍如何在开发应用程序时合理地使用动效,来获得更好的性能。主要通过减少布局和属性的变更频次,避免冗余刷新,从而降低性能开销。 基于上述考虑,提供四种较为推荐的动效实现方式:

  • 组件转场动画使用transition
  • 组件布局改动时使用图形变换属性动画
  • 动画参数相同时使用同一个animateTo
  • 多次animateTo时统一更新状态变量

合理使用动效

组件转场动画使用transition

在实现组件出现和消失的动画效果时,通常有两种方式:

  • 使用组件动画(animateTo),并在在动画结束回调中增加逻辑处理。
  • 直接使用转场动画(transition)。

animateTo需要在动画前后做两次属性更新,而transition只需做一次条件改变更新,性能更好。此外,使用transition,可以避免在结束回调中做复杂逻辑处理,开发实现更容易。因此,推荐优先使用transition。

反例:通过改变透明度属性,从1到0进行隐藏,并在动画结束回调中控制组件的消失。

  1. @Entry
  2. @Component
  3. struct MyComponent {
  4. @State mOpacity: number = 1;
  5. @State show: boolean = true;
  6. count: number = 0;
  7. build() {
  8. Column() {
  9. Row() {
  10. if (this.show) {
  11. Text('value')
  12. .opacity(this.mOpacity)
  13. }
  14. }
  15. .width('100%')
  16. .height(100)
  17. .justifyContent(FlexAlign.Center)
  18. Text('toggle state')
  19. .onClick(() => {
  20. this.count++;
  21. const thisCount: number = this.count;
  22. this.show = true;
  23. // 通过改变透明度属性,对Text控件做隐藏或出现的动画
  24. animateTo({ duration: 1000, onFinish: () => {
  25. // 在最后一个动画中,先让Text控件隐藏,再改变条件让Text控件消失
  26. if (thisCount === this.count && this.mOpacity === 0) {
  27. this.show = false;
  28. }
  29. } }, () => {
  30. this.mOpacity = this.mOpacity === 1 ? 0 : 1;
  31. })
  32. })
  33. }
  34. }
  35. }

正例:直接使用转场动画,实现Text控件透明度的出现与消失。

  1. @Entry
  2. @Component
  3. struct MyComponent {
  4. @State show: boolean = true;
  5. build() {
  6. Column() {
  7. Row() {
  8. if (this.show) {
  9. Text('value')
  10. // 设置id,使转场可打断
  11. .id('myText')
  12. .transition(TransitionEffect.OPACITY.animation({ duration: 1000 }))
  13. }
  14. }.width('100%')
  15. .height(100)
  16. .justifyContent(FlexAlign.Center)
  17. Text('toggle state')
  18. .onClick(() => {
  19. // 通过transition,做透明度的出现或消失动画
  20. this.show = !this.show;
  21. })
  22. }
  23. }
  24. }

组件布局改动时使用图形变换属性动画

改动组件的布局显示有两种方式:

  • 改动布局属性。当布局属性发生改变时,界面将重新布局。常见的布局属性有width、height、layoutWeight等。
  • 改动图形变换属性。图形变换是对组件布局结果的变换操作,如平移、旋转、缩放等操作。

界面布局是非常耗时的操作,而当图形变换属性发生变化时,并不会重新触发布局。因此,优先推荐使用图形变换属性来实现组件布局的改动。接下来,采用上述两种方式分别对组件实现放大10倍的效果。

反例:通过设置布局属性width和height,改变组件大小。

  1. @Entry
  2. @Component
  3. struct MyComponent {
  4. @State textWidth: number = 10;
  5. @State textHeight: number = 10;
  6. build() {
  7. Column() {
  8. Text()
  9. .backgroundColor(Color.Blue)
  10. .fontColor(Color.White)
  11. .fontSize(20)
  12. .width(this.textWidth)
  13. .height(this.textHeight)
  14. Button('布局属性')
  15. .backgroundColor(Color.Blue)
  16. .fontColor(Color.White)
  17. .fontSize(20)
  18. .margin({ top: 30 })
  19. .borderRadius(30)
  20. .padding(10)
  21. .onClick(() => {
  22. animateTo({ duration: 1000 }, () => {
  23. this.textWidth = 100;
  24. this.textHeight = 100;
  25. })
  26. })
  27. }
  28. }
  29. }

在对组件位置或大小变化做动画时,由于布局属性的改变会触发重新测量布局,导致性能开销大。scale属性的改变不会重新触发测量布局,性能开销小。因此,在组件位置大小持续发生变化的场景,如手指缩放的动画场景,推荐使用scale。

正例:通过设置图形变换属性scale,改变组件大小。

  1. @Entry
  2. @Component
  3. struct MyComponent {
  4. @State textScaleX: number = 1;
  5. @State textScaleY: number = 1;
  6. build() {
  7. Column() {
  8. Text()
  9. .backgroundColor(Color.Blue)
  10. .fontColor(Color.White)
  11. .fontSize(20)
  12. .width(10)
  13. .height(10)
  14. .scale({ x: this.textScaleX, y: this.textScaleY })
  15. .margin({ top: 100 })
  16. Button('图形变换属性')
  17. .backgroundColor(Color.Blue)
  18. .fontColor(Color.White)
  19. .fontSize(20)
  20. .margin({ top: 60 })
  21. .borderRadius(30)
  22. .padding(10)
  23. .onClick(() => {
  24. animateTo({ duration: 1000 }, () => {
  25. this.textScaleX = 10;
  26. this.textScaleY = 10;
  27. })
  28. })
  29. }
  30. }
  31. }

【性能对比】

使用Profiler工具分别抓取优化前后耗时(H:JSAnimateTo)进行对比分析。

优化前耗时:

优化后耗时:

动画耗时说明
优化前2ms600μs改动布局属性将重新布局,耗时久
优化后1ms240μs改动图形变换属性不会重新布局,耗时短

动画参数相同时使用同一个animateTo

每次animateTo都需要进行动画前后的对比,因此,减少animateTo的使用次数(例如使用同一个animateTo设置组件属性),可以减少该组件更新的次数,从而获得更好的性能。 如果各个属性要做动画的参数相同,推荐将它们放到同一个动画闭包中执行。

反例:相同动画参数的状态变量更新放在不同的动画闭包中。

  1. @Entry
  2. @Component
  3. struct MyComponent {
  4. @State textWidth: number = 200;
  5. @State color: Color = Color.Red;
  6. func1() {
  7. animateTo({ curve: Curve.Sharp, duration: 1000 }, () => {
  8. this.textWidth = (this.textWidth === 100 ? 200 : 100);
  9. });
  10. }
  11. func2() {
  12. animateTo({ curve: Curve.Sharp, duration: 1000 }, () => {
  13. this.color = (this.color === Color.Yellow ? Color.Red : Color.Yellow);
  14. });
  15. }
  16. build() {
  17. Column() {
  18. Row()
  19. .width(this.textWidth)
  20. .height(10)
  21. .backgroundColor(this.color)
  22. Text('click')
  23. .onClick(() => {
  24. this.func1();
  25. this.func2();
  26. })
  27. }
  28. .width('100%')
  29. .height('100%')
  30. }
  31. }

正例:将相同动画参数的动画合并在一个动画闭包中。

  1. @Entry
  2. @Component
  3. struct MyComponent {
  4. @State textWidth: number = 200;
  5. @State color: Color = Color.Red;
  6. func() {
  7. animateTo({ curve: Curve.Sharp, duration: 1000 }, () => {
  8. this.textWidth = (this.textWidth === 100 ? 200 : 100);
  9. this.color = (this.color === Color.Yellow ? Color.Red : Color.Yellow);
  10. });
  11. }
  12. build() {
  13. Column() {
  14. Row()
  15. .width(this.textWidth)
  16. .height(10)
  17. .backgroundColor(this.color)
  18. Text('click')
  19. .onClick(() => {
  20. this.func();
  21. })
  22. }
  23. .width('100%')
  24. .height('100%')
  25. }
  26. }

【性能对比】

使用Profiler工具分别抓取优化前后耗时(H:JSAnimateTo)进行对比分析。

优化前耗时:

优化后耗时:

动画耗时说明
优化前1ms379μs在不同的动画闭包中,耗时久
优化后999μs合并在一个动画闭包,耗时短

多次animateTo时统一更新状态变量

animateTo会将执行动画闭包前后的状态进行对比,对差异部分进行动画。为了对比,会在执行animateTo的动画闭包之前,将所有变更的状态变量和脏节点都刷新。 如果多个animateTo之间存在状态更新,会导致执行下一个animateTo之前又存在需要更新的脏节点,可能造成冗余更新。

反例:多个animateTo之间更新状态变量。

多个animateTo之间更新状态变量

以下代码在两个animateTo之间更新组件的其他状态。

  1. @Entry
  2. @Component
  3. struct MyComponent {
  4. @State textWidth: number = 200;
  5. @State textHeight: number = 50;
  6. @State color: Color = Color.Red;
  7. build() {
  8. Column() {
  9. Row()
  10. .width(this.textWidth)
  11. .height(10)
  12. .backgroundColor(this.color)
  13. Text('click')
  14. .height(this.textHeight)
  15. .onClick(() => {
  16. this.textWidth = 100;
  17. // textHeight是非动画属性
  18. this.textHeight = 100;
  19. animateTo({ curve: Curve.Sharp, duration: 1000 }, () => {
  20. this.textWidth = 200;
  21. });
  22. this.color = Color.Yellow;
  23. animateTo({ curve: Curve.Linear, duration: 2000 }, () => {
  24. this.color = Color.Red;
  25. });
  26. })
  27. }
  28. .width('100%')
  29. .height('100%')
  30. }
  31. }

在第一个animateTo前,重新设置了textWidth属性,所以Row组件需要更新一次。在第一个animateTo的动画闭包中,改变了textWidth属性,所以Row组件又需要更新一次并对比产生宽高动画。第二个animateTo前,重新设置了color属性,所以Row组件又需要更新一次。在第二个animateTo的动画闭包中,改变了color属性,所以Row组件再更新一次并产生了背景色动画。Row组件总共更新了4次属性。 此外还更改了与动画无关的状态textHeight,如果不需要改变无关状态,则不应改变造成冗余更新。

正例:统一更新状态变量。

统一更新状态变量1

 或 

统一更新状态变量2

正例1:在animateTo之前使用原始状态,让动画从原始状态过渡到指定状态,这样也能避免动画在开始时发生跳变。

  1. @Entry
  2. @Component
  3. struct MyComponent {
  4. @State textWidth: number = 100;
  5. @State textHeight: number = 50;
  6. @State color: Color = Color.Yellow;
  7. build() {
  8. Column() {
  9. Row()
  10. .width(this.textWidth)
  11. .height(10)
  12. .backgroundColor(this.color)
  13. Text('click')
  14. .height(this.textHeight)
  15. .onClick(() => {
  16. animateTo({ curve: Curve.Sharp, duration: 1000 }, () => {
  17. this.textWidth = (this.textWidth === 100 ? 200 : 100);
  18. });
  19. animateTo({ curve: Curve.Linear, duration: 2000 }, () => {
  20. this.color = (this.color === Color.Yellow ? Color.Red : Color.Yellow);
  21. });
  22. })
  23. }
  24. .width('100%')
  25. .height('100%')
  26. }
  27. }

在第一个animateTo之前,不存在需要更新的脏状态变量和脏节点,无需更新。在第一个animateTo的动画闭包中,改变了textWidth属性,所以Row组件需要更新一次并对比产生宽高动画。在第二个animateTo之前,由于也没有执行额外的语句,不存在需要更新的脏状态变量和脏节点,无需更新。在第二个animateTo的动画闭包中,改变了color属性,所以Row组件再更新一次并产生了背景色动画。Row组件总共更新了2次属性。

正例2:在animateTo之前显式的指定所有需要动画的属性初值,统一更新到节点中,然后再做动画。

  1. @Entry
  2. @Component
  3. struct MyComponent {
  4. @State textWidth: number = 200;
  5. @State textHeight: number = 50;
  6. @State color: Color = Color.Red;
  7. build() {
  8. Column() {
  9. Row()
  10. .width(this.textWidth)
  11. .height(10)
  12. .backgroundColor(this.color)
  13. Text('click')
  14. .height(this.textHeight)
  15. .onClick(() => {
  16. this.textWidth = 100;
  17. this.color = Color.Yellow;
  18. animateTo({ curve: Curve.Sharp, duration: 1000 }, () => {
  19. this.textWidth = 200;
  20. });
  21. animateTo({ curve: Curve.Linear, duration: 2000 }, () => {
  22. this.color = Color.Red;
  23. });
  24. this.textHeight = 100;
  25. })
  26. }
  27. .width('100%')
  28. .height('100%')
  29. }
  30. }

在第一个animateTo之前,重新设置了textWidth和color属性,所以Row需要更新一次。在第一个animateTo的动画闭包中,改变了textWidth属性,所以Row组件需要更新一次并对比产生宽高动画。在第二个animateTo之前,由于没有执行额外的语句,不存在需要更新的脏状态变量和脏节点,无需更新。在第二个animateTo的动画闭包中,改变了color属性,所以Row组件再更新一次并产生了背景色动画。Row组件总共更新了3次属性。

最后

小编在之前的鸿蒙系统扫盲中,有很多朋友给我留言,不同的角度的问了一些问题,我明显感觉到一点,那就是许多人参与鸿蒙开发,但是又不知道从哪里下手,因为资料太多,太杂,教授的人也多,无从选择。有很多小伙伴不知道学习哪些鸿蒙开发技术?不知道需要重点掌握哪些鸿蒙应用开发知识点?而且学习时频繁踩坑,最终浪费大量时间。所以有一份实用的鸿蒙(HarmonyOS NEXT)文档用来跟着学习是非常有必要的。 

为了确保高效学习,建议规划清晰的学习路线,涵盖以下关键阶段:


 鸿蒙(HarmonyOS NEXT)最新学习路线

​

该路线图包含基础技能、就业必备技能、多媒体技术、六大电商APP、进阶高级技能、实战就业级设备开发,不仅补充了华为官网未涉及的解决方案

路线图适合人群:

IT开发人员:想要拓展职业边界
零基础小白:鸿蒙爱好者,希望从0到1学习,增加一项技能。
技术提升/进阶跳槽:发展瓶颈期,提升职场竞争力,快速掌握鸿蒙技术

2.视频学习教程+学习PDF文档

HarmonyOS Next 最新全套视频教程

  纯血版鸿蒙全套学习文档(面试、文档、全套视频等)       

​​

总结

参与鸿蒙开发,你要先认清适合你的方向,如果是想从事鸿蒙应用开发方向的话,可以参考本文的学习路径,简单来说就是:为了确保高效学习,建议规划清晰的学习路线

文章知识点与官方知识档案匹配,可进一步学习相关知识
CS入门技能树Linux入门初识Linux42834 人正在系统学习中
鸿蒙NEXT全套学习资料
微信名片
注:本文转载自blog.csdn.net的让开,我要吃人了的文章"https://blog.csdn.net/weixin_55362248/article/details/141682965"。版权归原作者所有,此博客不拥有其著作权,亦不承担相应法律责任。如有侵权,请联系我们删除。
复制链接
复制链接
相关推荐
发表评论
登录后才能发表评论和回复 注册

/ 登录

评论记录:

未查询到任何数据!
回复评论:

分类栏目

后端 (14832) 前端 (14280) 移动开发 (3760) 编程语言 (3851) Java (3904) Python (3298) 人工智能 (10119) AIGC (2810) 大数据 (3499) 数据库 (3945) 数据结构与算法 (3757) 音视频 (2669) 云原生 (3145) 云平台 (2965) 前沿技术 (2993) 开源 (2160) 小程序 (2860) 运维 (2533) 服务器 (2698) 操作系统 (2325) 硬件开发 (2492) 嵌入式 (2955) 微软技术 (2769) 软件工程 (2056) 测试 (2865) 网络空间安全 (2948) 网络与通信 (2797) 用户体验设计 (2592) 学习和成长 (2593) 搜索 (2744) 开发工具 (7108) 游戏 (2829) HarmonyOS (2935) 区块链 (2782) 数学 (3112) 3C硬件 (2759) 资讯 (2909) Android (4709) iOS (1850) 代码人生 (3043) 阅读 (2841)

热门文章

101
推荐
关于我们 隐私政策 免责声明 联系我们
Copyright © 2020-2024 蚁人论坛 (iYenn.com) All Rights Reserved.
Scroll to Top