那些被忽略但很好用的 Web API / Geolocation

我的字典里没有放弃,因为已锁定你

现在有不少网站都有地图相关的功能,而为了解决地图绘制、路线运算、区域标示...等等复杂的功能,通常大家都会选用第三方的套件或服务,例如 Google Map API、Leaflet、MapBox 之类的,也因为有了这些好用的地图套件,所以浏览器本身的 Geolocation API 可能就容易被遗忘。


Geolocation

当然,Geolocation API 并没有上述那些套件的强大功能,它能做的就是取得使用者目前的地理座标位置,不过,要是你的功能并没有那麽复杂,单单只是想知道用户位置的话,其他大可不用杀鸡焉用牛刀,Geolocation API 就可以满足你了。

 

# Navigator.geolocation

Geolocation 的支援度是非常高的,电脑、手机,各家浏览器基本上都是可以使用的,不过在使用前你依然可以先进行检查,若浏览器支援的话,只要透过 navigator.geolocation 就能取得 Geolocation 实体。

if ("geolocation" in navigator) {
  console.log(navigator.geolocation);
} else {
  alert("你的装置或浏览器不支援定位功能");
}

 

# Geolocation.getCurrentPosition

有了 Geolocation 实体後,就可以用 getCurrentPosition method 来取得座标位置了,该方法有三个参数,其中第一个为必传,後两个为选填:

  • success: 一个回呼函式,会在成功取得位置资讯时触发,该函式会接到一个 Position 物件。
  • error: 一个回呼函式,会在方法发生错误时触发,该函式则会接到 PositionError 物件。
  • options: 一个物件,其中的属性可以用来设定获取位置时的规则。

参数 options 的详细属性:

  • enableHighAccuracy: 一个布林值,决定是否要以最高精准度来取得座标位置,预设为 false
  • timeout: 一个代表毫秒数的正数,规定设备必须要在多少时间内回应位置资讯,预设为 Infinity
  • maximumAge: 一个代表毫秒数的正数,表示可以接受多少毫秒以前的暂存位置,预设为 0

注意: 若将 enableHighAccuracy 打开,位置资讯的回传时间将会变长,且可能会使装置消耗更多电量。

function successHandler(position) {
  console.log(position);
}

function errorHandler(err) {
  console.log(err);
}

navigator.geolocation.getCurrentPosition(successHandler, errorHandler, {
  enableHighAccuracy: true,
  timeout: 5000,
  maximumAge: 0,
});

由於定位资讯属於隐私范围,所以在呼叫 getCurrentPosition 时,它会先确认装置的授权状态,若使用者不接受则会发生错误,若使用者未表明授权与否,则会出现询问对话框。

https://ithelp.ithome.com.tw/upload/images/20211014/201254318lwyENtkXv.png

 

# Geolocation.watchPosition

除了 getCurrentPosition 可以拿到执行当下的使用者定位之外,还有另外一个 method 是 watchPosition,它的功能与参数都与 getCurrentPosition 相同,差别是它会在使用者的位置发生改变时主动触发 Success CallBack,等於是在监听使用者的定位。

function successHandler(position) {
  console.log(position);
}

function errorHandler(err) {
  console.log(err);
}

// watchPosition 执行後会回传一个独一的 ID
const geoId = navigator.geolocation.watchPosition(successHandler, errorHandler);

 

# Geolocation.clearWatch

在上方 watchPosition 的范例中,我们用 geoId 来接它丢出来 ID 编号,我们可以将编号传入 clearWatch method 中,便可以使对应的 watchPosition 停止监听使用者定位。

const geoId = navigator.geolocation.watchPosition(successHandler, errorHandler);
navigator.geolocation.clearWatch(geoId);

 

# Position 物件

就如前面所说,Success CallBack 在成功获取位置资讯时会被触发,并且可以拿到 Position 物件,其中就包含了许多与地理位置相关的属性可以使用:

  • position.coords.longitude: 使用者所在位置的经度。
  • position.coords.latitude: 使用者所在位置的纬度。
  • position.coords.accuracy: 回传经纬度的水平误差(平面距离),单位为公尺。
  • position.coords.altitude: 使用者所在位置的海拔高度,单位为公尺。
  • position.coords.altitudeAccuracy: 回传高度的垂直误差(垂直高度),单位为公尺。
  • position.coords.heading: 使用者面向的方位,会以顺时针相对於正北方的夹角角度呈现。
  • position.coords.speed: 使用者面对的数度,单位为公尺/秒。

注意: 以上属性均为浮点数,部分属性会在装置无法提供时回传 null

 

# PositionError 物件

另一方面,Error CallBack 则会在发生错误时触发,并取得 PositionError 物件,该物件中的 code 属性将会告知我们目前的错误是何种类型及原因:

错误代号 错误名称 解释
1 PERMISSION_DENIED 没有获取装置定位的权限
2 POSITION_UNAVAILABLE 位置资讯获取错误
3 TIMEOUT 在 Timeout 前未取得定位资讯

 

# 简单的小范例

有了 Geolocation API 之後,我们就可以制作一些与位置有关的简单小功能,像下方的范例就是取得使用者位置後,将经纬度丢给後端来计算距离最近的门市,然後将门市资讯提供给用户。

<button onclick="getPosition()">搜寻最近的门市</button>
<div id="result"></div>

<script>
  const result = document.querySelector("#result");
  function successHandler(position) {
    const { longitude, latitude } = position.coords;

    // 取得经纬度後传给後端进行门市的搜寻
    axios.get("https://backend/store", { longitude, latitude }).then((res) => {
      const store = res.data;
      result.innerHTML = "离你最近的门市是" + store.name + "地址: " store.address;
    });
  }

  function errorHandler(err) {
    alert("暂时无法取得您的所在位置,请稍後再试");
  }

  function getPosition() {
    navigator.geolocation.getCurrentPosition(successHandler, errorHandler);
  }
</script>

 

每每说到装置定位、座标时,大家第一个想到的可能都是 Google Map 或其他地图套件,但其实浏览器本身就有 Geolocation API 可以帮我们处理简单的定位功能。所以要是你的功能未必要显示地图的话,其实你可以选择使用它喔。


<<:  [Day29] Windows Privilege Escalation

>>:  追求JS小姊姊系列 Day29 -- 方函式的能力展现:最後型态`async`

[Day03] Flutter GetX equatable

目前先和大家介绍一些基本的应用 大概Day15~16後会开始结合GetX 一般比较两个 基本型别是否...

Day 15:目前 NOJ 的部署流程

本来今天是要讲完 Grafana 的部分,不过我发现我还是来不及做完...目前的进度只有下面这样,我...

【从零开始的Swift开发心路历程-Day14】打造自己的私房美食名单Part3(完)

昨天已经能让TableViewCell显示餐厅资料了 但....好像有点单调,让我们来加点餐厅的图片...

[Day 26] 从 AsyncPipe 出发,微探讨 Angular 处理 pipe 的流程

昨天介绍了 AsyncPipe 的用法以及它可以带来的便利,今天要来看一下在这方便的背後是由那些东西...

【从零开始的 C 语言笔记】第十三篇-Array介绍与应用(1)

不怎麽重要的前言 上一篇我们介绍了一个满好用的函式库,只要知道怎麽使用math.h的很多函式,都可以...