解决百度地图javascript Api上万聚合点卡顿问题,以及覆盖物过多时的优化方案

在一个包含大量覆盖物的地图项目中,优化前的聚合点加载和地图操作导致卡顿。升级至官方最新MarkerClusterer库后,加载速度显著提升,且无先前问题。老项目中使用旧版本可能导致性能瓶颈。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

问题:

公司的项目需要在地图上显示上万个覆盖物(实际最多会有两万多个,后面提到的聚合点加载时间都以两万个点为准)。

其实在正常生成聚合点的过程并不会很慢(500ms左右),但是在聚合点生成后,用户如果操作地图(缩放、移动等),就会出现卡顿现象,这也是一开始发现的问题。在后续的优化过程中还发现另外两个问题,如果在加载聚合点之前,用户对地图进行过操作,加载聚合点就会变得非常缓慢(1000ms以上),同时,每次删除聚合点在重新添加都会比上一次慢

解决方案:

直接说解决方案,把项目中引入的MarkerClusterer文件替换成官方github最新的那个就行,文件地址

实测首次加载仅需100ms左右,且没有上面提到的其它问题出现,同时,如果屏幕可见范围内的点位较少时,加载时间也会相应缩短。

本来在后续优化过程中发现另外两个问题后,就觉得应该从源码入手解决,结果上github clone了源码,引入后发现就没问题了。原先引入的是这个代码,这个是旧版本的代码,看了一下git记录,其实2017/12/4就已经有优化了,但是有些老项目可能还在使用旧版本的MarkerClusterer。甚至是新项目在cv代码时也不小心引入了旧版本的MarkerClusterer。

2024/08/26 - 补充(好久没登账号了,刚看到一条评论,所以想补充一些东西)

新版本的聚合点代码,虽说是做了优化。但是并不是说就一定不会出现卡顿了,造成卡顿的原因是很多的,我自己主要遇到的问题,就是覆盖物导致的卡顿,但是地图上不会只有“聚合点”这一种覆盖物。

说下这次文章更新前,没有提到的两种卡顿,一个是在地图上拥有大量覆盖物,然后操作地图时,一个是在同时添加大量覆盖物时。(以下内容围绕这两个问题讨论)

操作地图时的卡顿

操作地图时的卡顿,我认为是百度地图需要实时计算各个覆盖物的位置,此时如果覆盖物过多,计算量较大,就会阻塞界面的渲染,从而导致卡顿。既然是计算覆盖物位置造成的,那就不让它计算。

我的方案是,监听百度地图的moving,moveend,zoomstart,zoomend事件,在moving和zoomstart时,去掉地图上的所有覆盖物,然后在moveend和zoomend时,将用户可见的部分重新添加到地图上。

百度地图提供了getBounds方法(在百度地图实例上调用),返回一个Bounds实例,可获取地图当前的可见范围。调用Bounds实例的containsPoint,可判断一个Point是否在Bounds的范围内

/**
 * @param point {BMap.Point}
 */
function isVisible(point){
  const bounds = bMapInstance.getBounds()
  return bounds.containsPoint(point)
}

如果是由多个点组成的覆盖物,可调用Bounds实例上的intersects方法,来判断两个Bounds是否有交集,以确定覆盖物是否可见

/**
 * @param bounds {BMap.Bounds}
 * @return {boolean}
 */
function isVisible(bounds) {
  const bMapBounds = bMapInstance.getBounds()
  return !!bMapBounds.intersects(bounds)
}

几个注意点:

一个是监听地图移动的事件,最好用moving不要用movestart,在线上测试时,发现了一个问题,在用户点击地图时会触发地图的movestart事件,但是之后没有触发moveend,这就会导致功能异常(我个人认为是线上浏览器版本太老,所以会有一些兼容性的问题)。

还有一个是要做防抖,在moveend和zoomend触发时,不代表地图的操作就已经停止了,可能短时间内还会再次触发moving和zoomstart,所以必须有一些防抖的处理

export function addStartAndEndEventToBMap(bMapInstance, start, end) {
  let doing = false
  function _start() {
    if (doing) return
    doing = true
    start()
  }
  function _end() {
    if (!doing) return
    doing = false
    setTimeout(() => !doing && end(), 200)
  }
  bMapInstance.addEventListener("zoomstart", () => {
    _start()
  })
  bMapInstance.addEventListener("moving", () => {
    _start()
  })
  bMapInstance.addEventListener("moveend", () => {
    _end()
  })
  bMapInstance.addEventListener("zoomend", () => {
    _end()
  })
}

同时添加大量覆盖物时的卡顿

这个应该就很好理解了,大量的代码执行势必会阻塞页面的渲染。我的解决方案是,在覆盖物过多时,将覆盖物分批次添加到地图上。保证每一批覆盖物的添加都不会造成明显的卡顿,同时在每一批中间用定时器隔开一段时间,这一段时间可以用来接收用户的交互以及进行页面的渲染。这样的方案会导致覆盖物的添加变成异步的,同时也会耗费更多的时间,但是可以解决卡顿问题。

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值