Vue+高德地图历史轨迹回放(进度条+倍速+时间)
原博主原创文章,亲妈式教学,整理的很是详细,但是没有放源码,为了更方便,这里放上源码。
按照原博主的文章一步步复制的代码,希望无误,不辜负原博主的代码。
其中,infoWindow原代码未定义,以下代码已加上。
首先,安装vue-amap
npm install -S vue-amap
在main.js里引入
// 导入AMap
import AMap from 'vue-amap'
// 注册
Vue.use(AMap)
// 加载
AMap.initAMapApiLoader({
// 高德的key
key: '你可爱的key',
// 插件集合
plugin: ['AMapManager', 'AMap.Autocomplete', 'AMap.PlaceSearch', 'AMap.Scale', 'AMap.OverView', 'AMap.ToolBar', 'AMap.MapType', 'AMap.PolyEditor', 'AMap.CircleEditor', 'Geocoder', 'Geolocation', 'AMap.MarkerClusterer', 'AMap.PolyEditor', 'AMap.CircleEditor', 'AMap.MouseTool', 'AMap.Driving', 'AMap.CitySearch', 'AMap.InfoWindow', 'AMap.LngLat', 'AMap.DistrictSearch', 'AMap.TileLayer.Traffic', 'AMap.Heatmap', 'AMap.Autocomplete', 'AMap.PlaceSearch'],
// 高德 sdk 版本,默认为 1.4.4
v: '1.4.4'
})
其次,以下代码放在.vue文件可以直接运行看效果
<template>
<div id="mapTrack">
<el-card class="box-card" shadow="never" :body-style="{ padding: '0' }">
<div id="container" :style="{ 'height': 'calc(100vh - 180' + 'px)' }">
</div>
<!-- 进度条 -->
<div class="map-control">
<!-- 开始按钮 -->
<Icon v-if="!start" class="play-icon" type="ios-play" @click="navgControl(playIcon)" />
<!-- 暂停按钮 -->
<Icon v-else class="play-icon" type="ios-pause" @click="navgControl('pause')" />
<!-- 开始时间-->
<span class="passed-time">{{ passedTime }}</span>
<!-- 滑动条修改2个地方 step步长 tip-forma提示框 -->
<Slider class="map-slider" v-model="sliderVal" @on-input="sliderChange" :step="0.0001" :tip-format="hideFormat">
</Slider>
<!-- 倍数 -->
<div class="map-times" @mouseenter="isTimesChoose = true" @mouseleave="isTimesChoose = false">
<div class="times-show">倍速{{ times }}</div>
<div class="choose-box">
<!-- <ul v-show="isTimesChoose"> -->
<ul v-show="isTimesChoose">
<li v-for="item in speedList" :key="item" :class="{ active: times == item }" @click="changeSpeed(item)">倍速
{{ item }}</li>
</ul>
</div>
</div>
<!-- 结束 -->
<span class="passed-time">{{ totalTime }}</span>
</div>
</el-card>
</div>
</template>
<script>
export default {
data() {
return {
// 后台传递来的路线
linePath: [
{
latitude: 39.997761,
longitude: 116.478935,
time: '2020-08-21 16:21:18'
},
{
latitude: 39.997825,
longitude: 116.478939,
time: '2020-08-21 16:21:21'
},
{
latitude: 39.998549,
longitude: 116.478912,
time: '2020-08-21 16:21:24'
},
{
latitude: 39.998555,
longitude: 116.478998,
time: '2020-08-21 16:21:27'
},
{
latitude: 39.99856,
longitude: 116.479282,
time: '2020-08-21 16:21:30'
},
{
latitude: 39.998528,
longitude: 116.479658,
time: '2020-08-21 16:21:33'
},
{
latitude: 39.998453,
longitude: 116.480151,
time: '2020-08-21 16:21:36'
},
{
latitude: 39.998302,
longitude: 116.480784,
time: '2020-08-21 16:21:39'
},
{
latitude: 39.998184,
longitude: 116.481149,
time: '2020-08-21 16:21:42'
},
{
latitude: 39.997997,
longitude: 116.481573,
time: '2020-08-21 16:21:45'
},
{
latitude: 39.997846,
longitude: 116.481863,
time: '2020-08-21 16:21:48'
},
{
latitude: 39.997718,
longitude: 116.482072,
time: '2020-08-21 16:21:51'
},
{
latitude: 39.997718,
longitude: 116.482362,
time: '2020-08-21 16:21:54'
},
{
latitude: 39.998935,
longitude: 116.483633,
time: '2020-08-21 16:21:57'
},
{
latitude: 39.998968,
longitude: 116.48367,
time: '2020-08-21 16:22:00'
},
{
latitude: 39.999861,
longitude: 116.484648,
time: '2020-08-21 16:22:03'
},
{
latitude: 39.999870,
longitude: 116.484648,
time: '2020-08-21 16:22:05'
},
{
latitude: 39.96,
longitude: 116.484648,
time: '2020-08-21 16:23'
},
{
latitude: 39.93,
longitude: 116.484648,
time: '2020-08-21 16:26'
},
{
latitude: 39.91,
longitude: 116.484648,
time: '2020-08-21 16:28'
},
{
latitude: 39.91,
longitude: 116.5,
time: '2020-08-21 16:30'
}
],
// 后台传递来的地图上的icon
icon: [
{
latitude: 39.997761,
longitude: 116.478935
},
{
latitude: 39.99856,
longitude: 116.479282
},
{
latitude: 39.999861,
longitude: 116.484648
}
],
pathList: [],
// 巡航器
navgtr: null,
passedTime: '00:00:00',
// 总时间
totalTime: '00:00:00',
playIcon: 'start', //开始按钮是重新开始还是继续
speedList: [1, 2, 3],
times: 1,
start: false,
sliderVal: 0,
isTimesChoose: false
}
},
created() {
},
mounted() {
// 设置地图基本上配置
const param = {
// 是否监控地图容器尺寸变化
resizeEnable: true,
// 初始地图级别
zoom: 15
}
this.map = new AMap.Map('container', param)
this.init()
},
methods: {
// 滑动条的提示
hideFormat() {
return this.passedTime
},
// 初始化
init() {
// 后台传递来的数据是json的,所以我们改成数组
// 轨迹
// 线条路线
let linePath = this.linePath
linePath.forEach(item => {
// 把json转为数组
this.pathList.push([item.longitude, item.latitude])
// 当前时间戳
item.stampTime = new Date(item.time).getTime()
})
linePath.forEach((item, i) => {
// 获得到下一个位置
let next = linePath[i + 1]
// 判断是否还有下一个
if (next) {
// 计算出间隔时间 每秒
item.intervalTime = (next.stampTime - item.stampTime) / 1000
// 计算出下一站
item.nextDistance = this.distanceFun(
[item.longitude, item.latitude],
[next.longitude, next.latitude]
)
// 求出具体的速度
item.nextSpeed =
item.nextDistance / 1000 / (item.intervalTime / 60 / 60)
}
})
// 设置路线
this.setPath()
// 创建起始和经过的icon
this.icon.forEach(item => {
this.addIcon(item)
})
// 在init方法加上这行代码
// 修改全局的路线数据
this.linePath = linePath
},
// 计算两个坐标点之间的距离
distanceFun(point1, point2) {
// 那数组转化为经纬度
let p1 = new AMap.LngLat(point1[0], point1[1])
let p2 = new AMap.LngLat(point2[0], point2[1])
// 计算2点直接的距离 distance这个函数有继续可以了解一下
let distance = Math.round(p1.distance(p2))
return distance
},
// 格式化时间(不解释)
getTime(sTime) {
let ss
let mm = '00'
let hh = '00'
if (sTime > 60) {
let s = sTime % 60
ss = s > 9 ? s : '0' + s
let mTime = parseInt(sTime / 60)
if (mTime > 60) {
let m = mTime % 60
mm = m > 9 ? m : '0' + m
hh = parseInt(mTime / 60)
} else {
mm = mTime > 9 ? mTime : '0' + mTime
}
} else {
ss = sTime > 9 ? sTime : '0' + sTime
}
return hh + ':' + mm + ':' + ss
},
// 滑动条改变事件
sliderChange(val) {
// 计算开始距离
let num = parseInt((val / 100) * this.pathList.length)
// 计算结束的距离
let decimal =
String((val / 100) * this.pathList.length).split('.')[1] || 0
// 移动小车
this.navgtr.moveToPoint(num, Number('0.' + decimal))
// 重新绘制
this.pathSimplifierIns.renderLater()
},
// 设置路线
setPath() {
let that = this
AMapUI.load(['ui/misc/PathSimplifier', 'lib/$'], function (
PathSimplifier
) {
if (!PathSimplifier.supportCanvas) {
console.log('当前环境不支持 Canvas!')
return
}
function onload() {
that.pathSimplifierIns.renderLater()
}
function onerror() {
console.log('图片加载失败!')
}
// 历史轨迹巡航器
that.pathSimplifierIns = new PathSimplifier({
zIndex: 100,
map: that.map, // 所属的地图实例
getPath: function (pathData) {
// 这里的pathData保存的是路线
return pathData.path
},
// 自动设置视图
autoSetFitView: true,
// 巡航器样式
renderOptions: {
// 路径导航样式
pathNavigatorStyle: {
// 一开始小车的旋转角度
initRotateDegree: 0,
// 小车的宽
width: 20,
// 小车的高
height: 32,
// 自动旋转
autoRotate: true,
// 折线拐点连接处样式
lineJoin: 'round',
// PathSimplifier提供了一个快捷方法用于创建图片内容的content(function):
// 图片地址
// 图片加载成功,重新绘制一次 onload方法
// 图片加载失败 onerror方法
content: PathSimplifier.Render.Canvas.getImageContent(
'https://webapi.amap.com/images/car.png',
onload,
onerror
),
// 这个位置提示一下 因为我们用的是图片所以看不见效果
// 想看见效果删除上面的content
// 他是一个三角形 这里就是设置边框颜色和内部颜色的
// 填充色
fillStyle: null,
// 描边色
strokeStyle: null,
// 边的宽度
lineWidth: 1,
// 巡航器经过的路径的样式
pathLinePassedStyle: {
lineWidth: 6,
strokeStyle: 'skyblue'
}
},
// 线条样式
pathLineStyle: {
lineWidth: 6,
strokeStyle: 'pink'
},
// 鼠标移入之后线条的样式
pathLineHoverStyle: {
lineWidth: 0,
borderWidth: 0
},
// 鼠标单击之后线条的样式
pathLineSelectedStyle: {
lineWidth: 6,
borderWidth: 0,
strokeStyle: 'blue'
},
pathTolerance: 0,
keyPointTolerance: -1,
renderAllPointsIfNumberBelow: 0 // 绘制路线节点,如不需要可设置为-1
}
})
// 历史轨迹巡航器设置数据 这里设置的就是上面pathData的数据
that.pathSimplifierIns.setData([
{
name: '轨迹',
path: that.pathList
}
])
// 开启中心自适应
that.pathSimplifierIns.setFitView(-1)
// 获取初始速度
// 设置默认的为 0.1
let startSpeed = 0.1
if (that.linePath.length !== 0) {
// 获取一开始的速度 并且判断是否为0 如果不为0直接返回 为0给一个初始的速度
startSpeed =
that.linePath[0].nextSpeed === 0 ? 0.1 : that.linePath[0].nextSpeed
}
// 对第一条线路(即索引 0)创建一个巡航器
that.navgtr = that.pathSimplifierIns.createPathNavigator(0, {
loop: false, // 循环播放
// 速度×倍速
speed: startSpeed * that.times
})
let linePath = that.linePath
let len = linePath.length
let startPoint = linePath[0]
let endPoint = linePath[len - 1]
// 计算总时间,hh:mm:ss 因为计算出来是时间搓 所以要进行格式化
that.totalTime = that.getTime(
(endPoint.stampTime - startPoint.stampTime) / 1000
)
// 创建 infoWindow 实例
let infoWindow = new AMap.InfoWindow({
anchor: 'bottom-center',
autoMove: false,
offset: new AMap.Pixel(0, -20),
content: '' //传入 dom 对象,或者 html 字符串
})
// 移动过程中
that.navgtr.on('move', function () {
that.playIcon = 'resume'
// 走到了第几个点
let idx = this.getCursor().idx
// 至下一个节点的比例位置
let tail = this.getCursor().tail
// 总路程
let totalIdx = idx + tail
// 计算下一个距离速度
let point = linePath[idx]
if (idx < len - 1) {
// 判断速度是否为0 如果为0 需要给个0.1的速度
point.nextSpeed === 0 && (point.nextSpeed = 0.1)
// 这里的速度记得×倍数
that.navgtr.setSpeed(point.nextSpeed * that.times)
}
// 剩余公里数,窗体随时移动展示
// 判断是否还有下一个点和时间
point &&
point.time &&
infoWindow.setContent(
`<p class="info-window">时间:<span>${point.time}`
)
// 设置提示框
infoWindow.open(that.map, that.navgtr.getPosition())
// 进度条实时展示tail
!that.isOnSlider && (that.sliderVal = (totalIdx / len) * 100)
// 已经播放时间
let sTime = parseInt(
(((endPoint.stampTime - startPoint.stampTime) / 1000) *
that.sliderVal) /
100
)
// 格式化时间
that.passedTime = that.getTime(sTime)
// 如果到头了,回到初始状态
if (that.navgtr.isCursorAtPathEnd()) {
// 设置为开始状态 让小车回到原来位置
that.playIcon = 'start'
// 设置图标状态
that.start = false
// 设置已经走过的时间
that.passedTime = that.totalTime
// 设置滑动条状态
that.sliderVal = 100
}
})
})
},
addIcon(item) {
// 设置每个icon的内容
const marker = new AMap.Marker({
// 图片
icon:
'//a.amap.com/jsapi_demos/static/demo-center/icons/poi-marker-default.png',
// 位置
position: [item.longitude, item.latitude],
//设置基点偏移
offset: new AMap.Pixel(-13, -30)
})
// 把图标放在地图上
marker.setMap(this.map)
},
// 暂停和播放按钮
navgControl(action) {
if (action === 'start') {
let that = this
this.passedTime = '00:00:00'
setTimeout(() => {
that.navgtr[action]()
}, 300)
} else {
this.navgtr[action]()
}
// 修改图标状态
this.start = !this.start
},
// 修改速度
changeSpeed(item) {
this.times = item
this.isTimesChoose = false
},
}
}
</script>
<style scoped lang="less">
@deep: ~'>>>';
// 设置图标大小
/deep/.amap-icon img {
width: 25px;
height: 34px;
}
// 设置循环器样式
.map-control {
// 绝对定位
position: absolute;
bottom: 0;
left: 0;
width: 1200px;
height: 80px;
line-height: 80px;
color: #fff;
background-image: linear-gradient(to top, rgba(0, 0, 0, 0.7), transparent);
padding: 0 40px;
z-index: 1000;
// 设置按钮大小
.play-icon {
font-size: 36px;
}
// 设置时间样式
.passed-time {
// 相对定位
position: relative;
// 转为行内元素
display: inline-block;
top: 1px;
margin-left: 15px;
font-size: 14px;
}
// 进度条样式
.map-slider {
// 转为行内元素
display: inline-block;
width: 75%;
margin-left: 15px;
position: relative;
top: 14px;
}
// 修改默认的进度条样式
.choose-box {
// 相对定位
position: absolute;
top: -135px;
left: -6px;
height: 162px;
// 过滤
transition: all 0.5s linear;
}
// 倍数
.map-times {
display: inline-block;
position: relative;
margin-left: 15px;
// 倍速样式
.times-show {
padding: 0 10px;
line-height: 24px;
font-size: 13px;
border: 1px solid #fff;
border-radius: 4px;
// 鼠标设置为默认样式
cursor: default;
}
// 倍数
ul {
background: rgba(0, 0, 0, 0.7);
padding: 10px;
width: 70px;
text-align: center;
border-radius: 3px;
li {
height: 26px;
line-height: 26px;
cursor: pointer;
}
li.active {
color: #ff8533;
}
li:hover {
font-size: 13px;
}
}
}
}
</style>
谢谢原博主的提供的代码,再次附上原创文章链接
https://blog.csdn.net/qq_47410017/article/details/120341090