高德地图上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。

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

相关推荐
ObjectX前端实验室5 小时前
【react18原理探究实践】异步可中断 & 时间分片
前端·react.js
SoaringHeart5 小时前
Flutter进阶:自定义一个 json 转 model 工具
前端·flutter·dart
努力打怪升级5 小时前
Rocky Linux 8 远程管理配置指南(宿主机 VNC + KVM 虚拟机 VNC)
前端·chrome
brzhang5 小时前
AI Agent 干不好活,不是它笨,告诉你一个残忍的现实,是你给他的工具太难用了
前端·后端·架构
brzhang5 小时前
一文说明白为什么现在 AI Agent 都把重点放在上下文工程(context engineering)上?
前端·后端·架构
reembarkation5 小时前
自定义分页控件,只显示当前页码的前后N页
开发语言·前端·javascript
gerrgwg6 小时前
React Hooks入门
前端·javascript·react.js
ObjectX前端实验室6 小时前
【react18原理探究实践】调度机制之注册任务
前端·react.js
汉字萌萌哒6 小时前
【 HTML基础知识】
前端·javascript·windows
ObjectX前端实验室7 小时前
【React 原理探究实践】root.render 干了啥?——深入 render 函数
前端·react.js