高德地图上marker过多(超过3000个)渲染卡顿过慢问题解决

背景

要使用高德地图在中国地图上标注出业务点(大概2000-3000个)。一开始,我们用的mock数据,界面没有卡死。后续我们接入了接口数据,发现界面卡死。此时,地图上要标注的点已经有2000-3000个。

因此,我在下面列出了两种方案和相关源代码。

方案一:遍历数据,使用marker直接渲染。(造成卡顿)

方案二:使用LabelsLayer(标注图层)和LabelMarker(标注)渲染。(轻松渲染2000-3000个数据点)
注意:高德地图版本我们使用的是1.4.15,在标点时,地图的viewMode为2D时标注建议在 3000 点内,viewMode为3D标注建议在 30000 点内。

方案一:遍历数据,使用marker直接渲染的方法

  1. vue文件如下
js 复制代码
 <template>
  <div class="static-china-content">
    <div class="static-result-content-wrapper">
      <div class="static-result-right">
        <div ref="amap" class="amap-inner" style="height: 100%"></div>
      </div>
    </div>
  </div>
</template>

<script>
import testJson from "./test.json"; // 这里是点的数据 
import mapMarkers1 from "@/assets/statistic-red-map.png"; // 这里使用的打点的图片
export default {
  name: "MapDemo",
  data() {
    return {
      markerList: [],
      amap: null,
      styleName: "amap://styles/e925346429e081692612c396263b6ce6",
      colors: ["#FF0303", "#FE6F14", "#FFC000", "#205AEC", "#008000"],
      mapMarkers: mapMarkers1,
    };
  },
  mounted() {
      // 这里链接里的key值要换成自己的
    this.loadScript(
      "https://webapi.amap.com/maps?v=1.4.15&key=xxxxxx&plugin=AMap.Scale,AMap.OverView,AMap.ToolBar",
      () => {
        this.initAmap();
        this.handleSearch();
      }
    );
  },
  methods: {
      // 加载地图工具函数
    loadScript(src, callback) {
      // 在你的 Vue 组件或 utils 文件夹中创建 loadScript 函数
      // 检查是否已经存在该脚本
      if (document.querySelector(`script[src="${src}"]`)) {
        callback && callback();
        return;
      }

      // 创建 script 标签
      const script = document.createElement("script");
      script.type = "text/javascript";
      script.src = src;

      // 添加错误处理
      script.onerror = () => {
        console.error(`Failed to load script: ${src}`);
        callback && callback(new Error("Script load failed"));
      };

      // 确保在加载完成后调用回调函数
      script.onload = () => {
        console.log(`Script loaded successfully: ${src}`);
        callback && callback(null);
      };

      // 将 script 标签添加到 head 中
      document.head.appendChild(script);
    },
    openInfo(item, position) {
      //构建信息窗体中显示的内容
      let list = `<ul><li><label>发布时间:</label><span>${item?.createTime}</span></li></ul>`;
      var content = `<div class="statistic-station-info">${list}</div>`;

      const infoWindow = new AMap.InfoWindow({
        content: content,
        offset: new AMap.Pixel(0, 0),
        retainWhenClose: true,
      });

      infoWindow.open(this.amap, position);
    },
    // 初始化高德地图
    initAmap() {
      let map = this.$refs.amap;

      this.amap = new AMap.Map(map, {
        // center: [this.longitude, this.latitude],
        zoom: 4, //地图级别
        //  mapStyle: this.styleName, //设置地图的显示样式
        viewMode: "2D", //设置地图模式
        lang: "zh_cn", //设置地图语言类型
        resizeEnable: true,
        showBuildingBlock: false,
        layers: [new AMap.TileLayer.Satellite()],
        // features: ["bg", "road", "admin", "water"],
      });
    },
    setMarker(item) {
      let marker = new AMap.Marker({
        icon: new AMap.Icon({
          image: this.mapMarkers,
          size: new AMap.Size(12, 13), //图标所处区域大小
          imageSize: new AMap.Size(12, 13), //图标大小
        }),
        position: new AMap.LngLat(item.eventLongitude, item.eventLatitude),
        // position: new AMap.LngLat(116.39, 39.9), // 经纬度对象,也可以是经纬度构成的一维数组[116.39, 39.9]
      });

      marker.on("click", () =>
        this.openInfo(item, [item.eventLongitude, item.eventLatitude])
      );

      // 将创建的点标记添加到已有的地图实例:
      this.amap.add(marker);
    },
    async handleSearch() {
      this.tableData = testJson?.dataset?.datas;
      this.tableData?.MapData.forEach((item) => this.setMarker(item));
      // this.setLabelsLayer(this.tableData?.MapData);
      this.amap.setFitView();
    }
  },
};
</script>

<style lang="less" scoped>
.static-result-right {
  width: 800px;
  height: 800px;
  .amap-inner {
    width: 100%;
    height: 100%;
  }
}
</style>

2.test.json数据如下

test.json 复制代码
{
  "success": true,
  "dataset": {
    "datas": {
      "MapData": [
        {
          "eventLatitude": 29.3243,
          "eventLongitude": 114.4567,
        }
      ]
   }
 }
}

但是,可以看到当我们的点过多时,页面就会卡顿,甚至卡死。所以面对点多的情况,我们下面使用方案二。

方案二:遍历数据,使用marker直接渲染的方法

1.test.json文件此处省略,该文件中地图的点有14000+个。 2.vue文件

js 复制代码
<template>
  <div class="static-china-content">
    <div class="static-result-content-wrapper">
      <div class="static-result-right">
        <div ref="amap" class="amap-inner" style="height: 100%"></div>
      </div>
    </div>
  </div>
</template>

<script>
import testJson from "./test.json"; // 这里是点数据
import mapMarkers1 from "@/assets/statistic-red-map.png";
export default {
  name: "MapDemo",
  data() {
    return {
      markerList: [],
      amap: null,
      styleName: "amap://styles/e925346429e081692612c396263b6ce6",
      colors: ["#FF0303", "#FE6F14", "#FFC000", "#205AEC", "#008000"],
      mapMarkers: mapMarkers1,
    };
  },
  mounted() {
   // 这里链接里的key值要换成自己的
    this.loadScript(
      "https://webapi.amap.com/maps?v=1.4.15&key=xxxxxx&plugin=AMap.Scale,AMap.OverView,AMap.ToolBar",
      () => {
        this.initAmap();
        this.handleSearch();
      }
    );
  },
  methods: {
    loadScript(src, callback) {
      // 在你的 Vue 组件或 utils 文件夹中创建 loadScript 函数

      // 检查是否已经存在该脚本
      if (document.querySelector(`script[src="${src}"]`)) {
        callback && callback();
        return;
      }

      // 创建 script 标签
      const script = document.createElement("script");
      script.type = "text/javascript";
      script.src = src;

      // 添加错误处理
      script.onerror = () => {
        console.error(`Failed to load script: ${src}`);
        callback && callback(new Error("Script load failed"));
      };

      // 确保在加载完成后调用回调函数
      script.onload = () => {
        console.log(`Script loaded successfully: ${src}`);
        callback && callback(null);
      };

      // 将 script 标签添加到 head 中
      document.head.appendChild(script);
    },
    openInfo(item, position) {
      //构建信息窗体中显示的内容
      let list = `<ul>
                    <li><label>发布时间:</label><span>${item?.createTime}</span></li></ul>`;
      var content = `<div class="statistic-station-info">
                        ${list}
                    </div>`;

      const infoWindow = new AMap.InfoWindow({
        content: content,
        offset: new AMap.Pixel(0, 0),
        retainWhenClose: true,
      });

      infoWindow.open(this.amap, position);
    },
    // 初始化高德地图
    initAmap() {
      let map = this.$refs.amap;

      this.amap = new AMap.Map(map, {
        // center: [this.longitude, this.latitude],
        zoom: 4, //地图级别
        //  mapStyle: this.styleName, //设置地图的显示样式
        viewMode: "2D", //设置地图模式
        lang: "zh_cn", //设置地图语言类型
        resizeEnable: true,
        showBuildingBlock: false,
        layers: [new AMap.TileLayer.Satellite()],
      });
    },
    async handleSearch() {
      this.tableData = testJson?.dataset?.datas;
      this.setLabelsLayer(this.tableData?.MapData);
      this.amap.setFitView();
    },
    setLabelsLayer(positions) {
      const markers = [];
       // 创建 AMap.LabelsLayer 图层
      const layer = new AMap.LabelsLayer({
        zooms: [3, 20],
        zIndex: 111,
        // 关闭标注避让,默认为开启,v1.4.15 新增属性
        animation: false,
        // 关闭标注淡入动画,默认为开启,v1.4.15 新增属性
        collision: false,
      });
      // 将图层添加到地图
      this.amap.add(layer);
      for (let i = 0; i < positions.length; i++) {
        const curPosition = positions[i];
        var icon = {
          type: "image",
          image: this.mapMarkers,
          size: [6, 9],
          anchor: "bottom-center",
          angel: 0,
          retina: true,
        };
        if (!curPosition?.eventLatitude || !curPosition?.eventLongitude) {
          continue;
        }
        const curData = {
          position: [curPosition?.eventLongitude, curPosition?.eventLatitude],
          icon,
          offset: new AMap.Pixel(-6, -13),
        };
        const labelMarker = new AMap.LabelMarker(curData);
        // 事件
        labelMarker.on("click", () =>
          this.openInfo(curPosition, labelMarker.getPosition())
        );
        markers.push(labelMarker);
      }
      // 一次性将海量点添加到图层
      layer.add(markers);
    },
  },
};
</script>

<style lang="less" scoped>
.static-result-right {
  width: 800px;
  height: 800px;
  .amap-inner {
    width: 100%;
    height: 100%;
  }
}
</style>

此时,地图上展示14000+个点是没有什么压力的。

写在最后

文章对比了两种针对海量数据打点在高德地图上展示的方案,对比过后,方案二更适合数据点多时的情况。

文章中使用的高德地图的版本是1.4.15。

文章参考链接:# 标注和标注图层-海量点

相关推荐
若梦plus5 分钟前
Nuxt.js基础与进阶
前端·vue.js
樱花开了几轉11 分钟前
React中为甚么强调props的不可变性
前端·javascript·react.js
风清云淡_A11 分钟前
【REACT18.x】CRA+TS+ANTD5.X实现useImperativeHandle让父组件修改子组件的数据
前端·react.js
小飞大王66612 分钟前
React与Rudex的合奏
前端·react.js·前端框架
若梦plus40 分钟前
React之react-dom中的dom-server与dom-client
前端·react.js
若梦plus42 分钟前
react-router-dom中的几种路由详解
前端·react.js
若梦plus42 分钟前
Vue服务端渲染
前端·vue.js
Mr...Gan1 小时前
VUE3(四)、组件通信
前端·javascript·vue.js
OEC小胖胖1 小时前
渲染篇(二):解密Diff算法:如何用“最少的操作”更新UI
前端·算法·ui·状态模式·web
万少1 小时前
AI编程神器!Trae+Claude4.0 简单配置 让HarmonyOS开发效率飙升 - 坚果派
前端·aigc·harmonyos