參考文獻:http://www.cnblogs.com/jicheng1014/p/3143859.htmlhtml
問題現象:數組
使用MarkerClusterer_min.js,能夠實現點聚合,可是當將帶有Label的Marker經過MarkerClusterer 實現點聚合的時候,當地圖發生任何移動、縮放 的時候,Marker 的Label 就會自動消失。ide
問題分析:性能
網上不少網友分析的緣由是,當地圖發生移動、縮放的時候,在MarkerClusterer_min.js中,map.removeOverlay(marker)這句話執行的時候,會自動將marker.label =null。所以,咱們能夠在Clusterer 處理的全部地方都先讀出來Label,等remove完畢以後再將這個Maker.setLabel 過去就ok了測試
解決方法:this
在map.removeOverlay以前添加marker.getLabel();
在map.removeOverlay以後添加marker.setLabel();spa
修改以後的jsprototype
/** * @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++) { 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; }; })();