地图性能优化

604 阅读2分钟

项目中使用的是高德地图,官网api:lbs.amap.com/api/javascr…

使用过程中,因为图层很多,导致操作地图时越来越卡,故需要优化,从以下几点开始尝试,本文仅做笔记记录。

  1. setFitView去掉过渡动画效果 image.png
 amapObj.map.setFitView(polygon);

改成

 amapObj.map.setFitView(polygon, true);
  1. svg改成批量请求
store.showFenceBoundary.listen((showFenceBoundary) => {
  if (!amapObj.map) return;

  const _svgUrl = Remote.gridMap.getSvg;
  if (showFenceBoundary) {
    if (amapObj.map.getZoom() < 11) return;
    gridFlexibleLayer = new AMap.TileLayer.Flexible({
      createTile: (x, y, z, success, fail) => {
        if (z < 12) return;
        $.get(_svgUrl, { x, y, z }, (packVo) => {
          if (!packVo) return;

          let vo;
          if (typeof packVo === 'string') {
            try {
              vo = JSON.parse(packVo);
            } catch (e) {
              fail('解析packVo出错');
              return;
            }
          }
          if (vo && vo.msgs && vo.msgs.length) {
            fail('packVo包含错误消息');
            return;
          }

          if (typeof packVo === 'object' && !Array.isArray(packVo) && packVo !== null && packVo.messages) {
            fail('packVo包含错误消息');
            return;
          }

          const canvasEle = document.createElement('canvas');
          canvasEle.width = canvasEle.height = 256;
          const content = canvasEle.getContext('2d');
          let str = `${XML2String(packVo)}`.replace(
            '<svg height="256" width="256">',
            '<svg xmlns="http://www.w3.org/2000/svg" style="fill-opacity: 0;stroke:#1890ff;stroke-width:1;">',
          );
          const img = new Image();
          img.src = 'data:image/svg+xml;base64,' + window.btoa(str);
          img.onload = () => {
            content.drawImage(img, 0, 0);
            success(canvasEle);
          };
        });
      },
    });
    amapObj.map.add(gridFlexibleLayer);
  } else {
    const { operateMode } = store.state;
    gridFlexibleLayer && amapObj.map.remove(gridFlexibleLayer);
    gridFlexibleLayer = null;
    if (operateMode === 'fenceDrawing') actions.operateModeChange('default');
  }
});

改成

// 队列存储请求参数
const gridRequestQueue = [];
let gridTimer = null;

// 批量请求发送逻辑
const batchSendGridRequests = () => {
  if (!gridRequestQueue.length) return;

  // 获取所有队列数据并清空
  const paramsList = gridRequestQueue.splice(0);

  // 按批次组织请求参数
  const batchedParams = paramsList.map(({ x, y, z }) => ({ x, y, z }));

  $.postJSONBody(Remote.gridMap.listSvg, batchedParams, (responseList) => {
    // 遍历批量请求的返回结果,按顺序处理
    if (responseList && responseList.voList && responseList.voList.length) {
      responseList.voList.forEach((packVo, index) => {
        const { success, fail } = paramsList[index];

        const canvasEle = document.createElement('canvas');
        canvasEle.width = canvasEle.height = 256;
        const content = canvasEle.getContext('2d', { willReadFrequently: true });
        let str = `${packVo}`.replace(
          '<svg height="256" width="256">',
          '<svg xmlns="http://www.w3.org/2000/svg" style="fill-opacity: 0;stroke:#1890ff;stroke-width:1;">',
        );
        const img = new Image();
        img.src = 'data:image/svg+xml;base64,' + window.btoa(str);
        img.onload = () => {
          content.drawImage(img, 0, 0);
          success(canvasEle);
        };
      });
    }
  });
};

// 定时启动批量请求逻辑
const startBatching = () => {
  if (!gridTimer) {
    gridTimer = setInterval(batchSendGridRequests, 500); // 每隔1秒处理一次队列
  }
};

// 主逻辑
store.showFenceBoundary.listen((showFenceBoundary) => {
  if (!amapObj.map) return;

  if (showFenceBoundary) {
    if (amapObj.map.getZoom() < 12) return;

    gridFlexibleLayer = new AMap.TileLayer.Flexible({
      createTile: (x, y, z, success, fail) => {
        if (z < 12) return; // 过滤掉z<12的参数

        // 将请求加入队列
        gridRequestQueue.push({ x, y, z, success, fail });

        // 启动批量处理
        startBatching();
      },
    });

    amapObj.map.add(gridFlexibleLayer);
  } else {
    const { operateMode } = store.state;

    // 移除图层并清理定时任务
    gridFlexibleLayer && amapObj.map.remove(gridFlexibleLayer);
    gridFlexibleLayer = null;

    if (operateMode === 'fenceDrawing') {
      actions.operateModeChange('default');
    }

    // 清理批量请求逻辑
    clearInterval(gridTimer);
    gridTimer = null;
    gridRequestQueue.length = 0; // 清空队列
  }
});
  1. 网点图层优化 保存近3次的搜索批次的结果,更早的结果在地图上清除掉。为什么要保存最近几次是为了交互上有隐藏的过渡效果,看上去没有闪烁。
// mapStoreListener.js文件
let geoSiteMapOverlay = {}; // 网点geojson图层
let batchSiteMapNumbers = []; // 每一批搜索的网点geojson的批号


/**
 * 网点区域展示
 */
store.siteMapListVo.listen((siteMapListVo) => {
  const { features = [] } = siteMapListVo || {};

  // 网关,清除网点图层
  if (!features.length && !store.state.showSiteCanton) {
    const sitePolygonOverlays = amapObj.map.getAllOverlays('polygon').filter((overlay) => {
      const extData = overlay.getExtData();
      return extData && extData.flag === 'siteMap';
    });
    const siteMarkerOverlays = amapObj.map.getAllOverlays('marker').filter((overlay) => {
      const extData = overlay.getExtData();
      return extData && extData.flag === 'siteMapMarker';
    });
    const siteOverlays = [...sitePolygonOverlays, ...siteMarkerOverlays];
    amapObj.map.remove(siteOverlays);
    geoSiteMapOverlay = {};
  }

  if (!features.length) return;

  const currentBatchNumber = Math.random().toString(36).slice(2, 11);
  batchSiteMapNumbers.push(currentBatchNumber);

  const geojson = new AMap.GeoJSON({
    geoJSON: siteMapListVo,
    getPolygon: function (item, lnglats) {
      const { properties = {} } = item;
      const { id, siteId, siteName } = properties;

      const key = `${id}-${siteId}`;
      let polygon = geoSiteMapOverlay && geoSiteMapOverlay[key] && geoSiteMapOverlay[key].polygon;

      if (!polygon) {
        polygon = new AMap.Polygon({
          path: lnglats,
          // strokeColor: 'black',
          fillColor: getSiteRandomFillColor(properties),
          strokeWeight: 1,
          fillOpacity: 0.2,
          bubble: true,
          zIndex: 13,
          extData: {
            sitePolygonId: `${id}-${siteId}`,
            flag: 'siteMap',
            batchNumber: currentBatchNumber,
          },
        });
        if (!geoSiteMapOverlay[key]) {
          geoSiteMapOverlay[key] = {};
        }
        geoSiteMapOverlay[key].polygon = polygon;
      } else {
        polygon.setExtData({
          sitePolygonId: `${id}-${siteId}`,
          flag: 'siteMap',
          batchNumber: currentBatchNumber,
        });
      }

      let labelMarker = geoSiteMapOverlay && geoSiteMapOverlay[key] && geoSiteMapOverlay[key].labelMarker;

      if (!labelMarker) {
        labelMarker = new AMap.Marker({
          position: polygon.getBounds().getCenter(),
          content: `<div class="amap_marker_content">${siteName}</div>`,
          offset: new AMap.Pixel(0, -10),
          // bubble: true,
          zIndex: 16,
          extData: {
            siteLabelId: `${id}-${siteId}`,
            flag: 'siteMapMarker',
            batchNumber: currentBatchNumber,
          },
        });
        geoSiteMapOverlay[key].labelMarker = labelMarker;

        labelMarker.on('click', function (event) {
          // 查看模式下才能点击网点标签
          if (!store.state.editMode) {
            actions.updateHighlightedPolygon(
              {
                type: 'FeatureCollection',
                features: [item],
              },
              'site',
              'click',
            );
          }
        });

        labelMarker.setMap(amapObj.map);
      } else {
        labelMarker.setExtData({
          sitePolygonId: `${id}-${siteId}`,
          flag: 'siteMapMarker',
          batchNumber: currentBatchNumber,
        });
      }
      return polygon;
    },
  });

  amapObj.map.add(geojson);
  if (batchSiteMapNumbers.length > 3) {
    const newSiteMapBatchNumbers = _.takeRight(batchSiteMapNumbers, 3);
    const oldSitetBatchNumbers = _.difference(batchSiteMapNumbers, newSiteMapBatchNumbers);
    // 清理对应批次的覆盖物
    const oldSitePolygonOverlays = amapObj.map.getAllOverlays('polygon').filter((overlay) => {
      const extData = overlay.getExtData();
      return extData && oldSitetBatchNumbers.includes(extData.batchNumber);
    });

    const oldSiteMarkerOverlays = amapObj.map.getAllOverlays('marker').filter((overlay) => {
      const extData = overlay.getExtData();
      return extData && oldSitetBatchNumbers.includes(extData.batchNumber);
    });
    const oldSiteOverlays = [...oldSitePolygonOverlays, ...oldSiteMarkerOverlays];
    amapObj.map.remove(oldSiteOverlays); // 移除最早批次的所有覆盖物
    batchSiteMapNumbers = newSiteMapBatchNumbers;
  }
});
// store.js文件
  // --- 加载网点
  onLoadSiteMapCompleted(res = {}) {
    const result = res.vo || {};
    // const newSiteMapFeatures = handleFeatures(this.state.siteMapListVo.features, result.features);

    this.setState({
      siteMapListVo: {
        features: result.features || [],
        type: 'FeatureCollection',
      },
    });
  },