问题回顾
几个月前,曾写过一篇文章,微信小程序map组件渲染几百个marker后,页面卡顿,如何解决,当时由于需求紧急,没有充足时间研究和查阅文档,当时想到的一种解决方案,原理是这样的:
控制地图可见区域大小,只获取地图当前区域范围内的maker,操作移动地图重新请求并加载maker。
现在暂时闲下来了,就想再好好研究下这个问题,在上一篇文章中,我曾贴了这张图
我们可以看到,在当前视图下,附近点位很多的时候,会出现一些数字点位。其实这种效果就是点聚合,简单来讲,就是将规定区域内,无法同时展示的点位通过数量点位展示在地图中,不必将所有点位都渲染展示出来。如此一来,使用了点聚合,不是就能解决点位过多,页面卡顿的问题了。
遥记得前些时候好像见过点聚合功能的介绍,好像在查阅微信小程序文档的时候也提到了点聚合。今天就好好阅读了下 微信地图map/ uni-app地图map 的文档,还真有,只怪当时了解太少,太着急,没认真研究文档啦。
关于点聚合,官方还给了一个示例代码。然后我就参照着示例代码,准备在我们的项目中试一试。完成后,感觉还不错。下图为我在平遥一码游中将其改为点聚合后的效果截图。
。
这里先说明一下,我发现在开发者工具中查看,体验效果不佳,直接点击聚点数字没反应,需要双击才能实现分离,分离效果并不理想,但在真机上只需轻轻点一下即可,不建议在微信开发者工具中体验和测试。
点聚合功能实现关键代码:
<template>
<map
id="mapId"
ref="mapRef"
:polyline="polyline"
:latitude="latitude"
:longitude="longitude"
:scale="scale"
:min-scale="minScale"
:max-scale="maxScale"
:show-location="showLocation"
@markertap="markertap"
@labeltap="markertap"
@regionchange="onMapRegionchange"
>
<cover-view slot="callout">
<cover-view class="c-callout" v-for="(item, i) in markers" :marker-id="i" :key="i">
{{ i + 1 }}
</cover-view>
</cover-view>
</map>
</template>
<script>
export default {
data: {
mapCtx : null, // 创建地图上下文对象。
markers : [], // 地图聚合点位数据
defaultTheme : true, // 是否使用默认的聚合样式
appItem : {
latitude: 37.211836,
longitude: 112.180908,
},
// 地图默认坐标
localLatitude: 37.202604,
localLongitude: 112.184751,
},
// 在 `onLoad` 生命周期钩子中调用 `uni.createMapContext` 时,地图组件可能还未完全渲染完成,这会导致 `mapCtx` 无法正确获取到地图上下文
//onLoad(){
//this.initMap()
//this.getMakersRequest()
//},
onReady() {
this.initMap()
this.getMakersRequest()
},
methods: {
initMap(){
//this.mapCtx = wx.createMapContext('mapId');
this.mapCtx = uni.createMapContext("mapId", this);
// 对聚合点进行初始化配置
this.mapCtx.initMarkerCluster({
enableDefaultStyle: this.defaultTheme, // 启用默认的聚合样式
zoomOnClick: true, // 点击已经聚合的标记点时是否实现聚合分离
gridSize: 60, // 聚合算法的可聚合距离,即距离小于该值的点会聚合至一起,以像素为单位
complete(res) {
console.log('initMarkerCluster', res)
}
})
!this.defaultTheme && this.customClusterTheme();
},
customClusterTheme(){
// initMarkerCluster 中参数 enableDefaultStyle 为 true 时不会触发下方 markerClusterCreate事件
// 如果需要自定义点聚合数字框UI的可以在这里书写逻辑,个人觉得如果UI没有特殊要求,默认的已经挺好看了
this.mapCtx.on('markerClusterCreate', res => {
console.log('clusterCreate', res)
const clusters = res.clusters
const markers = clusters.map(cluster => {
const {
center,
clusterId,
markerIds
} = cluster
return {
...center,
width: 0,
height: 0,
clusterId, // 必须
label: {
content: markerIds.length + '',
fontSize: 20,
width: 60,
height: 60,
bgColor: '#00ff00',
borderRadius: 30,
textAlign: 'center',
anchorX: 0,
anchorY: -30,
}
}
})
this.mapCtx.addMarkers({
markers,
clear: false,
complete(res) {
console.log('clusterCreate addMarkers', res)
}
})
})
},
// // 清空所有地图点位
//removeMarkers() {
//this.mapCtx.addMarkers({
//clear: true, // 是否先清空地图上所有 marker
//markers: [] // 同传入 map 组件的 marker 属性,为空相当于清空现有map中的所有marker点
//})
//},
// 获取地图点位
getMakersRequest(){
this.markers = []
this.$http.post("/xxxx/xxxx", {
}).then(({ data }) => {
this.markers = data.records.map((item, index) => {
return {
// 保留自定义参数, 用于查询或其他操作使用
...item,
//uid: gId,
//dataId: item.id,
//type: 'spot',
// 这里的joinCluster非常关键,它是点是否能正常聚合的关键一步
joinCluster : true, // 是否参与点聚合,默认false不参与点聚合
iconPath: `${this.$imgBaseUrl}/tour/icon-flag.png`, // 显示的图标
id: item.id, // marker 点击事件回调会返回此 id
latitude: item.latitude, // 纬度
longitude: item.longitude, // 经度
width: 25, // 标注图标宽度
height: 28, // 标注图标高
// 为标记点旁边增加标签
label: {
content: item.name,
borderRadius: 36,
borderWidth: 1,
borderColor: '#999',
bgColor: '#fff',
display: 'ALWAYS',
textAlign: 'center',
padding: 5
},
// 标记点上方的气泡窗口
callout: {
content: item.name,
padding: 5,
display: 'BYCLICK' // 'BYCLICK':点击显示; 'ALWAYS':常显
}
}
})
// // 定位到指定位置
//this.goToCenter({
//latitude: this.appItem.latitude,
//longitude: this.appItem.longitude
//})
// 将参与聚合的点位添加到地图上下文对象中
this.mapCtx.addMarkers({
markers : this.markers, // 添加请求到的makers到地图中
clear: true, // 设置为true,先清空地图上所有 marker
})
})
},
// // 设置中心点经纬度
//setCenter(latitude = '', longitude = '') {
//this.latitude = latitude || this.localLatitude;
//this.longitude = longitude || this.localLongitude;
//},
// // 定位到指定位置
//goToCenter(location) {
//this.showLocation = true
//this.setCenter(location.latitude,location.longitude)
//},
}
}
</script>
注意事项:
一、未改为点聚合功能前,地图中的点markers是响应式获取的,
<map id="mapId" ref="mapRef" :markers="markers"></map>
修改为点聚合后,在map的html结构中去掉了:markers="markers",
<map id="mapId" ref="mapRef"></map>
markers数据通过 MapContext.addMarkers 动态加入,
// 将参与聚合的点位添加到地图上下文对象中
this.mapCtx.addMarkers({
markers : this.markers, // 添加请求到的makers到地图中
clear: true, // 设置为true,先清空地图上所有 marker
})
个人经过验证,只能在map的html中去掉:markers="markers",然后通过 MapContext.addMarkers 动态添加点位才能实现点聚合,否则即便其他几个步骤都做了,书写无误,同样无法实现。
二、为了一个好的点聚合体验,地图的缩放比例scale需要我们根据实际情形设置一个合适的值,避免太大,数据多,达不到解决卡顿的问题,太小,就几个数字点位在地图上。在使用点聚合后,如无特殊情形,一般情况下,个人觉得,最大、最小缩放级别都可以不用设置了,直接使用默认值即可。
使用点聚合之前:
<map
id="mapId"
ref="mapRef"
:markers="markers"
:polyline="polyline"
:latitude="latitude"
:longitude="longitude"
:scale="scale"
:min-scale="minScale"
:max-scale="maxScale"
:show-location="showLocation"
@markertap="markertap"
@labeltap="markertap"
@regionchange="onMapRegionchange"
>
</map>
使用点聚合之后:
<map
id="mapId"
ref="mapRef"
:polyline="polyline"
:latitude="latitude"
:longitude="longitude"
:scale="scale"
:show-location="showLocation"
@markertap="markertap"
@labeltap="markertap"
@regionchange="onMapRegionchange"
>
</map>
如果不是在小程序中,地图使用点聚合功能就更不用担心了,毕竟小程序原生map加入聚合功能可是晚于web和app端,大家有兴趣或者需要用到的可以自己去查文档。比如在H5中使用腾讯地图点聚合功能,腾讯地图点聚合,哎呀,啰嗦的老毛病又犯了,今天就这样吧,很感谢您还能看到这里。