vue+openlayers实现轨迹回放动画
效果:
代码:
<!-- -->
<template>
<div class="route-review">
<button @click="toggleFn">{
{
buttonContent }}</button>
<div id="map"></div>
</div>
</template>
<script>
//这里可以导入其他文件(比如:组件,工具js,第三方插件js,json文件,图片文件等等)
//例如:import 《组件名称》 from '《组件路径》';
// //引入主要组件
// // import { Feature, Map, Overlay, View } from "ol";
import {
Map, View, Feature } from "ol";
// //引入layer
import ImageLayer from "ol/layer/Image";
import VectorLayer from "ol/layer/Vector";
// //引入source
import Static from "ol/source/ImageStatic";
import VectorSource from "ol/source/Vector";
// //交互
// import { Draw } from "ol/interaction";
// //其他
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 { Style, Icon, Text, Fill, Stroke } from "ol/style";
import {
getVectorContext } from "ol/render";
// //导入图片
import mapImage from "@/assets/img/map1.jpg"; //底图
import personImg from "@/assets/img/anchor.png";
// import startImg from "@/assets/img/start.jpeg";
// import endImg from "@/assets/img/end.jpeg";
// //引入图形算法工具类turf
// import * as turf from "@turf/turf";
export default {
//import引入的组件需要注入到对象中才能使用
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,
}),
}),
// //标记
// startFeature: null, //开始的标记
// startFeatureStyle: new Style({
// // 设置开始标记样式
// image: new Icon({
// anchor: [0.5, 1],
// src: startImg,
// scale: 0.03, // 图标缩放比例
// }),
// }),
// endFeature: null, //结束的标记
// endstartFeatureStyle: new Style({
// // 设置结束标记样式
// image: new Icon({
// anchor: [0.5, 1],
// src: endImg,
// }),
// }),
// 动画
isAnimate: false, //是否开始动画
speed: 2, //速度
buttonContent: "开始播放",
now: null, //开始的时间
};
},
//计算属性 类似于data概念
computed: {
},
//监控data中的数据变化
watch: {
},
//方法集合
methods: {
//初始化地图
initMap() {
//限制范围
let extent = [0, 0, 2560, 1920];
//投影
let projection = new Projection({
//SRS 标识符代码,例如EPSG:4326
code: "xkcd-image",
// 单位。必需,除非 定义 PROJ4 投影。code
units: "pixels",
//SRS 的有效性范围。
extent: extent,
});
//底图Layers
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
zoom: 2,
//配置最大zoom
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]);
//请求地图渲染(在下一个动画帧处)
// this.map.render();
},
//开始动画
beginAnimate() {
if (this.isAnimate) {
//结束动画
this.stopAnimate(false);
} else {
//开启动画(隐藏小人)
this.isAnimate = true;
this.buttonContent = "重新播放";
this.now = new Date().getTime(); //开始的时间
//去掉小人
this.PersonFeature.setStyle(null);
// postrender在layer渲染后调用轨迹播放
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();
}
},
//设置运动轨迹之后小人feature显示
createLabelStyle(personImg) {
return new Style({
image: new Icon({
anchor: [0.5, 0.8], // 居中
// anchorOrigin: 'top-right',
// anchorXUnits: 'fraction',
// anchorYUnits: 'pixels',
// offsetOrigin: 'top-right',
// offset:[0,10],
scale: 0.8, // 图标缩放比例
opacit: 1, // 透明度
src: personImg, // 图标的url
}),
});
},
},
beforeCreate() {
}, //生命周期 - 创建之前
//生命周期 - 创建完成(可以访问当前this实例)
created() {
},
beforeMount() {
}, //生命周期 - 挂载之前
//生命周期 - 挂载完成(可以访问DOM元素)
mounted() {
//创建地图
this.initMap();
this.getTrack();
},
beforeUpdate() {
}, //生命周期 - 更新之前
updated() {
}, //生命周期 - 更新之后
beforeDestroy() {
}, //生命周期 - 销毁之前
destroyed() {
}, //生命周期 - 销毁完成
activated() {
}, //如果页面有keep-alive缓存功能,这个函数会触发
};
</script>
<style scoped>
#map {
width: 1000px;
height: 800px;
border: 1px solid #333;
margin: 0 auto;
}
button {
display: block;
margin: 10px auto;
}
</style>