写点什么

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

作者:zhongcx
  • 2024-11-15
    广东
  • 本文字数:4431 字

    阅读完需:约 15 分钟

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

本文将介绍如何在鸿蒙 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([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 })}
复制代码

【完整代码】

@Componentstruct 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@Componentstruct 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") }}
复制代码


用户头像

zhongcx

关注

还未添加个人签名 2024-09-27 加入

还未添加个人简介

评论

发布
暂无评论
鸿蒙NEXT自定义组件:太极Loading_zhongcx_InfoQ写作社区