HarmonyOS 应用闪屏问题性能优化二
作者:李洋-蛟龙腾飞
- 2025-06-05 广东
本文字数:4410 字
阅读完需:约 14 分钟
动画过程中,Tabs 页签切换场景下的闪屏问题
问题现象
滑动 Tabs 组件时,上方标签不能同步更新,在下方内容完全切换后才会闪动跳转,产生闪屏问题。
1.@Entry2.@Component3.struct TabsError {4. @State currentIndex: number = 0;5. @State animationDuration: number = 300;6. @State indicatorLeftMargin: number = 0;7. @State indicatorWidth: number = 0;8. private textInfos: [number, number][] = [];9. private isStartAnimateTo: boolean = false;10.11.12. @Builder13. tabBuilder(index: number, name: string) {14. Column() {15. Text(name)16. .fontSize(16)17. .fontColor(this.currentIndex === index ? $r('sys.color.brand') : $r('sys.color.ohos_id_color_text_secondary'))18. .fontWeight(this.currentIndex === index ? 500 : 400)19. .id(index.toString())20. .onAreaChange((_oldValue: Area, newValue: Area) => {21. this.textInfos[index] = [newValue.globalPosition.x as number, newValue.width as number];22. if (this.currentIndex === index && !this.isStartAnimateTo) {23. this.indicatorLeftMargin = this.textInfos[index][0];24. this.indicatorWidth = this.textInfos[index][1];25. }26. })27. }.width('100%')28. }29.30.31. build() {32. Stack({ alignContent: Alignment.TopStart }) {33. Tabs({ barPosition: BarPosition.Start }) {34. TabContent() {35. Column()36. .width('100%')37. .height('100%')38. .backgroundColor(Color.Green)39. .expandSafeArea([SafeAreaType.SYSTEM], [SafeAreaEdge.TOP, SafeAreaEdge.BOTTOM])40. }41. .tabBar(this.tabBuilder(0, 'green'))42. .expandSafeArea([SafeAreaType.SYSTEM], [SafeAreaEdge.TOP, SafeAreaEdge.BOTTOM])43.44.45. TabContent() {46. Column()47. .width('100%')48. .height('100%')49. .backgroundColor(Color.Blue)50. .expandSafeArea([SafeAreaType.SYSTEM], [SafeAreaEdge.TOP, SafeAreaEdge.BOTTOM])51. }52. .tabBar(this.tabBuilder(1, 'blue'))53. .expandSafeArea([SafeAreaType.SYSTEM], [SafeAreaEdge.TOP, SafeAreaEdge.BOTTOM])54.55.56. TabContent() {57. Column()58. .width('100%')59. .height('100%')60. .backgroundColor(Color.Yellow)61. .expandSafeArea([SafeAreaType.SYSTEM], [SafeAreaEdge.TOP, SafeAreaEdge.BOTTOM])62. }63. .tabBar(this.tabBuilder(2, 'yellow'))64. .expandSafeArea([SafeAreaType.SYSTEM], [SafeAreaEdge.TOP, SafeAreaEdge.BOTTOM])65.66.67. TabContent() {68. Column()69. .width('100%')70. .height('100%')71. .backgroundColor(Color.Pink)72. .expandSafeArea([SafeAreaType.SYSTEM], [SafeAreaEdge.TOP, SafeAreaEdge.BOTTOM])73. }74. .tabBar(this.tabBuilder(3, 'pink'))75. .expandSafeArea([SafeAreaType.SYSTEM], [SafeAreaEdge.TOP, SafeAreaEdge.BOTTOM])76. }77. .barWidth('100%')78. .barHeight(56)79. .width('100%')80. .backgroundColor('#F1F3F5')81. .animationDuration(this.animationDuration)82. .expandSafeArea([SafeAreaType.SYSTEM], [SafeAreaEdge.TOP, SafeAreaEdge.BOTTOM])83. .onChange((index: number) => {84. this.currentIndex = index; // 监听索引index的变化,实现页签内容的切换。85. })86.87.88. Column()89. .height(2)90. .borderRadius(1)91. .width(this.indicatorWidth)92. .margin({ left: this.indicatorLeftMargin, top: 48 })93. .backgroundColor($r('sys.color.brand'))94. }.width('100%')95. }96.}复制代码
可能原因
在 Tabs 左右翻页动画的结束回调中,刷新了选中页面的 index 值。造成当页面左右转场动画结束时,页签栏中 index 对应页签的样式(字体大小、下划线等)立刻发生改变,导致产生闪屏。
解决措施
在左右跟手翻页过程中,通过 TabsAnimationEvent 事件获取手指滑动距离,改变下划线在前后两个子页签之间的位置。在离手触发翻页动画时,一并触发下划线动画,保证下划线与页面左右转场动画同步进行。
1.build() {2. Stack({ alignContent: Alignment.TopStart }) {3. Tabs({ barPosition: BarPosition.Start }) {4. TabContent() {5. Column()6. .width('100%')7. .height('100%')8. .backgroundColor(Color.Green)9. .expandSafeArea([SafeAreaType.SYSTEM], [SafeAreaEdge.TOP, SafeAreaEdge.BOTTOM])10. }11. .tabBar(this.tabBuilder(0, 'green'))12. .expandSafeArea([SafeAreaType.SYSTEM], [SafeAreaEdge.TOP, SafeAreaEdge.BOTTOM])13.14.15. TabContent() {16. Column()17. .width('100%')18. .height('100%')19. .backgroundColor(Color.Blue)20. .expandSafeArea([SafeAreaType.SYSTEM], [SafeAreaEdge.TOP, SafeAreaEdge.BOTTOM])21. }22. .tabBar(this.tabBuilder(1, 'blue'))23. .expandSafeArea([SafeAreaType.SYSTEM], [SafeAreaEdge.TOP, SafeAreaEdge.BOTTOM])24.25.26. TabContent() {27. Column()28. .width('100%')29. .height('100%')30. .backgroundColor(Color.Yellow)31. .expandSafeArea([SafeAreaType.SYSTEM], [SafeAreaEdge.TOP, SafeAreaEdge.BOTTOM])32. }33. .tabBar(this.tabBuilder(2, 'yellow'))34. .expandSafeArea([SafeAreaType.SYSTEM], [SafeAreaEdge.TOP, SafeAreaEdge.BOTTOM])35.36.37. TabContent() {38. Column()39. .width('100%')40. .height('100%')41. .backgroundColor(Color.Pink)42. .expandSafeArea([SafeAreaType.SYSTEM], [SafeAreaEdge.TOP, SafeAreaEdge.BOTTOM])43. }44. .tabBar(this.tabBuilder(3, 'pink'))45. .expandSafeArea([SafeAreaType.SYSTEM], [SafeAreaEdge.TOP, SafeAreaEdge.BOTTOM])46. }47. .onAreaChange((_oldValue: Area, newValue: Area) => {48. this.tabsWidth = newValue.width as number;49. })50. .barWidth('100%')51. .barHeight(56)52. .width('100%')53. .expandSafeArea([SafeAreaType.SYSTEM], [SafeAreaEdge.TOP, SafeAreaEdge.BOTTOM])54. .backgroundColor('#F1F3F5')55. .animationDuration(this.animationDuration)56. .onChange((index: number) => {57. this.currentIndex = index; // 监听索引index的变化,实现页签内容的切换。58. })59. .onAnimationStart((_index: number, targetIndex: number) => {60. // 切换动画开始时触发该回调。下划线跟着页面一起滑动,同时宽度渐变。61. this.currentIndex = targetIndex;62. this.startAnimateTo(this.animationDuration, this.textInfos[targetIndex][0], this.textInfos[targetIndex][1]);63. })64. .onAnimationEnd((index: number, event: TabsAnimationEvent) => {65. // 切换动画结束时触发该回调。下划线动画停止。66. let currentIndicatorInfo = this.getCurrentIndicatorInfo(index, event);67. this.startAnimateTo(0, currentIndicatorInfo.left, currentIndicatorInfo.width);68. })69. .onGestureSwipe((index: number, event: TabsAnimationEvent) => {70. // 在页面跟手滑动过程中,逐帧触发该回调。71. let currentIndicatorInfo = this.getCurrentIndicatorInfo(index, event);72. this.currentIndex = currentIndicatorInfo.index;73. this.indicatorLeftMargin = currentIndicatorInfo.left;74. this.indicatorWidth = currentIndicatorInfo.width;75. })76.77.78. Column()79. .height(2)80. .borderRadius(1)81. .width(this.indicatorWidth)82. .margin({ left: this.indicatorLeftMargin, top: 48 })83. .backgroundColor($r('sys.color.brand'))84. }85. .width('100%')86.}复制代码
TabsAnimationEvent 方法如下所示。
1.private getCurrentIndicatorInfo(index: number, event: TabsAnimationEvent): Record<string, number> {2. let nextIndex = index;3. if (index > 0 && event.currentOffset > 0) {4. nextIndex--;5. } else if (index < 3 && event.currentOffset < 0) {6. nextIndex++;7. }8. let indexInfo = this.textInfos[index];9. let nextIndexInfo = this.textInfos[nextIndex];10. let swipeRatio = Math.abs(event.currentOffset / this.tabsWidth);11. let currentIndex = swipeRatio > 0.5 ? nextIndex : index; // 页面滑动超过一半,tabBar切换到下一页。12. let currentLeft = indexInfo[0] + (nextIndexInfo[0] - indexInfo[0]) * swipeRatio;13. let currentWidth = indexInfo[1] + (nextIndexInfo[1] - indexInfo[1]) * swipeRatio;14. return { 'index': currentIndex, 'left': currentLeft, 'width': currentWidth };15.}16.private startAnimateTo(duration: number, leftMargin: number, width: number) {17. this.isStartAnimateTo = true;18. animateTo({19. duration: duration, // 动画时长20. curve: Curve.Linear, // 动画曲线21. iterations: 1, // 播放次数22. playMode: PlayMode.Normal, // 动画模式23. onFinish: () => {24. this.isStartAnimateTo = false;25. console.info('play end');26. }27. }, () => {28. this.indicatorLeftMargin = leftMargin;29. this.indicatorWidth = width;30. })31.}复制代码
运行效果如下图所示。
本文主要引用参考 HarmonyOS 官方文档
划线
评论
复制
发布于: 刚刚阅读数: 3
李洋-蛟龙腾飞
关注
鸿蒙开发先行者 2024-09-25 加入
还未添加个人简介









评论