学完这篇,你能用浏览器原生的 Geolocation API 拿到用户经纬度,并把它画到地图上,同时知道国内坐标系偏移、授权失败、微信浏览器这些坑该怎么绕。
第一步:先确认三个前提
Geolocation 不是"写个函数就能跑"的 API,它有三个硬前提:
- 必须 HTTPS(
localhost 例外)。HTTP 站点上 Chrome、Safari 会直接拒绝,navigator.geolocation 可能压根不存在。
- 必须用户授权。浏览器会弹一次权限框,用户点了"拒绝"之后,同一域名下不会自动再弹,你只能引导他去浏览器设置里手动清除。
- 先做能力检测:
if (!('geolocation' in navigator)) {
// 降级:手动填写城市,或走服务端 IP 定位
}
第二步:拿到经纬度(最小可用代码)
核心就一个方法:
navigator.geolocation.getCurrentPosition(
(pos) => {
const { latitude, longitude, accuracy } = pos.coords;
console.log(latitude, longitude, accuracy, pos.timestamp);
},
(err) => console.warn(err.code, err.message),
{ enableHighAccuracy: true, timeout: 10000, maximumAge: 60000 }
);
第三个参数是重点:
enableHighAccuracy: true —— 优先用 GPS,更准但更慢更耗电;桌面浏览器基本无效果。
timeout: 10000 —— 毫秒,超时直接报错,别设得太短。
maximumAge: 60000 —— 60 秒内复用缓存结果,避免用户每点一次就重新定位一次。
回调是异步的,别写成同步写法。
第三步:错误码别当黑盒
失败回调里的 err.code 只有三个值,含义和处理方式完全不同:
| code | 含义 | 处理建议 |
|---|
| 1 | 用户拒绝授权 | 提示去浏览器设置开启,或提供手动输入入口 |
| 2 | 位置不可用 | 设备定位关了、信号差、系统限制 |
| 3 | 超时 | 把 enableHighAccuracy 改 false、timeout 放到 20s,重试一次 |
注意:err.message 各浏览器措辞不一样,别拿它做判断,只认 code。
第四步:需要实时跟随时用 watchPosition
导航、打卡轨迹这类场景用 watchPosition,它会在位置变化时持续回调,返回值是监听 ID:
const id = navigator.geolocation.watchPosition(onSuccess, onError, {
enableHighAccuracy: true,
maximumAge: 0
});
// 不用了必须清掉,否则一直耗电
navigator.geolocation.clearWatch(id);
第五步:把坐标画到地图上
想零成本起步,用 Leaflet + OpenStreetMap,不需要任何 Key:
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css">
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script>
<div id="map" style="height:360px"></div>
<script>
const map = L.map('map').setView([latitude, longitude], 16);
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
attribution: '© OpenStreetMap'
}).addTo(map);
L.marker([latitude, longitude]).addTo(map).bindPopup('你在这里').openPopup();
// 把定位误差画成圆,用户一看就懂准不准
L.circle([latitude, longitude], {
radius: accuracy, color: '#2b7', fillOpacity: 0.15
}).addTo(map);
radius: accuracy 这一步很多人省掉,结果用户看到"IP 定位到隔壁城市"以为你的站坏了。
第六步:国内地图必须做坐标纠偏
这是最容易翻车的地方:
注意:浏览器返回的是 WGS-84 坐标,高德/腾讯底图用的是 GCJ-02,百度用的是 BD-09。直接把 WGS-84 坐标丢到高德地图上,会偏移几百米。
三种处理方式:
- 用 OSM 等国外底图 —— 原生 WGS-84,不折腾,但国内地图数据和路网不如高德细。
- 用高德/腾讯 —— 调官方坐标转换能力(高德 Web 服务 API 的
coordinate/convert 接口,或 JS API 的 AMap.convertFrom),别手写近似算法上生产。
- 用百度 —— 走
BMap.Convertor,同样别自己算。
第七步:几个高频翻车点
- 微信内置浏览器:部分安卓机型
getCurrentPosition 直接失败。这种场景要改用微信 JS-SDK 的 wx.getLocation,并且需要在公众号后台配置 JS 安全域名和签名。
- 逆地理编码(坐标转地址):Leaflet 配 Nominatim 免费,但要遵守使用条款(约 1 秒 1 次,必须带 UA 标识);国内地址建议用高德的逆地理接口,准确率高得多。
- 精度别乱承诺:
accuracy 上千说明是 WiFi/IP 定位,只能给到城市级,界面上写"当前位置附近"而不是精确地址。
- 如果你在 Clara BBS 这类带运行时插件钩子的系统上集成:把上面的脚本作为插件资源放进
content/plugins 目录即可,保存即生效,不用改编核心代码。
小结
- 前提是 HTTPS + 用户授权,先做
'geolocation' in navigator 能力检测。
getCurrentPosition 的第三个参数(enableHighAccuracy / timeout / maximumAge)决定体验。
- 错误只认
code:1 拒绝、2 不可用、3 超时,分别降级处理。
- 持续定位用
watchPosition,用完必须 clearWatch。
- 地图起步用 Leaflet + OSM,零 Key;画
radius: accuracy 圆让用户看懂精度。
- 国内底图必须做坐标系转换:WGS-84 → GCJ-02(高德/腾讯)或 BD-09(百度)。