获取位置信息
Geolocation API 获取位置后返回 Position 对象,其中包含 Coordinates 坐标信息和时间戳。本节将详细解析 Position 和 Coordinates 的所有属性,以及 getCurrentPosition 方法的配置选项如何影响获取结果的精度和效率。
前置知识
阅读本节前,建议先了解:Geolocation API 基础
基础概念
Position 对象结构
getCurrentPosition 和 watchPosition 成功回调接收一个 Position 对象:
javascript
function onSuccess(position) {
// Position 对象包含两个属性:
console.log(position.coords); // Coordinates 对象
console.log(position.timestamp); // 时间戳
}Coordinates 属性一览
| 属性 | 类型 | 说明 | 可用性 |
|---|---|---|---|
latitude | double | 纬度(度) | 始终可用 |
longitude | double | 经度(度) | 始终可用 |
accuracy | double | 精度半径(米) | 始终可用 |
altitude | `double | null` | 海拔(米) |
altitudeAccuracy | `double | null` | 海拔精度(米) |
heading | `double | null` | 行进方向(度,正北为 0) |
speed | `double | null` | 速度(m/s) |
语法与配置
Position 对象详解
javascript
function handlePosition(position) {
const coords = position.coords;
const timestamp = position.timestamp;
console.log('===== Position 对象 =====');
console.log('时间戳:', new Date(timestamp).toISOString());
console.log('\n===== Coordinates 对象 =====');
console.log('纬度:', coords.latitude);
console.log('经度:', coords.longitude);
console.log('精度:', coords.accuracy, '米');
console.log('海拔:', coords.altitude);
console.log('海拔精度:', coords.altitudeAccuracy);
console.log('方向:', coords.heading);
console.log('速度:', coords.speed);
}配置选项详解
javascript
const options = {
// 是否请求高精度(启用 GPS)
enableHighAccuracy: true,
// 请求超时时间(毫秒)
// 超过此时间未获取到位置则触发 TIMEOUT 错误
timeout: 15000,
// 缓存位置的最大年龄(毫秒)
// 如果缓存位置年龄小于此值,直接返回缓存
maximumAge: 0
};
navigator.geolocation.getCurrentPosition(onSuccess, onError, options);| 选项 | 默认值 | 推荐场景 |
|---|---|---|
enableHighAccuracy: true | false | 导航、地图应用 |
enableHighAccuracy: false | - | 附近推荐、天气服务 |
timeout: 5000 | Infinity | 需要快速响应 |
timeout: 30000 | - | 搜索 GPS 信号 |
maximumAge: 0 | 0 | 需要最新位置 |
maximumAge: 300000 | - | 5 分钟内可接受旧位置 |
maximumAge: Infinity | - | 任何缓存位置都可接受 |
详细说明
精度理解
accuracy 表示设备位置与真实位置的最大可能偏差半径:
javascript
function analyzeAccuracy(position) {
const accuracy = position.coords.accuracy;
if (accuracy < 10) {
console.log('极高精度 - 可能使用了 GPS');
} else if (accuracy < 100) {
console.log('高精度 - 可能使用了 WiFi');
} else if (accuracy < 1000) {
console.log('中精度 - 可能使用了基站三角定位');
} else {
console.log('低精度 - 可能仅使用了 IP 定位');
}
// 根据精度决定是否使用位置
if (accuracy > 1000) {
console.warn('位置精度不足,不建议用于精确位置服务');
}
}不同精度的对比
| 定位方式 | 典型精度 | 耗时 | 耗电 | 适用场景 |
|---|---|---|---|---|
| GPS | 3-10 米 | 5-30 秒 | 高 | 导航、户外运动 |
| WiFi | 20-100 米 | 1-5 秒 | 中 | 城市定位、室内 |
| 基站 | 100-1000 米 | 1-3 秒 | 低 | 粗略定位 |
| IP 地址 | 1-50 公里 | < 1 秒 | 极低 | 国家/城市级 |
maximumAge 缓存策略
javascript
// 每次都获取最新位置(最慢但最准)
const options1 = { maximumAge: 0 };
// 5 分钟内的缓存位置可接受(更快)
const options2 = { maximumAge: 300000 };
// 接受任何缓存位置(最快)
const options3 = { maximumAge: Infinity };
// 智能策略:根据场景选择
function getBestOptions(requireFresh = false) {
return {
enableHighAccuracy: requireFresh,
timeout: requireFresh ? 15000 : 5000,
maximumAge: requireFresh ? 0 : 300000
};
}经纬度实用工具
javascript
// 经纬度工具函数
const GeoUtils = {
// 计算两点之间的距离(Haversine 公式)
distance(lat1, lon1, lat2, lon2) {
const R = 6371000; // 地球半径(米)
const dLat = (lat2 - lat1) * Math.PI / 180;
const dLon = (lon2 - lon1) * Math.PI / 180;
const a = Math.sin(dLat / 2) * Math.sin(dLat / 2) +
Math.cos(lat1 * Math.PI / 180) * Math.cos(lat2 * Math.PI / 180) *
Math.sin(dLon / 2) * Math.sin(dLon / 2);
const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
return R * c;
},
// 格式化经纬度
formatLat(lat) {
const direction = lat >= 0 ? 'N' : 'S';
return `${Math.abs(lat).toFixed(6)}° ${direction}`;
},
formatLng(lng) {
const direction = lng >= 0 ? 'E' : 'W';
return `${Math.abs(lng).toFixed(6)}° ${direction}`;
},
// 判断是否在中国境内(粗略判断)
isChina(lat, lng) {
return lat >= 3.86 && lat <= 53.55 && lng >= 73.66 && lng <= 135.05;
},
// 生成地图链接
getMapLink(lat, lng) {
return `https://www.google.com/maps?q=${lat},${lng}`;
},
// 生成静态地图图片 URL
getStaticMapUrl(lat, lng, zoom = 14, size = '400x300') {
return `https://maps.googleapis.com/maps/api/staticmap?center=${lat},${lng}&zoom=${zoom}&size=${size}&markers=${lat},${lng}`;
}
};
// 使用示例
const distance = GeoUtils.distance(39.9042, 116.4074, 31.2304, 121.4737);
console.log(`北京到上海: ${(distance / 1000).toFixed(1)} 公里`);实战示例
详细位置信息展示
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>位置信息详情</title>
<style>
body { font-family: -apple-system, sans-serif; padding: 20px; background: #f5f7fa; }
.info-card {
max-width: 500px;
margin: 0 auto;
background: white;
border-radius: 12px;
padding: 24px;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
}
h2 { text-align: center; color: #1a1a2e; margin-bottom: 20px; }
.coords-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 12px;
margin-top: 16px;
}
.coord-item {
padding: 12px;
background: #f8f9fa;
border-radius: 8px;
text-align: center;
}
.coord-label { font-size: 12px; color: #888; margin-bottom: 4px; }
.coord-value { font-size: 18px; font-weight: 700; color: #333; font-family: monospace; }
.coord-unit { font-size: 12px; color: #aaa; }
.full-width { grid-column: 1 / -1; }
.accuracy-bar {
margin-top: 16px;
height: 8px;
background: #e0e0e0;
border-radius: 4px;
overflow: hidden;
}
.accuracy-fill {
height: 100%;
border-radius: 4px;
transition: width 0.5s;
}
.accuracy-fill.high { background: #4CAF50; }
.accuracy-fill.medium { background: #FF9800; }
.accuracy-fill.low { background: #F44336; }
.actions {
margin-top: 20px;
display: flex;
gap: 10px;
}
.btn {
flex: 1;
padding: 12px;
border: none;
border-radius: 8px;
cursor: pointer;
font-size: 14px;
font-weight: 600;
}
.btn-primary { background: #1a73e8; color: white; }
.btn-secondary { background: #e0e0e0; color: #333; }
.btn-secondary:disabled { opacity: 0.5; cursor: not-allowed; }
.precision-selector {
margin-top: 16px;
display: flex;
gap: 8px;
}
.precision-btn {
flex: 1;
padding: 8px;
border: 2px solid #e0e0e0;
background: white;
border-radius: 6px;
cursor: pointer;
font-size: 13px;
text-align: center;
}
.precision-btn.active {
border-color: #1a73e8;
background: #e3f2fd;
color: #1a73e8;
}
</style>
</head>
<body>
<div class="info-card">
<h2>位置信息详情</h2>
<div class="precision-selector">
<div class="precision-btn" onclick="getLocation(false)">低精度</div>
<div class="precision-btn active" onclick="getLocation(true)">高精度</div>
</div>
<div class="coords-grid">
<div class="coord-item">
<div class="coord-label">纬度 Latitude</div>
<div class="coord-value" id="lat">--</div>
</div>
<div class="coord-item">
<div class="coord-label">经度 Longitude</div>
<div class="coord-value" id="lng">--</div>
</div>
<div class="coord-item full-width">
<div class="coord-label">精度 Accuracy</div>
<div class="coord-value" id="accuracy">--</div>
<div class="accuracy-bar"><div class="accuracy-fill" id="accuracyBar"></div></div>
</div>
<div class="coord-item">
<div class="coord-label">海拔 Altitude</div>
<div class="coord-value" id="altitude">--</div>
</div>
<div class="coord-item">
<div class="coord-label">速度 Speed</div>
<div class="coord-value" id="speed">--</div>
</div>
<div class="coord-item">
<div class="coord-label">方向 Heading</div>
<div class="coord-value" id="heading">--</div>
</div>
<div class="coord-item">
<div class="coord-label">获取时间</div>
<div class="coord-value" id="timestamp" style="font-size:14px;">--</div>
</div>
</div>
<div class="actions">
<button class="btn btn-primary" onclick="refreshLocation()">刷新位置</button>
<button class="btn btn-secondary" id="mapBtn" onclick="openMap()" disabled>打开地图</button>
</div>
</div>
<script>
let currentLat = null, currentLng = null;
function getLocation(highAccuracy) {
// 更新精度选择器样式
document.querySelectorAll('.precision-btn').forEach((btn, i) => {
btn.classList.toggle('active', (i === 0 && !highAccuracy) || (i === 1 && highAccuracy));
});
navigator.geolocation.getCurrentPosition(
(position) => displayPosition(position),
(error) => console.error('获取失败:', error),
{
enableHighAccuracy: highAccuracy,
timeout: 15000,
maximumAge: 0
}
);
}
function displayPosition(position) {
const c = position.coords;
currentLat = c.latitude;
currentLng = c.longitude;
document.getElementById('lat').textContent = c.latitude.toFixed(6);
document.getElementById('lng').textContent = c.longitude.toFixed(6);
document.getElementById('accuracy').textContent = c.accuracy.toFixed(0) + ' 米';
document.getElementById('altitude').textContent = c.altitude !== null ? c.altitude.toFixed(0) + ' 米' : 'N/A';
document.getElementById('speed').textContent = c.speed !== null ? c.speed.toFixed(1) + ' m/s' : 'N/A';
document.getElementById('heading').textContent = c.heading !== null ? c.heading.toFixed(0) + '°' : 'N/A';
document.getElementById('timestamp').textContent = new Date(position.timestamp).toLocaleTimeString();
// 精度条
const bar = document.getElementById('accuracyBar');
const pct = Math.min(100, Math.max(5, 100 - (c.accuracy / 10)));
bar.style.width = pct + '%';
bar.className = `accuracy-fill ${c.accuracy < 50 ? 'high' : c.accuracy < 500 ? 'medium' : 'low'}`;
// 启用地图按钮
document.getElementById('mapBtn').disabled = false;
}
function refreshLocation() {
getLocation(true);
}
function openMap() {
if (currentLat && currentLng) {
window.open(`https://www.google.com/maps?q=${currentLat},${currentLng}`);
}
}
// 初始加载
getLocation(true);
</script>
</body>
</html>注意事项
altitude、altitudeAccuracy、heading、speed可能为null,使用前必须检查accuracy始终存在,应作为位置数据可信度的参考- 在室内环境中,GPS 信号弱,精度会显著降低
最佳实践
- 根据场景选择合适的精度:不需要米级精度时不开启高精度
- 设置合理的
timeout避免用户长时间等待 - 利用
maximumAge减少重复定位,节省电量和流量 - 始终检查
accuracy值,过滤低精度结果 - 对
null属性做好降级处理
下一节
继续学习:持续追踪