「这是我参与11月更文挑战的第7天,活动详情查看:2021最后一次更文挑战」。
前言
由于最近项目需要,需要在vue项目中使用OpenLayers来进行 GIS 地图的开发,网上对 OpenLayers 文章并不算太多,借此机会分享下自己在项目中实际使用的一些心得。
本系列将陆续分享项目过程中实现的一些功能点。
本文基于前文的轨迹绘制vue+OpenLayers 项目实践(六):历史轨迹绘制,为其添加个动画追踪效果。
具体实现
本文需要实现的功能:
- 丰富轨迹右下角卡片中的选项及按钮,添加自定义速度,开始结束暂停等功能按键;
- 实现动态追踪效果
ok,接下来进行具体实现:
1,引入小车icon
首先我们引入一个小车图片:
在代码中添加小车的图层,初始位置在第一个坐标点。
// 绘制轨迹
drawRoute() {
if (this.routeGeometry) {
this.routeSource.clear();
}
this.routeGeometry = new LineString(this.routes);
let route = new Feature(this.routeGeometry);
// 绘制点
let opints = this.drawPoint();
// 添加小车
let car = this.drawCar();
this.routeSource.addFeatures([route, ...opints, car]);
// 按轨迹边界缩放
this.mapFit();
},
...
// 小车
drawCar() {
const carMarker = new Feature({
geometry: new Point(this.routeGeometry.getFirstCoordinate()),
});
let _style = new Style({
image: new Icon({
anchor: [0.5, 0.5],
src: require("@/assets/img/car.png"),
imgSize: [20, 36],
}),
});
carMarker.setStyle(_style);
this.carGeometry = carMarker;
return carMarker;
},
复制代码
OK,添加了一个绿色小车。
2. 拓展右下角控件
<div class="speed">
速度:
<div class="speed-input">
<el-slider
v-model="speed"
:min="10"
:max="1000"
:step="10"
></el-slider>
</div>
<el-button type="primary" @click="changeAnimation">{{
animationText
}}</el-button>
</div>
复制代码
添加一个速度滑块控件。
扫描二维码关注公众号,回复:
13234425 查看本文章
3.动画实现
openlayers轨迹动画的实现原理:
- 通过speed对现有路径进行分割,分割出大量的坐标点。
- 通过监听postrender进行重新设置小车的坐标点来实现动画。
首先封装一下公共的方法:
- 我们需要小车能够转向,所以需要计算出小车旋转的弧度
- 计算出两个点位之间的总长度
- 根据总长度及speed来将两个点位间插入分割点,返回一个新数组
/*
* @Author: Shao Tao
* @Date: 2021-11-19 13:38:44
* @LastEditTime: 2021-11-19 15:53:14
* @LastEditors: Shao Tao
* @Description:
* @FilePath: \vue-openlayers\src\utils\openlayers\route.js
*/
import { getDistance } from "ol/sphere";
import { transform } from "ol/proj";
/**
* 根据坐标获取弧度
*/
export function getRotation(lng1, lat1, lng2, lat2) {
let rotation = Math.atan2(lng2 - lng1, lat2 - lat1);
return rotation;
}
/**
* 计算坐标2点之间的距离
*/
export function formatLength(map, pointArray) {
let length = 0;
if (map.getView().getProjection().code_ == "EPSG:4326") {
for (let i = 0, ii = pointArray.length - 1; i < ii; ++i) {
let c1 = pointArray[i];
let c2 = pointArray[i + 1];
length += getDistance(c1, c2);
}
} else if (map.getView().getProjection().code_ == "EPSG:3857") {
for (let i = 0, ii = pointArray.length - 1; i < ii; ++i) {
let c1 = pointArray[i];
let c2 = pointArray[i + 1];
c1 = transform(c1, "EPSG:3857", "EPSG:4326");
c2 = transform(c2, "EPSG:3857", "EPSG:4326");
length += getDistance(c1, c2);
}
}
return length;
}
/**
* 计算两点之间的中间点
* @param {*} map
* @param {Array} pointDoubleArray 二维数组坐标
* @param {num} speed 每个点之间的距离
*/
export function getCenterPoint(map, pointDoubleArray, speed) {
speed = speed == undefined ? 10 : speed;
let twolength = formatLength(map, pointDoubleArray);
let rate = twolength / speed; //比例 默认10m/点
let step = Math.ceil(rate); //步数(向上取整)
let arr = new Array(); //定义存储中间点数组
let c1 = pointDoubleArray[0]; //头部点
let c2 = pointDoubleArray[1]; //尾部点
let x1 = c1[0],
y1 = c1[1];
let x2 = c2[0],
y2 = c2[1];
for (let i = 1; i < step; i++) {
let coor = new Array(2);
coor[0] = ((x2 - x1) * i) / rate + x1;
coor[1] = ((y2 - y1) * i) / rate + y1;
arr.push(coor); //此时arr为中间点的坐标
}
arr.push(c2);
return arr;
}
复制代码
接着修改Route.vue文件,在获取路径后将路径进行分割getRoutesAll
// 修改getList方法,调用getRoutesAll方法
getList() {
....
this.getRoutesAll();
},
// 新增 分割路径点
getRoutesAll() {
this.lastRouteIndex = 0;
let _routesAll = [
{
coordinate: this.routes[0],
},
];
for (let i = 0, len = this.routes.length; i < len - 1; i++) {
const item = this.routes[i];
const itemNext = this.routes[i + 1];
const rotation = getRotation(...item, ...itemNext);
let points = getCenterPoint(this.openMap, [item, itemNext], this.speed);
points = points.map((item) => {
return {
rotation,
coordinate: item,
};
});
_routesAll = [..._routesAll, ...points];
}
this.routesAll = _routesAll;
},
复制代码
这样我们就生成里一个包含弧度以及坐标的轨迹数组。然后实现点位移动。
changeAnimation() {
this.animating ? this.stopAnimation() : this.startAnimation();
},
// 开始动画
startAnimation() {
this.animating = true;
this.lastTime = Date.now();
this.animationText = "停止";
this.routeLayer.on("postrender", this.moveFeature);
this.carFeature.setGeometry(null);
},
// 停止动画
stopAnimation() {
this.animating = false;
this.animationText = "开始";
this.carFeature.setGeometry(this.carGeometry);
this.routeLayer.un("postrender", this.moveFeature);
},
// 移动动画
moveFeature(event) {
// 具体移动动画方法
},
复制代码
实现moveFeature方法:
moveFeature(event) {
const len = this.routesAll.length;
if (this.lastRouteIndex < len - 1) {
this.lastRouteIndex++;
} else {
this.lastRouteIndex = 0;
}
const current = this.routesAll[this.lastRouteIndex];
this.carGeometry.setCoordinates(current.coordinate);
const vectorContext = getVectorContext(event);
let _style = new Style({
image: new Icon({
anchor: [0.5, 0.5],
rotation: current.rotation,
src: require("@/assets/img/car.png"),
imgSize: [20, 36],
}),
});
vectorContext.setStyle(_style);
vectorContext.drawGeometry(this.carGeometry);
this.openMap.render();
},
复制代码
ok,到此咱们就将整个轨迹移动实现了。
最终效果
往期目录:
最后
gitee 地址:gitee.com/shtao_056/v…
感谢大家阅读
如果能帮助到您,那更是我的万分荣幸。
后面应该会陆续的更新更多的功能使用,以及项目中遇到的一些问题,感兴趣的小伙伴可以点个收藏/关注。