pin_drop当前位置:知识文库 ❯ 图文

HTML5 API:HTML5 Geolocation API - 完整教程与代码示例

一、教程简介

Geolocation API 是 HTML5 提供的一组用于获取设备地理位置信息的接口。它允许 Web 应用在用户授权的前提下,获取设备的经纬度坐标、海拔、速度等信息。该 API 广泛应用于地图导航、位置签到、附近搜索、天气查询等场景,是构建位置感知型 Web 应用的核心基础。


二、核心概念

位置来源

Geolocation API 的位置信息可能来自以下来源(按精度从高到低排列):

  • GPS(全球定位系统):精度最高,通常在 10 米以内,但耗电较大且在室内信号弱

  • Wi-Fi 定位:通过附近 Wi-Fi 热点的 MAC 地址进行定位,精度较高

  • 蜂窝基站定位:通过移动通信基站进行定位,精度一般

  • IP 地址定位:精度最低,通常只能定位到城市级别

权限模型

Geolocation API 采用严格的权限控制机制:

  • 首次调用时会弹出权限请求提示

  • 用户可以选择允许、拒绝或忽略

  • 用户可以设置“始终允许”或“仅本次允许”

  • 权限被拒绝后,后续调用将直接触发错误回调

核心对象

所有 Geolocation API 的方法都挂载在 navigator.geolocation 对象上:

属性/方法 说明
getCurrentPosition() 获取当前位置
watchPosition() 持续监听位置变化
clearWatch() 停止监听位置变化

Position 对象

成功获取位置后,回调函数会接收一个 Position 对象:

属性 说明
coords.latitude 纬度(十进制)
coords.longitude 经度(十进制)
coords.altitude 海拔高度(米)
coords.accuracy 位置精度(米)
coords.altitudeAccuracy 海拔精度(米)
coords.heading 移动方向(度)
coords.speed 移动速度(米/秒)
timestamp 获取位置的时间戳

三、语法与用法

getCurrentPosition()

获取设备的当前地理位置。

代码示例

navigator.geolocation.getCurrentPosition(successCallback, errorCallback, options);

参数说明:

参数 类型 必填 说明
successCallback Function 成功获取位置时的回调函数
errorCallback Function 获取位置失败时的回调函数
options Object 配置选项

options 配置项:

属性 类型 默认值 说明
enableHighAccuracy Boolean false 是否请求高精度位置
timeout Number Infinity 请求超时时间(毫秒)
maximumAge Number 0 缓存位置的最大有效期(毫秒)

watchPosition()

注册一个位置监听器,当设备位置发生变化时自动调用回调函数。

代码示例

const watchId = navigator.geolocation.watchPosition(successCallback, errorCallback, options);

返回一个 watchID,用于后续取消监听。

clearWatch()

停止由 watchPosition() 注册的位置监听器。

代码示例

navigator.geolocation.clearWatch(watchId);

四、代码示例

示例一:基础位置获取

代码示例

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Geolocation API - 基础位置获取</title>
    <style>
        * { margin: 0; padding: 0; box-sizing: border-box; }
        body {
            font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
            min-height: 100vh;
            display: flex;
            justify-content: center;
            align-items: center;
            padding: 20px;
        }
        .container {
            background: #fff;
            border-radius: 16px;
            padding: 40px;
            max-width: 520px;
            width: 100%;
            box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
        }
        h1 { font-size: 24px; color: #333; margin-bottom: 8px; }
        .subtitle { color: #888; font-size: 14px; margin-bottom: 24px; }
        .btn-group { display: flex; gap: 12px; margin-bottom: 24px; }
        button {
            flex: 1; padding: 12px 20px; border: none; border-radius: 8px;
            font-size: 15px; font-weight: 600; cursor: pointer;
        }
        .btn-primary { background: linear-gradient(135deg, #667eea, #764ba2); color: #fff; }
        .btn-secondary { background: #f0f0f0; color: #555; }
        .result-card { background: #f8f9fa; border-radius: 12px; padding: 20px; margin-top: 16px; }
        .result-item { display: flex; justify-content: space-between; padding: 10px 0; border-bottom: 1px solid #eee; }
        .result-item:last-child { border-bottom: none; }
        .hidden { display: none; }
    </style>
</head>
<body>
    <div class="container">
        <h1>Geolocation 位置获取</h1>
        <p class="subtitle">点击按钮获取您当前的地理位置信息</p>
        <div class="btn-group">
            <button onclick="getLocation()">获取当前位置</button>
            <button onclick="getHighAccuracyLocation()">高精度定位</button>
        </div>
        <div id="resultCard" class="result-card hidden">
            <div class="result-item"><span>纬度</span><span id="latitude">--</span></div>
            <div class="result-item"><span>经度</span><span id="longitude">--</span></div>
            <div class="result-item"><span>精度</span><span id="accuracy">--</span></div>
            <div class="result-item"><span>海拔</span><span id="altitude">--</span></div>
            <div class="result-item"><span>速度</span><span id="speed">--</span></div>
        </div>
    </div>
    <script>
        function displayPosition(pos) {
            document.getElementById('resultCard').classList.remove('hidden');
            document.getElementById('latitude').textContent = pos.coords.latitude.toFixed(6) + '°';
            document.getElementById('longitude').textContent = pos.coords.longitude.toFixed(6) + '°';
            document.getElementById('accuracy').textContent = pos.coords.accuracy.toFixed(1) + ' 米';
            document.getElementById('altitude').textContent =
                pos.coords.altitude !== null ? pos.coords.altitude.toFixed(1) + ' 米' : '不可用';
            document.getElementById('speed').textContent =
                pos.coords.speed !== null ? pos.coords.speed.toFixed(1) + ' m/s' : '不可用';
        }
        function handleError(error) {
            const msgs = { 1: '用户拒绝', 2: '位置不可用', 3: '请求超时' };
            alert(msgs[error.code] || '未知错误');
        }
        function getLocation() {
            navigator.geolocation.getCurrentPosition(displayPosition, handleError, {
                enableHighAccuracy: false, timeout: 10000, maximumAge: 0
            });
        }
        function getHighAccuracyLocation() {
            navigator.geolocation.getCurrentPosition(displayPosition, handleError, {
                enableHighAccuracy: true, timeout: 15000, maximumAge: 0
            });
        }
    </script>
</body>
</html>

示例二:实时位置追踪(watchPosition)

代码示例

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Geolocation API - 实时位置追踪</title>
    <style>
        body { font-family: "Microsoft YaHei", sans-serif; background: #1a1a2e; padding: 20px; }
        .container { background: #16213e; border-radius: 16px; padding: 36px; max-width: 600px; margin: 0 auto; }
        h1 { font-size: 22px; color: #e0e0e0; margin-bottom: 6px; }
        .controls { display: flex; gap: 12px; margin: 20px 0; }
        button { padding: 10px 24px; border: none; border-radius: 8px; font-size: 14px; cursor: pointer; }
        .btn-start { background: linear-gradient(135deg, #00b894, #00cec9); color: #fff; }
        .btn-stop { background: linear-gradient(135deg, #e17055, #d63031); color: #fff; }
        button:disabled { opacity: 0.5; cursor: not-allowed; }
        .stats { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
        .stat-card { background: #0f3460; border-radius: 10px; padding: 16px; text-align: center; }
        .stat-label { color: #7a8ba0; font-size: 12px; }
        .stat-value { color: #00cec9; font-size: 18px; font-weight: 700; }
    </style>
</head>
<body>
    <div class="container">
        <h1>实时位置追踪</h1>
        <div class="controls">
            <button id="startBtn" class="btn-start" onclick="startTracking()">开始追踪</button>
            <button id="stopBtn" class="btn-stop" onclick="stopTracking()" disabled>停止追踪</button>
        </div>
        <div class="stats">
            <div class="stat-card"><div class="stat-label">纬度</div><div class="stat-value" id="lat">--</div></div>
            <div class="stat-card"><div class="stat-label">经度</div><div class="stat-value" id="lng">--</div></div>
            <div class="stat-card"><div class="stat-label">精度</div><div class="stat-value" id="acc">--</div></div>
            <div class="stat-card"><div class="stat-label">更新次数</div><div class="stat-value" id="count">0</div></div>
        </div>
    </div>
    <script>
        let watchId = null, updateCount = 0;
        function startTracking() {
            document.getElementById('startBtn').disabled = true;
            document.getElementById('stopBtn').disabled = false;
            watchId = navigator.geolocation.watchPosition(function(pos) {
                updateCount++;
                document.getElementById('lat').textContent = pos.coords.latitude.toFixed(6) + '°';
                document.getElementById('lng').textContent = pos.coords.longitude.toFixed(6) + '°';
                document.getElementById('acc').textContent = pos.coords.accuracy.toFixed(1) + ' m';
                document.getElementById('count').textContent = updateCount;
            }, function(err) { alert('定位失败:' + err.message); stopTracking(); },
            { enableHighAccuracy: true, timeout: 15000, maximumAge: 0 });
        }
        function stopTracking() {
            if (watchId !== null) { navigator.geolocation.clearWatch(watchId); watchId = null; }
            document.getElementById('startBtn').disabled = false;
            document.getElementById('stopBtn').disabled = true;
        }
    </script>
</body>
</html>

示例三:距离计算与位置周边搜索

代码示例

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Geolocation API - 距离计算</title>
    <style>
        body { font-family: "Microsoft YaHei", sans-serif; background: #f5f7fa; padding: 40px 20px; }
        .container { max-width: 640px; margin: 0 auto; }
        h1 { font-size: 28px; color: #2d3436; }
        .card { background: #fff; border-radius: 12px; padding: 24px; margin: 20px 0; box-shadow: 0 2px 12px rgba(0,0,0,0.08); }
        input { width: 100%; padding: 10px; border: 1px solid #ddd; border-radius: 8px; font-size: 14px; margin: 4px 0; }
        button { padding: 10px 24px; border: none; border-radius: 8px; font-size: 14px; cursor: pointer; margin-right: 8px; }
        .btn-blue { background: #0984e3; color: #fff; }
        .btn-green { background: #00b894; color: #fff; }
        .result-box { margin-top: 16px; padding: 16px; background: #f8f9fa; border-radius: 8px; }
    </style>
</head>
<body>
    <div class="container">
        <h1>Geolocation 距离计算</h1>
        <div class="card">
            <h2>两点距离计算</h2>
            <input type="text" id="lat1" placeholder="起点纬度" value="39.9042">
            <input type="text" id="lng1" placeholder="起点经度" value="116.4074">
            <input type="text" id="lat2" placeholder="终点纬度" value="31.2304">
            <input type="text" id="lng2" placeholder="终点经度" value="121.4737">
            <button class="btn-blue" onclick="calcDistance()">计算距离</button>
            <button class="btn-green" onclick="useMyLocation()">使用我的位置</button>
            <div id="distanceResult" class="result-box" style="display:none;"></div>
        </div>
    </div>
    <script>
        function haversineDistance(lat1, lng1, lat2, lng2) {
            const R = 6371;
            const dLat = (lat2 - lat1) * Math.PI / 180;
            const dLng = (lng2 - lng1) * Math.PI / 180;
            const a = Math.sin(dLat/2)**2 + Math.cos(lat1*Math.PI/180) * Math.cos(lat2*Math.PI/180) * Math.sin(dLng/2)**2;
            return R * 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
        }
        function calcDistance() {
            const dist = haversineDistance(+document.getElementById('lat1').value, +document.getElementById('lng1').value, +document.getElementById('lat2').value, +document.getElementById('lng2').value);
            const el = document.getElementById('distanceResult');
            el.style.display = 'block';
            el.innerHTML = '直线距离:' + dist.toFixed(2) + ' 千米(' + (dist*1000).toFixed(0) + ' 米)';
        }
        function useMyLocation() {
            navigator.geolocation.getCurrentPosition(function(pos) {
                document.getElementById('lat1').value = pos.coords.latitude.toFixed(6);
                document.getElementById('lng1').value = pos.coords.longitude.toFixed(6);
            }, function(err) { alert('获取位置失败:' + err.message); });
        }
    </script>
</body>
</html>

五、浏览器兼容性

浏览器 支持版本 备注
Chrome 5+ 完整支持
Firefox 3.5+ 完整支持
Safari 5+ 完整支持
Edge 12+ 完整支持
Opera 10.6+ 完整支持
IE 9+ 部分支持,IE9 开始支持
iOS Safari 3.2+ 完整支持
Android Browser 2.1+ 完整支持

提示:在现代浏览器中,Geolocation API 仅在 HTTPS 环境下可用(localhost 除外)。HTTP 环境下调用会被直接拒绝。


六、注意事项与最佳实践

1. 隐私与权限

  • 始终在用户交互后请求位置:不要在页面加载时自动请求,应在用户点击按钮等操作后触发

  • 解释为什么需要位置:在请求位置前向用户说明用途,提高授权率

  • 处理拒绝情况:优雅地处理用户拒绝授权的情况,提供手动输入位置的替代方案

  • 不要存储位置数据:除非用户明确同意,否则不要将位置数据发送到服务器或存储

2. 性能优化

  • 避免频繁调用getCurrentPosition 有一定耗时,不要在短时间内反复调用

  • 合理设置 maximumAge:如果位置精度要求不高,可以设置缓存时间减少 GPS 调用

  • 及时清除 watchPosition:不再需要追踪时务必调用 clearWatch,避免持续耗电

  • 谨慎使用 enableHighAccuracy:高精度模式耗电量大,仅在必要时启用

3. 错误处理

  • 始终提供错误回调:不要忽略错误处理,用户拒绝授权是常见场景

  • 设置合理的超时时间:避免用户长时间等待无响应

  • 提供降级方案:当定位不可用时,可以基于 IP 定位或让用户手动选择


七、代码规范示例

代码示例

// 推荐:封装 Geolocation 工具类
class GeolocationHelper {
    constructor(options = {}) {
        this.defaultOptions = {
            enableHighAccuracy: false,
            timeout: 10000,
            maximumAge: 0,
            ...options
        };
    }

    isSupported() {
        return 'geolocation' in navigator;
    }

    getCurrentPosition(options = {}) {
        return new Promise((resolve, reject) => {
            if (!this.isSupported()) {
                reject(new Error('Geolocation API 不可用'));
                return;
            }
            const mergedOptions = { ...this.defaultOptions, ...options };
            navigator.geolocation.getCurrentPosition(
                (position) => resolve(position),
                (error) => reject(this._formatError(error)),
                mergedOptions
            );
        });
    }

    watchPosition(onSuccess, onError, options = {}) {
        if (!this.isSupported()) {
            onError(new Error('Geolocation API 不可用'));
            return null;
        }
        const mergedOptions = { ...this.defaultOptions, ...options };
        return navigator.geolocation.watchPosition(
            onSuccess,
            (error) => onError(this._formatError(error)),
            mergedOptions
        );
    }

    clearWatch(watchId) {
        if (watchId !== null) {
            navigator.geolocation.clearWatch(watchId);
        }
    }

    _formatError(error) {
        const messages = { 1: '用户拒绝了位置请求', 2: '位置信息不可用', 3: '请求超时' };
        const message = messages[error.code] || '未知错误';
        const err = new Error(message);
        err.code = error.code;
        err.originalError = error;
        return err;
    }

    static getDistance(lat1, lng1, lat2, lng2) {
        const R = 6371;
        const dLat = (lat2 - lat1) * Math.PI / 180;
        const dLng = (lng2 - lng1) * Math.PI / 180;
        const a = Math.sin(dLat / 2) ** 2 +
            Math.cos(lat1 * Math.PI / 180) * Math.cos(lat2 * Math.PI / 180) *
            Math.sin(dLng / 2) ** 2;
        return R * 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
    }
}

// 使用示例
const geo = new GeolocationHelper({ timeout: 8000 });

async function getUserLocation() {
    try {
        const pos = await geo.getCurrentPosition({ enableHighAccuracy: true });
        console.log('纬度:', pos.coords.latitude);
        console.log('经度:', pos.coords.longitude);
        return pos;
    } catch (err) {
        console.error('定位失败:', err.message);
        return null;
    }
}

八、常见问题与解决方案

常见问题

为什么在本地开发时可以获取位置,部署后却失败?

原因:现代浏览器要求 Geolocation API 必须在安全上下文(HTTPS)中使用。
解决方案:将网站部署到 HTTPS 环境;开发环境使用 localhost 访问(被视为安全上下文)。

getCurrentPosition 没有任何响应,既不成功也不失败?

原因:可能是用户没有对权限弹窗做出选择(直接关闭了弹窗),某些浏览器不会触发回调。
解决方案:添加超时保护,使用 setTimeout 在 15 秒后显示降级 UI。

高精度定位在室内无法获取?

原因:GPS 信号在室内非常弱,无法提供高精度定位。
解决方案:先使用低精度快速获取大致位置,再尝试高精度更新;结合 Wi-Fi 定位提高室内精度;提供手动调整位置的功能。

watchPosition 在 iOS Safari 上不持续更新?

原因:iOS Safari 对后台位置更新有严格限制,页面不在前台时会暂停更新。
解决方案:在页面可见性变化时重新启动监听;使用 Page Visibility API 配合处理;提醒用户保持页面在前台。

如何计算两个坐标之间的距离?

解决方案:使用 Haversine 公式,基于地球半径(6371千米)和两点的经纬度差值计算大圆距离。公式为 a = sin^2(dLat/2) + cos(lat1) * cos(lat2) * sin^2(dLng/2),距离 d = R * 2 * atan2(sqrt(a), sqrt(1-a))


九、总结

Geolocation API 是构建位置感知型 Web 应用的核心工具。通过 getCurrentPosition 获取一次性位置,通过 watchPosition 实现持续追踪,配合 Haversine 公式等算法可以实现丰富的位置相关功能。使用时需特别注意隐私保护、HTTPS 要求和错误处理,确保在各种环境下都能提供良好的用户体验。合理设置 enableHighAccuracytimeoutmaximumAge 参数,可以在精度和性能之间取得最佳平衡。

  • getCurrentPosition:一次性获取设备当前位置

  • watchPosition:持续监听位置变化,适合轨迹追踪

  • clearWatch:停止位置监听,释放资源

  • HTTPS 要求:生产环境必须使用 HTTPS

  • 隐私保护:在用户交互后请求位置,说明用途

  • Haversine 公式:计算两点间的球面距离

标签: Geolocation HTML5 API 位置定位 GPS定位 距离计算 位置追踪

本文涉及AI创作

内容由AI创作,请仔细甄别

list快速访问

上一篇: 多媒体:SVG与Canvas对比 - 完整教程与代码示例 下一篇: HTML5 API:HTML5 Drag and Drop API - 完整教程与代码示例

poll相关推荐