鸿蒙NEXT自定义组件:太极Loading

鸿蒙NEXT自定义组件:太极Loading

 

【引言】(完整代码在最后面)

本文将介绍如何在鸿蒙NEXT中创建一个自定义的“太极Loading”组件,为你的应用增添独特的视觉效果。

【环境准备】

电脑系统:windows 10

开发工具:DevEco Studio NEXT Beta1 Build Version: 5.0.3.806

工程版本:API 12

真机:mate60 pro

语言:ArkTS、ArkUI

【项目分析】

1. 组件结构

我们将创建一个名为 TaiChiLoadingProgress 的自定义组件,它将模拟太极图的旋转效果,作为加载动画展示给用户。组件的基本结构如下:

@Component struct TaiChiLoadingProgress {   @Prop taiChiWidth: number = 400   @Prop @Watch('animationCurveChanged') animationCurve: Curve = Curve.Linear   @State angle: number = 0   @State cellWidth: number = 0   ... } 

2. 绘制太极图案

使用鸿蒙NEXT提供的UI组件,如 Rect 和 Circle,构建太极图的黑白两部分。关键在于利用 rotate 方法实现太极图的旋转效果。

build() {   Stack() {     Stack() {       // 黑色半圆背景       Stack() {         Rect().width(`${this.cellWidth}px`).height(`${this.cellWidth / 2}px`).backgroundColor(Color.Black)       }.width(`${this.cellWidth}px`).height(`${this.cellWidth}px`).rotate({ angle: -90 }).align(Alignment.Top)       // 大黑球 上       Stack() {         Circle().width(`${this.cellWidth / 2}px`).height(`${this.cellWidth / 2}px`).fill(Color.Black)         Circle().width(`${this.cellWidth / 8}px`).height(`${this.cellWidth / 8}px`).fill(Color.White)       }.width(`${this.cellWidth}px`).height(`${this.cellWidth}px`).align(Alignment.Top)       // 大白球 下       Stack() {         Circle().width(`${this.cellWidth / 2}px`).height(`${this.cellWidth / 2}px`).fill(Color.White)         Circle().width(`${this.cellWidth / 8}px`).height(`${this.cellWidth / 8}px`).fill(Color.Black)       }.width(`${this.cellWidth}px`).height(`${this.cellWidth}px`).align(Alignment.Bottom)     }     .width(`${this.cellWidth}px`)     .height(`${this.cellWidth}px`)     .borderWidth(1)     .borderColor(Color.Black)     .borderRadius('50%')     .backgroundColor(Color.White)     .clip(true)     .rotate({       angle: this.angle     })     .onVisibleAreaChange([0.0, 1.0], (isVisible: boolean, currentRatio: number) => {       if (isVisible && currentRatio >= 1.0) {         this.startAnim()       }       if (!isVisible && currentRatio <= 0.0) {         this.endAnim()       }     })   }   .width(`${this.taiChiWidth}px`)   .height(`${this.taiChiWidth}px`) } 

3. 动画实现

通过 animateTo 方法设置太极图的旋转动画,可以自定义动画曲线以实现不同的动画效果。

startAnim() {   animateTo({     duration: 2000,     iterations: -1,     curve: this.animationCurve   }, () => {     this.angle = 360 * 2   }) }  endAnim() {   animateTo({     duration: 0   }, () => {     this.angle = 0   }) } 

【完整代码】

@Component struct TaiChiLoadingProgress {   @Prop taiChiWidth: number = 400   @Prop @Watch('animationCurveChanged') animationCurve: Curve = Curve.Linear   @State angle: number = 0   @State cellWidth: number = 0    animationCurveChanged() {     this.endAnim()     this.startAnim()   }    startAnim() {     animateTo({       duration: 2000,       iterations: -1,       curve: this.animationCurve     }, () => {       this.angle = 360 * 2     })   }    endAnim() {     animateTo({       duration: 0     }, () => {       this.angle = 0     })   }    aboutToAppear(): void {     this.cellWidth = this.taiChiWidth / 2   }    build() {     Stack() {       Stack() {         //黑色 半圆 背景         Stack() {           Rect().width(`${this.cellWidth}px`).height(`${this.cellWidth / 2}px`).backgroundColor(Color.Black)         }.width(`${this.cellWidth}px`).height(`${this.cellWidth}px`).rotate({ angle: -90 }).align(Alignment.Top)          //大黑球 上         Stack() {           Stack() {             Circle().width(`${this.cellWidth / 2}px`).height(`${this.cellWidth / 2}px`).fill(Color.Black)             Circle().width(`${this.cellWidth / 8}px`).height(`${this.cellWidth / 8}px`).fill(Color.White)           }         }.width(`${this.cellWidth}px`).height(`${this.cellWidth}px`).align(Alignment.Top)          //大白球 下         Stack() {           Stack() {             Circle().width(`${this.cellWidth / 2}px`).height(`${this.cellWidth / 2}px`).fill(Color.White)             Circle().width(`${this.cellWidth / 8}px`).height(`${this.cellWidth / 8}px`).fill(Color.Black)           }         }.width(`${this.cellWidth}px`).height(`${this.cellWidth}px`).align(Alignment.Bottom)        }       .width(`${this.cellWidth}px`)       .height(`${this.cellWidth}px`)       .borderWidth(1)       .borderColor(Color.Black)       .borderRadius('50%')       .backgroundColor(Color.White)       .clip(true)       .rotate({         angle: this.angle       })       .onVisibleAreaChange([0.0, 1.0], (isVisible: boolean, currentRatio: number) => {         console.info('Test Row isVisible:' + isVisible + ', currentRatio:' + currentRatio)         if (isVisible && currentRatio >= 1.0) {           console.info('Test Row is fully visible.')           this.startAnim()         }          if (!isVisible && currentRatio <= 0.0) {           console.info('Test Row is completely invisible.')           this.endAnim()         }       })     }     .width(`${this.taiChiWidth}px`)     .height(`${this.taiChiWidth}px`)   } }  @Entry @Component struct Page08 {   @State loadingWidth: number = 150   @State isShowLoading: boolean = true;   @State animationCurve: Curve = Curve.Linear    build() {     Column({ space: 20 }) {        Text('官方Loading组件')       Column() {         LoadingProgress().width(this.loadingWidth)           .visibility(this.isShowLoading ? Visibility.Visible : Visibility.None)       }.height(this.loadingWidth).width(this.loadingWidth)        Text('自定义太极Loading组件')       Column() {         TaiChiLoadingProgress({ taiChiWidth: vp2px(this.loadingWidth), animationCurve: this.animationCurve })           .visibility(this.isShowLoading ? Visibility.Visible : Visibility.Hidden)       }.height(this.loadingWidth).width(this.loadingWidth)        Row() {         Flex({ wrap: FlexWrap.Wrap }) {           Text('显示/隐藏')             .textAlign(TextAlign.Center)             .width('200lpx')             .height('200lpx')             .margin('10lpx')             .backgroundColor(Color.Black)             .borderRadius(5)             .backgroundColor(Color.Orange)             .fontColor(Color.White)             .clickEffect({ level: ClickEffectLevel.LIGHT })             .onClick(() => {               this.isShowLoading = !this.isShowLoading             })           Text('Linear动画')             .textAlign(TextAlign.Center)             .width('200lpx')             .height('200lpx')             .margin('10lpx')             .backgroundColor(Color.Black)             .borderRadius(5)             .backgroundColor(Color.Orange)             .fontColor(Color.White)             .clickEffect({ level: ClickEffectLevel.LIGHT })             .onClick(() => {               this.animationCurve = Curve.Linear             })           Text('FastOutLinearIn动画')             .textAlign(TextAlign.Center)             .width('200lpx')             .height('200lpx')             .margin('10lpx')             .backgroundColor(Color.Black)             .borderRadius(5)             .backgroundColor(Color.Orange)             .fontColor(Color.White)             .clickEffect({ level: ClickEffectLevel.LIGHT })             .onClick(() => {               this.animationCurve = Curve.FastOutLinearIn             })           Text('EaseIn动画')             .textAlign(TextAlign.Center)             .width('200lpx')             .height('200lpx')             .margin('10lpx')             .backgroundColor(Color.Black)             .borderRadius(5)             .backgroundColor(Color.Orange)             .fontColor(Color.White)             .clickEffect({ level: ClickEffectLevel.LIGHT })             .onClick(() => {               this.animationCurve = Curve.EaseIn             })           Text('EaseOut动画')             .textAlign(TextAlign.Center)             .width('200lpx')             .height('200lpx')             .margin('10lpx')             .backgroundColor(Color.Black)             .borderRadius(5)             .backgroundColor(Color.Orange)             .fontColor(Color.White)             .clickEffect({ level: ClickEffectLevel.LIGHT })             .onClick(() => {               this.animationCurve = Curve.EaseOut             })           Text('EaseInOut动画')             .textAlign(TextAlign.Center)             .width('200lpx')             .height('200lpx')             .margin('10lpx')             .backgroundColor(Color.Black)             .borderRadius(5)             .backgroundColor(Color.Orange)             .fontColor(Color.White)             .clickEffect({ level: ClickEffectLevel.LIGHT })             .onClick(() => {               this.animationCurve = Curve.EaseInOut             })         }.width('660lpx')       }.width('100%').justifyContent(FlexAlign.Center)     }     .height('100%')     .width('100%')     .backgroundColor("#f9feff")   } } 

  

发表评论

相关文章