数据可视化大屏项目开发中,有一个需求是实现汽车的轨迹回放效果,今天我们实现这个功能
动态效果
功能介绍
主要包含以下功能:
- 车辆选择
- 地图展示
- 路线绘制
- 车辆动画
- 车辆信息展示
核心实现
1. 初始化地图
initMap() {
if (this.map) {
this.map.destroy();
}
this.map = new AMap.Map(this.$refs.mapContainer, {
zoom: 16,
center: [119.565775, 39.929105],
mapStyle: 'amap://styles/eebad0d61cdc83f8253e3fbcdf5cd2bc',
viewMode: '2D',
});
this.initDriving();
}
这段代码初始化了高德地图,设置了地图的中心点、缩放级别和样式。
2. 路线绘制
async drawRoute(start, end) {
// ... 省略部分代码
const result = await new Promise((resolve, reject) => {
this.driving.search(start, end, function (status, result) {
if (status === 'complete') {
resolve(result);
} else {
reject(result);
}
});
});
const steps = result.routes[0].steps;
const path = steps.reduce((accumulator, step) => accumulator.concat(step.path), []);
this.drawPolyline(path);
this.animateMarker(path);
}
这个方法使用高德地图的驾车路线规划API,获取起点到终点的路线,然后绘制路线并开始车辆动画。
3. 车辆动画
animateMarker(path) {
this.currentMarker = new AMap.Marker({
map: this.map,
position: path[0],
icon: new AMap.Icon({
size: new AMap.Size(52, 26),
image: car,
imageSize: new AMap.Size(52, 26),
}),
offset: new AMap.Pixel(-26, -13),
autoRotation: true,
});
this.currentMarker.moveAlong(path, 200);
}
这段代码创建了一个表示车辆的标记,并使用moveAlong
方法让车辆沿着路径移动。
4. 车辆信息更新
updateVehicleInfo(selectedVehicle) {
const vehicle = this.vehicleList.find(v => v.value === selectedVehicle);
if (vehicle) {
this.vehicleInfo = {
name: vehicle.name,
startAddress: vehicle.startAddress,
endAddress: vehicle.endAddress,
loadRate: vehicle.loadRate,
usageTime: vehicle.usageTime
};
}
}
当选择不同车辆时,这个方法会更新显示的车辆信息。
完整实例代码
<template>
<div class="gdMap">
<div class="searchBody">
<div class="searchItem">
<selected class="ml200" placeholder="选择车辆" v-model:active="selectedVehicle"
:list="vehicleList"></selected>
</div>
</div>
<div class="popInfo">
<p>车牌号: {{ vehicleInfo?.name || '' }}</p>
<p>起点: {{ vehicleInfo?.startAddress || '' }}</p>
<p>终点: {{ vehicleInfo?.endAddress || '' }}</p>
<p>满载率: {{ vehicleInfo?.loadRate || '' }}%</p>
<p>行驶时间: {{ vehicleInfo?.usageTime || '' }}</p>
</div>
<div class="container" ref="mapContainer" id="container"></div>
</div>
</template>
<script>
import car from './assets/car.png';
import selected from "./selected/index.vue";
export default {
data() {
return {
map: null,
driving: null,
selectedVehicle: '',
vehicleList: this.generateVehicleList(),
currentPolyline: null,
currentMarker: null,
vehicleInfo: null,
};
},
components: { selected },
mounted() {
this.initMap();
this.selectedVehicle = this.vehicleList[0].value; // 默认选中第一辆车
this.updateVehicleInfo(this.selectedVehicle);
},
watch: {
selectedVehicle(newVal) {
if (newVal) {
const vehicle = this.vehicleList.find(v => v.value === newVal);
if (vehicle) {
this.initMap(); // 重新初始化地图
this.drawRoute(vehicle.start, vehicle.end);
this.updateVehicleInfo(newVal);
}
}
}
},
methods: {
initMap() {
if (this.map) {
this.map.destroy(); // 销毁之前的地图实例
}
this.map = new AMap.Map(this.$refs.mapContainer, {
zoom: 16,
center: [119.565775, 39.929105], // 默认中心点
mapStyle: 'amap://styles/eebad0d61cdc83f8253e3fbcdf5cd2bc',
viewMode: '2D',
});
this.initDriving();
},
initDriving() {
this.driving = new AMap.Driving();
},
async drawRoute(start, end) {
try {
// 移除之前的轨迹线和车辆标记
if (this.currentPolyline) {
this.currentPolyline.setMap(null);
}
if (this.currentMarker) {
this.currentMarker.setMap(null);
}
// 设置地图中心点为车辆的起点
this.map.setCenter(start);
const result = await new Promise((resolve, reject) => {
this.driving.search(start, end, function (status, result) {
if (status === 'complete') {
resolve(result);
} else {
reject(result);
}
});
});
const steps = result.routes[0].steps;
const path = steps.reduce((accumulator, step) => accumulator.concat(step.path), []);
this.drawPolyline(path);
this.animateMarker(path);
} catch (error) {
console.log('获取驾车数据失败:' + error);
}
},
drawPolyline(path) {
this.currentPolyline = new AMap.Polyline({
path: path,
strokeColor: '#FF0000',
strokeWeight: 6,
map: this.map,
});
},
animateMarker(path) {
this.currentMarker = new AMap.Marker({
map: this.map,
position: path[0],
icon: new AMap.Icon({
size: new AMap.Size(52, 26),
image: car,
imageSize: new AMap.Size(52, 26),
}),
offset: new AMap.Pixel(-26, -13),
autoRotation: true,
});
this.currentMarker.moveAlong(path, 200);
// 添加点击事件
this.currentMarker.on('click', () => {
const vehicle = this.vehicleList.find(v => v.value === this.selectedVehicle);
if (vehicle) {
this.vehicleInfo = {
name: vehicle.name,
startAddress: vehicle.startAddress,
endAddress: vehicle.endAddress,
loadRate: vehicle.loadRate,
usageTime: vehicle.usageTime
};
}
});
},
updateVehicleInfo(selectedVehicle) {
const vehicle = this.vehicleList.find(v => v.value === selectedVehicle);
if (vehicle) {
this.vehicleInfo = {
name: vehicle.name,
startAddress: vehicle.startAddress,
endAddress: vehicle.endAddress,
loadRate: vehicle.loadRate,
usageTime: vehicle.usageTime
};
}
},
generateVehicleList() {
const addresses = ['山东路', '人民路', '解放路', '建设路', '和平路'];
const vehicles = [];
for (let i = 0; i < 10; i++) {
const start = [119.565775 + Math.random() * 0.01, 39.929105 + Math.random() * 0.01];
const end = [119.565775 + Math.random() * 0.01, 39.929105 + Math.random() * 0.01];
const startAddress = addresses[Math.floor(Math.random() * addresses.length)];
const endAddress = addresses[Math.floor(Math.random() * addresses.length)];
const loadRate = Math.floor(Math.random() * 100);
const usageTime = `${Math.floor(Math.random() * 5) + 1}小时${Math.floor(Math.random() * 60)}分钟`;
const plateNumber = `冀T${Math.random().toString(36).substr(2, 4).toUpperCase()}X`;
vehicles.push({
value: plateNumber,
name: plateNumber,
start: start,
end: end,
startAddress: startAddress,
endAddress: endAddress,
loadRate: loadRate,
usageTime: usageTime
});
}
return vehicles;
}
},
beforeDestroy() {
if (this.map) {
this.map.destroy();
}
},
};
</script>