DEV Community

Cover image for 鸿蒙NEXT开发案例:指南针
zhongcx
zhongcx

Posted on

1 1 1 1 1

鸿蒙NEXT开发案例:指南针

Image description

【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;
}
Enter fullscreen mode Exit fullscreen mode
  1. 累计旋转角度算法 累计计算旋转角度,确保角度在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度之间
}
Enter fullscreen mode Exit fullscreen mode
  1. 方向计算算法 根据传感器角度计算当前方向,匹配角度范围对应的方向名称。
private calculateDirection(angle: number): string {
    for (const range of DIRECTION_RANGES) {
        if (angle >= range.min && angle < range.max) {
            return range.name; // 返回对应的方向名称
        }
    }
    return '未知方向'; // 如果角度不在任何范围内,返回未知方向
}
Enter fullscreen mode Exit fullscreen mode

【完整代码】

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 }) { // 创建一个列布局,设置间距为20
      Row({ space: 5 }) { // 创建一个行布局,设置间距为5
        Text(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'); // 设置背景颜色
  }
}
Enter fullscreen mode Exit fullscreen mode

Hostinger image

Get n8n VPS hosting 3x cheaper than a cloud solution

Get fast, easy, secure n8n VPS hosting from $4.99/mo at Hostinger. Automate any workflow using a pre-installed n8n application and no-code customization.

Start now

Top comments (0)

AWS Security LIVE!

Join us for AWS Security LIVE!

Discover the future of cloud security. Tune in live for trends, tips, and solutions from AWS and AWS Partners.

Learn More

👋 Kindness is contagious

Please leave a ❤️ or a friendly comment on this post if you found it helpful!

Okay