zhidao 发表于 2025-2-7 03:07:49

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

 
【引言】(完整代码在最后面)
本文将介绍如何在鸿蒙NEXT中创建一个自定义的“太极Loading”组件,为你的应用增添独特的视觉效果。
【环境准备】
电脑系统:windows 10
开发工具:DevEco Studio NEXT Beta1 Build Version: 5.0.3.806
工程版本:API 12
真机:mate60 pro
语言:ArkTS、ArkUI
【项目分析】
1. 组件结构
我们将创建一个名为 TaiChiLoadingProgress 的自定义组件,它将模拟太极图的旋转效果,作为加载动画展示给用户。组件的基本结构如下:
@Componentstruct 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(, (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})}
【完整代码】
@Componentstruct TaiChiLoadingProgress {@Prop taiChiWidth: number = 400@Prop @Watch('animationCurveChanged') animationCurve: Curve = Curve.Linear@State angle: number = 0@State cellWidth: number = 0animationCurveChanged() {    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(, (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@Componentstruct Page08 {@State loadingWidth: number = 150@State isShowLoading: boolean = true;@State animationCurve: Curve = Curve.Linearbuild() {    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")}}
  
页: [1]
查看完整版本: 鸿蒙NEXT自定义组件:太极Loading