参考:openlayers6【二十八】批量设置闪烁点,feature要素通过postrender机制批量生成动态点_@必意玲-CSDN博客
效果图:
实现代码:
<template>
<div class="test">
<div id="map" ref="map" style="width: 100vw; height: 100vh"></div>
<div style="position: fixed; top: 100px; left: 100px">
<el-button @click="showWaveLayer = true">添加gif图层</el-button>
<el-button @click="showWaveLayer = false">移除gif图层</el-button>
<el-button @click="addData()">添加数据</el-button>
<el-button @click="deleteData()">删除数据</el-button>
</div>
<LjOlWave
v-if="showWaveLayer"
:map="map"
:speed="0.35"
:circleRadius="35"
:color="[255, 0, 0]"
:geojsonData="geojsonData"
></LjOlWave>
</div>
</template>
<script>
import "ol/ol.css";
import { Map, View } from "ol";
import { OSM } from "ol/source";
import { Tile as TileLayer } from "ol/layer";
import LjOlWave from "@/components/LjOlWave/index.vue";
export default {
components: { LjOlWave },
data() {
return {
map: {},
geojsonData: {
type: "FeatureCollection",
features: [
{
type: "Feature",
properties: {
title: "警报1",
},
geometry: {
type: "Point",
coordinates: [91.48879670091165, 37.83814884701121],
},
},
{
type: "Feature",
properties: {
title: "警报2",
},
geometry: {
type: "Point",
coordinates: [99.19515576149941, 26.713646654711134],
},
},
{
type: "Feature",
properties: {
title: "警报3",
},
geometry: {
type: "Point",
coordinates: [123.74363825288785, 44.363694825734726],
},
},
],
},
showWaveLayer: false,
};
},
mounted() {
this.initMap();
this.pointerMove();
this.getAnimateInfo();
},
methods: {
getAnimateInfo() {
this.map.on("click", (e) => {
let feature = this.map.forEachFeatureAtPixel(e.pixel, (feature) => {
return feature;
});
this.$message.success(feature.get("title"));
});
},
pointerMove() {
// 设置鼠标划过矢量要素的样式
this.map.on("pointermove", (e) => {
const isHover = this.map.hasFeatureAtPixel(e.pixel);
this.map.getTargetElement().style.cursor = isHover ? "pointer" : "";
});
},
addData() {
this.geojsonData = {
type: "FeatureCollection",
features: [
{
type: "Feature",
properties: {
title: "警报1",
},
geometry: {
type: "Point",
coordinates: [91.48879670091165, 37.83814884701121],
},
},
{
type: "Feature",
properties: {
title: "警报2",
},
geometry: {
type: "Point",
coordinates: [99.19515576149941, 26.713646654711134],
},
},
{
type: "Feature",
properties: {
title: "警报3",
},
geometry: {
type: "Point",
coordinates: [123.74363825288785, 44.363694825734726],
},
},
{
type: "Feature",
properties: {
title: "警报4",
},
geometry: {
type: "Point",
coordinates: [113.74363825288785, 34.363694825734726],
},
},
],
};
},
deleteData() {
this.geojsonData = {
type: "FeatureCollection",
features: [
{
type: "Feature",
properties: {
title: "警报1",
},
geometry: {
type: "Point",
coordinates: [91.48879670091165, 37.83814884701121],
},
},
{
type: "Feature",
properties: {
title: "警报2",
},
geometry: {
type: "Point",
coordinates: [99.19515576149941, 26.713646654711134],
},
},
{
type: "Feature",
properties: {
title: "警报3",
},
geometry: {
type: "Point",
coordinates: [123.74363825288785, 44.363694825734726],
},
},
],
};
},
// 初始化地图
initMap() {
this.map = new Map({
target: document.getElementById("map"),
layers: [
new TileLayer({
source: new OSM(),
}),
],
view: new View({
projection: "EPSG:4326",
center: [104.29806, 30.5263],
zoom: 4,
}),
});
},
},
};
</script>
LjOlWave/index.vue:
<template>
<div/>
</template>
<script>
import "ol/ol.css";
import VectorLayer from "ol/layer/Vector";
import VectorSource from "ol/source/Vector";
import { Feature } from "ol";
import { Style, Circle, Stroke } from "ol/style";
import { Point } from "ol/geom";
import { getVectorContext } from "ol/render";
import GeoJSON from "ol/format/GeoJSON";
import { unByKey } from "ol/Observable"; //移除事件
// 边界json数据
export default {
name: "LjOlWave",
props: {
map: {
type: Object,
default: () => {},
},
speed: {
type: Number,
default: 0.35,
},
circleRadius: {
type: Number,
default: 35,
},
geojsonData: {
type: Object,
default: () => {},
},
color: {
type: Array,
default: () => [255, 0, 0],
},
},
data() {
return {
pointLayer: {},
eventRender: {},
};
},
mounted() {
this.addLayer();
this.renderAnimate();
},
methods: {
addLayer() {
if (Object.keys(this.pointLayer).length !== 0) return;
// 设置图层
this.pointLayer = new VectorLayer({
source: new VectorSource({
features: new GeoJSON().readFeatures(this.geojsonData),
}),
});
// 添加图层
this.map.addLayer(this.pointLayer);
},
removeLayer() {
if (Object.keys(this.pointLayer).length == 0) return;
this.pointLayer.getSource().clear();
this.map.removeLayer(this.pointLayer);
this.pointLayer = {};
},
renderAnimate() {
if (Object.keys(this.pointLayer).length !== 0) {
this.pointLayer.getSource().clear();
}
// 循环添加feature
let pointFeatures = [];
for (let i in this.geojsonData.features) {
const feature = new Feature({
geometry: new Point(
this.geojsonData.features[i].geometry.coordinates
),
});
pointFeatures = [...pointFeatures, feature];
feature.setProperties(this.geojsonData.features[i].properties);
}
this.pointLayer.getSource().addFeatures(pointFeatures);
// 关键的地方在此:监听postrender事件,在里面重新设置circle的样式
let radius = 0;
this.eventRender = this.pointLayer.on("postrender", (e) => {
if (radius >= this.circleRadius) radius = 0;
let opacity = 1 - radius / this.circleRadius; //不透明度
let pointStyle = new Style({
image: new Circle({
radius: radius,
stroke: new Stroke({
color: `rgba(${this.color[0]},${this.color[1]},${this.color[2]},${opacity})`,
width: (this.circleRadius - radius) / 10, //设置宽度
}),
}),
});
// 获取矢量要素上下文
let vectorContext = getVectorContext(e);
vectorContext.setStyle(pointStyle);
pointFeatures.forEach((feature) => {
vectorContext.drawGeometry(feature.getGeometry());
});
radius = radius + this.speed; //调整闪烁速度
//请求地图渲染(在下一个动画帧处)
this.map.render();
});
},
},
destroyed() {
unByKey(this.eventRender);
this.removeLayer();
},
watch: {
geojsonData: {
handler() {
unByKey(this.eventRender);
this.renderAnimate();
},
deep: true,
},
},
};
</script>
高级封装:
更新数据时,只需传入更新后的geojson即可
实现代码:
<template>
<div>
<div id="map" ref="map" style="width: 100vw; height: 100vh"></div>
<!-- 警报按钮、警报面板 -->
<div
style="
position: fixed;
top: 40vh;
left: 23vw;
z-index: 999;
width: 50px;
height: 50px;
"
>
<!-- 警报按钮 -->
<div
@click="showWavePanel = !showWavePanel"
style="
display: inline-block;
width: 100%;
height: 100%;
display: flex;
flex-flow: column nowrap;
align-items: center;
justify-content: space-around;
background-color: #08305d;
cursor: pointer;
color: white;
"
>
<i class="el-icon-bell" style="font-size: 24px"></i>
<span style="font-size: 12px">警报</span>
</div>
<!-- 警报面板 -->
<LjOlWave
v-show="showWavePanel"
style="position: absolute; top: 0px; left: 60px"
:map="map"
:geojsonData="geojsonData"
></LjOlWave>
</div>
</div>
</template>
<script>
import "ol/ol.css";
import { Map, View } from "ol";
import { OSM } from "ol/source";
import { Tile as TileLayer } from "ol/layer";
import LjOlWave from "@/components/LjOlWave/index.vue";
export default {
components: { LjOlWave },
data() {
return {
map: {},
showWavePanel: false, //显示警报面板
geojsonData: {
type: "FeatureCollection",
features: [
{
type: "Feature",
properties: {
title: "警报1",
},
geometry: {
type: "Point",
coordinates: [104.2979, 30.528],
},
},
{
type: "Feature",
properties: {
title: "警报2",
},
geometry: {
type: "Point",
coordinates: [104.2987, 30.527],
},
},
{
type: "Feature",
properties: {
title: "警报3",
},
geometry: {
type: "Point",
coordinates: [104.2974, 30.526],
},
},
],
},
};
},
mounted() {
this.initMap();
this.pointerMove();
this.getAnimateInfo();
},
methods: {
getAnimateInfo() {
this.map.on("click", (e) => {
let feature = this.map.forEachFeatureAtPixel(e.pixel, (feature) => {
return feature;
});
this.$message.success(feature.get("title"));
});
},
pointerMove() {
// 设置鼠标划过矢量要素的样式
this.map.on("pointermove", (e) => {
const isHover = this.map.hasFeatureAtPixel(e.pixel);
this.map.getTargetElement().style.cursor = isHover ? "pointer" : "";
});
},
// 初始化地图
initMap() {
this.map = new Map({
target: document.getElementById("map"),
layers: [
new TileLayer({
source: new OSM(),
}),
],
view: new View({
projection: "EPSG:4326",
center: [104.29806, 30.5263],
zoom: 18,
}),
});
},
},
};
</script>
LjOlWave/index.vue:
<template>
<div
style="z-index: 99999; background-color: rgba(0, 0, 0, 0.48); padding: 10px"
>
<div>
<el-button size="mini" @click="addWaveLayer()">添加警报图层</el-button
>
<el-button size="mini" @click="removeWaveLayer()">移除警报图层</el-button>
</div>
<br />
<div style="color: white; font-size: 16px">
<div
style="
display: flex;
justify-content: space-between;
align-items: center;
"
>
<span style="width: 60px">半径:</span
><el-slider
v-model="option_wave.radius"
style="width: 200px"
></el-slider>
</div>
<div
style="
display: flex;
justify-content: space-between;
align-items: center;
"
>
速度:<el-slider
style="width: 200px"
v-model="option_wave.speed"
:min="0"
:max="1"
:step="0.01"
></el-slider>
</div>
<div
style="
display: flex;
justify-content: space-between;
align-items: center;
"
>
颜色:<el-color-picker v-model="colorPick" show-alpha></el-color-picker>
</div>
</div>
</div>
</template>
<script>
import "ol/ol.css";
import VectorLayer from "ol/layer/Vector";
import VectorSource from "ol/source/Vector";
import { Feature } from "ol";
import { Style, Circle, Stroke } from "ol/style";
import { Point } from "ol/geom";
import { getVectorContext } from "ol/render";
import GeoJSON from "ol/format/GeoJSON";
import { unByKey } from "ol/Observable"; //移除事件
// 边界json数据
export default {
name: "LjOlWave",
props: {
map: {
type: Object,
default: () => {},
},
geojsonData: {
type: Object,
default: () => {},
},
},
data() {
return {
pointLayer: {},
eventRender: {},
option_wave: {
radius: 55,
speed: 0.65,
},
colorPick: "rgba(255,0,0,1)",
};
},
computed: {
colorWave() {
let r = this.rgbaNum(this.colorPick, 0);
let g = this.rgbaNum(this.colorPick, 1);
let b = this.rgbaNum(this.colorPick, 2);
return [r, g, b];
},
},
methods: {
addWaveLayer() {
this.addLayer();
this.renderAnimate();
},
removeWaveLayer() {
unByKey(this.eventRender);
this.removeLayer();
},
//从rgba字符串中解析出各个值
rgbaNum(rgba, index) {
return rgba.match(/(\d(\.\d+)?)+/g)[index];
},
addLayer() {
if (Object.keys(this.pointLayer).length !== 0) return;
// 设置图层
this.pointLayer = new VectorLayer({
source: new VectorSource({
features: new GeoJSON().readFeatures(this.geojsonData),
}),
// zIndex: 9999999,
});
// 添加图层
this.map.addLayer(this.pointLayer);
},
removeLayer() {
if (Object.keys(this.pointLayer).length == 0) return;
this.pointLayer.getSource().clear();
this.map.removeLayer(this.pointLayer);
this.pointLayer = {};
},
renderAnimate() {
if (Object.keys(this.pointLayer).length !== 0) {
this.pointLayer.getSource().clear();
}
// 循环添加feature
let pointFeatures = [];
for (let i in this.geojsonData.features) {
const feature = new Feature({
geometry: new Point(
this.geojsonData.features[i].geometry.coordinates
),
});
pointFeatures = [...pointFeatures, feature];
feature.setProperties(this.geojsonData.features[i].properties);
}
this.pointLayer.getSource().addFeatures(pointFeatures);
// 关键的地方在此:监听postrender事件,在里面重新设置circle的样式
let radius = 0;
this.eventRender = this.pointLayer.on("postrender", (e) => {
if (radius >= this.option_wave.radius) radius = 0;
let opacity = 1 - radius / this.option_wave.radius; //不透明度
let pointStyle = new Style({
image: new Circle({
radius: radius,
stroke: new Stroke({
color: `rgba(${this.colorWave[0]},${this.colorWave[1]},${this.colorWave[2]},${opacity})`,
width: (this.option_wave.radius - radius) / 10, //设置宽度
}),
}),
});
// 获取矢量要素上下文
let vectorContext = getVectorContext(e);
vectorContext.setStyle(pointStyle);
pointFeatures.forEach((feature) => {
vectorContext.drawGeometry(feature.getGeometry());
});
radius += this.option_wave.speed; //调整闪烁速度
//请求地图渲染(在下一个动画帧处)
this.map.render();
});
},
},
watch: {
geojsonData: {
handler() {
unByKey(this.eventRender);
this.renderAnimate();
},
deep: true,
},
},
};
</script>