地理位置 #
一、插件安装 #
1.1 安装依赖 #
bash
npm install @capacitor/geolocation
npx cap sync
1.2 权限配置 #
iOS (Info.plist):
xml
<key>NSLocationWhenInUseUsageDescription</key>
<string>需要获取您的位置信息以提供更好的服务</string>
<key>NSLocationAlwaysUsageDescription</key>
<string>需要持续获取您的位置信息</string>
<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
<string>需要获取您的位置信息以提供更好的服务</string>
Android (AndroidManifest.xml):
xml
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
二、基本使用 #
2.1 获取当前位置 #
typescript
import { Geolocation } from '@capacitor/geolocation';
async function getCurrentPosition() {
const position = await Geolocation.getCurrentPosition({
enableHighAccuracy: true,
timeout: 10000,
maximumAge: 0
});
console.log('Latitude:', position.coords.latitude);
console.log('Longitude:', position.coords.longitude);
console.log('Accuracy:', position.coords.accuracy);
return position;
}
2.2 位置信息结构 #
typescript
interface Position {
coords: {
latitude: number; // 纬度
longitude: number; // 经度
accuracy: number; // 精度(米)
altitude: number | null; // 海拔(米)
altitudeAccuracy: number | null; // 海拔精度
heading: number | null; // 方向(度)
speed: number | null; // 速度(米/秒)
};
timestamp: number; // 时间戳
}
2.3 基本选项 #
typescript
interface GeolocationOptions {
// 是否使用高精度定位
enableHighAccuracy?: boolean;
// 超时时间(毫秒)
timeout?: number;
// 缓存最大年龄(毫秒)
maximumAge?: number;
}
// 使用示例
const position = await Geolocation.getCurrentPosition({
enableHighAccuracy: true, // 使用GPS
timeout: 10000, // 10秒超时
maximumAge: 0 // 不使用缓存
});
三、位置监听 #
3.1 实时监听位置 #
typescript
import { Geolocation } from '@capacitor/geolocation';
async function startWatching() {
const watchId = await Geolocation.watchPosition(
{
enableHighAccuracy: true,
timeout: 10000,
maximumAge: 0
},
(position, error) => {
if (error) {
console.error('Watch error:', error);
return;
}
console.log('New position:', position);
updateMap(position);
}
);
return watchId;
}
// 停止监听
async function stopWatching(watchId: string) {
await Geolocation.clearWatch({ id: watchId });
}
3.2 位置监听管理 #
typescript
class LocationWatcher {
private watchId: string | null = null;
private callbacks: Set<(position: any) => void> = new Set();
async start(options = {}) {
if (this.watchId) {
await this.stop();
}
this.watchId = await Geolocation.watchPosition(
{
enableHighAccuracy: true,
timeout: 10000,
maximumAge: 0,
...options
},
(position, error) => {
if (error) {
console.error('Location error:', error);
return;
}
this.callbacks.forEach(cb => cb(position));
}
);
}
async stop() {
if (this.watchId) {
await Geolocation.clearWatch({ id: this.watchId });
this.watchId = null;
}
}
subscribe(callback: (position: any) => void): () => void {
this.callbacks.add(callback);
return () => this.callbacks.delete(callback);
}
}
export const locationWatcher = new LocationWatcher();
四、权限管理 #
4.1 检查权限 #
typescript
async function checkPermissions() {
const permissions = await Geolocation.checkPermissions();
console.log('Location:', permissions.location);
console.log('Coarse Location:', permissions.coarseLocation);
return permissions;
}
4.2 请求权限 #
typescript
async function requestPermissions() {
const permissions = await Geolocation.requestPermissions();
if (permissions.location === 'granted') {
// 权限已授予
return true;
}
return false;
}
4.3 完整权限处理 #
typescript
import { Geolocation } from '@capacitor/geolocation';
import { Dialog } from '@capacitor/dialog';
import { AppLauncher } from '@capacitor/app-launcher';
async function ensureLocationPermission(): Promise<boolean> {
// 检查权限
const permissions = await Geolocation.checkPermissions();
if (permissions.location === 'granted') {
return true;
}
// 请求权限
const result = await Geolocation.requestPermissions();
if (result.location === 'granted') {
return true;
}
// 权限被拒绝
if (result.location === 'denied') {
const { value } = await Dialog.confirm({
title: '位置权限',
message: '需要位置权限才能使用此功能,是否前往设置开启?'
});
if (value) {
// 打开应用设置
await AppLauncher.openUrl({ url: 'app-settings:' });
}
}
return false;
}
五、距离计算 #
5.1 Haversine公式 #
typescript
function calculateDistance(
lat1: number,
lon1: number,
lat2: number,
lon2: number
): number {
const R = 6371e3; // 地球半径(米)
const φ1 = lat1 * Math.PI / 180;
const φ2 = lat2 * Math.PI / 180;
const Δφ = (lat2 - lat1) * Math.PI / 180;
const Δλ = (lon2 - lon1) * Math.PI / 180;
const a = Math.sin(Δφ / 2) * Math.sin(Δφ / 2) +
Math.cos(φ1) * Math.cos(φ2) *
Math.sin(Δλ / 2) * Math.sin(Δλ / 2);
const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
return R * c; // 返回距离(米)
}
// 使用示例
const distance = calculateDistance(
39.9042, 116.4074, // 北京
31.2304, 121.4737 // 上海
);
console.log(`距离: ${(distance / 1000).toFixed(2)} 公里`);
5.2 距离工具类 #
typescript
class DistanceCalculator {
private static R = 6371e3; // 地球半径
static between(
point1: { lat: number; lng: number },
point2: { lat: number; lng: number }
): number {
return this.calculate(point1.lat, point1.lng, point2.lat, point2.lng);
}
static calculate(lat1: number, lon1: number, lat2: number, lon2: number): number {
const φ1 = this.toRad(lat1);
const φ2 = this.toRad(lat2);
const Δφ = this.toRad(lat2 - lat1);
const Δλ = this.toRad(lon2 - lon1);
const a = Math.sin(Δφ / 2) * Math.sin(Δφ / 2) +
Math.cos(φ1) * Math.cos(φ2) *
Math.sin(Δλ / 2) * Math.sin(Δλ / 2);
const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
return this.R * c;
}
static formatDistance(meters: number): string {
if (meters < 1000) {
return `${Math.round(meters)}米`;
}
return `${(meters / 1000).toFixed(1)}公里`;
}
private static toRad(deg: number): number {
return deg * Math.PI / 180;
}
}
// 使用示例
const distance = DistanceCalculator.between(
{ lat: 39.9042, lng: 116.4074 },
{ lat: 31.2304, lng: 121.4737 }
);
console.log(DistanceCalculator.formatDistance(distance));
六、地图集成 #
6.1 Google Maps集成 #
typescript
// 使用Google Maps JavaScript API
declare global {
interface Window {
google: any;
}
}
async function initMap(elementId: string) {
const position = await Geolocation.getCurrentPosition();
const map = new window.google.maps.Map(
document.getElementById(elementId),
{
center: {
lat: position.coords.latitude,
lng: position.coords.longitude
},
zoom: 15
}
);
new window.google.maps.Marker({
position: {
lat: position.coords.latitude,
lng: position.coords.longitude
},
map,
title: '当前位置'
});
return map;
}
6.2 高德地图集成 #
typescript
// 使用高德地图JS API
declare global {
interface Window {
AMap: any;
}
}
async function initAMap(elementId: string) {
const position = await Geolocation.getCurrentPosition();
const map = new window.AMap.Map(elementId, {
zoom: 15,
center: [position.coords.longitude, position.coords.latitude]
});
new window.AMap.Marker({
position: [position.coords.longitude, position.coords.latitude],
map
});
return map;
}
七、完整示例 #
7.1 React组件 #
tsx
import { useState, useEffect, useCallback } from 'react';
import { Geolocation, Position } from '@capacitor/geolocation';
function LocationTracker() {
const [position, setPosition] = useState<Position | null>(null);
const [loading, setLoading] = useState(false);
const [error, setError] = useState<string | null>(null);
const [watching, setWatching] = useState(false);
const [watchId, setWatchId] = useState<string | null>(null);
const getCurrentPosition = useCallback(async () => {
setLoading(true);
setError(null);
try {
const pos = await Geolocation.getCurrentPosition({
enableHighAccuracy: true,
timeout: 10000
});
setPosition(pos);
} catch (e) {
setError(e instanceof Error ? e.message : '获取位置失败');
} finally {
setLoading(false);
}
}, []);
const startWatching = useCallback(async () => {
setWatching(true);
const id = await Geolocation.watchPosition(
{
enableHighAccuracy: true,
timeout: 10000
},
(pos, err) => {
if (err) {
setError(err.message);
return;
}
setPosition(pos);
}
);
setWatchId(id);
}, []);
const stopWatching = useCallback(async () => {
if (watchId) {
await Geolocation.clearWatch({ id: watchId });
setWatchId(null);
}
setWatching(false);
}, [watchId]);
useEffect(() => {
return () => {
if (watchId) {
Geolocation.clearWatch({ id: watchId });
}
};
}, [watchId]);
return (
<div className="location-tracker">
<div className="actions">
<button onClick={getCurrentPosition} disabled={loading}>
{loading ? '获取中...' : '获取当前位置'}
</button>
{!watching ? (
<button onClick={startWatching}>开始追踪</button>
) : (
<button onClick={stopWatching}>停止追踪</button>
)}
</div>
{error && <div className="error">{error}</div>}
{position && (
<div className="position-info">
<p>纬度: {position.coords.latitude.toFixed(6)}</p>
<p>经度: {position.coords.longitude.toFixed(6)}</p>
<p>精度: {position.coords.accuracy.toFixed(0)}米</p>
{position.coords.altitude && (
<p>海拔: {position.coords.altitude.toFixed(0)}米</p>
)}
{position.coords.speed && (
<p>速度: {position.coords.speed.toFixed(1)}米/秒</p>
)}
</div>
)}
</div>
);
}
export default LocationTracker;
7.2 Vue组合式函数 #
typescript
// composables/useGeolocation.ts
import { ref, onUnmounted } from 'vue';
import { Geolocation, Position } from '@capacitor/geolocation';
export function useGeolocation() {
const position = ref<Position | null>(null);
const loading = ref(false);
const error = ref<string | null>(null);
const watching = ref(false);
let watchId: string | null = null;
const getCurrentPosition = async () => {
loading.value = true;
error.value = null;
try {
position.value = await Geolocation.getCurrentPosition({
enableHighAccuracy: true,
timeout: 10000
});
} catch (e) {
error.value = e instanceof Error ? e.message : '获取位置失败';
} finally {
loading.value = false;
}
};
const startWatching = async () => {
watching.value = true;
watchId = await Geolocation.watchPosition(
{
enableHighAccuracy: true,
timeout: 10000
},
(pos, err) => {
if (err) {
error.value = err.message;
return;
}
position.value = pos;
}
);
};
const stopWatching = async () => {
if (watchId) {
await Geolocation.clearWatch({ id: watchId });
watchId = null;
}
watching.value = false;
};
onUnmounted(() => {
if (watchId) {
Geolocation.clearWatch({ id: watchId });
}
});
return {
position,
loading,
error,
watching,
getCurrentPosition,
startWatching,
stopWatching
};
}
八、后台定位 #
8.1 iOS后台定位 #
xml
<!-- Info.plist -->
<key>UIBackgroundModes</key>
<array>
<string>location</string>
</array>
8.2 Android后台定位 #
xml
<!-- AndroidManifest.xml -->
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
九、常见问题 #
9.1 定位不准确 #
typescript
// 使用高精度定位
const position = await Geolocation.getCurrentPosition({
enableHighAccuracy: true,
timeout: 20000,
maximumAge: 0
});
9.2 定位超时 #
typescript
// 增加超时时间
const position = await Geolocation.getCurrentPosition({
enableHighAccuracy: true,
timeout: 30000 // 30秒
});
9.3 室内定位 #
typescript
// 室内可能需要使用网络定位
const position = await Geolocation.getCurrentPosition({
enableHighAccuracy: false // 使用网络定位
});
十、总结 #
10.1 核心功能 #
| 功能 | 方法 |
|---|---|
| 获取位置 | Geolocation.getCurrentPosition() |
| 监听位置 | Geolocation.watchPosition() |
| 停止监听 | Geolocation.clearWatch() |
| 检查权限 | Geolocation.checkPermissions() |
| 请求权限 | Geolocation.requestPermissions() |
10.2 下一步 #
了解地理位置后,让我们学习 存储系统!
最后更新:2026-03-28