當將自定義的Marker(含有Label)經過MarkerClusterer 管理的時候,當地圖發生任何移動、縮放 的時候,Marker 的Label 就會自動消失。javascript
這個問題主要是因爲百度的點聚合API<script type="text/javascript" src="http://api.map.baidu.com/library/MarkerClusterer/1.2/src/MarkerClusterer_min.js"></script>css
的bug形成的。其實誘發這個問題的緣由是在於API代碼中的map.removeOverlay(marker),在這句話執行的時候,會自動將marker.label =null;html
咱們能夠在Clusterer 處理的全部地方都先讀出來Label,等remove完畢以後再將這個Maker.setLabel 過去就ok了java
tmplabel = this._markers[i].getLabel();api
this._markers[i].getMap() && this._map.removeOverlay(this._markers[i]);數組
this._markers[i].setLabel(tmplabel);ide
修改過的MakerClusterer.js代碼:性能
/** * @fileoverview MarkerClusterer標記聚合器用來解決加載大量點要素到地圖上產生覆蓋現象的問題,並提升性能。 * 主入口類是<a href="symbols/BMapLib.MarkerClusterer.html">MarkerClusterer</a>, * 基於Baidu Map API 1.2。 * * @author Baidu Map Api Group * @version 1.2 */ /** * @namespace BMap的全部library類均放在BMapLib命名空間下 */ var BMapLib = window.BMapLib = BMapLib || {}; (function(){ /** * 獲取一個擴展的視圖範圍,把上下左右都擴大同樣的像素值。 * @param {Map} map BMap.Map的實例化對象 * @param {BMap.Bounds} bounds BMap.Bounds的實例化對象 * @param {Number} gridSize 要擴大的像素值 * * @return {BMap.Bounds} 返回擴大後的視圖範圍。 */ var getExtendedBounds = function(map, bounds, gridSize){ bounds = cutBoundsInRange(bounds); var pixelNE = map.pointToPixel(bounds.getNorthEast()); var pixelSW = map.pointToPixel(bounds.getSouthWest()); pixelNE.x += gridSize; pixelNE.y -= gridSize; pixelSW.x -= gridSize; pixelSW.y += gridSize; var newNE = map.pixelToPoint(pixelNE); var newSW = map.pixelToPoint(pixelSW); return new BMap.Bounds(newSW, newNE); }; /** * 按照百度地圖支持的世界範圍對bounds進行邊界處理 * @param {BMap.Bounds} bounds BMap.Bounds的實例化對象 * * @return {BMap.Bounds} 返回不越界的視圖範圍 */ var cutBoundsInRange = function (bounds) { var maxX = getRange(bounds.getNorthEast().lng, -180, 180); var minX = getRange(bounds.getSouthWest().lng, -180, 180); var maxY = getRange(bounds.getNorthEast().lat, -74, 74); var minY = getRange(bounds.getSouthWest().lat, -74, 74); return new BMap.Bounds(new BMap.Point(minX, minY), new BMap.Point(maxX, maxY)); }; /** * 對單個值進行邊界處理。 * @param {Number} i 要處理的數值 * @param {Number} min 下邊界值 * @param {Number} max 上邊界值 * * @return {Number} 返回不越界的數值 */ var getRange = function (i, mix, max) { mix && (i = Math.max(i, mix)); max && (i = Math.min(i, max)); return i; }; /** * 判斷給定的對象是否爲數組 * @param {Object} source 要測試的對象 * * @return {Boolean} 若是是數組返回true,不然返回false */ var isArray = function (source) { return '[object Array]' === Object.prototype.toString.call(source); }; /** * 返回item在source中的索引位置 * @param {Object} item 要測試的對象 * @param {Array} source 數組 * * @return {Number} 若是在數組內,返回索引,不然返回-1 */ var indexOf = function(item, source){ var index = -1; if(isArray(source)){ if (source.indexOf) { index = source.indexOf(item); } else { for (var i = 0, m; m = source[i]; i++) { if (m === item) { index = i; break; } } } } return index; }; /** *@exports MarkerClusterer as BMapLib.MarkerClusterer */ var MarkerClusterer = /** * MarkerClusterer * @class 用來解決加載大量點要素到地圖上產生覆蓋現象的問題,並提升性能 * @constructor * @param {Map} map 地圖的一個實例。 * @param {Json Object} options 可選參數,可選項包括:<br /> * markers {Array<Marker>} 要聚合的標記數組<br /> * girdSize {Number} 聚合計算時網格的像素大小,默認60<br /> * maxZoom {Number} 最大的聚合級別,大於該級別就不進行相應的聚合<br /> * minClusterSize {Number} 最小的聚合數量,小於該數量的不能成爲一個聚合,默認爲2<br /> * isAverangeCenter {Boolean} 聚合點的落腳位置是不是全部聚合在內點的平均值,默認爲否,落腳在聚合內的第一個點<br /> * styles {Array<IconStyle>} 自定義聚合後的圖標風格,請參考TextIconOverlay類<br /> */ BMapLib.MarkerClusterer = function(map, options){ if (!map){ return; } this._map = map; this._markers = []; this._clusters = []; var opts = options || {}; this._gridSize = opts["gridSize"] || 60; this._maxZoom = opts["maxZoom"] || 18; this._minClusterSize = opts["minClusterSize"] || 2; this._isAverageCenter = false; if (opts['isAverageCenter'] != undefined) { this._isAverageCenter = opts['isAverageCenter']; } this._styles = opts["styles"] || []; var that = this; this._map.addEventListener("zoomend",function(){ that._redraw(); }); this._map.addEventListener("moveend",function(){ that._redraw(); }); var mkrs = opts["markers"]; isArray(mkrs) && this.addMarkers(mkrs); }; /** * 添加要聚合的標記數組。 * @param {Array<Marker>} markers 要聚合的標記數組 * * @return 無返回值。 */ MarkerClusterer.prototype.addMarkers = function(markers){ for(var i = 0, len = markers.length; i <len ; i++){ this._pushMarkerTo(markers[i]); } this._createClusters(); }; /** * 把一個標記添加到要聚合的標記數組中 * @param {BMap.Marker} marker 要添加的標記 * * @return 無返回值。 */ MarkerClusterer.prototype._pushMarkerTo = function(marker){ var index = indexOf(marker, this._markers); if(index === -1){ marker.isInCluster = false; this._markers.push(marker);//Marker拖放後enableDragging不作變化,忽略 } }; /** * 添加一個聚合的標記。 * @param {BMap.Marker} marker 要聚合的單個標記。 * @return 無返回值。 */ MarkerClusterer.prototype.addMarker = function(marker) { this._pushMarkerTo(marker); this._createClusters(); }; /** * 根據所給定的標記,建立聚合點 * @return 無返回值 */ MarkerClusterer.prototype._createClusters = function(){ var mapBounds = this._map.getBounds(); var extendedBounds = getExtendedBounds(this._map, mapBounds, this._gridSize); for(var i = 0, marker; marker = this._markers[i]; i++){ if(!marker.isInCluster && extendedBounds.containsPoint(marker.getPosition()) ){ this._addToClosestCluster(marker); } } }; /** * 根據標記的位置,把它添加到最近的聚合中 * @param {BMap.Marker} marker 要進行聚合的單個標記 * * @return 無返回值。 */ MarkerClusterer.prototype._addToClosestCluster = function (marker){ var distance = 4000000; var clusterToAddTo = null; var position = marker.getPosition(); for(var i = 0, cluster; cluster = this._clusters[i]; i++){ var center = cluster.getCenter(); if(center){ var d = this._map.getDistance(center, marker.getPosition()); if(d < distance){ distance = d; clusterToAddTo = cluster; } } } if (clusterToAddTo && clusterToAddTo.isMarkerInClusterBounds(marker)){ clusterToAddTo.addMarker(marker); } else { var cluster = new Cluster(this); cluster.addMarker(marker); this._clusters.push(cluster); } }; /** * 清除上一次的聚合的結果 * @return 無返回值。 */ MarkerClusterer.prototype._clearLastClusters = function(){ for(var i = 0, cluster; cluster = this._clusters[i]; i++){ cluster.remove(); } this._clusters = [];//置空Cluster數組 this._removeMarkersFromCluster();//把Marker的cluster標記設爲false }; /** * 清除某個聚合中的全部標記 * @return 無返回值 */ MarkerClusterer.prototype._removeMarkersFromCluster = function(){ for(var i = 0, marker; marker = this._markers[i]; i++){ marker.isInCluster = false; } }; /** * 把全部的標記從地圖上清除 * @return 無返回值 */ MarkerClusterer.prototype._removeMarkersFromMap = function(){ for(var i = 0, marker; marker = this._markers[i]; i++){ marker.isInCluster = false; tmplabel = marker.getLabel(); this._map.removeOverlay(marker); marker.setLabel(tmplabel); } }; /** * 刪除單個標記 * @param {BMap.Marker} marker 須要被刪除的marker * * @return {Boolean} 刪除成功返回true,不然返回false */ MarkerClusterer.prototype._removeMarker = function(marker) { var index = indexOf(marker, this._markers); if (index === -1) { return false; } tmplabel = marker.getLabel(); this._map.removeOverlay(marker); marker.setLabel(tmplabel); this._markers.splice(index, 1); return true; }; /** * 刪除單個標記 * @param {BMap.Marker} marker 須要被刪除的marker * * @return {Boolean} 刪除成功返回true,不然返回false */ MarkerClusterer.prototype.removeMarker = function(marker) { var success = this._removeMarker(marker); if (success) { this._clearLastClusters(); this._createClusters(); } return success; }; /** * 刪除一組標記 * @param {Array<BMap.Marker>} markers 須要被刪除的marker數組 * * @return {Boolean} 刪除成功返回true,不然返回false */ MarkerClusterer.prototype.removeMarkers = function(markers) { var success = false; for (var i = 0; i < markers.length; i++) { var r = this._removeMarker(markers[i]); success = success || r; } if (success) { this._clearLastClusters(); this._createClusters(); } return success; }; /** * 從地圖上完全清除全部的標記 * @return 無返回值 */ MarkerClusterer.prototype.clearMarkers = function() { this._clearLastClusters(); this._removeMarkersFromMap(); this._markers = []; }; /** * 從新生成,好比改變了屬性等 * @return 無返回值 */ MarkerClusterer.prototype._redraw = function () { this._clearLastClusters(); this._createClusters(); }; /** * 獲取網格大小 * @return {Number} 網格大小 */ MarkerClusterer.prototype.getGridSize = function() { return this._gridSize; }; /** * 設置網格大小 * @param {Number} size 網格大小 * @return 無返回值 */ MarkerClusterer.prototype.setGridSize = function(size) { this._gridSize = size; this._redraw(); }; /** * 獲取聚合的最大縮放級別。 * @return {Number} 聚合的最大縮放級別。 */ MarkerClusterer.prototype.getMaxZoom = function() { return this._maxZoom; }; /** * 設置聚合的最大縮放級別 * @param {Number} maxZoom 聚合的最大縮放級別 * @return 無返回值 */ MarkerClusterer.prototype.setMaxZoom = function(maxZoom) { this._maxZoom = maxZoom; this._redraw(); }; /** * 獲取聚合的樣式風格集合 * @return {Array<IconStyle>} 聚合的樣式風格集合 */ MarkerClusterer.prototype.getStyles = function() { return this._styles; }; /** * 設置聚合的樣式風格集合 * @param {Array<IconStyle>} styles 樣式風格數組 * @return 無返回值 */ MarkerClusterer.prototype.setStyles = function(styles) { this._styles = styles; this._redraw(); }; /** * 獲取單個聚合的最小數量。 * @return {Number} 單個聚合的最小數量。 */ MarkerClusterer.prototype.getMinClusterSize = function() { return this._minClusterSize; }; /** * 設置單個聚合的最小數量。 * @param {Number} size 單個聚合的最小數量。 * @return 無返回值。 */ MarkerClusterer.prototype.setMinClusterSize = function(size) { this._minClusterSize = size; this._redraw(); }; /** * 獲取單個聚合的落腳點是不是聚合內全部標記的平均中心。 * @return {Boolean} true或false。 */ MarkerClusterer.prototype.isAverageCenter = function() { return this._isAverageCenter; }; /** * 獲取聚合的Map實例。 * @return {Map} Map的示例。 */ MarkerClusterer.prototype.getMap = function() { return this._map; }; /** * 獲取全部的標記數組。 * @return {Array<Marker>} 標記數組。 */ MarkerClusterer.prototype.getMarkers = function() { return this._markers; }; /** * 獲取聚合的總數量。 * @return {Number} 聚合的總數量。 */ MarkerClusterer.prototype.getClustersCount = function() { var count = 0; for(var i = 0, cluster; cluster = this._clusters[i]; i++){ cluster.isReal() && count++; } return count; }; /** * @ignore * Cluster * @class 表示一個聚合對象,該聚合,包含有N個標記,這N個標記組成的範圍,並有予以顯示在Map上的TextIconOverlay等。 * @constructor * @param {MarkerClusterer} markerClusterer 一個標記聚合器示例。 */ function Cluster(markerClusterer){ this._markerClusterer = markerClusterer; this._map = markerClusterer.getMap(); this._minClusterSize = markerClusterer.getMinClusterSize(); this._isAverageCenter = markerClusterer.isAverageCenter(); this._center = null;//落腳位置 this._markers = [];//這個Cluster中所包含的markers this._gridBounds = null;//以中心點爲準,向四邊擴大gridSize個像素的範圍,也即網格範圍 this._isReal = false; //真的是個聚合 this._clusterMarker = new BMapLib.TextIconOverlay(this._center, this._markers.length, {"styles":this._markerClusterer.getStyles()}); //this._map.addOverlay(this._clusterMarker); } /** * 向該聚合添加一個標記。 * @param {Marker} marker 要添加的標記。 * @return 無返回值。 */ Cluster.prototype.addMarker = function(marker){ if(this.isMarkerInCluster(marker)){ return false; }//也可用marker.isInCluster判斷,外面判斷OK,這裏基本不會命中 if (!this._center){ this._center = marker.getPosition(); this.updateGridBounds();// } else { if(this._isAverageCenter){ var l = this._markers.length + 1; var lat = (this._center.lat * (l - 1) + marker.getPosition().lat) / l; var lng = (this._center.lng * (l - 1) + marker.getPosition().lng) / l; this._center = new BMap.Point(lng, lat); this.updateGridBounds(); }//計算新的Center } marker.isInCluster = true; this._markers.push(marker); var len = this._markers.length; if(len < this._minClusterSize ){ this._map.addOverlay(marker); //this.updateClusterMarker(); return true; } else if (len === this._minClusterSize) { for (var i = 0; i < len; i++) { tmplabel = this._markers[i].getLabel(); this._markers[i].getMap() && this._map.removeOverlay(this._markers[i]); this._markers[i].setLabel(tmplabel); } } this._map.addOverlay(this._clusterMarker); this._isReal = true; this.updateClusterMarker(); return true; }; /** * 判斷一個標記是否在該聚合中。 * @param {Marker} marker 要判斷的標記。 * @return {Boolean} true或false。 */ Cluster.prototype.isMarkerInCluster= function(marker){ if (this._markers.indexOf) { return this._markers.indexOf(marker) != -1; } else { for (var i = 0, m; m = this._markers[i]; i++) { if (m === marker) { return true; } } } return false; }; /** * 判斷一個標記是否在該聚合網格範圍中。 * @param {Marker} marker 要判斷的標記。 * @return {Boolean} true或false。 */ Cluster.prototype.isMarkerInClusterBounds = function(marker) { return this._gridBounds.containsPoint(marker.getPosition()); }; Cluster.prototype.isReal = function(marker) { return this._isReal; }; /** * 更新該聚合的網格範圍。 * @return 無返回值。 */ Cluster.prototype.updateGridBounds = function() { var bounds = new BMap.Bounds(this._center, this._center); this._gridBounds = getExtendedBounds(this._map, bounds, this._markerClusterer.getGridSize()); }; /** * 更新該聚合的顯示樣式,也即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); }); }; /** * 刪除該聚合。 * @return 無返回值。 */ Cluster.prototype.remove = function(){ for (var i = 0, m; m = this._markers[i]; i++) { var tmplabel = this._markers[i].getLabel(); this._markers[i].getMap() && this._map.removeOverlay(this._markers[i]); this._markers[i].setLabel(tmplabel); }//清除散的標記點 this._map.removeOverlay(this._clusterMarker); this._markers.length = 0; delete this._markers; } /** * 獲取該聚合所包含的全部標記的最小外接矩形的範圍。 * @return {BMap.Bounds} 計算出的範圍。 */ Cluster.prototype.getBounds = function() { var bounds = new BMap.Bounds(this._center,this._center); for (var i = 0, marker; marker = this._markers[i]; i++) { bounds.extend(marker.getPosition()); } return bounds; }; /** * 獲取該聚合的落腳點。 * @return {BMap.Point} 該聚合的落腳點。 */ Cluster.prototype.getCenter = function() { return this._center; }; })();
在使用點聚合的網頁中,引用此js文件。測試
<!DOCTYPE html> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <meta name="viewport" content="initial-scale=1.0, user-scalable=no" /> <style type="text/css"> body, html,#allmap {width: 100%;height: 100%;overflow: hidden;margin:0;} </style> <script type="text/javascript" src="http://api.map.baidu.com/api?v=2.0&ak=GnyzTKlAnHyVFMOcTSiwuFur"></script> <script type="text/javascript" src="http://api.map.baidu.com/library/TextIconOverlay/1.2/src/TextIconOverlay_min.js"></script> <script type="text/javascript" src="MarkClusterer.js"></script> <title>點聚合</title> </head> <body> <div id="allmap"></div> </body> </html> <script type="text/javascript"> // 百度地圖API功能 var map = new BMap.Map("allmap"); map.centerAndZoom(new BMap.Point(116.404, 39.915), 4); map.enableScrollWheelZoom(); myMarkerClusterer(); function myMarkerClusterer() { var markerArr = [ { title: "名稱:金陵飯店", point: "118.789287,32.048784", address: "南京", tel: "12306" }, { title: "名稱:金奧費爾蒙酒店", point: "118.789287,32.048784", address: "南京", tel: "18500000000" }, { title: "名稱:黃埔大酒店", point: "118.817663,32.048446", address: "南京", tel: "18500000000" }, { title: "名稱:蘇寧銀河諾富特", point: "118.897611,32.091566", address: "南京", tel: "18500000000" } ]; var point = new Array(); //存放標註點經緯信息的數組 var marker = new Array(); //存放標註點對象的數組 var info = new Array(); //存放提示信息窗口對象的數組 var label = new Array(); for (var i = 0; i < markerArr.length; i++) { var p0 = markerArr[i].point.split(",")[0]; // var p1 = markerArr[i].point.split(",")[1]; //按照原數組的point格式將地圖點座標的經緯度分別提出來 point[i] = new BMap.Point(p0, p1); //循環生成新的地圖點 marker[i] = new BMap.Marker(point[i]); //按照地圖點座標生成標記 map.addOverlay(marker[i]); //marker[i].setAnimation(BMAP_ANIMATION_BOUNCE); //跳動的動畫 label[i] = new BMap.Label(markerArr[i].title, { offset: new window.BMap.Size(20, -10) }); label[i].enableMassClear = true; marker[i].setLabel(label[i]); info[i] = new BMap.InfoWindow("<p style=’font-size:12px;lineheight:1.8em;’>" + markerArr[i].title + "</br>地址:" + markerArr[i].address + "</br> 電話:" + markerArr[i].tel + "</br></p>"); // 建立信息窗口對象 } for (var i = 0; i < marker.length; i++) { (function () { var index = i; marker[i].addEventListener('click', function () { this.openInfoWindow(info[index]); }); })(); } //最簡單的用法,生成一個marker數組,而後調用markerClusterer類便可。 var markerClusterer = new BMapLib.MarkerClusterer(map, { markers: marker }); } </script>