vue+openlayers实现轨迹回放动画
效果:
代码:
<!-- -->
<template>
<div class="route-review">
<button @click="toggleFn">{{ buttonContent }}</button>
<div id="map"></div>
</div>
</template>
<script>
import { Map, View, Feature } from "ol";
import ImageLayer from "ol/layer/Image";
import VectorLayer from "ol/layer/Vector";
import Static from "ol/source/ImageStatic";
import VectorSource from "ol/source/Vector";
import Projection from "ol/proj/Projection";
import { getCenter } from "ol/extent";
import { LineString, Point } from "ol/geom";
import { Style, Stroke, Icon } from "ol/style";
import { getVectorContext } from "ol/render";
import mapImage from "@/assets/img/map1.jpg";
import personImg from "@/assets/img/anchor.png";
export default {
components: {},
data() {
return {
map: null,
routeLayer: null,
routeSource: new VectorSource(),
routeCoords: [],
routeLength: 0,
routeFeature: null,
routeStyles: new Style({
stroke: new Stroke({
width: 6,
color: [237, 212, 0, 0.8],
}),
}),
PersonFeature: null,
PersonFeatureStyle: new Style({
image: new Icon({
anchor: [0.5, 0.8],
scale: 0.8,
opacit: 1,
src: personImg,
}),
}),
isAnimate: false,
speed: 2,
buttonContent: "开始播放",
now: null,
};
},
computed: {},
watch: {},
methods: {
initMap() {
let extent = [0, 0, 2560, 1920];
let projection = new Projection({
code: "xkcd-image",
units: "pixels",
extent: extent,
});
let mapLayer = new ImageLayer({
source: new Static({
url: mapImage,
projection: projection,
imageExtent: extent,
}),
});
this.routeLayer = new VectorLayer({
source: this.routeSource,
});
this.map = new Map({
target: "map",
layers: [mapLayer, this.routeLayer],
view: new View({
projection: projection,
center: getCenter(extent),
zoom: 2,
maxZoom: 10,
}),
});
},
getTrack() {
let res = [
[256.83593750000006, 370.91015624999994],
[261.71875000000006, 425.59765624999994],
[253.90625000000006, 469.54296875],
[338.86718750000006, 488.09765625],
[406.25000000000006, 488.09765625],
[471.67968750000006, 488.09765625],
[544.9218750000001, 471.49609375],
[547.8515625000001, 416.80859374999994],
[542.9687500000001, 366.02734374999994],
];
this.routeCoords = res.map((item) => {
return (item = [item[0], item[1]]);
});
this.routeLength = this.routeCoords.length;
this.routeFeature = new Feature(new LineString(this.routeCoords));
this.routeFeature.setStyle(this.routeStyles);
this.PersonFeature = new Feature(new Point(this.routeCoords[0]));
this.PersonFeature.setStyle(this.PersonFeatureStyle);
this.routeLayer
.getSource()
.addFeatures([this.routeFeature, this.PersonFeature]);
},
beginAnimate() {
if (this.isAnimate) {
this.stopAnimate(false);
} else {
this.isAnimate = true;
this.buttonContent = "重新播放";
this.now = new Date().getTime();
this.PersonFeature.setStyle(null);
this.routeLayer.on("postrender", this.moveFeature);
this.map.render();
}
},
stopAnimate(ended) {
this.isAnimate = false;
this.buttonContent = "开始播放";
let ii = ended ? this.routeLength - 1 : 0;
let coord = this.routeCoords[ii];
this.PersonFeature.setGeometry(new Point(coord));
this.PersonFeature.setStyle(this.createLabelStyle(personImg));
this.routeLayer.un("postrender", this.moveFeature);
},
moveFeature(e) {
let vectorContext = getVectorContext(e);
let frameState = e.frameState;
console.log("渲染图层的画布", vectorContext);
console.log("frameState", frameState);
if (this.isAnimate) {
let elapsedTime = frameState.time - this.now;
console.log("frameState.time", frameState.time);
console.log("now", this.now);
console.log("elapsedTime", elapsedTime);
let index = Math.round((this.speed * elapsedTime) / 1000);
console.log("index", index);
if (index >= this.routeLength) {
this.stopAnimate();
return;
}
let currentPoint = new Point(this.routeCoords[index]);
let feature = new Feature(currentPoint);
vectorContext.drawFeature(feature, this.PersonFeatureStyle);
}
this.map.render();
},
toggleFn() {
if (this.buttonContent == "重新播放") {
this.stopAnimate(false);
} else {
this.beginAnimate();
}
},
createLabelStyle(personImg) {
return new Style({
image: new Icon({
anchor: [0.5, 0.8],
scale: 0.8,
opacit: 1,
src: personImg,
}),
});
},
},
beforeCreate() {},
created() {},
beforeMount() {},
mounted() {
this.initMap();
this.getTrack();
},
beforeUpdate() {},
updated() {},
beforeDestroy() {},
destroyed() {},
activated() {},
};
</script>
<style scoped>
#map {
width: 1000px;
height: 800px;
border: 1px solid #333;
margin: 0 auto;
}
button {
display: block;
margin: 10px auto;
}
</style>