最近遇到一个需求改造,大概是要实现一个手机H5页面内只展示一个省份,以及省份内的市区,只需要显示市区名称,当点击对应市区的时候展示业务相关信息。以陕西为例,大概是下面这个样子:
废话不多说直接开始。
首先随便建一个html,引入百度地图script,这里我选用的最新3.0版本,然后新建一个div,作为地图的容器:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
<title>demo</title>
</head>
<body>
<div id="content" style="width: 100%;height: 600px;"></div>
</body>
<script type="text/javascript" src="http://api.map.baidu.com/api?v=3.0&ak=你的ak"></script>
</html>
然后初始化:
let map = new BMap.Map("content");
let point = new BMap.Point(108.42505257266626, 35.91078193716419);
map.centerAndZoom(point, 7);
(ps:这个中心点怎么得到呢,你在map对象上加一个click方法,在其回调方法中可以获取对应鼠标点下位置的百度坐标下的经纬度,你想要哪个省份作为中心点,就缩放到对应的省份,然后自己点一下就能知道坐标了。)
这时候打开页面你会得到如下地图:
接下来明确一下我们想要的目标效果:
- 省份以外的地图不需要展示;
- 省份只需要展示一个轮廓,不需要展示这写地名啊、路线之类。
之前自己写的时候走了点弯路,这里就不多废话,直接说一下思路了:
- 因为这里其实使用地图只作为一个展示轮廓的作用,所以原始地图的主要功能都不需要了,所以把地图的拖拽、缩放等功能都屏蔽掉;
- 如何屏蔽掉原始的地图?可以直接使用一个纯色的覆盖物把当前可见范围的地图都遮挡起来。
- 如何只显示省份的轮廓?可以使用百度地图提供的api获取省份的边界坐标点数组,使用这个数组绘制一个多边形覆盖物,显示出来就是单个省份的轮廓了。
let map = new BMap.Map("content");
let point = new BMap.Point(108.42505257266626, 35.91078193716419);
map.centerAndZoom(point, 7);
//设置放大级别固定为7
map.setMaxZoom(7);
map.setMinZoom(7);
//禁止拖拽
map.disableDragging();
//禁用双击放大
map.disableDoubleClickZoom();
//禁用双指操作缩放
map.disablePinchToZoom();
//禁用键盘操作
map.disableKeyboard();
//创建一个覆盖物把当前显示的地图全部挡住,作为白色背景
//获取四个角的坐标
let currentBounds = map.getBounds();
let sw = currentBounds.getSouthWest();
let ne = currentBounds.getNorthEast();
var wn = new BMap.Point(sw.lng, ne.lat);
var es = new BMap.Point(ne.lng, sw.lat);
//创建矩形覆盖物
let bgPly = new BMap.Polygon([sw, wn, ne, es], { fillColor: '#ffffff', fillOpacity: 1, strokeColor: '#ffffff' });
map.addOverlay(bgPly);
//根据地区名称(省市县)在地图上绘制多边形区域
let boundary = new BMap.Boundary();
boundary.get('陕西', rs => {
//多边形的坐标点数组
let plyPoints = [];
rs.boundaries[0].split(';').map(item => {
item = item.split(',');
plyPoints.push(new BMap.Point(parseFloat(item[0]), parseFloat(item[1])));
})
let ply = new BMap.Polygon(plyPoints, { fillColor: '#A8D780', fillOpacity: 1, strokeColor: '#000000', strokeOpacity: 0, strokeWeight: 1 });
map.addOverlay(ply);
});
上边的代码运行效果如下:
是不是已经达到我们预想的显示效果了呢?
这里标注下上边代码的几个关键地方
let currentBounds = map.getBounds();//该方法获取当前地图显示范围的西南和东北角的经纬度坐标
...
let boundary = new BMap.Boundary();//该对象可以通过名称获取行政区划的边界坐标点
boundary.get('陕西', rs => {
....
rs.boundaries[0].split(';').map(item => {//这里返回的rs.boundaries是一个数组,每个数组都是以分号分隔的一系列坐标点,这里我都转成了百度的Point对象数组
item = item.split(',');
plyPoints.push(new BMap.Point(parseFloat(item[0]), parseFloat(item[1])));
})
...
剩下的市区都依样画葫芦同样都可以画出来,大家可以自己实现。其他的点击事件、显示名称等,可以参照百度开发平带的的覆盖物api自行实现。
ps.有两个比较坑的地方:
- 尽管上面的代码把拖拽等原始地图的时间都禁用了,但是还会偶尔出现地图被移动了的情况,原因不明。我的解决办法是给map添加一个被移动的监听事件,一旦移动了,就重置下整体的逻辑。这并不是个很好的解决方式,因为页面会闪一下。
2.boundary.get方法的返回值返回的数组会有多个,至于这些多个值代表什么意思,文档里面并没有写。我在上面示例里面取的是第一个数组,但是实际上,有些行政区划,第一个数组里面的点明显不正确,可以判断这些数组的长度,取最长的那个(一般都是几百上千的长度),暂时未发现有什么问题。