vue使用高德地图点标记,刚开始使用的是Marker,但是数目超过300,滑动就卡顿,按文档来说,Marker 类型推荐在数据量为 500 以内时使用,不应该卡顿。后边就开始对这个bug进行两天脑秃的探究了
既然Marker类型不行,看了文档, LabelMarker支持更多的点标记,那就换成 LabelMarker,然后自信满满的发布,结果还是不行。
按照文档写了代码,都不行,只能跟官方发工单沟通,我把项目中使用到的初始化代码贴给了官方,官方看完也说没问题,给了一个js的demo,运行了一下,确实不卡。demo代码贴一下
Labelmarker_Test.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);}});
我试了官方给的js demo,数据多的时候确实不卡,所以我怀疑是不是vue的问题,我试着不用Vue的初始化,在vue中使用js那种方式来初始化
在public下的index.html中的 里添加
然后对应的Vue里使用
var map = new AMap.Map('container', {viewMode: '2D', // 默认使用 2D 模式,如果希望使用带有俯仰角的 3D 模式,请设置 viewMode: '3D',zoom: 11, //初始化地图层级center: [116.397428, 39.90923] //初始化地图中心点});
但是还是不行,还是会卡顿
既然技术上不好实现,那修改需求呢,比如对于大量数据,我分N组,比如四组吧,然后监听地图缩放事件,在某一范围,只显示第一组,然后在另一范围,显示第二组,每组显示的数据比较少,就不会出现卡顿,但是这是治标不治本的办法,如果数据很多呢,每组超过几千,岂不是还是很卡,所以放弃
只能技术上解决了,按照官方代码,一点一点的删项目中的代码,看看到底是哪一块代码引起的卡顿,经过各种尝试,最终锁定了一个变量,这个变量是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", //设置您的keyversion: "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",// 图片 urlimage: imageUrl,// 图片尺寸size: [44, 50],// 图片相对 position 的锚点,默认为 bottom-centeranchor: "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关联,这样不会卡顿