百度地圖基於react實現
背景需求
在某一次需求中被要求,研發一個地圖頁面,能夠具備搜尋定位,自動定位,地圖與標記拖移的功能,並且要求使用百度地圖研發。
因為專案使用的是react,因為習慣性的搜尋react百度地圖,現網其實有成熟的元件,但是使用一遍下來,感覺有兩種問題:
1: 通過文件翻閱,元件無法滿足需求,例如搜尋定位,拖動地圖中心點標記不動
2: 元件庫似乎還不完善,有時會出現無法載入地圖
最終決定還是使用sdk進行原生的研發
開發流程
一、sdk引入
因為使用react,必不可少的使用元件庫,包括引入sdk,這裡因為不想影響其他頁面,加上是ahooks的粉絲,我採用的是useExternal去動態注入js資源,這樣子保證全域性的js唯一性。
import { useExternal } from 'ahooks'
useExternal("//api.map.baidu.com/api?v=2.0&ak=你的金鑰";
二、建立百度地圖例項
在引入sdk之後,根據sdk文件我們需要建立一個地圖例項。
注意我們所需要實現的需求:
1: 建立地圖
2: 初始化地圖能夠根據當前位置進行定位並展示
3: 地圖具備搜尋
4: 標記位於中心不可移動,拖動地圖獲取標記經緯度
1:建立地圖
建立地圖需要注意,sdk的地圖例項物件需要繫結對應id的div,且該div必須具備有寬度與高度,才能正確建立地圖
<div id="container"></div> const map = new BMapGL.Map('container'); // 建立Map例項 map.centerAndZoom(new BMapGL.Point(116.404, 39.915), 18); // 初始化地圖,設定中心點座標和地圖級別
注:此時最好將該map物件存起來,後續在地圖上進行其他操作時可以更便捷
2:根據當前位置進行定位並展示中心點
此時我們需要將地圖的中心點設定為我們當前的位置,在此就遇到許多坑了,先貼上官方的方案
// 獲取當前定位並且設定中心點 const geolocation = new BMapGL.Geolocation(); geolocation.enableSDKLocation()// 開啟sdk輔助定位,適用於weiview geolocation.getCurrentPosition((r: any) => { // 設定當前城市用以搜尋使用 setCity(r.address.city) const { lat, lng } = r.point map.panTo({ lat, lng }, {})// 定位至中心點 setCenterPoint({ lat, lng })
此時利用百度api的Geolocation物件,我們可以獲取到當前位置的經緯度並且定位地圖,但是在此會存在經緯度偏差較大的問題,後面上網查詢大致原因,總結了一下
百度的定位方法會優先使用H5的navigator.geolocation.getCurrentPosition
進行原生gps定位並且內部轉化為百度座標系。這裡需要注意,百度使用的是百度座標系,其他例如原生方法,高德地圖等獲取的是國測局座標系,若使用原生則需要將gps座標呼叫百度的Convertor方法轉化為百度座標才可在地圖中展示正常,方法如下
navigator.geolocation.getCurrentPosition((position) => {
const lat = position?.coords?.latitude;
const lng = position?.coords?.longitude;
console.log({ position, lat, lng })
const convertor = new BMap.Convertor();
const pointArr = [];
pointArr.push({ lat, lng });
convertor.translate(pointArr, 1, 5, (data) => {
const { pointArrData = [] } = data
const point = {
lat: pointArrData[0].lat,
lng: pointArrData[0].lng
}
})
}, (err) => console.log({ err }));
而原生的navigator.geolocation
,谷歌已經對非https
協議的網站禁用了該功能,因此開發環境的定位有時會使用ip定位,此時的經緯度就與當前所在位置產生偏差。這也就是為什麼百度的定位方法會導致定位不準確的原因
解決方法網上說的是使用百度介面進行定位,但我自己掉了這些介面依舊是不準確的,個人覺得最優解便是試一試原生navigator.geolocatione
的經緯度轉為百度座標系
3: 地圖具備搜尋
這裡選擇最為簡便的方式,百度似乎有提供介面進行資訊的請求,這個後續可能回去做,而緊急需求只採用了最為簡便易行的方式,但就是這種方式依舊遇到了坑,引用百度文件裡的方法,將搜尋物件與一個input框進行繫結,根據input裡的值進行展示關聯地址名稱,通過Autocomplete
物件繫結確認事件可以將地址列表的某一項點選做操作,例如展示到地圖中心
<div id="r-result">
<input className={styles.inputCss} type="text" id="suggestId" size={24} placeholder='請輸入查詢地址' />
</div>
const ac = new BMap.Autocomplete( // 建立一個自動完成的物件,suggestId為input的id,city是當前城市名稱,也可以設定經緯度
{
"input": "suggestId",
"location": city
});
ac.addEventListener("onconfirm", (e: any) => { // 滑鼠點選下拉列表後的事件
const { value } = e.item;
myMap.clearOverlays();
const myValue = value.province + value.city + value.district + value.street + value.business;
// 搜尋呼叫
const myFun = (resoult: any, local: any) => {
const pp = local.getResults().getPoi(0).point; // 獲取第一個智慧搜尋的結果
// 建立圖示標記
const marker = new BMapGL.Marker(pp, {
enableDragging: true,
});
myMap.addOverlay(marker);
setCenterPoint(pp)
myMap.centerAndZoom(pp, 18);
}
const local = new BMap.LocalSearch(city)
local.search(myValue, { forceLocal: true });
local.setSearchCompleteCallback((i: any) => myFun(i, local));
});
其實主要用到的是百度的LocalSearch
物件進行搜尋,但是這裡會遇到一個問題,按照下面百度文件提供的方法,會存在無法取得搜尋成功後點擊對應地址的回撥
var local = new BMap.LocalSearch(map, { //智慧搜尋
onSearchComplete: myFun
});
我發現這裡的onSearchComplete
會無法執行,但是將該程式碼放在html檔案中是正常的,也就是百度文件的例子,不知道是不是sdk在react中執行報錯
因此改用了下面的回撥呼叫
local.setSearchCompleteCallback((i: any) => myFun(i, local));
這樣就可以成功執行回調了
4: 標記位於中心不可移動,拖動地圖獲取標記經緯度
其實方案就是將一個img定位放在頁面中心,獲取經緯度則在map物件的拖動結束事件dragend
中用百度map物件提供的map.getCenter()
獲取,實際操作誤差不超過10m
頁面耗時1d完成,主要都在解決定位不準確的問題,其實到現在也沒有很明確的方案,似乎一切問題到了生產的https就可以解決,但因為還沒上線
,還有待商榷