问题描述

vue使用高德地图点标记,刚开始使用的是Marker,但是数目超过300,滑动就卡顿,按文档来说,Marker 类型推荐在数据量为 500 以内时使用,不应该卡顿。后边就开始对这个bug进行两天脑秃的探究了

1.换成 LabelMarker

既然Marker类型不行,看了文档, LabelMarker支持更多的点标记,那就换成 LabelMarker,然后自信满满的发布,结果还是不行。

2.与官方沟通寻求解决方案

按照文档写了代码,都不行,只能跟官方发工单沟通,我把项目中使用到的初始化代码贴给了官方,官方看完也说没问题,给了一个js的demo,运行了一下,确实不卡。demo代码贴一下

Labelmarker_Test.html

<!doctype html>
<html>

<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="initial-scale=1.0, user-scalable=no, width=device-width">
    <link rel="stylesheet" href="https://a.amap.com/jsapi_demos/static/demo-center/css/demo-center.css" />
    <title>地图显示</title>
    <style>
        html,
        body,
        #container {
            width: 100%;
            height: 100%;
        }
    </style>
</head>

<body>
    <div id="container"></div>
    <!-- 加载地图JSAPI脚本 -->
    <script src="https://webapi.amap.com/maps?v=2.0&key=自己的key"></script>
    <script src="/data.js"></script>

    <script>
        var map = new AMap.Map('container', {
            viewMode: '2D', // 默认使用 2D 模式,如果希望使用带有俯仰角的 3D 模式,请设置 viewMode: '3D',
            zoom: 11, //初始化地图层级
            center: [116.397428, 39.90923] //初始化地图中心点
        });
        let markers = [];
        for (var i = 0; i < data.length; i++) {
            var marker;
            let item = data[i];
            // console.log("地图marker", item);
            let imageUrl = 'https://a.amap.com/jsapi_demos/static/demo-center/marker/express2.png';
            var icon = {
                // 图标类型,现阶段只支持 image 类型
                type: "image",
                // 图片 url
                image: imageUrl,
                // 图片尺寸
                size: [44, 50],
                // 图片相对 position 的锚点,默认为 bottom-center
                anchor: "center",
            };

            var text = {
                // 要展示的文字内容
                content: item.orgName,
                // 文字方向,有 icon 时为围绕文字的方向,没有 icon 时,则为相对 position 的位置
                direction: "top",
                // 在 direction 基础上的偏移量
                offset: [0, 0],
                // 文字样式
                style: {
                    // 字体大小
                    fontSize: 12,
                    // 字体颜色
                    fillColor: "#000000",
                    //backgroundColor: "#ffffff",
                },
            };

            if (item.latitude && item.longitude) {
                let positionV = [item.longitude, item.latitude];
                marker = new AMap.LabelMarker({
                    name: "标注2", // 此属性非绘制文字内容,仅最为标识使用
                    position: positionV,
                    zIndex: i,
                    opacity: 1,
                    // 将第一步创建的 icon 对象传给 icon 属性
                    icon: icon,
                    // 将第二步创建的 text 对象传给 text 属性
                    text: text,
                    zooms: [3, 20],
                    allowCollision: false,
                });
            }
         
            var onMarkerClick = function (e) {
                console.log("marker 点击");
                that.hospital = item;
                that.sheetShow = true;
            };
            marker.on("click", onMarkerClick); //绑定click事件

            markers.push(marker);
        }

        var labelsLayer = new AMap.LabelsLayer({
            zooms: [3, 20],
            zIndex: 1000,
            // 该层内标注是否避让
            collision: true,
            // 设置 allowCollision:true,可以让标注避让用户的标注
            allowCollision: true,
        });
        labelsLayer.add(markers);
        map.add(labelsLayer);
    </script>
</body>

</html>

data.js

const data = [
    { unifiedOrgCode: "320211PD3854", orgName: "门诊部", branchCode: null, branchName: null, orgLevel: null, orgGrade: null, provinceCode: null, cityCode: null, orgAddress: "太湖新城金融街11号", latitude: 31.488750, longitude: 120.304928, introduction: null, orgUrl: "https://www.wxhealth.net:8241/File/GetFile/76c0c8d6be03c2e1", reserveNote: null, orgTraffic: null, feature: null, ticketDetial: null, isReservation: 1, appointDays: 7, canAppointToday: 1, doctorAppointDays: null, showOrder: null, accessType: null, h5Info: null, distance: "0.10", contactPhone: null, characteristics: null, orgType: "clinic", districtCode: "3201", economicType: 2 }, { unifiedOrgCode: "320211PD78", orgName: "同仁大药房", branchCode: null, branchName: null, orgLevel: null, orgGrade: null, provinceCode: null, cityCode: null, orgAddress: "区观山路38号", latitude: 31.491185, longitude: 120.302667, introduction: null, orgUrl: "https://www.wxhealth.net:8241/be03c2e1", reserveNote: null, orgTraffic: null, feature: null, ticketDetial: null, isReservation: 1, appointDays: 7, canAppointToday: 1, doctorAppointDays: null, showOrder: null, accessType: null, h5Info: null, distance: "0.40", contactPhone: null, characteristics: null, orgType: "clinic", districtCode: "320211", economicType: 2 },
]

有非常多的数据,我这边只贴出来数据结构。如果要模拟数据,可以使用循环遍历,然后动态修改经纬度的办法,比如

 demoData.forEach((element) => {
         //TODO 增加多条数据,测试是否卡顿
            let latTest = element.latitude;
            let lonTest = element.longitude;
            for (let i = 0; i < 50; i++) {
              element.latitude = latTest + 0.00001 * i;
              element.longitude = lonTest + 0.00001 * i;
              mapData.push(element);
            }
          });

是否只有Vue会卡顿

我试了官方给的js demo,数据多的时候确实不卡,所以我怀疑是不是vue的问题,我试着不用Vue的初始化,在vue中使用js那种方式来初始化
在public下的index.html中的 <head>里添加

  <script src="https://webapi.amap.com/maps?v=2.0&key=自己的key"></script> 

然后对应的Vue里使用

 var map = new AMap.Map('container', {
            viewMode: '2D', // 默认使用 2D 模式,如果希望使用带有俯仰角的 3D 模式,请设置 viewMode: '3D',
            zoom: 11, //初始化地图层级
            center: [116.397428, 39.90923] //初始化地图中心点
        });

但是还是不行,还是会卡顿

治标不治本的办法

既然技术上不好实现,那修改需求呢,比如对于大量数据,我分N组,比如四组吧,然后监听地图缩放事件,在某一范围,只显示第一组,然后在另一范围,显示第二组,每组显示的数据比较少,就不会出现卡顿,但是这是治标不治本的办法,如果数据很多呢,每组超过几千,岂不是还是很卡,所以放弃

不经意发现的bug

只能技术上解决了,按照官方代码,一点一点的删项目中的代码,看看到底是哪一块代码引起的卡顿,经过各种尝试,最终锁定了一个变量,这个变量是data里的localMap,我在map初始化以后,将this.localMap=map。这个在逻辑上是没有问题的,所以我刚开始并没有在意,但是确实是这段代码引起的卡顿,所以我反馈给官方,
在这里插入图片描述
然后官方给的回复:
在这里插入图片描述

所有地图相关的实例不要放在 vue 的可响应数据中,响应数据会劫持属性,地图的属性会被修改,另外,劫持的属性可能和渲染有关,那么会增加很多响应的计算,会很卡;

懂了吧,就是不能在地图相关的实例里,进行任何data里字段的赋值,否则就会很卡

怎样获得地图实例

我现在的需求是,我地图控件的上边,加了一个图标,用来设置居中位置,使用了setCenter方法,这个需要用到当前地图的对象,所以我想使用data里的字段与map关联,但是这样会卡顿,有什么办法拿到map的对象,在外层进行操作????

既然不能在地图实例代码里给外层字段赋值,想到的第一个办法就是把map对象return出去,然后用字段接收,但是呢,还是不行,再一个办法,就是通过方法,传入map对象赋值,还是不行,所以,继续与官方沟通,得到的回复是

地图加载后,把AMap挂载到window上面,后面直接从window中获取即可。

然后怎么才能获取到呢,试了几种办法,也没找到怎么获取AMap的setCenter方法,最后百度了一下,可以这样得到实例,将map与window关联,window.map=new AMap.Map(“container”, {…});然后在外层通过window.map对地图操作

  window.map.setCenter([120.318321, 31.497963]);
 initAMap() {
      
    
      AMapLoader.load({
        key: "自己的key", //设置您的key
        version: "2.0",
        plugins: ["AMap.ToolBar", "AMap.Driving"],
        AMapUI: {
          version: "1.1",
          plugins: [],
        },
        Loca: {
          version: "2.0",
        },
      })
        .then((AMap) => {
           window.map = new AMap.Map("container", {
            viewMode: "3D",
            zoom: 10,
            zooms: [2, 22],
            center: [120.318791, 31.497993],
          });
          let markers = [];
          let demoData = dataInJs();
          let mapData = [];

          demoData.forEach((element) => {
            // //TODO 增加多条数据,测试是否卡顿,正式发布删除
            let latTest = element.latitude;
            let lonTest = element.longitude;
            for (let i = 0; i < 10; i++) {
              element.latitude = latTest + 0.00001 * i;
              element.longitude = lonTest + 0.00001 * i;
              mapData.push(element);
            }
          });

          console.log("mapData", mapData);
          for (var i = 0; i < mapData.length; i++) {
            var marker;
            let item = mapData[i];
            let imageUrl =
              "https://a.amap.com/jsapi_demos/static/demo-center/marker/express2.png";

            var icon = {
              // 图标类型,现阶段只支持 image 类型
              type: "image",
              // 图片 url
              image: imageUrl,
              // 图片尺寸
              size: [44, 50],
              // 图片相对 position 的锚点,默认为 bottom-center
              anchor: "center",
            };

            var text = {
              // 要展示的文字内容
              content: item.orgName,
              // 文字方向,有 icon 时为围绕文字的方向,没有 icon 时,则为相对 position 的位置
              direction: "top",
              // 在 direction 基础上的偏移量
              offset: [0, 0],
              // 文字样式
              style: {
                // 字体大小
                fontSize: 12,
                // 字体颜色
                fillColor: "#000000",
              },
            };

            if (item.latitude && item.longitude) {
              let positionV = [item.longitude, item.latitude];
              marker = new AMap.LabelMarker({
                name: "标注2", // 此属性非绘制文字内容,仅最为标识使用
                position: positionV,
                zIndex: i,
                opacity: 1,
                // 将第一步创建的 icon 对象传给 icon 属性
                icon: icon,
                // 将第二步创建的 text 对象传给 text 属性
                text: text,
                zooms: [3, 20],
                allowCollision: false,
              });
            }

            var onMarkerClick = function (ee) {
              console.log("marker 点击" + ee);
            };
            marker.on("click", onMarkerClick); //绑定click事件

            markers.push(marker);
          }

          var labelsLayer = new AMap.LabelsLayer({
            zooms: [3, 20],
            zIndex: 1000,
            // 该层内标注是否避让
            collision: true,
            // 设置 allowCollision:true,可以让标注避让用户的标注
            allowCollision: true,
          });
          labelsLayer.add(markers);
            window.map.add(labelsLayer);
          //  this.setLocalMap(map);
          setTimeout(() => {
            console.log("定位");
             this.testMap();
          }, 1000);
         
        })
        .catch((e) => {
          console.log(e);
        });

      //return map;
    },

总结

标注就使用官方文档上的就可以了,如果还是卡顿,检查一下是否在map实例化的时候,有对data里字段的操作,比如this.localMap=map之类的。如果想要获取到map的实例,用于外层对地图的操作,可以给window.map,然后获取,将map实例与window关联,这样不会卡顿

Logo

前往低代码交流专区

更多推荐