【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'); // 設定背景顏色 } }