写在前面
前两天接到了这样一个需求,在ant-design-pro模板项目中使用百度地图,实现关键字检索定位到某点,并在该点标注圆形围栏,且该围栏可编辑(自由改变圆形和半径)。最终效果如下
下面说一下我在开发过程中遇到的坑,这里只说坑,至于申请密匙那些东西,自行百度。
react-bmap?
因项目对接要求,本需求必须使用百度地图(说是对接其他接口时不同地图之间有误差,我没试,暂且信了),于是我惯性的希望找到一个类似于 google-map-react的组件库,于是就有找到了react-bmap,安装使用没什么问题,可是当使用BMapLib
时,发现还得用到其实例,然后写法上再依赖原百度地图API,个人感觉比较混乱,看issue时,发现同样有人在使用BMapLib
是有一些其他问题,给出的解释大概为react-bmap只是对BMAP的基本封装,不涉及那些扩展方法,所以遇到扩展需求啥的还是得获取到map实例后做原生操作,于是个人感觉,好像react-bmap的封装比较基本。但本人本着不放弃的态度决定再尝试一下,于是有了下面这样的代码
import { Map, Marker, Circle, NavigationControl, InfoWindow, ScaleControl, OverviewMapControl } from 'react-bmap'
<Map center={{ lng: 116.402544, lat: 39.928216 }} zoom="11" enableScrollWheelZoom ref={this.getmap} >
{/*this.getmap用来其他操作地图实例用*/}
<Marker position={{ lng: 116.402544, lat: 39.928216 }} />
<NavigationControl />
<ScaleControl />
<Circle
center={{ lng: 116.403119, lat: 39.929543 }}
fillColor="blue"
strokeColor="white"
radius="3000"
enableEditing
/>
<OverviewMapControl />
<InfoWindow position={{ lng: 116.402544, lat: 39.928216 }} text="内容" title="标题" />
</Map>
上面的代码是页面级的组件(我做demo用,实际项目是在弹层中渲染),但是我发现Circle
控件在设置了enableEditing
属性后,虽然出现了可编辑的原型,但组件卸载的时候,总会报出一堆错误,看文档发现enableEditing
是一个方法,不是一个属性,也是需要拿到其实例来操作的。但在查阅文档时发现react-bmap的使用文档几乎没有,于是有点不耐烦了,其正确的使用方法也不想去探究了,于是决定,就使用未封装过的吧。这里不是吐槽哈,react-bmap对于基本需求还是很满足的。
JavaScript Api
- 第一个坑
const { BMap } = window
我们需要从window里面拿到BMap
。再进行其他文档中的操作,看清楚字母大小写!!小编在使用的时候,写成了'BMAP',好几次怀疑自己是否引入了包或使用了正确的密匙,这是一个低级错误。另外就是注意要从window里面取。这里和官网上面的html demo写法不一样。
- 第二个坑,我的需求中不需要用到
BMapLib.DrawingManager
。这是做鼠标自由标注用的,而我的项目中是只做具体的某点的标注。因为对文档的不熟悉和对百度地图功能用途不够了解,导致了错误的方向,这里耽误了我半天的事件去研究,包括对其api的各种尝试,不过明白是怎么回事后才发现自己并不需要它。
3.第三个坑,antd Modal
组件的挂载节点!!!经过前两个坑以后,基本文档已经翻了10多遍了。对文档基本熟悉后,采用了下面的方式实现关键字检索的操作。
componentDidMount(){
const that = this
// 初始化地图
const map = new BMap.Map('container', { enableMapClick: false });
map.centerAndZoom(new BMap.Point(116.404, 39.915), 11); // 初始化地图,设置中心点坐标和地图级别
const point = new BMap.Point(116.404, 39.915); // 设置中心点坐标
map.centerAndZoom(point, 11);
map.enableScrollWheelZoom(true)
map.addControl(new BMap.NavigationControl());
map.addControl(new BMap.ScaleControl());
map.addControl(new BMap.OverviewMapControl());
const ac = new BMap.Autocomplete( // 建立一个自动完成的对象
{
input: document.getElementById('suggestId'),
location: map,
});
ac.addEventListener('onhighlight', e => { // 鼠标放在下拉列表上的事件
let str = '';
let DataValue = e.fromitem.value;
let value = '';
if (e.fromitem.index > -1) {
value = DataValue.province + DataValue.city + DataValue.district + DataValue.street + DataValue.business;
}
str = `FromItem<br />index = ${e.fromitem.index}<br />value = ${value}`
value = '';
if (e.toitem.index > -1) {
DataValue = e.toitem.value;
value = DataValue.province + DataValue.city + DataValue.district + DataValue.street + DataValue.business;
}
str += `<br />ToItem<br />index = ${e.toitem.index}<br />value = ${value}`
const searchResultPanel = document.getElementById('searchResultPanel')
searchResultPanel.innerHTML = str
});
let myValue;
ac.addEventListener('onconfirm', e => { // 鼠标点击下拉列表后的事件
const _value = e.item.value;
myValue = _value.province + _value.city + _value.district + _value.street + _value.business;
document.getElementById('searchResultPanel').innerHTML = `onconfirm<br />index = ${e.item.index}<br />myValue = ${myValue}`;
setPlace();
that.formRef.current.setFieldsValue({ address: myValue })
});
function setPlace() {
map.clearOverlays(); // 清除地图上所有覆盖物
const local = new BMap.LocalSearch(map, { // 智能搜索
onSearchComplete: myFun,
});
function myFun() {
const pp = local.getResults().getPoi(0).point; // 获取第一个智能搜索的结果
map.centerAndZoom(pp, 17);
that.newCircle = new BMap.Circle(pp, 200, { strokeColor: "red", strokeWeight: 2, strokeOpacity: 0.5 }) // 添加标注
map.addOverlay(that.newCircle)
that.newCircle.enableEditing()
}
local.search(myValue);
}
}
render() {
return (
<>
<div id="r-result" style={{ width: '100%', marginBottom: '5px' }}><Input id="suggestId" style={{ width: '300px' }} allowClear placeholder="输入地点搜索..." /></div>
<div id="searchResultPanel" style={{ border: '1px solid #C0C0C0', width: '150px', height: 'auto', display: 'none' }} />
<div id="container" style={{ width: '100%', height: '600px' }} />
</>
)
}
上面的代码不需要详细看,和官网上面的一样,我主要想说的坑是上面的代码在页面组件中运行良好,在弹层组件中竟然会报错!大概是某个实例或者方法没有找到之类的。于是各种debug,各种验证,我发现,在生命周期中,本该通过document.getElementById('suggestId')
获取到各种需要的dom节点,可是Modal组件里面的元素在DidMount生命周期中获取不到,于是思考、翻看API(这里又是俩小时的时间)。我忽然想到这可能和Modal
组件默认挂载于document.body
上有关。导致里面的元素不能在其挂载后立即拿到dom。(这是我猜的),于是,我看antd 文档里面的,将getContainer
设置为false让其挂载到当前节点。终于好了!!!不再报错了。很稳定。
- 第四个坑:自动补全弹层被遮挡!上面的地图加载没有问题后,我开始尝试关键字检索,可是发现搜索结果没有。一开始我是以为自动补全不生效呢(真是一步一个坑),于是对比了demo(页面级别组件)和我的项目(弹层组件),发现和demo中一样,我的项目也是出现了对应的‘检索结果Dom节点’,于是我想,这一定是弹层的
z-index
太大给遮挡了。于是百度后,发现果然有人遇到一样的问题,解决方案就是让搜索结果的弹层区域比Modal
组件的z-index
要高。于是我在react的全局样式文件中加这样一段css
.tangram-suggestion-main {z-index: 9999999999;}
因为z-index
有最大值,干脆一顿9,超出最大值,直接让他取默认最大值了,这里偷懒了,没有取具体的最大值,感兴趣的可以自己看下。经过上面的样式修改,关键字检索算基本完事了。
这个时候对文档已经相当熟悉了,一路按照需求开发,可是又又又又遇到坑了。
- 第五坑。这个坑不知道该叫什么名字了,没兴趣的小伙伴可以撤了。因为和我的业务就有些相关了。我业务中要求编辑圆形,表单的值也得跟着变,表单中输入半径,圆的大小也会跟着变。当时思考了一会该监听什么样的事件去获取当前圆的信息,又是翻文档,http://lbsyun.baidu.com/cms/jsapi/reference/jsapi_reference_3_0.html#a3b16
找到了对应的事件,到这里基本可以说没啥问题了,不过后面还是发现一个bug。在我输入表单半径利用Circle
类的setRadius
方法改变半径后,却发现,我的圆形的拖动点不好使了。点能拖动,可是圆缺不跟着变,这个时候代码手感真好,于是想到解决方案,在编辑表单设置圆的半径后禁用掉其编辑功能待我们鼠标划入到圆形中再开启,于是就有了下面的解决方案
//表单值改变触发
radiusChange = (e) => {
const arr = {...this.newCircle}
const newArr = Object.keys(arr)
if(newArr.length === 0){
// 判断当前是否有具体地点
message.warning('请先选择地点')
}else {
this.newCircle.disableEditing()
this.newCircle.setRadius(e)
}
}
// didmount生命周期中的圆标记的处理方法中
that.newCircle.addEventListener('lineupdate', debounce((target) => {
that.CirclePoint = target.target.point
that.formRef.current.setFieldsValue({ radius: parseInt(target.target.Ca, 10) })
}, 200))
that.newCircle.addEventListener('mouseover', debounce(() => {
that.newCircle.enableEditing()
that.inputnum.blur() //不知道为啥这里需要半径输入框脱标处理,是写的过程中发现的,脱标处理后就好了。算个小bug吧,注意一下
}, 200))
-
第六坑,在搜索后,有很大几率出现圆的编辑圆形和地图地标详情重合的情况,如下图
导致圆形没法拖动,这里的解决办法是,我们在初始化地图时设置
const map = new BMap.Map('container', { enableMapClick: false });
即enableMapClick: false
即可,这样就禁用掉了详情按钮。小坑一个。也记下来。
写在最后
经过上面一天多的踩坑,原本的需求基本也就实现了。核心代码也已经在上面了,有使用的同学,做好debug,把漏掉的代码补上,基本都是可以正常运行的。