鸿蒙NEXT开发案例:转盘

news/2024/11/13 8:09:58/文章来源:https://www.cnblogs.com/zhongcx/p/18537619

 

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

在鸿蒙NEXT系统中,开发一个有趣且实用的转盘应用不仅可以提升用户体验,还能展示鸿蒙系统的强大功能。本文将详细介绍如何使用鸿蒙NEXT系统开发一个转盘应用,涵盖从组件定义到用户交互的完整过程。

【2】环境准备

电脑系统:windows 10

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

工程版本:API 12

真机:mate60 pro

语言:ArkTS、ArkUI

【3】难点分析

1. 扇形路径的计算

难点:创建扇形的路径需要精确计算起始点、结束点和弧线参数。尤其是涉及到三角函数的使用,初学者可能会对如何将角度转换为坐标感到困惑。

解决方案:可以通过绘制简单的示意图来帮助理解扇形的构造,并在代码中添加详细注释,解释每一步的计算过程。

2. 动态角度计算

难点:在转盘旋转时,需要根据单元格的比例动态计算每个单元格的角度和旋转角度。这涉及到累加和比例计算,可能会导致逻辑错误。

解决方案:使用数组的 reduce 方法来计算总比例,并在计算每个单元格的角度时,确保逻辑清晰。可以通过单元测试来验证每个单元格的角度是否正确。

3. 动画效果的实现

难点:实现转盘的旋转动画需要对动画的持续时间、曲线和结束后的状态进行管理。初学者可能会对如何控制动画的流畅性和效果感到困惑。

解决方案:可以参考鸿蒙NEXT的动画文档,了解不同的动画效果和参数设置。通过逐步调试,观察动画效果并进行调整。

4. 用户交互的处理

难点:处理用户点击事件,尤其是在动画进行时,如何禁用按钮以防止重复点击,可能会导致状态管理的复杂性。

解决方案:在按钮的点击事件中,使用状态变量(如 isAnimating)来控制按钮的可用性,并在动画结束后恢复按钮的状态。

5. 组件的状态管理

难点:在多个组件之间传递状态(如当前选中的单元格、转盘的角度等)可能会导致状态管理混乱。

解决方案:使用状态管理工具(如 @State 和 @Trace)来确保状态的统一管理,并在需要的地方进行状态更新,保持组件之间的解耦。

【完整代码】

import { CounterComponent, CounterType } from '@kit.ArkUI'; // 导入计数器组件和计数器类型// 定义扇形组件
@Component
struct Sector {@Prop radius: number; // 扇形的半径@Prop angle: number; // 扇形的角度@Prop color: string; // 扇形的颜色// 创建扇形路径的函数createSectorPath(radius: number, angle: number): string {const centerX = radius / 2; // 计算扇形中心的X坐标const centerY = radius / 2; // 计算扇形中心的Y坐标const startX = centerX; // 扇形起始点的X坐标const startY = centerY - radius; // 扇形起始点的Y坐标const halfAngle = angle / 4; // 计算半个角度// 计算扇形结束点1的坐标const endX1 = centerX + radius * Math.cos((halfAngle * Math.PI) / 180);const endY1 = centerY - radius * Math.sin((halfAngle * Math.PI) / 180);// 计算扇形结束点2的坐标const endX2 = centerX + radius * Math.cos((-halfAngle * Math.PI) / 180);const endY2 = centerY - radius * Math.sin((-halfAngle * Math.PI) / 180);// 判断是否为大弧const largeArcFlag = angle / 2 > 180 ? 1 : 0;const sweepFlag = 1; // 设置弧线方向为顺时针// 生成SVG路径命令const pathCommands =`M${startX} ${startY} A${radius} ${radius} 0 ${largeArcFlag} ${sweepFlag} ${endX1} ${endY1} L${centerX} ${centerY} L${endX2} ${endY2} A${radius} ${radius} 0 ${largeArcFlag} ${1 -sweepFlag} ${startX} ${startY} Z`;return pathCommands; // 返回路径命令}// 构建扇形组件build() {Stack() {// 创建第一个扇形路径Path().width(`${this.radius}px`) // 设置宽度为半径.height(`${this.radius}px`) // 设置高度为半径.commands(this.createSectorPath(this.radius, this.angle)) // 设置路径命令.fillOpacity(1) // 设置填充透明度.fill(this.color) // 设置填充颜色.strokeWidth(0) // 设置边框宽度为0.rotate({ angle: this.angle / 4 - 90 }); // 旋转扇形// 创建第二个扇形路径Path().width(`${this.radius}px`) // 设置宽度为半径.height(`${this.radius}px`) // 设置高度为半径.commands(this.createSectorPath(this.radius, this.angle)) // 设置路径命令.fillOpacity(1) // 设置填充透明度.fill(this.color) // 设置填充颜色.strokeWidth(0) // 设置边框宽度为0.rotate({ angle: 180 - (this.angle / 4 - 90) }); // 旋转扇形}}
}// 定义单元格类
@ObservedV2
class Cell {@Trace angle: number = 0; // 扇形的角度@Trace title: string; // 当前格子的标题@Trace color: string; // 背景颜色@Trace rotate: number = 0; // 在转盘要旋转的角度angleStart: number = 0; // 轮盘所在区间的起始angleEnd: number = 0; // 轮盘所在区间的结束proportion: number = 0; // 所占比例// 构造函数constructor(proportion: number, title: string, color: string) {this.proportion = proportion; // 设置比例this.title = title; // 设置标题this.color = color; // 设置颜色}
}// 定义转盘组件
@Entry
@Component
struct Wheel {@State cells: Cell[] = []; // 存储单元格的数组@State wheelWidth: number = 600; // 转盘的宽度@State currentAngle: number = 0; // 当前转盘的角度@State selectedName: string = ""; // 选中的名称isAnimating: boolean = false; // 动画状态colorIndex: number = 0; // 颜色索引colorPalette: string[] = [ // 颜色调色板"#26c2ff","#978efe","#c389fe","#ff85bd","#ff7051","#fea800","#ffcf18","#a9c92a"];// 组件即将出现时调用aboutToAppear(): void {// 初始化单元格this.cells.push(new Cell(1, "跑步", this.colorPalette[this.colorIndex++ % this.colorPalette.length]));this.cells.push(new Cell(2, "跳绳", this.colorPalette[this.colorIndex++ % this.colorPalette.length]));this.cells.push(new Cell(1, "唱歌", this.colorPalette[this.colorIndex++ % this.colorPalette.length]));this.cells.push(new Cell(4, "跳舞", this.colorPalette[this.colorIndex++ % this.colorPalette.length]));this.calculateAngles(); // 计算角度}// 计算每个单元格的角度private calculateAngles() {// 根据比例计算总比例const totalProportion = this.cells.reduce((sum, cell) => sum + cell.proportion, 0);this.cells.forEach(cell => {cell.angle = (cell.proportion * 360) / totalProportion; // 计算每个单元格的角度});let cumulativeAngle = 0; // 累计角度this.cells.forEach(cell => {cell.angleStart = cumulativeAngle; // 设置起始角度cumulativeAngle += cell.angle; // 更新累计角度cell.angleEnd = cumulativeAngle; // 设置结束角度cell.rotate = cumulativeAngle - (cell.angle / 2); // 计算旋转角度});}// 构建转盘组件build() {Column() {Row() {Text('转盘').fontSize(20).fontColor("#0b0e15"); // 显示转盘标题}.width('100%').height(44).justifyContent(FlexAlign.Center); // 设置行的宽度和高度// 显示当前状态Text(this.isAnimating ? '旋转中' : `${this.selectedName}`).fontSize(20).fontColor("#0b0e15").height(40);Stack() {Stack() {// 遍历每个单元格并绘制扇形ForEach(this.cells, (cell: Cell) => {Stack() {Sector({ radius: lpx2px(this.wheelWidth) / 2, angle: cell.angle, color: cell.color }); // 创建扇形Text(cell.title).fontColor(Color.White).margin({ bottom: `${this.wheelWidth / 1.4}lpx` }); // 显示单元格标题}.width('100%').height('100%').rotate({ angle: cell.rotate }); // 设置宽度和高度,并旋转});}.borderRadius('50%') // 设置圆角.backgroundColor(Color.Gray) // 设置背景颜色.width(`${this.wheelWidth}lpx`) // 设置转盘宽度.height(`${this.wheelWidth}lpx`) // 设置转盘高度.rotate({ angle: this.currentAngle }); // 旋转转盘// 创建指针Polygon({ width: 20, height: 10 }).points([[0, 0], [10, -20], [20, 0]]) // 设置指针的点.fill("#d72b0b") // 设置指针颜色.height(20) // 设置指针高度.margin({ bottom: '140lpx' }); // 设置指针底部边距// 创建开始按钮Button('开始').fontColor("#c53a2c") // 设置按钮字体颜色.borderWidth(10) // 设置按钮边框宽度.borderColor("#dd2218") // 设置按钮边框颜色.backgroundColor("#fde427") // 设置按钮背景颜色.width('200lpx') // 设置按钮宽度.height('200lpx') // 设置按钮高度.borderRadius('50%') // 设置按钮为圆形.clickEffect({ level: ClickEffectLevel.LIGHT }) // 设置点击效果.onClick(() => { // 点击按钮时的回调函数if (this.isAnimating) { // 如果正在动画中,返回return;}this.selectedName = ""; // 清空选中的名称this.isAnimating = true; // 设置动画状态为正在动画animateTo({ // 开始动画duration: 5000, // 动画持续时间为5000毫秒curve: Curve.EaseInOut, // 动画曲线为缓入缓出onFinish: () => { // 动画完成后的回调this.currentAngle %= 360; // 保持当前角度在0到360之间for (const cell of this.cells) { // 遍历每个单元格// 检查当前角度是否在单元格的角度范围内if (360 - this.currentAngle >= cell.angleStart && 360 - this.currentAngle <= cell.angleEnd) {this.selectedName = cell.title; // 设置选中的名称为当前单元格的标题break; // 找到后退出循环}}this.isAnimating = false; // 设置动画状态为未动画},}, () => { // 动画进行中的回调this.currentAngle += (360 * 5 + Math.floor(Math.random() * 360)); // 更新当前角度,增加随机旋转});});}// 创建滚动区域Scroll() {Column() {// 遍历每个单元格,创建输入框和计数器ForEach(this.cells, (item: Cell, index: number) => {Row() {// 创建文本输入框,显示单元格标题TextInput({ text: item.title }).layoutWeight(1) // 设置输入框占据剩余空间.onChange((value) => { // 输入框内容变化时的回调item.title = value; // 更新单元格标题});// 创建计数器组件CounterComponent({options: {type: CounterType.COMPACT, // 设置计数器类型为紧凑型numberOptions: {label: `当前占比`, // 设置计数器标签value: item.proportion, // 设置计数器初始值min: 1, // 设置最小值max: 100, // 设置最大值step: 1, // 设置步长onChange: (value: number) => { // 计数器值变化时的回调item.proportion = value; // 更新单元格的比例this.calculateAngles(); // 重新计算角度}}}});// 创建删除按钮Button('删除').onClick(() => {this.cells.splice(index, 1); // 从单元格数组中删除当前单元格this.calculateAngles(); // 重新计算角度});}.width('100%').justifyContent(FlexAlign.SpaceBetween) // 设置行的宽度和内容对齐方式.padding({ left: 40, right: 40 }); // 设置左右内边距});}.layoutWeight(1); // 设置滚动区域占据剩余空间}.layoutWeight(1) // 设置滚动区域占据剩余空间.margin({ top: 20, bottom: 20 }) // 设置上下外边距.align(Alignment.Top); // 设置对齐方式为顶部对齐// 创建添加新内容按钮Button('添加新内容').onClick(() => {// 向单元格数组中添加新单元格this.cells.push(new Cell(1, "新内容", this.colorPalette[this.colorIndex++ % this.colorPalette.length]));this.calculateAngles(); // 重新计算角度}).margin({ top: 20, bottom: 20 }); // 设置按钮的上下外边距}.height('100%') // 设置组件高度为100%.width('100%') // 设置组件宽度为100%.backgroundColor("#f5f8ff"); // 设置组件背景颜色}
}

  

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/830431.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

VMware Tanzu CLI 1.5.0 - VMware Kubernetes 发新版的命令行工具

VMware Tanzu CLI 1.5.0 - VMware Kubernetes 发新版的命令行工具VMware Tanzu CLI 1.5.0 - VMware Kubernetes 发新版的命令行工具 VMware 构建、签名和支持的开源 Kubernetes 容器编排平台的完整分发版 请访问原文链接:https://sysin.org/blog/vmware-tanzu-cli/ 查看最新版…

读数据工程之道:设计和构建健壮的数据系统33未来

未来1. 未来 1.1. 运营的优先级和最佳实践与技术可能会改变,但生命周期的主要阶段会在许多年内保持不变 1.2. 随着组织以新的方式利用数据,将需要新的基础、系统和工作流来满足这些需求 1.3. 如果工具变得更容易使用,数据工程师就会向价值链上游移动,专注于更高级别的工作 …

干货分享:开启PWM调光之门,一起来做呼吸灯

PWM作为一种灵活且高效的信号调制手段,在电气设备的性能控制和调节中发挥着重要作用,常用于电机控制、灯光调光、音频信号生成、加热控制等应用。 本文将以合宙低功耗4G模组经典型号——Air780E为例,展示PWM(脉冲宽度调制)输出呼吸灯的实现方法,帮助大家深入理解如何在项…

干货分享:Air780E软件指南:字符串处理

一、Lua字符串介绍关于字符串,Lua提供了一些灵活且强大的功能,一些入门知识如下: 1.1 字符串定义 在Lua中,字符串可以用单引号或双引号"来定义。例如: localstr1=Hello,World! localstr2="Hello,Lua!" 1.2 字符串长度 可以使用#操作符获取字符串的长度。例…

干货分享:通用加解密函数(crypto),Air780E篇

一、加解密概述加解密算法是保证数据安全的基础技术,无论是在数据传输、存储,还是用户身份验证中,都起着至关重要的作用.随着互联网的发展和信息安全威胁的增加,了解并掌握常用的加解密算法已经成为开发者和安全从业者的必修课. 常见的15种加密解密算法分别是:散列哈希[MD…

Docker仓库之Harbor企业级镜像仓库的搭建与使用

本章将和大家分享Docker仓库之Harbor企业级镜像仓库的搭建与使用。本章将和大家分享Docker仓库之Harbor企业级镜像仓库的搭建与使用。废话不多说,下面我们直接进入主题。 一、企业最爱:Harbor企业级镜像仓库 Harbor是VMware公司开源的一个企业级Docker Registry项目,项目地址…

面试:什么是死锁,如何避免或解决死锁;MySQL中的死锁现象,MySQL死锁如何解决

面试:什么是死锁,死锁产生的四个必要条件,如何避免或解决死锁;数据库锁,锁分类,控制事务;MySQL中的死锁现象,MySQL死锁如何解决文章目录 前言 一、死锁1.1 什么是死锁 1.2 死锁产生的四个必要条件 1.3 模拟产生死锁的代码 1.4 死锁的产生原因二、如何避免或解决死锁2.1…

23. 使用MySQL之使用存储过程

1. 存储过程 迄今为止,使用的大多数SQL语句都是针对一个或多个表的单条语句。并非所有操作都这么简单,经常会有一个完整的操作需要多条语句才能完成。 例如,考虑以下的情形。为了处理订单,需要核对以保证库存中有相应的物品。如果库存有物品,这些物品需要预定以便不将它们…

【java开发】 Java 打包方式总结

前言 由于笔者之前也一直在使用 IDEA, Maven 等成熟工具|框架的打包方式, 也没有仔细研究过这个 JAR 包打包之中的细节, 网上公开的视频也没有找到, 但文章倒挺多的, 那周六日就简单看一下吧, 将这些打包方式都整理整理. 本篇文章彻底理解 Maven & IDEA & 原生的打包方…

分享一套基于thinkphp开发的小说内容管理系统源码,附安装教程,100%开源。

小说内容管理系统系统介绍小说内容管理系统是一套用于快速构建小说发布与阅读的小说内容管理平台。使用小说内容管理系统可以快速搭建一个完整的小说阅读网站,节省开发时间和成本。 小说内容管理系统是一套基于ThinkPHP6 + MySql + Layui + BUI 开发的小说行业内容管理系统。 …

【数据库】Cassandra的安装及基本操作

Cassandra 数据库安装 官方文档 安装环境Ubuntu 22.04.5LTS Cassandra 4.0.14 JDK 11操作步骤下载安装包curl -OL https://dlcdn.apache.org/cassandra/4.0.14/apache-cassandra-4.0.14-bin.tar.gz解压操作解压tar -zxvf ./apache-cassandra-4.0.14-bin.tar.gz运行数据库,该步…

【数据库】GeoMesa的安装及基本操作

GeoMesa-Cassandra 安装 官方文档_安装 GeoMesa Cassandra 安装环境Ubuntu 22.04.5 LTS Cassandra 3.11.16 JDK 11 geomesa-cassandra_2.12-5.1.0操作步骤下载 bin文件 wget https://github.com/locationtech/geomesa/releases/download/geomesa-5.1.0/geomesa-cassandra_2.12-…