1
鸿蒙 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") }}复制代码
划线
评论
复制
发布于: 刚刚阅读数: 5
zhongcx
关注
还未添加个人签名 2024-09-27 加入
还未添加个人简介









评论