创建地图
initMap() {
if (this.map) return
const projection = 'EPSG:3857'
const url = 'https://webst02.is.autonavi.com/appmaptile?style=6&x={x}&y={y}&z={z}'
this.map = new ol.Map({
target: 'olMap',
projection,
view: new ol.View({
center,
zoom,
minZoom: 8, // 最小缩放级别
maxZoom: 18, // 最大缩放级别
projection,
// 因为存在非整数的缩放级别,所以设置该参数为true来让每次缩放结束后自动缩放到距离最近的一个整数级别,这个必须要设置,当缩放在非整数级别时地图会糊
constrainResolution: true,
}),
controls: ol.control.defaults({
zoom: false,
attribution: false,
}),
layers: [
new ol.layer.Tile({
source: new ol.source.XYZ({
url: url,
wrapX: true,
style: 'default',
}),
projection,
}),
],
})
this.map.on('singleclick', this.mapClick)
// this.map.on('moveend', this.moveEnd)
},
聚合
// 点聚合
cluster(data) {
const source = new ol.source.Vector()
data.forEach((item, index) => {
const coordinates = ol.proj.transform([item.longitude, item.latitude], 'EPSG:4326', 'EPSG:3857')
const feature = new ol.Feature({
id: `point_${index}`,
data: item,
geometry: new ol.geom.Point(coordinates),
})
// 创建样式 - 一定要给每个点位设置单独的样式
const style = new ol.style.Style({
image: new ol.style.Icon({
anchor: [0.5, 1], // 显示位置
size: [36, 36], // 尺寸
src: this.getPointIconByType(item),
}),
})
feature.setStyle(style)
source.addFeature(feature)
})
// 聚合
const cluster = new ol.source.Cluster({
source: source,
distance: 100,
})
// 创建图层
const that = this
this.clusterLayer = new ol.layer.Vector({
source: cluster,
style: function (feature, resolution) {
const size = feature.get('features').length
if (size == 1) {
//const data = feature?.getProperties()?.features[0]?.getProperties()?.data
//that.setAlarm(data, true)
const style = feature?.getProperties()?.features[0].getStyle()
return style
} else {
//feature?.getProperties()?.features.map((v) => v.getProperties().data)
//.forEach((v) => {
// that.setAlarm(v, false)
//})
return new ol.style.Style({
image: new ol.style.Icon({
anchor: [0.32, 0.35], // 显示位置
size: [48, 48], // 尺寸
src: require('@/assets/gis/blue.png'), // 图片url
}),
text: new ol.style.Text({
text: size.toString(),
fill: new ol.style.Fill({
color: 'white',
}),
}),
})
}
},
})
// 添加图层
this.map.addLayer(this.clusterLayer)
},
聚合里面的点位有告警时,替换点位图标,展示告警动画
-
alarmArr是告警数组,在页面上循环创建多个div,作为动画容器
-
注意这里使用的v-show
-
我在使用v-if时发现,多次缩放聚合图层时,只有第一次缩放时效果正常,而v-show则每次缩放时都正常
<!-- 批量告警闪烁 -->
<template v-for="item of alarmArr">
<div :key="item.alarmId" v-show="item.active" class="point_animation" :id="item.alarmId">
<p></p>
<span></span>
</div>
</template>
<style lang="scss">
.point_animation {
background: transparent;
width: 6px;
height: 6px;
border-radius: 50%;
position: absolute;
p,
span {
position: absolute;
width: 4px;
height: 4px;
// background-color: #f004;
animation: point_animation 1.5s infinite;
box-shadow: 0px 0px 5px #f00;
margin: 0px;
border-radius: 50%;
}
span {
animation-delay: 0.8s;
}
@keyframes point_animation {
10% {
transform: scale(10);
}
100% {
transform: scale(8);
}
}
}
</style>
告警动画
- 把上面聚合图层代码里面注释的代码打开
- 再加上下面的代码
setAlarm(data, flag) {
if (!data?.info) return
const item = this.alarmArr.filter((v) => v.alarmId === data.info.alarmId)[0]
item.active = flag
if (flag) {
this.$nextTick(() => {
const popup = new ol.Overlay({
id: item.alarmId,
element: document.getElementById(item.alarmId),
autoPan: true, //跟随地图移动
positioning: 'center-center',
offset: [-8, -24],
stopEvent: false,
autoPanAnimation: {
duration: 250,
},
})
// 弹出popup
popup.setPosition(ol.proj.transform([item.longitude, item.latitude], 'EPSG:4326', 'EPSG:3857'))
this.map.addOverlay(popup)
})
}
},
- 告警动画出来了,聚合图层缩放时,动画效果也随之展开消失
告警动画二
-
上面的动画dom绑定的是告警id
-
一个设备是可以有多个告警的,告警id是会变化的
-
当处理完一个告警,展示下一个告警时,告警id已经发生变化,此时动画弹框dom被删除了
-
我使用的是setPosition, 但是dom还是被删了
-
那就换一种实现方式
-
用设备id绑定告警dom,设备id是不会变的
-
初始渲染n个告警动画,使用setPositon(undefined)隐藏,在需要显示的时候,设置正确的位置
-
修改代码如下
<!-- 批量告警闪烁动画 -->
<div v-for="item of alarmArr" :key="item.deviceId">
<div class="point_animation" :id="item.deviceId">
<p></p>
<span></span>
</div>
</div>
// 此处为获取设备列表后代码
this.alarmArr = JSON.parse(JSON.stringify(res.resultList))
this.$nextTick(() => {
this.alarmArr.forEach((v) => {
const popup = new ol.Overlay({
id: v.deviceId,
element: document.getElementById(v.deviceId),
autoPan: true, //跟随地图移动
positioning: 'center-center',
offset: [-this.keepScale(2) + 16, -this.keepScale(2) + 19], //32,38
stopEvent: false,
autoPanAnimation: {
duration: 250,
},
})
// 弹出popup
this.alarmPopupCollect.push(popup)
popup.setPosition(undefined)
this.map.addOverlay(popup)
})
})
// 控制告警动画是否展示
setAlarm(data, flag) {
const curPop = this.alarmPopupCollect.filter((v) => v.getId() === data.deviceId)[0]
if (!flag) {
this.$nextTick(() => {
curPop.setPosition(undefined)
})
return
}
if (data.info) {
this.$nextTick(() => {
curPop.setPosition(this.coordTransform([data.longitude, data.latitude]))
})
} else {
this.$nextTick(() => {
curPop.setPosition(undefined)
})
}
},
踩坑
- map.removeOverlay(_overlay)
用于移除指定的overlay,该方法会删除overley以及dom节点,
(重新添加overlay时,添加dom会有问题,需要注意,比如弹窗)