鸿蒙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")
}
}
鸿蒙NEXT自定义组件:太极Loading的更多相关文章
- vue自定义组件(vue.use(),install)+全局组件+局部组件
相信大家都用过element-ui.mintui.iview等诸如此类的组件库,具体用法请参考:https://www.cnblogs.com/wangtong111/p/11522520.html ...
- vue2 自定义全局组件(Loading加载效果)
vue2 自定义全局组件(Loading加载效果) github地址: https://github.com/ccyinghua/custom-global-component 一.构建项目 vue ...
- 【全网首发】鸿蒙开源三方组件--强大的弹窗库XPopup组件
目录: 1.介绍 2.效果一览 3.依赖 4.如何使用 5.下载链接 6.<鸿蒙开源三方组件>文章合集 1. 介绍 XPopup是一个弹窗库,可能是Harmony平台最好的弹窗库.它从 ...
- vue中自定义组件(插件)
vue中自定义组件(插件) 原创 2017年01月04日 22:46:43 标签: 插件 在vue项目中,可以自定义组件像vue-resource一样使用Vue.use()方法来使用,具体实现方法: ...
- [转] vue自定义组件(通过Vue.use()来使用)即install的使用
在vue项目中,我们可以自定义组件,像element-ui一样使用Vue.use()方法来使用,具体实现方法: 1.首先新建一个Cmponent.vue文件 // Cmponent.vue<te ...
- 微信小程序入坑之自定义组件
前言 最近接触微信小程序,再次之前公司用的前端框架是vue ,然后对比发现,开发小程序是各种限制,对于开发者非常不友好.各种槽点太多,完全吐槽不过来,所以在此不多说,打算下次专门写一篇文章吐槽一下.本 ...
- Writing Your Own Widget(自定义组件)
英文地址:http://dojotoolkit.org/reference-guide/1.10/quickstart/writingWidgets.html#quickstart-writingwi ...
- vue自定义组件(通过Vue.use()来使用)即install的使用
在vue项目中,我们可以自定义组件,像element-ui一样使用Vue.use()方法来使用,具体实现方法: 1.首先新建一个loading.vue文件 // Cmponent.vue <te ...
- Android开发之自定义组件和接口回调
说到自定义控件不得不提的就是接口回调,在Android开发中接口回调用的还是蛮多的.在这篇博客开始的时候呢,我想聊一下iOS的自定义控件.在iOS中自定义控件的思路是继承自UIView, 在UIVie ...
- Android自定义组件
[参考的原文地址] http://blog.csdn.net/l1028386804/article/details/47101387效果图: 实现方式: 一:自定义一个含有EditText和Butt ...
随机推荐
- 使用C#爬取快手作者主页,并下载视频/图集
最近发现一些快手的作者,作品还不错,出于学习研究的目的,决定看一下怎么爬取数据.现在网上有一些爬虫工具,不过大部分都失效了,或者不开源.于是自己就写了一个小工具.先看一下成果: 软件只需要填写作者ui ...
- Windows 设置 FRP 自动启动
由于 frps/frpc 不是 Windows 服务应用程序,因此我们不能直接使用 New-Service 命令创建 frps/frpc 服务.我们可以使用下面的方法将 frps/frpc 封装为 W ...
- 使用 nuxi dev 启动 Nuxt 应用程序的详细指南
title: 使用 nuxi dev 启动 Nuxt 应用程序的详细指南 date: 2024/9/2 updated: 2024/9/2 author: cmdragon excerpt: 摘要:本 ...
- 火山引擎VeDI赋能小城酒店业,助力“流量”向“留量”转化
更多技术交流.求职机会,欢迎关注字节跳动数据平台微信公众号,回复[1]进入官方交流群. 今年,"去小城过假期"正悄然流行.根据途牛旅游发布的<2024年上半年度旅游消费报 ...
- maven jetty指定端口号启动
mvn jetty 启动指定端口号 方法 mvn jetty:run -Djetty.port=端口号 备注: 通过以上命令在 windows 中 powershell 下运行时,可能会碰到以下问题: ...
- TypeScript 高级教程 – 把 TypeScript 当强类型语言使用 (第一篇)
前言 原本是想照着 TypeScript 官网 handbook 写个教程的. 但提不起那个劲... 所以呢, 还是用我自己的方式写个复习和进阶笔记就好了呗. 以前写过的 TypeScript 笔记: ...
- CTFSHOW pwn03 WrriteUp
本文来自一个初学CTF的小白,如有任何问题请大佬们指教! 题目来源 CTFShow pwn - pwn03 (ret2libc) https://ctf.show/challenges 思路 1.下载 ...
- [OI] 指针与迭代器
取地址与解引用 一般来说,我们有一个取地址符 & 可以返回该变量的地址. int main(){ int a; cout<<&a; } 0x6ffe1c 如果我们现在有一个 ...
- Android Qcom USB Driver学习(零)
该系列文章总目录链接与各部分简介: Android Qcom USB Driver学习(零) 文章标题 文章链接 文章简介 Android Qcom USB Driver学习(零) https://b ...
- NET Core 基础 - 删除字符串最后一个字符的七大类N种实现方式
今天想通过和大家分享如何删除字符串最后一个字符的N种实现方法,来回顾一些基础知识点. 01.第一类.字符串方式 这类方法是通过string类型自身方法直接实现. 1.Substring方法 相信大多数 ...