缘起
近日, 产品提了个新的功能,需要用到地图点聚合功能。 了解了
leaflet
框架,是基于地图瓦片实现的,但是我司要求使用百度地图实现该功能, 发现 leaflet 对天地图、高德比较友好。 对百度地图支持度并不好,遂弃用。
后来,研究百度聚合demo发现,百度聚合只支持自定义样式,并不支持自定义文字。
最终,我发现百度地图聚合功能是第三方库实现的,可以自己下载下来, 重写聚合设置聚合文字相关代码及功能。 简单记录如下,希望对以后有需要的童鞋有所帮助吧。
分析
研究百度地图聚合源码发现,如果打算自定义文字需要修改两处:
一、修改 BMapLib.MarkerClusterer.js
插件中 Cluster.prototype.updateClusterMarker
方法,实现过程如下:
/**
* 更新该聚合的显示样式,也即TextIconOverlay。
* @return 无返回值。
*/
Cluster.prototype.updateClusterMarker = function () {
if (this._map.getZoom() > this._markerClusterer.getMaxZoom()) {
this._clusterMarker && this._map.removeOverlay(this._clusterMarker);
for (var i = 0, marker; marker = this._markers[i]; i++) {
this._map.addOverlay(marker);
}
return;
}
if (this._markers.length < this._minClusterSize) {
this._clusterMarker.hide();
return;
}
this._clusterMarker.setPosition(this._center);
this._clusterMarker.setText(this._markers.length);
var thatMap = this._map;
var thatBounds = this.getBounds();
this._clusterMarker.addEventListener("click", function(event){
thatMap.setViewport(thatBounds);
});
};
方法中: this._clusterMarker.setText(this._markers.length);
表示, 聚合点显示的文字内容是 当前 Cluster 类 _markers
属性 的 长度;
二、修改 TextIconOverlay.js
中的 构造方法 和 样式方法
查看 TextIconOverlay.js
的源码发现, text 只支持 数字类型的值,而且和 styles 属性绑定,需要修改constructor 方法, 和 设置属性的方法。
性空
如上,知道了聚合的文字是 Cluster.prototype.updateClusterMarker
方法 , 那么我们需要进行以下步骤:
1、把百度地图的聚合库存到自己的项目;
2、不要使用百度地图默认的maker,改用自己定义的复杂覆盖物;
3、重写updateClusterMarker
方法;
4、重写TextIconOverlay.js
的 setText() 方法;
5、引用类聚合文件,由百度的官方网站,改为自己的项目路径;
6、大功告成。
- 第一步,自己操作即可;
- 第二步,自定义自己的复杂覆盖物,传入自定义 text
// 引入复杂覆盖物监听插件
import EventWrapper from './EventWrapper.js';
//合并参数
function extend(o, n, override) {
for(let key in n){
if(n.hasOwnProperty(key) && (!o.hasOwnProperty(key) || override)){
o[key]=n[key];
}
}
return o;
}
//复杂的自定义覆盖物
function ComplexCustomOverlay(opts){
let def = {
_point: '',
_text: '',
_initalizeCallBack: function () {
let div = document.createElement("div");
div.className = 'map-ha-popbox';
let span = document.createElement("span");
span.className = 'name';
let i = document.createElement("i");
span.appendChild(document.createTextNode(this.getOption('_text')));
span.appendChild(i);
div.appendChild(span);
return div;
}, //initalize的回调方法。
_eventCallBack: function () {
//覆盖物事件监听的回调方法(渲染到地图上时才可调用):点击等事件用 EventWrapper插件
let me = this;
EventWrapper.addDomListener(this._dom, "touchend", function() {
//这里 me 是 复杂覆盖物本身
console.log(me);
//注意: 这里this 指的是 dom 元素,不是复杂覆盖物
console.log(this);
});
} // 相关事件的回调方法
};
this._setting = extend(def, opts, true);
};
//复杂覆盖物实例,继承自百度 overlay
ComplexCustomOverlay.prototype = new BMap.Overlay();
//重写 initialize 方法
ComplexCustomOverlay.prototype.initialize = function(map){
this._map = map;
this._dom = '';
if (this._setting['_initalizeCallBack'] && typeof(this._setting['_initalizeCallBack']) == 'function') {
this._dom = this._setting['_initalizeCallBack'].call(this);
this._map.getPanes().labelPane.appendChild(this._dom);
}
this._cwidth = this._dom.clientWidth;
this._cheight = this._dom.clientHeight;
//获取到div元素之后,才可以添加事件回调方法
if (this._setting['_eventCallBack'] && typeof(this._setting['_eventCallBack']) == 'function') {
this._setting['_eventCallBack'].call(this);
}
//这里必须返回DOM元素,clearOverlays方法会清除这里的dom元素
return this._dom;
}
//重写 draw 方法
ComplexCustomOverlay.prototype.draw = function(){
this._map && this._updatePosition();
}
ComplexCustomOverlay.prototype._updatePosition = function() {
if (this._dom && this.getOption('_point')) {
let pixel = this._map.pointToOverlayPixel(this.getOption('_point'));
let iObj = this._div.getElementsByTagName('i')[0];
this._div.style.left = pixel.x - this._cwidth/2 + "px";
// 多个overlay 循环 渲染的时候,非第一个overlay会有个 offsetTop 值
this._div.style.top = pixel.y - this._div.offsetTop - this._cheight - iObj.offsetHeight + "px";
}
};
ComplexCustomOverlay.prototype.getNum = function(){
return this.getOption('_num');
}
ComplexCustomOverlay.prototype.getOption = function(key) {
return this._setting[key];
}
export default ComplexCustomOverlay;
通过查看百度地图聚合源码,发现 Cluster 类调用了 marker
的 getPosition
和 getMap
方法,我们需要自定义这俩方法如下:
//在调用聚合方法时会将会调用标注的getPosition和getMap方法
ComplexCustomOverlay.prototype.getMap = function(){
return this._map;
};
ComplexCustomOverlay.prototype.getPosition = function(){
return this.getOption('_point');
};
// 获取自定义text,不建议通过 marker._setting['_text'] 获取
ComplexCustomOverlay.prototype.getText = function(){
return this.getOption('_text');
};
- 第三步,重写
Cluster.prototype.updateClusterMarker
方法:
/**
* 更新该聚合的显示样式,也即TextIconOverlay。
* @return 无返回值。
*/
Cluster.prototype.updateClusterMarker = function () {
if (this._map.getZoom() > this._markerClusterer.getMaxZoom()) {
this._clusterMarker && this._map.removeOverlay(this._clusterMarker);
for (var i = 0, marker; marker = this._markers[i]; i++) {
this._map.addOverlay(marker);
}
return;
}
if (this._markers.length < this._minClusterSize) {
this._clusterMarker.hide();
return;
}
this._clusterMarker.setPosition(this._center);
// 这里重写
// this._clusterMarker.setText(this._markers.length);
// 自定义文字
var _text = "";
for (var i = 0, marker; marker = this._markers[i]; i++) {
//自定义文字,这里简单连接
_text += marker.getText() + "
"; // setText 方法是 TextOverlay 的 innerHtml 赋值, 这里支持 html 标记
}
this._clusterMarker.setText(_text);
var thatMap = this._map;
var thatBounds = this.getBounds();
this._clusterMarker.addEventListener("click", function(event){
thatMap.setViewport(thatBounds);
});
};
- 第四步,文字覆盖物 TextOverly ,重写
setText
方法:
TextOverly.prototype.setText = function(text) {
if(text && (!this._text || (this._text.toString() != text.toString()))){
this._text = text;
this._updateText(); // 更新内容
this._updatePosition(); // 渲染样式
}
};
TextOverly.prototype._updateText = function() {
if (this._div) {
let spanDom = this._div.getElementsByTagName('span')[0];
spanDom.innerHTML = this._text;
this._cwidth = this._div.clientWidth;
this._cheight = this._div.clientHeight;
}
};
TextOverly.prototype._updatePosition = function() {
if (this._div && this._point) {
let pixel = this._map.pointToOverlayPixel(this._point);
this._div.style.position = "absolute";
this._div.style.left = pixel.x - Math.ceil(parseInt(this._cwidth) / 2) + "px";
this._div.style.top = pixel.y - Math.ceil(parseInt(this._cheight)/ 2) + "px";
}
};
- 第五步、第六步略。
最终,我们实现了自定义 百度地图点聚合文字 信息。
附Github: https://github.com/sphenginx/vue-slider
注: 代码库并不是修改的
TextIconOverlay
覆盖物,而是自定义了SVGIconOverlay
覆盖物, 基于SVG的,可以自定义文字 和 填充颜色 等。
聚合类Cluster
的_clusterMarker
由TextIconOverlay
改为SVGIconOverlay
。 具体可以查看源码
欢迎使用及PR
附:
类 BMapLib.MarkerClusterer