该项目原为vue3+高德地图项目, 由于公司原因, 打算换成腾讯地图, 在原有项目基础功能上进行调整更改, 开发过程挺折磨, 有遇到类似需求的小伙伴咱们多交流学习一下.
该文章主要记录腾讯地图v1版 MultiMarker点标记功能中的样式应用, 更换点标记的图片等方法
var markerLayer = new TMap.MultiMarker({
map: map, //指定地图容器
//样式定义
styles: {
//创建一个styleId为"myStyle"的样式(styles的子属性名即为styleId)
"myStyle": new TMap.MarkerStyle({
"width": 25, // 点标记样式宽度(像素)
"height": 35, // 点标记样式高度(像素)
"src": '../img/marker.png', //图片路径
//焦点在图片中的像素位置,一般大头针类似形式的图片以针尖位置做为焦点,圆形点以圆心位置为焦点
"anchor": { x: 16, y: 32 }
})
},
//点标记数据数组
geometries: [{
"id": "1", //点标记唯一标识,后续如果有删除、修改位置等操作,都需要此id
"styleId": 'myStyle', //指定样式id
"position": new TMap.LatLng(39.954104, 116.357503), //点标记坐标位置
"properties": {//自定义属性
"title": "marker1"
}
}, {//第二个点标记
"id": "2",
"styleId": 'marker',
"position": new TMap.LatLng(39.994104, 116.287503),
"properties": {
"title": "marker2"
}
}
]
});
文档中指出: styles(为对象格式) 中的每个对象名对应 geometries(为数组格式) 中每一项中的styleId对象值, 来匹配数据的每一项所对应要渲染的样式和icon标记
腾讯地图v1版文档-MultiMarker介绍链接跳转
简而言之就是先定义好要渲染样式, 再根据数据数组每一项的styleId去匹配对应的样式来渲染
看不懂没关系, 上面文档代码多看几遍大概了解后再往下
既然说要先定义好样式信息, 再数据进行匹配, 那我们可以根据数据信息里的状态来定义不同样式
// 给数据注入每一项状态对应的styleId
markerGeometries.forEach(function (item:any) {
let styleId = ''
if ( item.starLevel === '五星店') {
styleId = 'start5'
} else if (item.starLevel === '四星店') {
styleId = 'start4'
} else if (item.starLevel === '三星店') {
styleId = 'start3'
} else if (item.starLevel === '二星店') {
styleId = 'start2'
} else {
styleId = 'start1'
}
markerGeometries.push({
position: item.center,
...data,
styleId,
// 还能传点其他你要用到的参数
})
})
接下来配置styles的对象,
再次强调: 对象里的对象key对应上面markerGeometries数据里的styleId的值
// 标记点样式
markersStyle = {
'width': 30,
'height': 30,
'anchor': { x: 17, y: 21},
'color': 'rgba(0, 0, 0, 0)', // 标注点文本颜色
'size': 22, // 标注点文本文字大小
'offset': { x: 0, y: -26 }, // 标注点文本文字基于direction方位的偏移属性
}
markers = new TMap.MultiMarker({
id: 'markers',
map: that.map,
styles: {
start5: new TMap.MarkerStyle({
...that.markersStyle,
'src': require('@/assets/img/start5.png'),
}),
start4: new TMap.MarkerStyle({
...that.markersStyle,
'src': require('@/assets/img/start4.png'),
}),
start3: new TMap.MarkerStyle({
...that.markersStyle,
'src': require('@/assets/img/start3.png'),
}),
start2: new TMap.MarkerStyle({
...that.markersStyle,
'src': require('@/assets/img/start2.png'),
}),
start1: new TMap.MarkerStyle({
...that.markersStyle,
'src': require('@/assets/img/start1.png'),
}),
},
geometries: markerGeometries
});
这里图片是放在了项目本地中, 用require方法进行了引入, 如果你引入了图片url链接, 项目可能会报跨域, 解决方法就是让管图片url链接对应的服务器的后端同学放开限制或让其在服务器配置中添加对应项目的域名
之前的思路适合有明确状态和对应icon的情况下, 对于自定义渲染icon就无能为力了, 在该段落讲介绍新的方式解决
既然要自定义icon标记点, 那就根据数据每一项的id来作为 styleId来使用, 再注入对应的图片src
// 标记点样式
const markersStyle = {
'width': 30,
'height': 30,
'anchor': { x: 17, y: 21},
// 'src': require('@/assets/img/online.png'),
'color': 'rgba(0, 0, 0, 0)', // 标注点文本颜色
'size': 22, // 标注点文本文字大小
'offset': { x: 0, y: -26 }, // 标注点文本文字基于direction方位的偏移属性
}
// 生成点标记的样式数据
var MkStyles = {}
config.data.forEach((item:any) => {
MkStyles[item.id] = new TMap.MarkerStyle({
...markersStyle,
'src':item.icon || require('@/assets/markers/customer_icon.png')
// 这里用了短路运算, 预防图片链接为空
})
})
↑ 这里用了短路运算, 预防图片链接为空
markerGeometries.forEach(function (item:any) {
markerGeometries.push({
position: item.center,
...data,
styleId: data.id,
// 还能传点其他你要用到的参数
})
})
↑ styld注入数组每一项对应的id
if (markers) {
// 已创建过点标记图层,直接更新数据
markers.setGeometries(markerGeometries);
} else {
markers = new TMap.MultiMarker({
id: 'markers',
map: that.map,
styles: MkStyles,
geometries: markerGeometries
});
}
↑ styles 直接引入上面处理好的对象
该方法适合需要渲染不同标记点icon的场景, 较为灵活, 但是数据如果多起来会卡顿, 因为每一个id都要声明配置一个styleId的数据, 如果太卡还是用解决方案一吧, 或许有更好解决思路, 欢迎各位伙伴大佬留言分享一下
注意点: 跨域问题, 一定要让管服务器的后端同学配置好图片url的跨域访问限制(放开或加项目域名和ip等)
下面是我完整解决方案的代码, 没有怎么整理可以参考一下, 用的是方法2
用了点聚合和信息窗口等方法
//设置标点
setData(config: any) {
console.log('门店标记信息', config)
const that = this//全部门店
this.rightClickConfig = { rightList: config.rightList, rightClick: config.rightClick, checkDealer: config.checkDealer}
this.list = config.data
var clusterBubbleList: any = [];
var markerGeometries: any = [];
// 标记点样式
const markersStyle = {
'width': 30,
'height': 30,
'anchor': { x: 17, y: 21},
// 'src': require('@/assets/img/online.png'),
'color': 'rgba(0, 0, 0, 0)', // 标注点文本颜色
'size': 22, // 标注点文本文字大小
'offset': { x: 0, y: -26 }, // 标注点文本文字基于direction方位的偏移属性
}
// 生成点标记的样式数据
var MkStyles = {}
config.data.forEach((item:any) => {
MkStyles[item.id] = new TMap.MarkerStyle({
...markersStyle,
'src':item.icon || require('@/assets/markers/customer_icon.png')
})
})
// 门店聚合物
if (config.data.length > 0) {
if (this.markerCluster) {
this.markerCluster.setGeometries(config.data)
const geometries = config.data
// console.log(1111, geometries)
// setGeometries({geometries})
} else {
this.markerCluster = new TMap.MarkerCluster({
// id: 'cluster', //图层id
map: that.map, //设置点聚合显示在哪个map对象中(创建map的段落省略)
enableDefaultStyle: false, //使用默认样式
minimumClusterSize: 12, //最小聚合点数:2个
geometries: config.data, // 坐标列表
zoomOnClick: true, //点击聚合数字放大展开
gridSize: 160, //聚合算法的可聚合距离,即距离小于该值的点会聚合至一起,默认为60,以像素为单位
averageCenter: false, //每个聚和簇的中心是否应该是聚类中所有标记的平均值
maxZoom: 13 //采用聚合策略的最大缩放级别,若地图缩放级别大于该值,则不进行聚合,标点将全部被展开
})
}
// 监听聚合簇变化
this.markerCluster.on('cluster_changed', function (e:any) {
// console.log('滚轮聚合', e)
// 销毁旧聚合簇生成的覆盖物
if (clusterBubbleList.length) {
clusterBubbleList.forEach(function (item:any) {
item.destroy();
})
clusterBubbleList = [];
}
markerGeometries = []
// 根据新的聚合簇数组生成新的覆盖物和点标记图层
var clusters = that.markerCluster.getClusters()
clusters.forEach(function (item:any) {
if (item.geometries.length > 1) {
// @ts-ignore
let clusterBubble = new ClusterBubble({
map: that.map,
position: item.center,
content: item.geometries.length
});
clusterBubble.on('click', () => {
that.map.fitBounds(item.bounds)
});
clusterBubbleList.push(clusterBubble)
} else {
const data = item.geometries[0]
let content = data.distance ? (data.distance + 'km') : 0
markerGeometries.push({
position: item.center,
...data,
styleId: data.id,
content,
// 还能传点其他你要用到的参数
})
}
});
if (markers) {
// 已创建过点标记图层,直接更新数据
markers.setGeometries(markerGeometries);
} else {
markers = new TMap.MultiMarker({
id: 'markers',
map: that.map,
styles: MkStyles,
geometries: markerGeometries
});
}
if (infoArr.length) {
// console.log('销毁信息窗', distanceInfo);
infoArr.forEach((item: { setMap: (arg0: null) => any; }) => item.setMap(null));
infoArr = [];
that.triggerCode(markerGeometries);
} else {
that.triggerCode(markerGeometries);
}
// 监听鼠标左键
markers.on("click", function (evt: any) {
// console.log('左键', evt)
btnInfo.close()
that.setBtnInfo(evt)
that.setInfoWindow(evt)
that.changeCheckDealer(evt.geometry)
})
// 监听鼠标右键
markers.on("rightclick", function (evt: any) {
// console.log('右键', evt)
that.setBtnInfo(evt)
that.setInfoWindow(evt)
that.changeCheckDealer(evt.geometry)
})
})
} else {
// 销毁所有信息窗口和标记
if (markers) {
markers.setGeometries([])
if (infoArr.length) {
infoArr.forEach((item: { setMap: (arg0: null) => any; }) => item.setMap(null));
infoArr = [];
}
}
let listener = w.document.getElementsByName("btnInfo")
if (listener) {
listener.forEach((btnItem:any) => {
btnInfo.close()
})
}
if (infoWindow) {
infoWindow.close()
}
// 滚动气泡变化时销毁
this.markerCluster && this.markerCluster.on('cluster_changed', function (e:any) {
if (markers) {
markers.setGeometries([])
if (infoArr.length) {
// console.log('销毁距离信息窗', infoArr);
infoArr.forEach((item: { setMap: (arg0: null) => any; }) => item.setMap(null));
infoArr = [];
}
}
var clusters = that.markerCluster.getClusters()
clusters && clusters.forEach(function (item:any) {
if (item.geometries.length > 1) {
clusterBubbleList.push({})
}
})
// 添加监听, 销毁转档按钮信息框
if (listener) {
listener.forEach((btnItem:any) => {
btnInfo.close()
})
}
})
}
}