鴻蒙開發案例:指南針

zhongcx發表於2024-11-04

【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 }) { // 建立一個列布局,設定間距為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'); // 設定背景顏色
  }
}

  

相關文章