位置: IT常识 - 正文

高德地图「海量点标记 + 海量标注」卡顿问题 解决方案(高德地图海量点图层刷新)

编辑:rootadmin
高德地图「海量点标记 + 海量标注」卡顿问题 解决方案

推荐整理分享高德地图「海量点标记 + 海量标注」卡顿问题 解决方案(高德地图海量点图层刷新),希望有所帮助,仅作参考,欢迎阅读内容。

文章相关热门搜索词:高德地图海量点,高德地图海量点标记,高德地图-精准地图,导航出行必备,高德地图海量点,高德地图海量点图层刷新,高德地图海量点,高德地图海量点图层刷新,高德地图海量点卡顿问题,内容如对您有帮助,希望把文章链接给更多的朋友!

        最近刚做了个和地图相关的需求,涉及到「海量点标记 + 海量标注」。当数据量达到三千以上的时候,「海量标注」会明显拖慢页面的加载/响应速度,非常影响用户体验,因此我对其进行了优化。感觉还挺有挑战性的,在这里总结一下,如果再能给遇到同样问题的你一点帮助的话,那就再好不过啦!😉

        🚩 如果你所做的需求仅涉及到「海量点标记」,那你不需要看这篇博客,因为高德地图的「海量点标记」已经优化的很好了,它可以为数量在万级以上的点标记提供良好的解决方案,这里有高德地图海量点标记的「官方文档」和「实例」可以参考。

        🚩 本篇博客针对数据量较大时,「海量标注」会拖慢页面的加载/响应速度的问题做了分析和优化实践,其中心思想分为以下几点:

通过将 海量标注 延迟加载(懒加载)的方式,加快页面首屏渲染速度检测 海量标注 中的数据项,判断其坐标是否在浏览器视口区域,进行分片渲染监听地图缩放及移动事件,先删除原标注图层,再根据第二步渲染新图层对 海量标注 中的公共部分进行提取,通过 海量点标记 的方式渲染,减少 DOM 节点数针对点击后高亮被选中的标注,单独添加图层进行叠加,减少第三步带来的延迟1、适用场景说明

        这里大致描述一下我所做的需求,大家可以类比一下,进而判断是否是适用于你 🤔️

        我的需求是这样的:用户进入页面时先加载地图,并在地图上展示一些点标记。当地图被放大到一定倍数的时候,隐藏点标记图层,展示标注图层。标注图层里的每个标注有一个小的图点标识图和气泡组成,气泡内含有一些数据信息。当用户点击某个气泡时,需要高亮当前气泡,并将其展示在最上层。

        效果图大致如下:

 2、地图及海量点标记

        下面就从地图搭建开始,一步步实现上面的功能,然后对页面卡顿问题进行优化。因为几乎都是调用高德地图提供的 API,所以我在这里只粘贴部分代码,算是提供个思路吧 🤓️

        关于初始化地图和设置「海量点标记」的主要是思路如下:

geocoder = new AMap.Geocoder({ city: cityId // 支持传入城市名、adcode 和 citycode})geocoder.getLocation(cityName.value, function(status, result) { if (status === 'complete' && result.info === 'OK') { map = new AMap.Map('map', { resizeEnable: true, //是否监控地图容器尺寸变化 zoom: 10, //初始化地图层级 center: [result.geocodes[0].location.lng, result.geocodes[0].location.lat], //初始化地图中心点 }); // 展示地图 map setMassMarks() // 设置海量点标记 marker }})

        设置「海量点标记」的方法如下:

setMassMarks() { const lowPriceIconStyle = { url: priceIconMap.low, size: new AMap.Size(30, 30), anchor: 'center' } const normalIconStyle = { url: priceIconMap.normal, size: new AMap.Size(30, 30), anchor: 'center' } const highPriceIconStyle = { url: priceIconMap.high, size: new AMap.Size(30, 30), anchor: 'center' } const styleObjectArr = [lowPriceIconStyle, normalIconStyle, highPriceIconStyle] const markerList = houseList.map(item => ({ lnglat: item.location.split(','), name: item.name, id: item.id, style: item.level - 1 })) massMarks = new AMap.MassMarks(markerList, { zIndex: 500, // 海量点图层叠加的顺序 zooms: [3, 14], // 在指定地图缩放级别范围内展示海量点图层 style: styleObjectArr // 设置样式数组 }); massMarks.setMap(map);}

       上面 ⬆️ 两段代码主要是先初始化地图,然后用 cityName 通过 AMap.Geocoder (高德地图的编码转换器) 获取城市的具体坐标,从而设置地图的中心点坐标。然后通过高德的 AMap.MassMarks 方法设置「海量点标记」展示在 3-14 zoom 层级,再通过 style 参数做点标记的样式区分。

3、海量点标注

        页面一进入的「海量点标记」图层已经实现了,下面是设置「海量标注」:

setLabelsLayer() { labels = [] houseList.map(item => { const normalMarker = new AMap.Marker({ zooms: [14, 20], offset: new AMap.Pixel(0, -15), extData: item }); normalMarker.setContent( `<div class="amap-info-window"> <div class="amap-info-title">${item.name}</div> <div class="amap-info-price">${item.location}</div> <img class="amap-info-dot" src=""> </div>`); normalMarker.setPosition(item.location.split(',')) normalMarker.on('click', function(e){ setNormalMarkerSelected(e.target.getExtData().id) }); labels.push(normalMarker) }) map.add(labels)}

        关于「海量标注」,高德地图的官方解释是:当需要在地图添加千级以上的点标记时,LabelMarker 是代替 Marker 的更好选择。不同于 MassMarks ,LabelMarker 不仅可以绘制图标,还可以为图标添加文字信息,且万级以上数据也具有较好性能,配置也更加灵活。

        但是其实性能并没有那么好,当数据量达到三千级以上的时候,LabelMarker 的画面流畅程度大大缩减了,基本上是用户所不能接受的地步,因此我们需要对其进行优化。

        ps:不知道是不是我打开方式有问题,有没有人使用过「海量标注」,并像官方所说的那样:万级以上数据也具有较好性能?我标记了三千多个标注的时候页面的响应速度就会超级卡,如果大家有更好的思路的话请指教 😍

4、卡顿优化实践

        先说一下我们上面的思路吧:进入页面加载地图,并设置「海量点标记」和 「海量标注」。

       ⬆️ 这算是一个最简易版本,虽然我们已经采用了高德地图所提倡的「海量点标记」和 「海量标注」,但是无论是页面的首屏加载速度还是用户交互的响应速度都很慢,为了增强用户体验,我们需要在上面思路的基础上进行优化!

       1、针对首屏加载速度的优化:其实用户一进入到页面看到的是「海量点标记」的图层,「海量标注」图层是隐藏在后面的,当用户放大地图的时候才会展示出来。因此,我们可以先把「海量标注」的加载放到后面,这样可以加快页面首屏渲染速度。

       既然要把「海量标注」放在后面加载,那我们就需要监听地图的缩放事件,当地图放大到一定级别时就去加载「海量标注」图层:

setMapListener() { map.on('zoomend', () => { // 监听地图缩放结束后的等级 zoom = map.getZoom() if (zoom >= 14) { setLabelsLayer() // 设置海量标注 具体代码在上面 } })}

        把「海量标注」图层的加载时机放在后面虽然可以加快首屏的渲染速度,但是当数据量较大的时候,一次性加载超多「海量标注」的 DOM 元素会让浏览器的渲染引擎吃不消的。既然这样我们可不可以不要一次性加载那么多?先把视口区域的标注加载出来?

       2、针对海量标注的分片加载优化:检测「海量标注」中的数据项,判断其坐标是否在浏览器视口区域,从而进行分片渲染:

executeConditionRender() { let screenCoordinateRange = map.getBounds() let northEast = [screenCoordinateRange.northEast.lng, screenCoordinateRange.northEast.lat] let southEast = [screenCoordinateRange.southWest.lng, screenCoordinateRange.northEast.lat] let southWest = [screenCoordinateRange.southWest.lng, screenCoordinateRange.southWest.lat] let northWest = [screenCoordinateRange.northEast.lng, screenCoordinateRange.southWest.lat] screenHouseList = houseList.filter(item => { return AMap.GeometryUtil.isPointInRing(item.location.split(','), [northEast, southEast, southWest, northWest]) }) setLabelsLayer() // 设置海量标注 具体代码在上面,用 screenHouseList 替换 houseList}

        上述代码中,我们可以通过高德地图提供的 getBounds 方法,拿到当前视口中地图的东北角和西南角的坐标,进而可以算出视口的四个点坐标,然后通过 AMap.GeometryUtil.isPointInRing 方法,判断海量标注图层中的数据是否在视口范围内,进行分片加载。

        除此之外我们还需要监听用户的滑动事件,当用户滑动地图之后,重新执行如上逻辑:

const setMapListener = () => { map.on('zoomend', () => { // 监听地图缩放结束后的等级 zoom = map.getZoom() if (zoom >= 14) { executeConditionRender() } }) map.on('moveend', () => { // 监听地图中心点的位置变化 if (zoom >= 14) { executeConditionRender() } })}高德地图「海量点标记 + 海量标注」卡顿问题 解决方案(高德地图海量点图层刷新)

       我们上述的编码思想是:当用户放大或移动视口时,先判断当前 zoom 是否在海量标注展示的级别,如果是的话就加载当前视口内的标注。但是这样会产生一个问题,标注图层的数量会随着用户的操作次数的增加而堆积,这样也是会影响页面流畅程度的,因此我们需要对其进行优化。

        3、针对海量标注图层堆积问题的优化:在下一次加载视口区域内的标注图层之前,我们需要先把之前的图层删除掉,这样就可以避免图层堆积的问题:

executeConditionRender() { let screenCoordinateRange = map.getBounds() let northEast = [screenCoordinateRange.northEast.lng, screenCoordinateRange.northEast.lat] let southEast = [screenCoordinateRange.southWest.lng, screenCoordinateRange.northEast.lat] let southWest = [screenCoordinateRange.southWest.lng, screenCoordinateRange.southWest.lat] let northWest = [screenCoordinateRange.northEast.lng, screenCoordinateRange.southWest.lat] screenHouseList = houseList.filter(item => { return AMap.GeometryUtil.isPointInRing(item.location.split(','), [northEast, southEast, southWest, northWest]) }) map.remove(labels) // 删除原来气泡层的海量标注 setLabelsLayer() // 设置海量标注 具体代码在上面,用 screenHouseList 替换 houseList}

        到这里,我们主要的优化工作已经做的差不多了,页面已经是比较流畅的了。上面说的「中心思想」中的第四步(提取公共标识图)和第五步(高亮选中的标注)相对来说比较简单了,这里就简单说一下吧:

       4、针对屏内标注 DOM 元素过多的优化:第四步(提取公共标识图)是因为每个标注都带一个小的圆点图片,每个小圆点图片都是一个 DOM 元素,当海量标注比较密集的时候,一屏内也能有成百上千个标注,也就会有成百上千个小圆点图片的 DOM 元素,这会给页面绘制速度增加负担的,这里也是可以优化一下的。

        对 海量标注 中的公共部分进行提取,通过 海量点标记 的方式渲染,减少 DOM 元素,相当于是借用了高德地图的海量点标记来帮我们做的一次优化!具体代码比较简单,这里就不贴了,只是新加了一个圆点标记图层。

        5、针对高亮标注数据处理延迟的优化:我们之前的第三步优化是针对海量标注图层堆积的问题处理,它其实做了两件事:先删除原来的图层,然后再遍历一下原视口中的标注点,将被点击的标注进行高亮样式处理,没被点击的标注进行重新绘制。

        上述这种处理方法其实是有优化空间的,我的想法是通过单独添加高亮标注图层来节省原图层的重新计算和渲染,这样就相当于节省了第三步中删除图层和重新绘制的成本,可能不是很形象,我们画一下大致流程就很好明白了:

// 优化前:用户点击标注 ➡️ 删除旧图层 ➡️ 重新计算(点击的标注设置高亮,普通标注重新绘制) ➡️ 页面添加新的标注图层// 优化后:用户点击标注 ➡️ 删除旧的高亮图层 ➡️ 重新设置高亮图层 ➡️ 页面添加新的标注图层

        这样看起来直观很多,其实就是相当于节省了一部分「重新计算」的成本。

5、优化效果对比

        在数据量较大的情况下(我的数据量在3500+),通过上述的五个步骤的优化之后,页面的各方面性能都有了明显的提升,下面我们做一下优化前后的性能对比,一起感受一下优化之后的丝滑吧!😏

        1、首屏加载时间对比:这也是对上述「1、针对首屏加载速度的优化」的优化验证,通过将「海量标注」延迟加载的方式,大大加快了页面首屏的加载速度,对比图如下:

         没有将「海量标注」做推迟加载处理时,页面首屏加载地图展示出来的时间在 12s 左右,而且还是不完全展示(只展示了海量点标记,地图没真正展示出来)。优化之后,地图和「海量点标记」完全加载出来只用了 3s,这个时间虽然不能说很快,但是 3s 的时间普通用户是完全可以接受的。优化之后,首屏的加载时间可以说提升了 4 倍!amazing!😲

        2、帧率统计对比:这也是对上述「2、针对海量标注的分片加载优化」的优化验证,通过只展示屏内的标注,可以大大缩减海量标注的数量,进而在用户和页面发生交互行为时,页面的响应速度会更快、帧率更高,对比图如下:

        在没做「海量标注」分片加载时, 一次性将大量的标注信息渲染出来,不仅需要很长的加载时间,而且页面上的标注节点太多,会导致用户和页面发生交互行为之后,页面的响应速度很慢,平均帧率只有不到 10 fps。优化之后,平均帧率可以接近 30 fps。要知道,人眼在 24帧/s 的情况下就会产生暂留效果,30 fps 可以说是很流畅了!wonderful!😲

        3、DOM 节点数对比:这是对上述「2、针对海量标注的分片加载优化 + 3、针对海量标注图层堆积问题的优化 + 4、针对屏内标注 DOM 元素过多的优化」的优化验证,因为上述优化点都有对减少 DOM 节点直接或间接产生贡献,对比图如下: 

         没有做上述「2 / 3 / 4」步骤点优化时,页面的 DOM 节点数量在 40000+,由于页面的 DOM 节点较多,所以当用户和页面发生交互行为时,CPU的占用率在 90% 以上,JS 的内存体积也在 250M+,进而导致页面卡顿明显。优化之后,可以将页面上的 DOM 节点数控制在 6000 以下,此时当用户和页面交互时,CPU 的占用率在 50% 左右,JS 的内存体积在 100M 以下,因此页面交互会流畅很多!😈

        4、整体性能统计对比:通过上述五个步骤的优化,页面的各方面性能都有了一个明显的提升,我们看一下整体的 Summary 看板的统计指标,对比图如下:

优化前后 Summary 指标对比指标优化前优化后Loading255ms8msScripting5122ms2118msRendering3843ms103msPainting33ms35msTotal12956ms4306ms

       通过 Summary 的各项指标以及真实的使用体验可以看出,上述所做的五个优化步骤效果还是很明显的,无论是页面首屏加载速度,还是交互响应速度,以及帧率、DOM 节点数...,各方面性能都有一个明显的提升。What a wonderful world!🎉

6、写在最后

      这是第一次做和地图强相关的需求,一开始以为没什么难度,就是照着高德 API 抄一抄就行了,没想到数据量较大时还是有很多可以钻研和学习的地方,真是 surprise 🤒️

        做这个需求几天里,心里一直被这个事情占据着,觉都没睡好,做梦都是考试找不到考场 😭。不过最后还是搞定了,心里不由的开心和兴奋,感觉自己又进步了!

        一开始遇到海量标注卡顿的问题,我先请教公司前辈,但是没人用过海量标注,进而也就没遇到过卡顿的问题。但是同事们也给了些思路,我也静下心来慢慢做了分析,也从网上找到了点优化思路,感觉各种优化的方法还是有迹可循的。

        毕业工作快一年了,这个需求也算是我在无人指导的情况下独自完成的,完成之后非常有成就感的,我也把它视为一个我成长的里程碑。Keep running!

        说实话,工作之后自己的时间却是少了,主动学习写博客的次数也越来越少,想想毕业时给自己定的目标「踏踏实实学点前端,以后做一名专业的前端开发工程师」,着实感觉有点惭愧,反思一下自己,还是得持续学习啊!

        ps:写这篇博客时,感觉好像回到了写毕业论文的时候 😂

本文链接地址:https://www.jiuchutong.com/zhishi/287337.html 转载请保留说明!

上一篇:白沙国家公园中的石膏沙丘,新墨西哥 (© Grant Kaye/Cavan Images)(白沙国家公园中标公告)

下一篇:uniApp常见面试题(uniapp相关面试题)

  • qq扩列功能怎么找不到了2021(qq扩列功能怎么关闭了2023)

    qq扩列功能怎么找不到了2021(qq扩列功能怎么关闭了2023)

  • 三星手机系统更新在哪里(三星手机系统更新几年)

    三星手机系统更新在哪里(三星手机系统更新几年)

  • 华为p40pro听筒是否是屏幕振动发声的

    华为p40pro听筒是否是屏幕振动发声的

  • 苹果提醒事项内容没了(苹果提醒事项内存)

    苹果提醒事项内容没了(苹果提醒事项内存)

  • 微博会员苹果无法购买(苹果开微博会员不生效)

    微博会员苹果无法购买(苹果开微博会员不生效)

  • iqooz1是什么手机(iqooz1是什么型号)

    iqooz1是什么手机(iqooz1是什么型号)

  • 抖音怎么买僵尸粉?(抖音怎么买僵尸号)

    抖音怎么买僵尸粉?(抖音怎么买僵尸号)

  • 什么是直播电商(什么是直播电商中的人货场)

    什么是直播电商(什么是直播电商中的人货场)

  • 小米mix2机身尺寸(小米mix 2尺寸)

    小米mix2机身尺寸(小米mix 2尺寸)

  • 手机号码挂失后别人还能用吗(手机号码挂失后号码还可以用吗)

    手机号码挂失后别人还能用吗(手机号码挂失后号码还可以用吗)

  • qq看点评论区怎么加好友(qq看点评论区怎么恢复)

    qq看点评论区怎么加好友(qq看点评论区怎么恢复)

  • 华为电量剩20提示音关闭(华为电池剩百分20掉电快)

    华为电量剩20提示音关闭(华为电池剩百分20掉电快)

  • 怎样给ppt幻灯片添加标题(ppt怎么弄幻灯片)

    怎样给ppt幻灯片添加标题(ppt怎么弄幻灯片)

  • 为什么原相机拍的照片是反的(为什么原相机拍照脸上有斑)

    为什么原相机拍的照片是反的(为什么原相机拍照脸上有斑)

  • 三摄像头有什么用(什么叫三摄像头手机)

    三摄像头有什么用(什么叫三摄像头手机)

  • itunes备份文件在哪(itunes备份文件在电脑查看)

    itunes备份文件在哪(itunes备份文件在电脑查看)

  • 把通讯录导到微信上去(怎样把通讯录转到微信列表)

    把通讯录导到微信上去(怎样把通讯录转到微信列表)

  • 抖音长腿特效拍摄教程(抖音拍摄长腿特效在哪里)

    抖音长腿特效拍摄教程(抖音拍摄长腿特效在哪里)

  • 小米cc9pro支持无限充电吗(小米cc9pro有没有无线充电)

    小米cc9pro支持无限充电吗(小米cc9pro有没有无线充电)

  • 拼多多拼单成功会怎样(拼多多拼单成功返现是真的吗)

    拼多多拼单成功会怎样(拼多多拼单成功返现是真的吗)

  • 微信二维码收款收费吗(微信二维码收款限额是多少)

    微信二维码收款收费吗(微信二维码收款限额是多少)

  • 快手背景图怎么删除(快手背景图怎么制作)

    快手背景图怎么删除(快手背景图怎么制作)

  • ps怎么弄曝光(ps怎样曝光)

    ps怎么弄曝光(ps怎样曝光)

  • qq音乐怎么分享歌单(qq音乐怎么分享一起听歌)

    qq音乐怎么分享歌单(qq音乐怎么分享一起听歌)

  • 华为p30pro电池容量(华为P30pro电池容量多大)

    华为p30pro电池容量(华为P30pro电池容量多大)

  • 骑自行车的好处和坏处(骑自行车的好处功效与作用)

    骑自行车的好处和坏处(骑自行车的好处功效与作用)

  • 帝国cms源码怎么安装(帝国cms商城源码)

    帝国cms源码怎么安装(帝国cms商城源码)

  • 调整税率后增值税发票的开具
  • 运输企业内账
  • 百旺税控盘汇总表怎么看
  • 工会刻章费用计入什么支出
  • 发票丢了还能再开么
  • 发出商品的会计核算
  • 融资租赁租入固定资产折旧可以税前扣除吗
  • 交易性金融资产公允价值变动计入
  • 发微信验证消息
  • 纳税申报表如何导出
  • 国税申报需要带什么资料
  • 差旅补贴需要缴纳个税吗
  • 开出增值税发票没收到怎么抵扣进项税?
  • 企业所得税季报和年报的区别
  • 平台服务费如何开票
  • 私车公用税务处理
  • 报税申报不了
  • 如何处理走逃失的人
  • 增加以前年度收入是否需要更正申报年报
  • 财务会计怎么学好
  • 劳务公司包工包料的法律法规
  • 差额纳税的会计处理
  • 客户扣了一部分不合格产品的货款,请问如何做账呢?
  • 银行的贷款损失准备会计分录
  • win8.1技巧
  • 股东实缴出资的证明
  • php运用于哪些领域
  • 坏账准备怎么做会计科目
  • 抵债资产账务处理程序
  • 不动产经营租赁包括哪些内容
  • 免征税费需要申报吗
  • neoDVDstd.exe - neoDVDstd是什么进程 有什么用
  • 基建期土地摊销
  • 结算应付职工薪酬怎么算
  • 报销生育险怎么报
  • LNMP部署laravel以及xhprof安装使用教程
  • Aerial view of Chapel Bridge over the river Reuss in Lucerne, Switzerland (© Neleman Initiative/Gallery Stock)
  • laravel框架最新版本
  • 即征即退进项税转出
  • 黑色金属冶炼压延品的税收编码是多少
  • 固定资产多少可以上市
  • 取得了水电费发票怎么做
  • 筹建期所得税年度申报如何填列
  • 小规模纳税人的个人所得税怎么算
  • 资产管理公司的监管部门是谁
  • 普通发票税额没有打印全可以用么
  • 一般纳税人跨年冲红报年度所得税怎么做
  • 收入费用表格式
  • 核销单取消后出口收汇流程
  • 应付职工薪酬的工资是实发工资还是应发工资
  • 购车税费怎样计算2023
  • 员工奖励账务处理流程
  • 贴现法年末偿还金额
  • 厂房装修费用账务处理
  • 房屋租赁合同税率多少
  • 支付平台费率
  • 资产评估增值的调整方法
  • 个人贷款打到公司账户存在什么风险
  • 哪些行业不能开电子发票
  • 以前年度损益调整结转到哪里
  • 民办学校学费收入
  • jquery常用的事件绑定函数有哪些
  • wins9
  • 连接远程mq
  • xp系统的硬盘装到win7电脑
  • centos做服务器
  • win10怎么旋转屏幕
  • 微软推送win11
  • win7旗舰版u盘在电脑上读不出来
  • windows8的ie浏览器在哪
  • unity项目资源
  • js设计模式书
  • unity learn premium
  • dos判断变量是否为空
  • 深入理解javascript特性.pdf
  • js框选
  • 安卓网络管理类app
  • javascript获取当前年份
  • 城管大队长级别高吗
  • 深圳国税咨询电话是多少
  • 免责声明:网站部分图片文字素材来源于网络,如有侵权,请及时告知,我们会第一时间删除,谢谢! 邮箱:opceo@qq.com

    鄂ICP备2023003026号

    网站地图: 企业信息 工商信息 财税知识 网络常识 编程技术

    友情链接: 武汉网站建设