文章目录
前言一、接入API1、登录百度地图2、创建应用,获取密钥3、引入API4、当作模块导入BMap二、使用1、引入2、展示地图三、效果展示总结前言
最近在开发一款react项目,其中有一个小功能是地理位置关键词输入提示。多番考虑下,选择接入百度地图API。在使用的过程中,遇到了一些问题,在此记录并分享一下。
一、接入API
首先,我们需要获得百度地图API的使用权,然后才能在项目中引用。
1、登录百度地图
/
2、创建应用,获取密钥
登录后,点击控制台,选择我的应用,点击创建应用,然后保存密钥。
3、引入API
将百度API的script引入到项目的index.html文件。
<script type="text/javascript" src="http://api./api?v=2.0&ak=你的密钥"></script>
4、当作模块导入BMap
在webpack.config.js的module.exports里添加:
externals:{'BMap':'BMap',}
二、使用
1、引入
在jsx文件中引入BMap:
import BMap from 'BMap'
2、展示地图
由于项目需要实现让用户选择某一商品的地理位置,所以需要根据用户输入的关键词给出位置提示,或者通过用户点击地图选择位置。
这一功能作为 antd 的Form中的一项,限制比较大,但能实现。
(1)设置两个内容块放置输入框和地图
(map组件需要设置长宽)
<div id="l-map"></div><input type="text" id="suggestId" />
如果直接在Form.Item中这样写,则只能显示输入框,map框会被隐藏。所以,使用help属性让<div id="l-map"></div>
显示:
<Form.Item label="地址" name="address"help={<div id='l-map' className='map'>点我</div>}><input type="text" id="suggestId" /></Form.Item>
(2)在组件第一次渲染后初始化地图
componentDidMount(){map = new BMap.Map("l-map");map.centerAndZoom("北京",12); // 初始化地图,设置城市和地图级别。map.enableScrollWheelZoom(true);//让地图能随鼠标滚轮缩放}
这时,界面上已经能显示输入框和地图了。
(3)添加自动完成对象
var ac = new BMap.Autocomplete( //建立一个自动完成的对象{"input" : "suggestId","location" : map});
将map与input绑定。这样,当在输入框输入时,输入框下方会显示此输入的位置提示信息列表。
按照官方教程,当点击列表中的一项,输入框会呈现你的选择。然而,事与愿违,百度地图API的赋值与React不兼容。React中的Input是受控组件,不能直接id赋值。所以这里,就需要想替代办法了。
我的想法是拦截输出并获取提示列表,然后展示列表并添加事件响应。
(4)设计输出
首先,将其原来的输出列表隐藏:
.tangram-suggestion{display: none;}
采用一种野蛮的方法:检查元素后发现包裹列表的组件类名为“tangram-suggestion”,直接在样式中设置隐藏。
其次,获取位置提示列表,并显示在输入框下。
这里可以使用antd的AutoComplete组件,并且将options属性设置为组件state。只要更新state.addressOptions,就能更新选项了。(onSelect方法后文实现)
<AutoComplete id="suggestId"options={this.state.addressOptions}onSelect={this.onSelect}/>
拦截位置提示列表,并赋值给state.addressOptions。修改自动完成的对象如下:
new BMap.Autocomplete( //建立一个自动完成的对象{"input" : "suggestId","location" : map,onSearchComplete: function(e) {let searchResult=[];for(let i=0;i<e.Hr.length;i++){let _value=e.Hr[i];let a=_value.province + _value.city + _value.district + _value.street + _value.business;searchResult.push({value:a});}this.setState({addressOptions:searchResult});}},);
然后,实现AutoComplete的onSelect方法。onSelect方法传递的参数为列表中所选择的那一项的内容。清除地图上所有覆盖物,然后让地图中心移动到选择的位置。
(map为全局变量,后文解释)
onSelect = (data) => {map.clearOverlays();map.centerAndZoom(data,18);};
(5)添加地图点击响应
当点击地图上某一位置,输入框会显示该位置,并且地图中心移动到选择的位置。
由于点击地图只能获取经纬度信息,所以需要通过BMap.Geocoder转换为地理位置。
map.addEventListener('click', function(e){var pt = e.point;var geoc = new BMap.Geocoder();var addComp;geoc.getLocation(pt, (rs)=>{addComp = rs.address;this.uploadRef.current.setFieldsValue({address: addComp});map.clearOverlays();map.centerAndZoom(pt,18);map.addOverlay(new BMap.Marker(pt));}); });
对于输入框赋值,本人尝试了无数的方法,最终找到一种:setFieldsValue()赋值。this.uploadRef为Form的ref属性,地址输入框的name为address。转换后的处理函数需绑定到组件。
(6)移除事件监听
将触发函数提取为函数:
map.addEventListener('click', this.mapClick);
mapClick=(e)=> {var pt = e.point;var geoc = new BMap.Geocoder();var addComp;geoc.getLocation(pt, (rs)=>{addComp = rs.address;this.uploadRef.current.setFieldsValue({address: addComp});map.clearOverlays();map.centerAndZoom(pt,18);map.addOverlay(new BMap.Marker(pt));}); }
此时,为了能在其他函数中使用map,需要将map设置为全局变量(考虑变量作用域)。在组件外声明:
let map;
在componentWillUnmount移除事件。
componentWillUnmount(){// 注销监听事件map.removeEventListener('click', this.mapClick);}
(7)优化——函数防抖
输入框输入后触发自动完成对象,是一个高频函数。为了减轻资源加载的负担,将函数延迟处理,使用debounce()。
import debounce from 'lodash/debounce';
new BMap.Autocomplete( //建立一个自动完成的对象{"input" : "suggestId","location" : map,onSearchComplete: this.searchComplete},);
searchComplete=(e)=> {// console.log(e);let searchResult=[];for(let i=0;i<e.Hr.length;i++){let _value=e.Hr[i];let a=_value.province + _value.city + _value.district + _value.street + _value.business;searchResult.push({value:a});}this.setState({addressOptions:searchResult});}
this.searchComplete = debounce(this.searchComplete, 500);
三、效果展示
React项目使用百度地图API效果展示.mp4
总结
接入百度地图API这一过程完全是参考前辈们的经验,并没有遇到问题。感谢愿意分享的好心人!
在使用过程中,遇到了形形色色的bug:变量作用域的问题、组件自带函数传递参数的问题、赋值的问题。我通常的处理方式是控制台输出,哪里不清楚就输出哪些。
经过不断的探索,最终找到了一种方式实现了地理位置关键词输入提示这一功能。