鸿蒙开发案例:指南针

news/2024/11/6 8:31:59/文章来源:https://www.cnblogs.com/zhongcx/p/18524331

 

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

在本文中,我们将介绍如何使用鸿蒙系统(HarmonyOS)开发一个简单的指南针应用。通过这个案例,你可以学习如何使用传感器服务、状态管理以及UI构建等基本技能。

【2】环境准备

电脑系统:windows 10

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

工程版本:API 12

真机:Mate 60 Pro

语言:ArkTS、ArkUI

【3】算法分析

1. 角度差计算算法

计算当前角度与目标角度之间的差值,考虑了角度的周期性(0度和360度等效)。

private calculateAngleDifference(currentAngle: number, targetAngle: number): number {let diff = targetAngle - currentAngle;if (diff > 180) {diff -= 360; // 顺时针旋转超过180度,调整为负值} else if (diff < -180) {diff += 360; // 逆时针旋转超过180度,调整为正值}return diff;
}

2. 累计旋转角度算法

累计计算旋转角度,确保角度在0到360度之间。以便旋转动画能正确实现

private updateRotationAngle(angleDifference: number, newAngle: number): void {this.cumulativeRotation += angleDifference; // 累加旋转角度this.rotationAngle += angleDifference; // 更新当前旋转角度this.currentAngle = newAngle; // 更新当前传感器角度this.rotationAngle = (this.rotationAngle % 360 + 360) % 360; // 保持在0到360度之间
}

3. 方向计算算法

根据传感器角度计算当前方向,匹配角度范围对应的方向名称。

private calculateDirection(angle: number): string {for (const range of DIRECTION_RANGES) {if (angle >= range.min && angle < range.max) {return range.name; // 返回对应的方向名称}}return '未知方向'; // 如果角度不在任何范围内,返回未知方向
}

【完整代码】

import { sensor } from '@kit.SensorServiceKit'; // 导入传感器服务模块
import { BusinessError } from '@kit.BasicServicesKit'; // 导入业务错误处理模块// 定义方向范围类
class DirectionRange {name: string = ''; // 方向名称min: number = 0; // 最小角度max: number = 0; // 最大角度
}// 定义各个方向的范围
const DIRECTION_RANGES: DirectionRange[] = [{ name: '北', min: 337.5, max: 360 },{ name: '北', min: 0, max: 22.5 },{ name: '东北', min: 22.5, max: 67.5 },{ name: '东', min: 67.5, max: 112.5 },{ name: '东南', min: 112.5, max: 157.5 },{ name: '南', min: 157.5, max: 202.5 },{ name: '西南', min: 202.5, max: 247.5 },{ name: '西', min: 247.5, max: 292.5 },{ name: '西北', min: 292.5, max: 337.5 }
];// 定义指南针组件
@Entry
@Component
struct Compass {@State directionMessage: string = ''; // 当前方向的名称@State rotationAngle: number = 0; // 当前旋转角度@State currentAngle: number = 0; // 当前传感器角度@State cumulativeRotation: number = 0; // 累计旋转角度private threshold: number = 1; // 设置阈值,用于过滤小的旋转变化// 组件即将出现时调用aboutToAppear(): void {sensor.getSensorList((error: BusinessError) => {if (error) {console.error('获取传感器列表失败', error); // 如果获取传感器列表失败,打印错误信息return;}this.startOrientationUpdates(); // 开始监听传感器数据});}// 开始监听传感器的方位数据private startOrientationUpdates(): void {sensor.on(sensor.SensorId.ORIENTATION, (orientationData) => {const alpha = orientationData.alpha; // 获取当前的方位角this.directionMessage = this.calculateDirection(alpha); // 计算当前方向const angleDifference = this.calculateAngleDifference(this.currentAngle, alpha); // 计算角度差if (Math.abs(angleDifference) > this.threshold) { // 如果角度变化超过阈值this.updateRotationAngle(angleDifference, alpha); // 更新旋转角度}}, { interval: 10000000 }); // 设置传感器更新间隔,单位为纳秒,10000000表示1秒}// 计算两个角度之间的差异private calculateAngleDifference(currentAngle: number, targetAngle: number): number {let diff = targetAngle - currentAngle; // 计算角度差if (diff > 180) {diff -= 360; // 顺时针旋转超过180度,调整为负值} else if (diff < -180) {diff += 360; // 逆时针旋转超过180度,调整为正值}return diff; // 返回调整后的角度差}// 更新旋转角度private updateRotationAngle(angleDifference: number, newAngle: number): void {this.cumulativeRotation += angleDifference; // 累加旋转角度this.rotationAngle += angleDifference; // 更新当前旋转角度this.currentAngle = newAngle; // 更新当前传感器角度// 动画更新animateToImmediately({}, () => {this.rotationAngle = this.cumulativeRotation; // 将旋转角度设置为累计旋转角度});console.log(`累计旋转角度: ${this.cumulativeRotation}`); // 打印累计旋转角度}// 根据角度计算方向private calculateDirection(angle: number): string {for (const range of DIRECTION_RANGES) {if (angle >= range.min && angle < range.max) {return range.name; // 返回对应的方向名称}}return '未知方向'; // 如果角度不在任何范围内,返回未知方向}// 构建用户界面build() {Column({ space: 20 }) { // 创建一个列布局,设置间距为20Row({ space: 5 }) { // 创建一个行布局,设置间距为5Text(this.directionMessage) // 显示当前方向.layoutWeight(1) // 设置布局权重.textAlign(TextAlign.End) // 文本对齐方式.fontColor('#dedede') // 文本颜色.fontSize(50); // 文本大小Text(`${Math.floor(this.currentAngle)}°`) // 显示当前角度.layoutWeight(1) // 设置布局权重.textAlign(TextAlign.Start) // 文本对齐方式.fontColor('#dedede') // 文本颜色.fontSize(50); // 文本大小}.width('100%').margin({ top: 50 }); // 设置宽度和上边距Stack() { // 创建一个堆叠布局Stack() { // 内部堆叠布局Circle() // 创建一个圆形.width(250) // 设置宽度.height(250) // 设置高度.fillOpacity(0) // 设置填充透明度.strokeWidth(25) // 设置边框宽度.stroke('#f95941') // 设置边框颜色.strokeDashArray([1, 5]) // 设置边框虚线样式.strokeLineJoin(LineJoinStyle.Round); // 设置边框连接方式Text('北') // 创建一个文本,显示“北”.height('100%') // 设置高度.width(40) // 设置宽度.align(Alignment.Top) // 设置对齐方式.fontColor('#ff4f3f') // 设置文本颜色.rotate({ angle: 0 }) // 设置旋转角度.padding({ top: 80 }) // 设置内边距.textAlign(TextAlign.Center); // 设置文本对齐方式Text('东') // 创建一个文本,显示“东”.height('100%') // 设置高度.width(40) // 设置宽度.align(Alignment.Top) // 设置对齐方式.fontColor('#fcfdfd') // 设置文本颜色.rotate({ angle: 90 }) // 设置旋转角度.padding({ top: 80 }) // 设置内边距.textAlign(TextAlign.Center); // 设置文本对齐方式Text('南') // 创建一个文本,显示“南”.height('100%') // 设置高度.width(40) // 设置宽度.align(Alignment.Top) // 设置对齐方式.fontColor('#fcfdfd') // 设置文本颜色.rotate({ angle: 180 }) // 设置旋转角度.padding({ top: 80 }) // 设置内边距.textAlign(TextAlign.Center); // 设置文本对齐方式Text('西') // 创建一个文本,显示“西”.height('100%') // 设置高度.width(40) // 设置宽度.align(Alignment.Top) // 设置对齐方式.fontColor('#fcfdfd') // 设置文本颜色.rotate({ angle: 270 }) // 设置旋转角度.padding({ top: 80 }) // 设置内边距.textAlign(TextAlign.Center); // 设置文本对齐方式}.width('100%') // 设置宽度.height('100%') // 设置高度.borderRadius('50%') // 设置圆角.margin({ top: 50 }) // 设置上边距.rotate({ angle: -this.rotationAngle }) // 设置旋转角度.animation({}); // 设置动画效果Line() // 创建一个线条.width(5) // 设置宽度.height(40) // 设置高度.backgroundColor('#fdfffe') // 设置背景颜色.borderRadius('50%') // 设置圆角.margin({ bottom: 200 }); // 设置下边距}.width(300) // 设置宽度.height(300); // 设置高度}.height('100%') // 设置高度.width('100%') // 设置宽度.backgroundColor('#18181a'); // 设置背景颜色}
}

  

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

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

相关文章

读数据工程之道:设计和构建健壮的数据系统27转换

转换1. 转换 1.1. 转换与查询不同1.1.1. 查询是根据过滤和连接逻辑从各种来源检索数据1.1.2. 转换将结果持久化,供其他转换或查询使用1.1.2.1. 结果可以被短暂地或永久地保存1.1.3. 除了持久性,转换区别于查询的另一个特点是复杂性1.1.3.1. 你可能会建立复杂的数据管道,结合…

使用 ollama 在本地试玩 LLM

在 chatGPT 的推动下。LLM 简直火出天际,各行各业都在蹭。听说最近 meta 开源的 llama3 模型可以轻松在普通 PC 上运行,这让我也忍不住来蹭一层。以下是使用 ollama 试玩 llama3 的一些记录。 什么是 llama LLaMA(Large Language Model Meta AI)是Meta开发的大规模预训练语…

获取绝对路径 【文件找不到】

场景# main.py 部分代码def _run_login_script(self):import subprocess# 定义 tools 目录下 login.py 脚本的路径script_path = os.path.join(os.getcwd(), "tools", "login.py")if "CONDA_DEFAULT_ENV" in os.environ and os.environ["CO…

Motivation Challenge

LightTR: A Lightweight Framework for Federated Trajectory Recovery general的问题,数据来源于边缘设备。无法很好的训练一个最优的模型 框架分散训练的得问题 (边缘设备) 一般来说,这些网络是由一堆时空(ST)块组成的,旨在学习轨迹之间的复杂的时空依赖性。st块包含基…

浅谈Windows下的线程细节

绪论 最近阅读了《windows核心编程》关于线程的章节,原书作者讨论得颇为深入,初读者极易被绕晕,我专门写这篇文章供初读者参考阅读。本文的最后,着重讨论了Windows线程API与c/c++运行时库的注意事项。由于本人水平有限,文章难免有纰漏,还望各位读者指正。 Windows提供的创…

校招回顾 | “青春不散场,梦想正起航”,极限科技(INFINI Labs)亮相湖北工业大学 2025 秋季校园招聘会

10 月 31 日,极限科技(INFINI Labs) 受邀参加 湖北工业大学 2025 届秋季校园招聘会,这不仅是一次与满怀激情的青年学子们的深度碰撞,更是一场关于青春与未来的美好邂逅。让我们一起回顾校招现场的精彩瞬间,重温那些闪耀的时刻。 一、梦想起航,共赴盛宴 怀揣着满满的诚意…

如何用 Spring AI + Ollama 构建生成式 AI 应用

为了构建生成式AI应用,需要完成两个部分:AI大模型服务:有两种方式实现,可以使用大厂的API,也可以自己部署,本文将采用ollama来构建 应用构建:调用AI大模型的能力实现业务逻辑,本文将采用Spring Boot + Spring AI来实现Ollama安装与使用进入官网:https://ollama.com/ ,…

MyBatis-Plus条件构造器:构建安全、高效的数据库查询

MyBatis-Plus 提供了一套强大的条件构造器(Wrapper),用于构建复杂的数据库查询条件。一、关于条件构造器(Wrapper) 1.1 简介 MyBatis-Plus 提供了一套强大的条件构造器(Wrapper),用于构建复杂的数据库查询条件。Wrapper 类允许开发者以链式调用的方式构造查询条件,无需编…

数据结构 - 图之代码实现

图遍历分为深度优先遍历(DFS)和广度优先遍历(BFS),DFS一直往下走直到没路再返回,BFS先走所有路一步。文章还介绍了以邻接矩阵存储无向图的实现方法,包括定义、初始化、获取点数量等操作。书接上回,我们继续来聊聊图的遍历与实现。01、遍历 在图的基本功能中有个很重要的…

CF1554E You

题面题解 注意a[u]是点u位置的a,不是每选一个点然后把非标记个数丢进vector里( 每选择一个点,相当于把相邻的非标记的边标为外向,最后一个点u的外向边个数就是a[u] 又观察发现每种边定向方案都可以构造(拓扑),所以一共有2^(n-1)种方案 设f[k]表示gcd=k,g[k]表示k|gcd,…

汽车虚拟仿真软件有哪些?行业软件大盘点!

汽车虚拟仿真可以大大提高汽车的研发效率和质量,降低成本和风险,增强汽车的竞争力和创新能力。本文将带领大家了解汽车虚拟仿真软件有哪些、汽车虚拟仿真实际应用以及汽车云交互实时渲染平台三个要点。汽车虚拟仿真是指利用计算机技术,根据汽车的设计、制造、测试、运行等各…

《机器学习》 学习记录 - 第四章

第4章 决策树 4.1 基本流程 决策树(decision tree)是一类常见的机器学习方法,也叫“判定树”。顾名思义,决策树是基于树的结构进行决策的。 一般的,一棵决策树包含一个根结点、若干个内部结点和若干个叶结点:叶结点对应于决策结果,其他每个结点则对应于一个属性测试; 每…