移动端页面中的定位
本来页面中定位是一件很单纯的事,直接调用HTML5接口就是。
navigator.geolocation.getCurrentPosition(
function (position) {
// ...
},
onerror,
options
);
但是这可能是浏览器的原生接口中最脆弱的一个,会因为各种原因失败,会提供不了足够的信息。然而对于移动web页面来说,定位往往是刚需,原生的接口不能满足需求时,必须有额外的复杂度多出来。所以这里说移动端页面中的定位,不是介绍如何使用HTML5的定位接口,而是讨论一个相对可靠、强大的移动端页面获取位置的解决方案,从提高成功率,提升定位速度以及丰富信息三个角度来说。
提高成功率
HTML5定位接口的成功率往往不高,绝大部分阵亡在请求用户授权上,即使这里通过了,还有一定概率无法获取位置(GPS没开,开了没用,用了没信号,有信号没成功)或者定位超时。所以可以从以下两个角度提高成功率:
-
更多定位手段
- native应用辅助定位
自家的app提供增强功能,当然这个只对自家app装机量高的大厂才有意义。这里有两个思路:
- web服务
app启动一个本地的web服务,监听http请求,提供定位服务;移动端页面通过jsonp方式与本地的web服务器通信,调用定位功能。这个在ios上是行不通的,在android上就很有意义。
- cookie
app定位成功后把结果写入webview的cookie中,这样在webview中打开页面就可以读到位置信息。具体怎么操作不太懂,ios能不能实现也不清楚。
- 服务端定位
服务端定位只能依据IP。WIFI环境下通过IP定位相对靠谱。非wifi环境下,根据运营商提供的IP地址是可以定位到所在城市的,但没法更精确了。另好像某运营商比较坑爹,会一直提供手机号所在城市对应的IP地址;这意味着如果你是(该运营商)上海的手机号,你不管去哪,服务器拿到的你的IP都是上海的。
- 特殊环境中特殊手段
如微信中、百度框中、支付宝服务窗等特殊环境,环境本身提供了定位能力,可以优先使用之。
- cookie共享
以上通过任何方式定位成功的结果可以在cookie中保存起来,短时间内可以重复使用。这是基于用户这段时间内没有位移很多的假设的,所以这个时间间隔不能设置太久,不然就是bug了。
-
引导用户授权共享位置信息
通过各种方式(包括让用户对你的站点持有好感)鼓励用户允许使用浏览器的定位功能。
如果用户点击了拒绝,定位的回调函数中是能捕捉到这个信息的,说服用户另作选择。(这太难了,这个时候浏览器已经记住了用户的选择,即使说服了用户,让用户手动去清楚浏览器中关于本站点的记忆也是很麻烦的,何况不同浏览器的操作方式还不一样)
提升速度
-
灵活组合定位手段
不是所有环境下都应该把所有手段用上,很多手段都可以通过一些简单的判断排除掉。比如IOS下就别尝试native辅助的方式了,不然坐等jsonp请求超时,怎么也得几秒吧。再如上述更多定位手段中的第三点“特殊环境”,是最容易被排除的,一般通过尝试读取某个全局变量就可以马上得出结论。
-
耗时行为并行化
现在你有了不止一个定位手段,不要挨个尝试,读cookie这种同步的行为除外。大部分定位方法,比如HTML5定位,native应用辅助定位,以及微信、支付宝服务窗定位接口等,都是需要耗时的过程。HTML5定位即使在较顺利的情况下都需要几百毫秒来完成。所以把这些需要耗时的手段一起调用起来,哪个先完成(成功的回调函数执行)就用哪个的结果。
丰富信息
HTML5定位是只能获取到当前的经纬度信息的:
{
coords: {
latitude: ...,
longitude: ...,
accuracy: ...
}
}
如果你期望的是这样的:
{
coords: {
latitude: ...,
longitude: ...,
accuracy: ...
},
address: {
city: ...,
district: ...,
street: ...
}
}
就需要通过经纬度确定具体位置的服务(专业术语叫逆地理编码。。),大多数的地图服务会提供。比如 http://api.map.baidu.com/geocoder/v2/?ak=E4805d16520de693a3fe707cdc962045&callback=renderReverse&location=39.983424,116.322987&output=json&pois=1
要注意的是,大多数时候我们会把定位功能封装成一个独立的模块,由需要定位的地方调用。如果这个模块集成了通过第三方地图服务丰富位置信息的功能,这会导致定位行为中多出了一个串行
的ajax步骤(有时候如果你需要的信息不能通过一次请求获取到时,甚至需要更多的ajax请求)。然而这个步骤是非必须的,计算两点的距离(很常见的场景,比如将对象按照离我最近排序),经纬度足以。在非wifi环境下,一次ajax请求的代价是不容忽视的,做好功能的裁剪,或者是通过一个标志所需信息内容的参数,或者是通过更细的模块划分,很有必要。
依照上述的思路实现一个通用的定位模块,其实是个挺考验代码能力的事,并行、串行行为流的维护,错误的捕捉处理,单次周期内多次调用的处理,行为的裁剪,不同级别结果的缓存等,并且要写得清晰易懂才好维护。