likes
comments
collection
share

举一反三,小程序地图中如何优化渲染大量添加物!

作者站长头像
站长
· 阅读数 2

首先优化思路还是以下几点:

  • 切割数据数组,分组轮询
  • 根据地图事件轮询更新信息
  • 根据当前地图缩放大小判断是否显示区域、标签

相比于高德地图API,小程序的地图添加物主要是直接控制几个数组实现的,所以在实现的过程中我们要在合适的时机改变数组,防止频繁操作数组。

先看下小程序地图组件的使用

<map
   show-location
   longitude="{{longitude}}"
   latitude="{{latitude}}"
   markers="{{markers}}"
   polygons="{{polygons}}"
   circles="{{circles}}"
   scale="{{scale}}"
   bindregionchange="regionchange"></map>

事件更新

首先我们要在地图视口发生变化的时候触发重新计算页面添加物的逻辑:

  regionchange(e) {
    if(e.type == 'end'){
      // 通过防抖和地图信息比对,防止地图拖动时,触发多次的请求
      if (this.timer) clearTimeout(this.timer)
      this.timer = setTimeout(() => {
        if (e.causedBy !== 'scale' && (e.detail.centerLocation.latitude !== this.data.latitude || e.detail.centerLocation.longitude !== this.data.longitude)) {
          this.currentScale = e.detail.scale
          this.setData({
            longitude: e.detail.centerLocation.longitude,
            latitude: e.detail.centerLocation.latitude
          }, () => {
            this.chunkLoad()
          })
        } else if (e.causedBy === 'scale' && e.detail.scale !== this.currentScale) {
          this.currentScale = e.detail.scale
          this.chunkLoad()
        }
      }, 300)
    }
  },

页面缩放比例和中心点变化的时候我们就会去更新页面信息。

数组切割

这块的处理逻辑和PC端没有区别,但是PC端我们使用了定时器来分批渲染,可以让页面慢慢渲染添加物,但是小程序是通过数组控制的添加物,我们要确保数组结果的准确性,让页面尽量少地重新渲染,所以就用一个缓存数组来暂存,最后再用setData一次性加入到地图中:

// 切割方法
function chunkArray (chunkedArr, chunkSize) {
  const chunkedArr = []
  let i = 0
  while (i < arr.length) {
    chunkedArr.push(arr.slice(i, i + chunkSize))
    i += chunkSize
  }
  return chunkedArr
}

this.listChunk = chunkArray(list, 100)
chunkLoad() {
  let that = this
  // 调用 getRegion 方法获取当前地图可见区域信息
  that.map.getRegion({
    success: function(res) {
      that.mapRegion = res
      that.markerCaches = []
      that.polygonCaches = []
      if (!that.mapRegion) return
      // 分块加载,防止同一帧内轮询次数过多
      for (let i = 0; i < that.listChunk.length; i++) {
        setTimeout(() => {
          that.drawMap(that.listChunk, i, 'site')
        }, i * 100)
      }
    }
  })
}

然后我们再看看加载添加物的方法:

function drawMap(list, chunkIndex) {
  for (let index = 0; index < list[chunkIndex].length; index++) {
       const item = list[chunkIndex][index]
       const { lat, lon } = park || {}
       if (!lat || !lon) continue
       const contains = contains({longitude: lon, latitude: lat})
       // 判断当前marker在视口内时创建标签与区域
       if (contains) {
         const marker = {...}
         const polygon = {...}
         this.markerCache.push(marker)
         this.polygonCaches.push(polygon)
       }
    	 this.setData({
         markers: this.markerCache,
         polygons: this.polygonCaches
       })
  }
}
// 通过经纬度比对判断当前点是否在视野范围内
function contains(point) {
    const { longitude, latitude } = point
    const area = this.mapRegion
    return area.northeast.longitude > longitude && area.southwest.longitude < longitude && area.northeast.latitude > latitude && area.southwest.latitude < latitude
}

缩放优化

最后一步就是优化显示了,还是之前文章说到的当缩小到一定程度,地图上上千个点堆叠在一起,本身已经没有了意义,所以还需要根据缩放信息去判断页面上的信息是否应该展示或者说换一种方式展示。

我们继续优化drawMap方法:

function drawMap(list, chunkIndex) {
  const zoom = this.currentScale;
  for (let index = 0; index < list[chunkIndex].length; index++) {
       ...
       if (contains) {
         let marker = null
         if (zoom <= 13) {
           // 缩小到一定程度,简化页面标签显示,可以简化为一个圆点
           marker = {...}
         } else {
           // 正常显示marker
           marker = {...}
         }
         this.markerCache.push(marker)
         // 根据当前地图缩放大小判断是否显示区域信息
         if (zoom <= 14) continue
         const polygon = {...}
         this.polygonCaches.push(polygon)
       }
    	 this.setData({
         markers: this.markerCache,
         polygons: this.polygonCaches
       })
  }
}

目前为止,我们已经实现了小程序地图组件添加物的动态渲染,当然不同机型和不同数据量使用体验肯定是有差异的,只是目前在我的项目里面够用了,如果还有更好的优化方案欢迎评论区交流。