效过图:
全部代码:
<!-- 轨迹回放 -->
<template>
<view class="wrap" @tap="handleWrapTap">
<map style="width: 100%; height: 100vh" id="myMap" :latitude="latitude" :longitude="longitude" :include-points="points" :polyline="polyline" :markers="markers"></map>
<view class="tips">
<view class="item tips_speed">{{ `当前时速${spd}km/h` }}</view>
<view class="item tips_mile">{{
`已行驶${mlg > 0 ? mlg / 10 : 0}km`
}}</view>
</view>
<view class="bottom_wrap">
<view class="header">
<view class="header-content col" @click="putOut()">
<img class="img" src="/business_module/static/location/exit-play.png" />
<view>退出播放</view>
</view>
<progress style="width: 400rpx" class="item" id="progress" @tap="handleTapProgress" @activeend="handleActiveEnd" @touchmove="handletouchmove" @touchstart="handletouchstart" @touchend="handletouchend" :active="true" active-mode="forwards" :percent="percent" stroke-width="6" duration="10" />
<view class="header-content col">
<view class="progress">
<view id="group" class="btn_group" v-if="showGroup">
<image id="group1" @tap="speedMove" class="img speed mb20" mode="aspectFit" src="/business_module/static/location/add-speed.png" />
<image id="group2" @tap="reduceMove" class="img speed mb20" mode="aspectFit" src="/business_module/static/location/reduce-speed.png" />
<image id="group3" @tap="!startMove ? startMoveCar() : stopMove()" class="img" mode="aspectFit" :src="
!startMove
? '/business_module/static/location/start-icon.png'
: '/business_module/static/location/stop-icon.png'
" />
</view>
<image id="image" @tap="handleStartMove" v-else class="img_start" mode="aspectFit" :src="
!startMove
? '/business_module/static/location/start-icon.png'
: '/business_module/static/location/stop-icon.png'
" />
</view>
<view>开始播放</view>
</view>
</view>
<uni-segmented-control :current="current" :values="items" style-type="text" active-color="#0173FF" @clickItem="onClickItem" />
<view class="progress_wrap">
<uni-datetime-picker v-model="datetimerange" type="datetimerange" rangeSeparator="至" />
</view>
<view class="bottom">
<view class="average_speed">
<view class="value">{{ spd }}</view>
<view class="label">平均速度(km/h)</view>
</view>
<view class="average_speed">
<view class="value">{{ mlg > 0 ? mlg / 10 : 0 }}</view>
<view class="label">总里程(km)</view>
</view>
</view>
</view>
</view>
</template>
<script>
import store from "@/store/index"
import common from "@/utils/common"
import date from "@/utils/date"
export default {
data() {
return {
pwd: '', // 隐私密码
datetimerange: [], // 搜索时间范围date.currentDate(), date.currentDate()
// 车辆所有数据
carData: {
// 路线点
track: [],
// 车辆信息
vehicle_info: {
license_plate_number: "无",
start_time: "2021-04-06 00:00:00",
arrive_time: "2021-04-06 09:32:33",
},
},
carInfo: null,
percent: 0, // 进度条百分比
showGroup: false,
// 搜索条件
formData: {
license: "",
startTime: "",
endTime: "",
},
// 中心经纬度
latitude: "",
longitude: "",
// 标记点
markers: [],
posi: {
latitude: 0,
longitude: 0,
iconPath: "../../static/images/brand.png",
callout: {
content: "ssss", // 车牌信息
display: "BYCLICK",
fontWeight: "bold",
color: "#5A7BEE", //文本颜色
fontSize: "12px",
bgColor: "#ffffff", //背景色
padding: 5, //文本边缘留白
textAlign: "center",
},
label: {
content: 'dddd',
color: '#666666',
fontSize: '12',
bgColor: '#ffffff',
padding: 2,
borderRadius: 4
},
anchor: {
x: 0.5,
y: 0.6,
},
width: 40,
height: 40,
id: 9,
},
// 线数据
polyline: [{
points: [],
color: "#3591fc",
arrowLine: true, //带箭头的线
width: 4,
}, ],
lineArr: [], // 所有轨迹点数组
points: [],
timer: null,
width: "",
startX: "",
spd: 0,
mlg: 0,
gtm: "",
tipsDate: null, // 日期提示
wait: 50,
wait2: 50,
iNum: 0, // 当前运动点在所有轨迹点数组的下标
startMove: false, // 开始移动
mapContext: null,
clientX_start: 0,
clientX_end: 0,
posi_x: 0,
current: 0,
items: ["今天", "昨天", "本周", "本月", "自定义"],
}
},
computed: {
currentDevice: () => store.state.device.currentDevice,
},
watch: {
datetimerange(newval) {
// 如果选择自定, 必须先选择时间再查询
if (!newval || !newval.length) {
return
}
this.getData()
},
// 车辆当前时间点
gtm: {
handler() {
if (this.gtm) {
this.tipsDate = {
date: `${this.gtm[0].substring(4).substring(0, 2)}-${this.gtm[0].substring(4).substring(2)}`,
// time: `${this.gtm[1].substring(0, 2)}:${this.gtm[1].substring(2,4)}:${this.gtm[1].substring(4)}`,
}
}
},
deep: true,
immediate: true,
},
},
methods: {
onClickItem(e) {
this.current = e.currentIndex
// 选择时间段
this.setTime()
},
handletouchmove(e) {
this.startMove = false
let currentX = e.touches[0].pageX
let currentY = e.touches[0].pageY
let tx = currentX - this.lastX
let ty = currentY - this.lastY
let text = ""
//左右方向滑动
if (Math.abs(tx) > Math.abs(ty)) {
if (tx < 0) {
text = "向左滑动"
} else if (tx > 0) {
text = "向右滑动"
}
}
//上下方向滑动
else {
if (ty < 0) {
text = "向上滑动"
} else if (ty > 0) {
text = "向下滑动"
}
}
//将当前坐标进行保存以进行下一次计算
this.lastX = currentX
this.lastY = currentY
this.text = text
this.clientX_end = currentX
},
handletouchstart(e) {
this.clientX_start = e.touches[0].pageX
},
handletouchend(e) {
uni.showLoading({
title: "加载中",
})
// 清楚定时器
this.startMove = false
clearInterval(this.timer)
this.timer = null
let endX = parseInt(e.detail.x)
let x = parseInt(this.clientX_end - this.clientX_start)
this.posi_x += parseInt(this.clientX_end - this.clientX_start)
this.posi_x = this.posi_x > 0 ? this.posi_x : 0
// 点击进度条 设置进度
this.percent = this.posi_x > 0 ? (this.posi_x / this.width).toFixed(2) * 100 : 0
// 获取当前进度节点对应的 经纬度数据
let index = this.posi_x > 0 ? parseInt((this.posi_x / this.width) * this.lineArr.length) : 0
index = index > this.lineArr.length ? this.lineArr.length - 1 : index
let posi = {
...this.posi,
latitude: this.lineArr[index].lat ? this.lineArr[index].lat : 0,
longitude: this.lineArr[index].lng ? this.lineArr[index].lng : 0,
}
this.$set(this.markers, 1, posi)
this.spd = this.lineArr[index].spd
this.mlg = this.lineArr[index].mlg
this.gtm = this.lineArr[index].gtm.split("/")
this.iNum = parseInt(this.percent / (100 / this.lineArr.length))
setTimeout(function() {
uni.hideLoading()
}, 1000)
},
// handleChange() {
// clearInterval(this.timer)
// uni.navigateTo({
// url: "/pages/car/carList",
// })
// },
// 点击 控制标签(开始、加速、减速)以外的部分就隐藏控制标签
handleWrapTap(e) {
let arr = ["group", "group1", "group2", "group3", "image"]
let isInclude = arr.includes(e.target.id)
this.showGroup = isInclude
},
// 点击进度条
handleTapProgress(e) {
// 清楚定时器
this.startMove = false
clearInterval(this.timer)
let endX = parseInt(e.detail.x)
let x = endX - this.startX
// 点击进度条 设置进度
this.percent = (x / this.width).toFixed(2) * 100
this.posi_x = this.percent * (this.width / 100)
// 获取当前进度节点对应的 经纬度数据
let index = parseInt((x / this.width) * this.lineArr.length)
index = index > this.lineArr.length ? this.lineArr.length - 1 : index
let posi = {
...this.posi,
latitude: this.lineArr[index].lat,
longitude: this.lineArr[index].lng,
}
this.$set(this.markers, 1, posi)
this.spd = this.lineArr[index].spd
this.mlg = this.lineArr[index].mlg
this.gtm = this.lineArr[index].gtm.split("/")
this.iNum = parseInt(this.percent / (100 / this.lineArr.length))
},
// 播放完毕
handleActiveEnd(e) {},
// 点击开始图标
handleStartMove() {
this.showGroup = true
this.startMoveCar()
},
startMoveCar() {
// common.toast("开始播放")
this.startMove = true
this.start()
},
// 退出播放
putOut() {
this.iNum = 0
this.percent = 0
this.startMove = false
clearInterval(this.timer)
const currentPoint = this.lineArr[0]
// 退出播放, 将车辆图标平移到起始点
this.mapContext.translateMarker({
// 平移marker, 带动画
markerId: 9,
destination: {
longitude: currentPoint.lng,
latitude: currentPoint.lat,
},
autoRotate: true,
// rotate: rotate, // 传入角度直接报错
duration: 200,
animationEnd: function() {
this.spd = currentPoint.spd
this.mlg = currentPoint.mlg
this.gtm = currentPoint.gtm.split("/")
}.bind(this),
fail(e) {
console.log(e, "平移marker 失败")
},
})
},
// 暂停播放
stopMove() {
this.startMove = false
clearInterval(this.timer)
// this.iNum = 1
// common.toast("暂停播放")
},
// 加速播放
speedMove() {
if (this.wait2 / this.wait === 8) {
common.toast(`当前播放速度为×8,以达到最大,无法再加速`)
} else {
clearInterval(this.timer)
this.startMove = false
this.wait = this.wait / 2
common.toast(`当前播放速度为×${this.wait2 / this.wait}`)
this.startMove = true
this.start()
}
},
// 减速播放
reduceMove() {
if (this.wait2 / this.wait === 0.25) {
common.toast(`当前播放速度为0.25,以达到最小,无法再减速`)
} else {
clearInterval(this.timer)
this.startMove = false
this.wait = this.wait * 2
common.toast(`当前播放速度为×${this.wait2 / this.wait}`)
this.startMove = true
this.start()
}
},
// 开始播放
start() {
clearInterval(this.timer)
this.timer = null
this.mapContext = uni.createMapContext("myMap", this)
if (!this.lineArr[this.iNum]) {
return
}
const long = this.lineArr[this.iNum].lng
const lat = this.lineArr[this.iNum].lat
const rotate = parseInt(this.lineArr[this.iNum].agl)
this.startMove ?
(this.timer = setInterval(() => {
this.$set(
this.posi.label,
"content",
'[' + this.carInfo.license_plate_number + ']' + this.lineArr[this.iNum].gtm
)
this.startMove ?
this.mapContext.translateMarker({
// 平移marker, 带动画
markerId: 9,
destination: {
longitude: long,
latitude: lat,
},
autoRotate: true,
// rotate: rotate, // 传入角度直接报错
duration: 200,
animationEnd: function() {
this.spd = this.lineArr[this.iNum].spd
this.mlg = this.lineArr[this.iNum].mlg
this.gtm = this.lineArr[this.iNum].gtm.split("/")
this.percent = (100 / this.lineArr.length) * this.iNum
this.posi_x = this.percent * (this.width / 100)
if (this.iNum === this.lineArr.length - 1) {
this.percent = 100
clearInterval(this.timer)
}
this.iNum++
this.start()
}.bind(this),
fail(e) {
console.log(e, "平移marker 失败")
},
}) :
""
clearInterval(this.timer)
}, this.wait)) :
""
},
// 设置 起始、终点 标记点
setMarkers() {
this.$set(this.posi, "latitude", this.lineArr[0].lat)
this.$set(this.posi, "longitude", this.lineArr[0].lng)
this.spd = this.lineArr[0].spd
this.mlg = this.lineArr[0].mlg
this.gtm = this.lineArr[0].gtm.split("/")
this.points = []
// this.lineArr.forEach((v) => {
// this.points.push({
// latitude: v.lat,
// longitude: v.lng,
// })
// })
this.markers = [{
latitude: this.lineArr[0].lat,
longitude: this.lineArr[0].lng,
iconPath: "/business_module/static/location/begin.png",
width: 21,
height: 21,
id: 8,
},
{
...this.posi,
},
{
latitude: this.lineArr[this.lineArr.length - 1].lat,
longitude: this.lineArr[this.lineArr.length - 1].lng,
iconPath: "/business_module/static/location/end.png",
width: 21,
height: 21,
id: 10,
},
]
},
// 设置 路线
setPolyline() {
if (!this.lineArr || !this.lineArr.length) {
return
}
this.polyline[0].points.splice(0, this.polyline[0].points.length)
this.lineArr.forEach((line) => {
this.polyline[0].points.push({
longitude: line.lng,
latitude: line.lat,
})
})
},
async getTrajectory() {
// 轨迹数据
const param = {
deviceId: this.currentDevice.deviceId,
startTime: this.datetimerange && this.datetimerange[0],
endTime: this.datetimerange && this.datetimerange[1],
checkFilter: false,
pwd: this.pwd
}
const res = await this.$store.dispatch("device/getTrajectory", param)
if (!res.success) {
this.$tips.error(res.respMag)
return
}
if (res.content && res.content.listCheck.length > 0) {
// 经纬度坐标系转换
const data = res.content.listCheck
data.forEach((it) => {
const newd = common.transform(it.lng, it.lat)
it.lng = newd[0]
it.lat = newd[1]
it.spd = it.speed
it.gtm = it.gpsTime
it.mlg = 0
})
this.carData.track = data
} else {
this.$tips.info('该时间段没有轨迹数据')
this.carData.track.splice(0, this.carData.track.length)
this.carData.track.push({
agl: 0,
gtm: date.currentPosDateTime(),
hgt: 0,
lat: this.currentDevice.lat,
lng: this.currentDevice.lon,
lon: null,
mlg: 0,
spd: 0,
}, {
agl: 0,
gtm: date.currentPosDateTime(),
hgt: 0,
lat: this.currentDevice.lat,
lng: this.currentDevice.lon,
lon: null,
mlg: 0,
spd: 0,
})
}
},
// 获取车辆轨迹数据
async getData(data) {
await this.getTrajectory()
if (this.currentDevice && this.currentDevice.deviceId) {
this.carData.vehicle_info.license_plate_number = this.currentDevice.carnum
}
// 获取后台数据
this.lineArr = this.carData.track
this.carInfo = this.carData.vehicle_info
this.carInfo.start_time = this.carInfo.start_time.substr(5)
this.carInfo.arrive_time = this.carInfo.arrive_time.substr(5)
if (this.carInfo.license_plate_number) {
this.carInfo.license = this.carInfo.license_plate_number
}
console.log(this.carInfo, this.carData.vehicle_info, 'ddddd', this.currentDevice)
// 设置 中心经纬度
this.latitude = this.lineArr[0].lat
this.longitude = this.lineArr[0].lng
// 设置车牌号
this.$set(
this.posi.label,
"content",
this.carInfo.license_plate_number + ''
)
// 设置初始标记点 起点 终点
this.setMarkers()
this.setPolyline()
},
setTime() {
switch (this.current) {
case 0:
this.datetimerange = [
date.addDay(-1).split(" ")[0] + ' 00:00:00',
date.currentDate(),
]
break
case 1:
this.datetimerange = [
date.addDay(-2).split(" ")[0] + ' 00:00:00',
date.addDay(-1).split(" ")[0] + ' 00:00:00',
]
break
case 2:
this.datetimerange = [
date.currentWeekFirstDay(),
date.currentWeekLastDay(),
]
break
case 3:
this.datetimerange = [
date.currentMonthFirstDay(),
date.currentMonthLastDay(),
]
break
case 4:
this.datetimerange = []
break
}
// console.log(this.datetimerange, "----")
},
},
async mounted() {
this.setTime()
// 获取progress进度条的长度
const query = uni.createSelectorQuery().in(this)
query
.select("#progress")
.boundingClientRect((data) => {
this.width = data.width
this.startX = data.left
})
.exec()
console.log(this.width, this.startX, '进度条的长和款')
},
onLoad(query) {
this.pwd = (query && query.pwd) || ''
},
}
</script>
<style lang="scss" scoped>
.tips {
position: absolute;
top: 20%;
left: 30rpx;
.mr10 {
margin-right: 10rpx;
}
.item {
height: 50rpx;
width: 255rpx;
background: #ffffff;
border-radius: 50rpx;
margin-bottom: 20rpx;
font-weight: bold;
font-size: 26rpx;
line-height: 50rpx;
text-align: center;
color: #5a7bee;
}
}
.bottom_wrap {
position: absolute;
bottom: 0rpx;
width: 100%;
background: #fff;
// padding: 30rpx;
box-sizing: border-box;
height: 410rpx;
.header {
display: flex;
justify-content: space-between;
height: 100rpx;
padding: 0 60rpx;
.header-content {
align-items: center;
justify-content: center;
font-size: 22rpx;
}
.img {
width: 40rpx;
height: 40rpx;
// margin-bottom: 40rpx;
}
.license {
font-weight: bold;
font-size: 34rpx;
line-height: 48rpx;
color: #000000;
}
.change {
font-size: 34rpx;
line-height: 48rpx;
color: #5a7bee;
}
}
.flex {
display: flex;
font-size: 24rpx;
line-height: 34rpx;
color: #888888;
}
.progress_wrap {
margin: 30rpx 0 10rpx;
.flex {
display: flex;
justify-content: space-between;
font-weight: bold;
font-size: 24px;
line-height: 34px;
padding-left: 100rpx;
color: #000000;
}
}
.progress {
display: flex;
position: relative;
// padding-left: 100rpx;
// margin-top: 50rpx;
width: 56rpx;
height: 56rpx;
.img_start {
width: 56rpx;
height: 56rpx;
position: absolute;
left: 0;
bottom: 0rpx;
}
.btn_group {
display: flex;
flex-direction: column;
align-items: center;
position: absolute;
left: 0;
bottom: 0rpx;
padding: 20rpx 10rpx;
background: #ffffff;
box-shadow: 0px 1px 4px rgba(0, 0, 0, 0.25);
border-radius: 50rpx;
.img {
width: 56rpx;
height: 56rpx;
// margin-bottom: 40rpx;
}
.mb20 {
margin-bottom: 40rpx;
}
.speed {
width: 43rpx;
height: 36rpx;
}
}
}
.bottom {
display: flex;
margin-bottom: 40rpx;
.average_speed {
display: flex;
flex-direction: column;
justify-content: space-between;
width: 50%;
.value {
width: 100%;
text-align: center;
// font-weight: bold;
font-size: 34rpx;
line-height: 48rpx;
color: #333333;
}
.label {
width: 100%;
text-align: center;
font-weight: normal;
font-size: 26rpx;
line-height: 36rpx;
color: #000000;
}
}
}
}
</style>