记录一下,由于项目需要做车辆的历史轨迹回放,查了很多资料,在高德地图里有这几种解决方案。
所用技术:vue3 + TS +element UI plus +高德地图
这是相关的Demo借鉴
下面是效果图:
1,这是高德地图提供的轨迹回放demo
2,这是使用的高德地图AMap UI的巡航器
讲一下实现方法
1,轨迹回放的有两种写法,第一个是初始化加载的时候就把监听事件放进去
const initMap = () => {
AMapLoader.load({
key: "key", // 申请好的Web端开发者Key,首次调用 load 时必填
version: "2.0", // 指定要加载的 JSAPI 的版本,缺省时默认为 1.4.15
plugins: ["AMap.moveAnimation"], // 需要使用的的插件列表,如比例尺'AMap.Scale'等
AMapUI: {
// 是否加载 AMapUI,缺省不加载
version: "1.1", // AMapUI 版本
plugins: [], // 需要加载的 AMapUI ui插件
},
})
.then((AMap) => {
const map = new AMap.Map("container", {
//设置地图容器id
viewMode: "3D", //是否为3D地图模式
zoom: 13, //初始化地图级别
center: current_position.value, //初始化地图中心点位置
mapStyle: "amap://styles/fresh",
})
// 添加插件
AMap.plugin(
[
"AMap.ToolBar",
"AMap.Scale",
"AMap.HawkEye",
"AMap.Geolocation",
"AMap.MapType",
"AMap.MouseTool",
"AMap.Polyline",
],
function () {
//异步同时加载多个插件
// 添加地图插件
// map.addControl(new AMap.ToolBar()) // 工具条控件;范围选择控件
map.addControl(new AMap.Scale()) // 显示当前地图中心的比例尺
// map.addControl(new AMap.HawkEye()) // 显示缩略图
// map.addControl(new AMap.Geolocation()) // 定位当前位置
// map.addControl(new AMap.MapType()) // 实现默认图层与卫星图,实时交通图层之间切换
// 以下是鼠标工具插件
const mouseTool = new AMap.MouseTool(map)
// mouseTool.rule();// 用户手动绘制折线图,测量距离
// mouseTool.measureArea() // 测量面积
}
)
// 实例化点标记
const marker = new AMap.Marker({
icon: "//a.amap.com/jsapi_demos/static/demo-center/icons/poi-marker-default.png",
position: current_position.value, // 这里我们通过上面的点击获取经纬度坐标,实时添加标记
// 通过设置 offset 来添加偏移量
offset: new AMap.Pixel(-26, -54),
})
//这里监听标注点的点击事件,触发实现动画效果
marker.on("click", function () {
animationMove()
})
marker.setMap(map)
// 历史轨迹动画
function animationMove() {
AMap.plugin("AMap.MoveAnimation", function () {
var marker,
lineArr = [
[116.478935, 39.997761],
[116.478939, 39.997825],
[116.478912, 39.998549],
[116.478912, 39.998549],
[116.478998, 39.998555],
[116.478998, 39.998555],
[116.479282, 39.99856],
[116.479658, 39.998528],
[116.480151, 39.998453],
[116.480784, 39.998302],
[116.480784, 39.998302],
[116.481149, 39.998184],
[116.481573, 39.997997],
[116.481863, 39.997846],
[116.482072, 39.997718],
[116.482362, 39.997718],
[116.483633, 39.998935],
[116.48367, 39.998968],
[116.484648, 39.999861],
]
var map = new AMap.Map("container", {
resizeEnable: true,
center: [116.397428, 39.90923],
zoom: 17,
})
marker = new AMap.Marker({
map: map,
position: [116.478935, 39.997761],
icon: "https://a.amap.com/jsapi_demos/static/demo-center-v2/car.png",
offset: new AMap.Pixel(-13, -26),
})
// 绘制轨迹
var polyline = new AMap.Polyline({
map: map,
path: lineArr,
showDir: true,
strokeColor: "#28F", //线颜色
// strokeOpacity: 1, //线透明度
strokeWeight: 6, //线宽
// strokeStyle: "solid" //线样式
})
var passedPolyline = new AMap.Polyline({
map: map,
strokeColor: "#AF5", //线颜色
strokeWeight: 6, //线宽
})
marker.on("moving", function (e: any) {
passedPolyline.setPath(e.passedPath)
map.setCenter(e.target.getPosition(), true)
})
map.setFitView()
marker.moveAlong(lineArr, {
// 每一段的时长
duration: 500, //可根据实际采集时间间隔设置
// JSAPI2.0 是否延道路自动设置角度在 moveAlong 里设置
autoRotation: true,
})
})
}
})
.catch((e) => {
console.log(e)
})
}
initMap()
2,轨迹回放的第二种写法,比较灵活一点
const listPath = async () => {
//获取历史轨迹点
list_Position.value = []
//请求接口获取轨迹点数据
const res: any = await getHistoryLocus(listSearch)
//轨迹起点坐标
start_position.value = changePosition(
res.result[0].gLatitude,
res.result[0].gLongitude
) as any
lineList.value = res.result
res.result.forEach((item: any) => {
list_Position.push(changePosition(item.gLatitude, item.gLongitude))
})
console.log(list_Position, `打印list_Positionlist_Position`)
//加载地图
let marker: any,
map = new AMap.Map("container", {
resizeEnable: true,
center: start_position.value,
zoom: 13,
})
marker = new AMap.Marker({
icon: "https://a.amap.com/jsapi_demos/static/demo-center-v2/car.png",
position: start_position.value,
offset: new AMap.Pixel(-13, -30),
})
marker.on("click", function () {
;(AMap as any).plugin("AMap.MoveAnimation", function () {
marker.moveAlong(list_Position, {
// 每一段的时长
duration: 500, //可根据实际采集时间间隔设置
// JSAPI2.0 是否延道路自动设置角度在 moveAlong 里设置
autoRotation: true,
})
})
})
marker.setMap(map)
// 绘制轨迹
var polyline = new AMap.Polyline({
map: map,
path: list_Position,
showDir: true,
strokeColor: "#28F", //线颜色
// strokeOpacity: 1, //线透明度
strokeWeight: 6, //线宽
// strokeStyle: "solid" //线样式
})
var passedPolyline = new AMap.Polyline({
map: map,
strokeColor: "#AF5", //线颜色
strokeWeight: 6, //线宽
})
marker.on("moving", function (e: any) {
passedPolyline.setPath(e.passedPath)
map.setCenter(e.target.getPosition())
})
;(map as any).setFitView()
}
3,使用AMap UI的轨迹巡航器来实现,配合进度条,这样写会有个BUG就是点击开始的时候,直接拖动进度条会报错
const listPath = async () => {
//获取历史轨迹点
list_Position.value = []
//请求接口获取轨迹点数据
const res: any = await getHistoryLocus(listSearch)
//轨迹起点坐标
start_position.value = changePosition(
res.result[0].gLatitude,
res.result[0].gLongitude
) as any
lineList.value = res.result
res.result.forEach((item: any) => {
list_Position.push(changePosition(item.gLatitude, item.gLongitude))
})
console.log(list_Position, `打印list_Positionlist_Position`)
// -----------------轨迹巡航器--------------------------------------
//@ts-ignore 单行忽略 解决TS语法提示 AMap UI 不存在
AMapUI.load(
["ui/misc/PathSimplifier", "lib/$", "ui/overlay/SimpleMarker"],
function (PathSimplifier: any, $: any, SimpleMarker: any) {
if (!PathSimplifier.supportCanvas) {
alert("当前环境不支持 Canvas!")
return
}
// $.SimpleMarker
let pathSimplifierIns: any = new PathSimplifier({
zIndex: 100,
SimpleMarker: SimpleMarker,
//autoSetFitView:false,
map: map, //所属的地图实例
getPath: function (pathData: any, pathIndex: any) {
return pathData.path
},
getHoverTitle: function (
pathData: any,
pathIndex: any,
pointIndex: any
) {
if (pointIndex >= 0) {
//point
return (
pathData.name + ",点:" + pointIndex + "/" + pathData.path.length
)
}
return pathData.name + ",定位点数量" + pathData.path.length
},
renderOptions: {
pathLineStyle: {
dirArrowStyle: true,
},
getPathStyle: function (pathItem: any, zoom: any) {
let lineWidth = Math.round(4 * Math.pow(1.1, zoom - 3))
return {
pathLineStyle: {
strokeStyle: "#28F",
lineWidth: 6,
},
pathLineSelectedStyle: {
lineWidth: 6,
},
pathNavigatorStyle: {
fillStyle: "#AF5",
},
}
},
},
})
//设置数据
pathSimplifierIns.setData([
{
name: "路线1",
path: list_Position,
},
])
// 巡航器的样式
let pathNavigatorStyles = [
{
width: 16,
height: 32,
//使用图片
content: PathSimplifier.Render.Canvas.getImageContent(
"https://a.amap.com/jsapi_demos/static/demo-center-v2/car.png",
pathSimplifierIns.renderLater()
),
},
]
//对第一条线路(即索引 0)创建一个巡航器
var navg1 = pathSimplifierIns.createPathNavigator(0, {
loop: false, //循环播放
speed: times.value, //巡航速度,单位千米/小时
pathNavigatorStyle: pathNavigatorStyles[0],
})
navgtr.value = navg1
navg1.on("move", function () {
let idx = navgtr.value.getCursor().idx //走到了第几个点
let tail = navgtr.value.getCursor().tail //至下一个节点的比例位置
let totalIdx = idx + tail
// 进度条实时展示tail
!isOnSlider.value && (currentIndex.value = totalIdx)
//地图中心跟随变化
map.setCenter(list_Position[idx])
// 已经播放时间
// 如果到头了,回到初始状态
console.log(
navgtr.value.isCursorAtPathEnd(),
`打印navgtr.value.isCursorAtPathEnd()`
)
if (navgtr.value.isCursorAtPathEnd()) {
currentIndex.value = list_Position.length
}
})
pathSimplifier.value = pathSimplifierIns
}
)
gmap.value = map
console.log(res, `打印getHistoryLocusresres`)
}
// 开始按钮
const startBtn = () => {
navgtr.value.start()
}
// 暂停按钮
const stopBtn = () => {
navgtr.value.pause()
}
// 快进
const changeSpeed = () => {
times.value = times.value + 200
navgtr.value.setSpeed(times.value)
console.log(times.value, `打印times.valuetimes.value`)
}
// 慢放
const slowSpeed = () => {
if (times.value > 400) {
times.value = times.value - 200
}
navgtr.value.setSpeed(times.value)
console.log(times.value, `打印times.valuetimes.value`)
}
//继续
const puseSpeed = () => {
navgtr.value.resume()
}
const stopMove = () => {
console.log(`打印sahdjlahdasb;`)
navgtr.value.pause()
}
//进度条带动巡航器变化
const changeSliderValue = () => {
navgtr.value.pause()
navgtr.value.moveToPoint(currentIndex.value, 0)
pathSimplifier.value.renderLater()
gmap.value.setCenter(list_Position[currentIndex.value])
}
// 滑块的提示信息
const handlDelta = (e: any) => {
e = e.toFixed(0)
let nowtime: any = ""
let dayMile: any = 0
let totalMile: any = 0
let str = ""
if (lineList.value) {
lineList.value.forEach((item: any, index: any) => {
nowtime =
item.gReportTime.split("T")[0] +
" " +
item.gReportTime.split("T")[1].split("+")[0]
dayMile = item.sDayMileage.toFixed(2)
totalMile = Number(totalMile) + Number(dayMile)
if (index == e) {
str =
nowtime +
" 今日:" +
dayMile +
" km 累计:" +
totalMile.toFixed(2) +
" km"
}
})
}
return str
}
这是dom的结构
<div class="fix_bottom_close">
<el-tabs type="border-card">
<el-tab-pane label="时间轴">
<el-button-group>
<el-button type="primary" @click="slowSpeed">慢放</el-button>
<el-button type="primary" @click="startBtn"> 开始 </el-button>
<el-button type="primary" @click="stopBtn"> 暂停 </el-button>
<el-button type="primary" @click="puseSpeed"> 继续 </el-button>
<el-button type="primary" @click="changeSpeed"> 快进 </el-button>
</el-button-group>
<div class="fix_bottom_slider">
<el-slider
ref="timeSlider"
v-model="currentIndex"
show-stops
:max="list_Position.length"
:format-tooltip="handlDelta"
@change="changeSliderValue"
/>
</div>
</el-tab-pane>
<el-tab-pane label="速度曲线">Config</el-tab-pane>
<el-tab-pane label="行程数据">Role</el-tab-pane>
<el-tab-pane label="详细数据">Task</el-tab-pane>
</el-tabs>
</div>