位置: 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相关面试题)

  • 华为matepad平板可以插u盘吗(华为matepad平板可以插电话卡吗)

    华为matepad平板可以插u盘吗(华为matepad平板可以插电话卡吗)

  • iphonexsmax重量多少克(xsmax重量多少克)

    iphonexsmax重量多少克(xsmax重量多少克)

  • 微信可以发压缩文件夹吗(微信可以发压缩文件不)

    微信可以发压缩文件夹吗(微信可以发压缩文件不)

  • qq闪照截图2次不给看了(qq闪照截图2次不给看了怎么办)

    qq闪照截图2次不给看了(qq闪照截图2次不给看了怎么办)

  • 三星手机怎么看是不是国行(三星手机怎么看版本)

    三星手机怎么看是不是国行(三星手机怎么看版本)

  • 苹果11的ios出厂系统是什么(苹果11出厂设置在哪里)

    苹果11的ios出厂系统是什么(苹果11出厂设置在哪里)

  • 相机发明在哪一年(相机发明者)

    相机发明在哪一年(相机发明者)

  • 手机没信号可以打通110吗(手机没信号可以收到短信吗)

    手机没信号可以打通110吗(手机没信号可以收到短信吗)

  • 物联卡停机多久注销(物联卡停机后咋恢复)

    物联卡停机多久注销(物联卡停机后咋恢复)

  • 一个手机可以登几个微信(一个手机可以登录三个微信号吗)

    一个手机可以登几个微信(一个手机可以登录三个微信号吗)

  • 设置表格外框线为3磅蓝色单实线(设置表格外框线为红色)

    设置表格外框线为3磅蓝色单实线(设置表格外框线为红色)

  • 计算机冷启动和热启动的区别(计算机冷启动和热启动的快捷键)

    计算机冷启动和热启动的区别(计算机冷启动和热启动的快捷键)

  • 华为路由器连接wifi成功但上不了网怎么办(华为路由器连接互联网失败是怎么回事)

    华为路由器连接wifi成功但上不了网怎么办(华为路由器连接互联网失败是怎么回事)

  • 华为手机怎么拍不了照片(华为手机怎么拍试卷去答案)

    华为手机怎么拍不了照片(华为手机怎么拍试卷去答案)

  • 苹果刷机有害处吗(苹果刷机有没有害处)

    苹果刷机有害处吗(苹果刷机有没有害处)

  • 苹果怎么进入dfu模式(苹果怎么进入双系统)

    苹果怎么进入dfu模式(苹果怎么进入双系统)

  • pr添加字幕如何移动(pr添加字幕如何保持一个大小)

    pr添加字幕如何移动(pr添加字幕如何保持一个大小)

  • 抖音怎么刷新不了(抖音怎么刷新不出想看的直播间)

    抖音怎么刷新不了(抖音怎么刷新不出想看的直播间)

  • xr是不是双卡双待(xr是不是双卡手机)

    xr是不是双卡双待(xr是不是双卡手机)

  • 苹果xr怎么改全屏(苹果xr全屏手写功能怎么改成半屏手写)

    苹果xr怎么改全屏(苹果xr全屏手写功能怎么改成半屏手写)

  • 拼多多怎么购物(拼多多怎么购物?)

    拼多多怎么购物(拼多多怎么购物?)

  • win7分屏设置(win7如何分屏显示)

    win7分屏设置(win7如何分屏显示)

  • 这可能是最好、最详细的VSCode开发uni-app教程吧(这可能是最好的144平米小平层户型)

    这可能是最好、最详细的VSCode开发uni-app教程吧(这可能是最好的144平米小平层户型)

  • 税款所属期
  • 在电子税务局可以抄税吗
  • 非居民纳税机构都包含哪些?
  • 金税盘入账的会计分录
  • 金税工程是什么单位
  • 个人所得劳务报酬税率
  • 车辆购置税的税率是多少
  • 生产经营所得的税率表
  • 社保费公司部分怎么算
  • 通用机打发票如何验旧
  • 普通合伙企业要交什么税
  • 盘盈存货会计处理会计分录
  • 冲销暂估材料怎么做账
  • 银行理财产品赎回
  • 海关证需要年检吗
  • 原材料采购没正式发票能否入账?
  • 库存红字冲销
  • 停车场收入怎么做账
  • 小规模纳税人隔月可以作废发票吗
  • 机动车发票如何抵扣进项税
  • 消费税暂行条例实施细则
  • 浅谈收入与成本的关系
  • 处置固定资产按残值还是净值
  • 委托销售的增值税税率
  • 经营性租入的固定资产需要计提折旧吗
  • 购买少数股东权益的会计处理
  • 小规模适用税率
  • 工会残保金必须缴纳吗
  • linux系统 推荐
  • windows7便签删除了怎么恢复
  • 波来古市在哪里
  • 蒙塔巴诺督查
  • 配置多数据源的好处
  • 医疗知识科普图片
  • PHP中empty,isset,is_null用法和区别
  • vue foreach is not a function
  • 采购员出差预借差旅费
  • springboot -d
  • php自定义表单
  • 技术咨询费属于
  • 待处理财产损溢在资产负债表中填哪里
  • 织梦建站详细教程
  • 投资性房地产由成本模式转为公允价值模式差额计入
  • 水利建设基金申报
  • 发票一式两联
  • 其他应付款包括应付股利和应付利息吗
  • 进口货物如何支付外汇
  • SQL Server 2008 数据库有哪些版本?
  • db2 describe
  • 公司土地使用权可以自主转让吗
  • 税务局报季度税
  • 土地使用权转让法律规定
  • 企业所得税研发费用加计扣除例题
  • 刚成立的公司老板要我入股
  • 个人技术转让费税率是多少
  • 加油的增值税发票几个点
  • 货款扣除质量赔款
  • 本月应负担的修理费
  • 个人生产经营所得税怎么申报
  • 商品销售折让怎么计算
  • 预提业务
  • 删除命令windows
  • windowsserver2008r2忘记开机密码怎么办
  • bios没有usb启动项解决方法联想
  • 系统警告设置
  • js数组entries
  • 批处理bat
  • Bullet(Cocos2dx)之优化PhysicsDraw3D
  • 如何改变this指向
  • 关于javascript
  • unity自带的ui叫什么
  • c语言深入剖析
  • 安卓手机管家下载
  • jquery的用法
  • unity中ngui
  • asyncio 的 coroutine对象 与 Future对象使用指南
  • 面向对象的编程语言更适合大规模编程
  • 电子专票票种核定
  • 浙江银行上班时间查询
  • 成都城市建设规划管理局
  • 免责声明:网站部分图片文字素材来源于网络,如有侵权,请及时告知,我们会第一时间删除,谢谢! 邮箱:opceo@qq.com

    鄂ICP备2023003026号

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

    友情链接: 武汉网站建设