關於VUE專案地圖開發中大量點標記繪製一些總結

tzpstorm發表於2019-03-01

問題說明

在地圖開發中,當地圖中繪製大量的標記點後,無論是拖動或者縮放,都會感覺到明顯的卡頓現象。(一般超過800個點後就比較明顯了).在平時的工作業務中,由於公司的實時監控頁面需要展現5000-20000車輛的實時定位跟蹤,特別是切換到車輛密集的港口碼頭卡頓現象非常嚴重(如下圖),看起來非常難看,使用者體驗也非常差。在此寫下一些開發優化中的心得體會(本文中使用的是高德地圖為參考)。

關於VUE專案地圖開發中大量點標記繪製一些總結

解決思路

  1. 首先我們應該將地圖上的所有覆蓋物(包括Marker,Icon,Text,Polygon)分組新增到多個OverlayGroup中對每一類統一管理。部分覆蓋物預設隱藏,需要時才展現。

  2. 不要將大量的覆蓋物資訊都直接掛載在地圖物件上,可以按需載入,減少地圖在移動後重新渲染的工作量

  3. 合理的使用覆蓋物聚合(聚合雖然會減少卡頓,但是覆蓋物數量太多依舊會比較卡)

解決方法

首先,新增一個地圖設定勾選皮膚,將除車輛點標記以外的所有覆蓋物按型別分類,新增到一個個單獨的OverlayGroup中預設不掛載在地圖上,在勾選時才呼叫OverlayGroup.setMap(map)方法新增到地圖上。去掉了這些不必要覆蓋物,首次進入時立馬清爽了許多。

關於VUE專案地圖開發中大量點標記繪製一些總結

儘管去掉了各類網點,文字,等覆蓋物,但是我們需要渲染的車輛標記點依舊是非常多的。在此基礎上,我們採用按螢幕載入,篩選出螢幕內的點,螢幕外的標記點不渲染,監控每次地圖的moveend,zoomend,resize,每次的改變後重新計算螢幕內的標記點,當螢幕內標記點較少,比如200以下(這個隨個人需要定),直接繪製在地圖上。當螢幕內標記點比較多,大於200,呼叫聚合方法,將標記點聚合後繪製在地圖上。

參考程式碼

    /**監聽地圖拖拽,放大事件,根據螢幕範圍動態渲染點位 */
      reloadMarks(){
        AMap.event.addListener(this.map,`moveend`,()=> {
          this.computeMarkers();
        })
        AMap.event.addListener(this.map,`zoomend`,()=> {
          this.computeMarkers();
        })
        AMap.event.addListener(this.map,`resize`,()=> {
          this.computeMarkers();
        })
      },
      /** 根據當前螢幕範圍帥選marker */
      computeMarkers(){
        this.newViewData={};
        //獲取當前檢視範圍
        let now_bounds = this.map.getBounds();
        //遍歷車輛資料,為了減少後臺傳入的重複資料,我的車輛資訊列表一直使用物件儲存
        for(let sel_deviceNo in this.carDataObj0){
          let item=this.carDataObj0[sel_deviceNo];
          //判斷當前點的座標是否存在於檢視內
          if(now_bounds.contains(item.gcj02_coords.split(`,`))){
            //將當前螢幕內的檢視點儲存
            this.newViewData[sel_deviceNo] = item;
          }
        }
        this.renderMarker();
        now_bounds=null;
      },
      /** 建立聚合**/
      createCluser(markerArr) {
        AMap.plugin([`AMap.MarkerClusterer`], () => {
          this.cluster = new AMap.MarkerClusterer(this.map, markerArr, {
            gridSize: 80,
            renderCluserMarker,
            minClusterSize: 1,
            maxZoom: 18
          });
        })
      },
      /** 將markers渲染到map上 */
      renderMarker(){
        //判斷目前是否有當前覆蓋物組
        !this.overLayGroup && this.overLayGroup = new AMap.OverlayGroup([]);
        let MarkerAddArr = [];
        for(let sel_deviceNo in this.newViewData){
            /**我的所有建立的Marker點物件都是長期存在於this.markerObj中,並不銷燬,每次資料更
            新後呼叫marker物件的setPosition(),setAngle(),setIcon()等方法改變狀態,再篩選出
            需要掛載上的一起加到地圖上 **/
            if(this.markerObj[sel_deviceNo]){
              MarkerAddArr.push(this.markerObj[sel_deviceNo]);
            }
          }
        this.overLayGroup.clearOverlays();
        //超過200個點則開啟聚合模式  且在聚合模式下不渲染皮膚展示點
        if(MarkerAddArr.length>=200){
            if(this.cluster){
              this.cluster.clearMarkers();
              this.cluster.setMarkers(MarkerAddArr);
            }else{
              this.createCluser(MarkerAddArr);
            }
        }else{
            if(this.cluster){
                this.cluster.clearMarkers();
            }
            this.overLayGroup.addOverlays(MarkerAddArr);
            this.overLayGroup.setMap(this.map);
        }
        MarkerAddArr=null;
      },
複製程式碼

程式碼直接從業務中複製的,耦合度比較高,還請見諒。
做完了標記點按螢幕分組載入之後,我們在新增一個簡單的防抖方法,防止連續的拖動或者縮放導致computeMarkers方法被觸發,減少方法呼叫次數

const debounce = (fn, wait=500) =>{
  return function() {
    clearTimeout(fn.timer)
    fn.timer = setTimeout(fn.bind(this, ...arguments), wait)
  }
}
複製程式碼

完成後效果如下:減少了地圖的計算與渲染,每次只計算視野內的點數,因此總數的大小不會影響地圖效能,當視野內超過200個點都會聚合,200以下時顯示。

關於VUE專案地圖開發中大量點標記繪製一些總結

平時寫文章比較少,文筆很差,還請多多見諒,謝謝。

相關文章