百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 博客教程 > 正文

un-app引入vue-baidu插件实现轨迹回放(可控制速度、进度)

connygpt 2024-08-28 14:12 7 浏览

纯纯的干货废话不多说、手摸手教。

第一步

安装插件,有两种方式。

第一种是npm安装

$ npm install vue-baidu-map --save

第二种是直接引入cdn

<script src="https://unpkg.com/vue-baidu-map"></script>

第二步

注册组件:有局部注册、和全局注册

全局注册

import Vue from 'vue'
import BaiduMap from 'vue-baidu-map'

Vue.use(BaiduMap, {
  // ak 是在百度地图开发者平台申请的密钥 详见 http://lbsyun.baidu.com/apiconsole/key */
  ak: 'YOUR_APP_KEY'
})
<template>
  <baidu-map class="bm-view">
  </baidu-map>
</template>

<style>
.bm-view {
  width: 100%;
  height: 300px;
}
</style>

局部注册(俗称按需引入)

<template>
  <baidu-map class="bm-view" ak="YOUR_APP_KEY">
  </baidu-map>
</template>

<script>
import BaiduMap from 'vue-baidu-map/components/map/Map.vue'
export default {
  components: {
    BaiduMap
  }
}
</script>

<style>
.bm-view {
  width: 100%;
  height: 300px;
}
</style>

按需引入组件的好处,可以选择局部注册百度地图组件,这将减少工程打包后的容量尺寸。

常见的问题可以查看官网Vue Baidu Map

第三步

实现这个功能的难点在与如何让轨迹在运动过程中可以设置图标、切图标跟随轨迹运动、在绘制的过程中视口也要不断的变化、让运动的视觉处于地图视口的中间、以便于在运动的时候跑出地图以外的地方去、视口没有跟随就会看不见。其次就是控制轨迹回放的速度、以及轨迹回放的进度、其实官网也有相关的属性、主要的是如何融入到你的业务中去、比如滑动调节速度、暂停播放轨迹等。

具体的实现看一下代码

<template>
	<view>
		<view class="guided-tour-container">
			<baidu-map :style="{ height: windowHeight + 'px', width: windowWidth + 'px' }"
				ak="你的百度地图的key" :scroll-wheel-zoom='true' @ready="initMap" :center="center"
				:zoom="18">
				<bm-geolocation anchor="BMAP_ANCHOR_TOP_RIGHT" :showAddressBar="false" :autoLocation="true"
					@locationError="locationError" :offset="{ width: 15, height: 320 }"></bm-geolocation>

				<bm-polyline :path="polylinePath" stroke-color="blue" :stroke-opacity="0.8" :stroke-weight="4"
					:icons='icon' :editing="false" @lineupdate="updatePolylinePath"></bm-polyline>
				<bm-marker v-for="(item, index) in listData" :key="index" :position="{ lng: item.lng, lat: item.lat }"
					:icon="{
					url: item.icon,
					size: { width: 40, height: 48 }
				}" :offset="{ width: -5, height: -17 }">
				</bm-marker>
				<!-- <bm-label v-for="(items, indexs) in listDatas" :key="'label' + indexs" :content="items.mcType"
							:position="{ lng: items.lng, lat: items.lat }" :labelStyle="labelStyle"
							:offset="{ width:-50, height:-10 }" title="Hover me" /> -->
				<bml-lushu :path="path" :icon="carIcon" :play="play" :speed="speed" :rotation="true" :autoView="true"
					ref="bmlLushu" @stop="stop" @ready="lushuReady"></bml-lushu>
			</baidu-map>

			<view class="btn">
				<view class="speed" style="width: 85%;">
					<image src="../../static/tezhong/sotp.png" style="margin-right: 30rpx;" class="icons" v-if="isPlay"
						@click="starts"></image>
					<image src="../../static/tezhong/play.png" style="margin-right: 30rpx;" class="icons" v-else
						@click="starts"></image>
					<!-- <van-slider v-model="progressWidth"/> -->
					<u-line-progress active-color="#6693FD" :percent="progressWidth"></u-line-progress>
				</view>
				<view style="color: #fff;padding: 0 20rpx 0 20rpx;">
					<!-- 2024-02-14 17:22:00 -->
				</view>
				<view class="speed" style="width: 100%;">
					<view style="color: #fff;min-width: 60rpx;margin-right: 32rpx;">速度</view>
					<van-slider v-model="num" active-color="#6693FD" max="2000" min="50" bar-height="15"
						@change="changeSpeed">
						<template #button>
							<div class="custom-button">{{ num }}</div>
						</template>
					</van-slider>
					<view style="color: #fff;padding-left: 20rpx;min-width: 112rpx;margin-left: 10rpx;">(米/秒)</view>
				</view>
			</view>
		</view>
		<u-toast ref="uToast" />
	</view>
</template>

<script>
	import BaiduMap from 'vue-baidu-map/components/map/Map.vue'
	import {
		BmGeolocation,
		BmlMarkerClusterer,
	} from "vue-baidu-map/components";
	import {
		BmlLushu
	} from "vue-baidu-map";
	import BmLabel from 'vue-baidu-map/components/overlays/Label'
	import BmMarker from "vue-baidu-map/components/overlays/Marker";
	import BmPolygon from "vue-baidu-map/components/overlays/Polygon.vue"
	import BmPolyline from "vue-baidu-map/components/overlays/Polyline.vue"
	let map;
	let geolocation;
	let locationMarker;
	let mk;
	export default {
		components: {
			BaiduMap,
			BmGeolocation,
			BmPolyline,
			BmPolygon,
			BmMarker,
			BmlLushu
		},
		data() {
			return {
				isPlay: false,
				num: 50,
				icon: '../../static/tezhong/home2.png',
				labelStyle: {
					color: 'black',
					fontSize: '14px',
					border: 'none',
					background: 'none',
					fontWeight: '600'
				},
				isShow: false,
				textCustom: {
					color: '#fff',
					opacity: 0
				},
				keyword: '',
				markers: [],
				windowWidth: 0,
				windowHeight: 0,
				minZoom: 4,
				maxZoom: 19,
				isBmapInit: false,
				zoom: 17,
				map: null,
				current: 0,
				value1: 0,
				value2: 'a',
				option1: [],
				option2: [],
				listData: [],
				pageNum: 1,
				pageSize: 10,
				total: null,
				listHeight: 520,
				lat: null,
				lng: null,
				uniLat: null,
				uniLng: null,
				idMap: {},
				// 经纬度:114.94050 , 25.83518 
				center: {
					lng: 114.94050,
					lat: 25.83518

				},
				isActive: 0,
				polylinePath: [],
				deviceId: '',
				startTime: '',
				endTime: '',
				carIcon: {
					url: "http://api.map.baidu.com/library/LuShu/1.2/examples/car.png",
					size: {
						width: 52,
						height: 26
					},
					opts: {
						anchor: {
							width: 27,
							height: 13
						}
					}
				},
				play: false,
				path: [],
				speed: 50, // 路数播放速度 米/s
				progressWidth:0
			}
		},
		watch: {
			keyword: function(val) {
				this.$u.debounce(() => {
					this.$refs.paging.reload(true)
				}, 500)
			},
		},
		// onReachBottom() {
		// 	console.log('触底');
		// 	this.$refs.paging.doLoadMore(true)
		// 	this.$refs.paging.scrollToBottom()
		// },
		methods: {
			starts() {
				if (this.isPlay) {
					if (this.progressWidth == 100) {
						this.progressWidth = 0
					}
					this.start()
					this.isPlay = false
				} else {
					this.stop()
					this.isPlay = true
				}
			},
			changeSpeed(e) {
				console.log('拖动的速度', e);
				this.speed = e
			},
			updatePolylinePath(e) {
				this.polylinePath = e.target.getPath()
			},
			addPolylinePoint() {
				this.polylinePath.push({
					lng: 116.404,
					lat: 39.915
				})
			},
			initMap({
				BMap,
				map
			}) {
				this.center.lng = 114.94050;
				this.center.lat = 25.83518;
				// this.address = this.$route.query.address || '';
				this.BMap = BMap;
				this.map = map;
				this.zoom = 15;
				this.getList();
			},
			locationError({
				StatusCode
			}) {
				uni.showToast({
					title: '检验位置定位失败',
					icon: 'none'
				})
			},
			getMarker({
				type,
				target,
				point
			}, item, i) {
				console.log('item', item, i, point);
				uni.navigateTo({
					url: '/pages/specialEquipment/info'
				})
			},
			getClick() {
				console.log('我触发了地图');
				plus.key.hideSoftKeybord();
				document.activeElement.blur();
				console.log('执行了');
			},
			change(index) {
				this.current = index;
			},
			async getList() {

				var params = {
					endTime: this.endTime,
					deviceId: this.deviceId,
					startTime: this.startTime
				}
				let res = await this.$teZhongApi.trajectory(params)
				if (res.code == 200 || res.code == 0) {
					console.log('轨迹类别', res.data)
					this.polylinePath = res.data
					this.trajectoryList = res.data
					if (this.polylinePath.length == 0) {
						uni.showToast({
							title: '暂无轨迹',
							icon: 'none'
						})
					} else {
						// 清空之前的轨迹和标记
						this.map.clearOverlays();
						// 将接口返回的经纬度数据转换成BMapGL.Point对象
						const newPoints = this.polylinePath.map(item => new BMapGL.Point(item.lng, item.lat));
						this.path = newPoints
						this.play = true // 路书播放

						var pl = new BMapGL.Polyline(newPoints);
						// var pls = new BMapGL.Polyline(newPoints, { strokeColor: 'red' });

						// 更新标记点
						const startMarker = new BMapGL.Marker(newPoints[0], {
							icon: new BMapGL.Icon('../../static/tezhong/start.png', new BMapGL.Size(32, 32))
						});
						const endMarker = new BMapGL.Marker(newPoints[newPoints.length - 1], {
							icon: new BMapGL.Icon('../../static/tezhong/end.png', new BMapGL.Size(32, 32))
						});
						this.map.addOverlay(startMarker);
						this.map.addOverlay(endMarker);
					}
				}
			},
			lushuReady(res) {
				this.timer_lushu = setTimeout(() => {
					// 全局搜BMapLib.LuShu可查找到Lushu.vue和 bmaplib.lulhu/index.js源码位置
					// 通过源码可得知路书bml-lushu使用 this.originInstance 存储路书实例,故使用refs调用
					let lushu =
						this.$refs.bmlLushu && this.$refs.bmlLushu.originInstance ?
						this.$refs.bmlLushu.originInstance :
						"";

					this.lushu = lushu; // 存储路书实例

					let proto = Object.getPrototypeOf(lushu); // 原型

					let _this = this;

					// 处理信息窗口
					function handleInfoWindow(index, length) {

						var opts = {
							width: 260, // 信息窗口宽度
							height: 80, // 信息窗口高度
							title: "", // 信息窗口标题
						}
						console.log(_this.trajectoryList);
						let trajectoryItem = _this.trajectoryList[index]
						let trajectoryItemPoint = new BMap.Point(_this.path[index].lng, _this.path[index].lat)
						var infoWindow = new BMap.InfoWindow(
							`设备名称:${trajectoryItem.deviceName}<br>设备类型:${trajectoryItem.equipType}<br>定位时间:${trajectoryItem.gpsTime}<br>`,
							opts); // 创建信息窗口对象 
						_this.map.openInfoWindow(infoWindow, trajectoryItemPoint)
					}
					// 处理进度条宽度
					function handlePercent(index, length) {
						let percent = Math.floor((index / length) * 100000) / 1000;
						_this.progressWidth = percent;
						if (_this.progressWidth == 100) {
							_this.isPlay = true
						}
					}
					// 点位时间
					function handleStationPoint(index) {
						// 如果路段的下标大于小车当前的index则表示小车在该路段
						// let pointIndex = _this.pathLength.findIndex(v => v > index);
						// let lastFlag = pointIndex == -1;
						// pointIndex = lastFlag ? _this.drivingList.length - 1 : pointIndex;

						// let pointArr = _this.drivingList[pointIndex] || [];
						// let pointObj = lastFlag ? pointArr[1] : pointArr[0] || {};
						// pointObj && Object.keys(pointObj).length
						// 	? _this.throughDate = pointObj.sdate
						// 	: "";
					}

					// 改原型实现进度条和经过点位时返回信息 (改源码无效,因为已经被打包压缩到vue-baidu-map了)
					proto._moveNext = function(index) {
						var me = this; // 此处的this指lushu实例
						if (index < this._path.length - 1) {
							handlePercent.call(_this, index, this._path.length); // 进度条
							handleInfoWindow.call(_this, index, this._path.length); // 信息窗口
							handleStationPoint.call(_this, index); // 点位

							me._move(me._path[index], me._path[index + 1], me._tween.linear);
						} else {
							handlePercent.call(_this, 1, 1); // 进度条设置为100%
							me.stop();
						}
					};
				}, 100);
			},
			stop() {
				this.play = false;
			},
			start() {
				this.play = true
			},

			// 播放/暂停
			playClick() {
				if (!this.map) {
					return;
				}
				if (!this.path.length) {
					alert("暂无轨迹")
					return;
				}
				this.play = !this.play;
			},
		},
		mounted() {
		},
		onLoad(option) {
			this.deviceId = option.deviceId
			this.startTime = option.startTime
			this.endTime = option.endTime
			if (option.deviceId) {
				// this.getList()
			}
		},
		onShow() {
			uni.getSystemInfo({
				success: (res => {
					this.windowWidth = res.windowWidth
					this.windowHeight = res.windowHeight
				})
			})
		}
	}
</script>

<style scoped lang="scss">
	page {
		background-color: #F9F9F9;
	}

	.btn {
		/* width: 343px; */
		width: 92%;
		height: 111px;
		border-radius: 8px;
		background: #000;
		/* opacity: 0.7; */
		background-color: rgba(7, 0, 0, 0.7);
		/* 设置背景颜色为红色,透明度为 0.7 */
		z-index: 9999999999 !important;
		position: absolute;
		bottom: 20rpx;
		left: 30rpx;
		padding: 20rpx 0 0 20rpx;
	}

	.custom-button {
		width: 16px;
		color: #fff;
		font-size: 10px;
		line-height: 18px;
		text-align: center;
		background-color: #6693FD;
		border-radius: 100px;
		padding-right: 24rpx;
	}

	.icons {
		width: 70rpx;
		height: 60rpx;
		// padding: 20rpx 20rpx 0 20rpx;
	}

	.speed {
		display: flex;
		// width: 90%;
		align-items: center;
		padding: 20rpx;
	}

	.title-tops {
		height: 108rpx;
		text-align: center;
		background-color: #fff;
		display: flex;
		justify-content: center;
		align-items: center;
		font-weight: 700;
	}

	.BMapLabel {
		max-width: none;
	}

	// .BMapLabel {
	//   z-index: -1!important;
	// }

	.middles {
		display: flex;
		justify-content: center;
		flex-direction: column;
		align-items: center;
	}

	.tab_color {
		display: flex;
		justify-content: center;
		flex-direction: column;
		align-items: center;
		color: #5982ff;
	}

	.tab_no {
		display: flex;
		justify-content: center;
		flex-direction: column;
		align-items: center;
		color: #333
	}

	.bottom-tab {
		display: flex;
		justify-content: space-around;
		align-items: center;
		padding-top: 20rpx;
	}

	.overflow-list {
		// overflow-y: auto;
		// overflow-x: hidden;
		height: 500rpx;
	}

	.centers {
		display: flex;
		justify-content: center;
		align-items: center;
	}

	.list-text {
		display: flex;
		padding: 34rpx;
		background-color: #fff;
		// margin: 20rpx;
		justify-content: space-between;

		.imgs {
			width: 100rpx;
			height: 40rpx;
			padding: 10rpx;
		}

		.title {
			font-family: "PingFang SC Bold";
			font-weight: bold;
			font-size: 28rpx;
			color: #273147;
			margin-bottom: 10rpx;
		}

		.desc {
			font-family: "PingFang SC Medium";
			font-weight: 500;
			font-size: 26rpx;
			color: #273147;
			//超出一行省略号
			white-space: nowrap; //禁止换行
			overflow: hidden;
			text-overflow: ellipsis; //
		}
	}

	.tab {
		position: absolute;
		top: 0;
		width: 100%
	}

	.guided-tour-container {
		width: 100%;
		height: 100%;
		position: relative;

		::v-deep .anchorBL {
			display: none;
		}

		.location-icon {
			width: 35px;
			height: 35px;
			position: absolute;
			right: 20rpx;
			bottom: 20rpx;
			z-index: 99999;

		}
	}

	::v-deep .u-input--border {
		border: none;
	}

	.seach_text {
		position: fixed;
		width: 100%;
		top: 0;
		z-index: 9999;

		.seach-input {
			box-shadow: 0 8rpx 16rpx rgba(39, 49, 71, 0.08);
			display: flex;
			background-color: #fff;
			border-radius: 8rpx;
		}
	}
</style>

需要注意的是:轨迹移动附带的窗口需要修改vue-baidu里面的源码的方法proto._moveNext、具体的处理也是在 handleInfoWindow回调里面进行处理的。

具体的效果




相关推荐

3分钟让你的项目支持AI问答模块,完全开源!

hello,大家好,我是徐小夕。之前和大家分享了很多可视化,零代码和前端工程化的最佳实践,今天继续分享一下最近开源的Next-Admin的最新更新。最近对这个项目做了一些优化,并集成了大家比较关注...

干货|程序员的副业挂,12个平台分享

1、D2adminD2Admin是一个完全开源免费的企业中后台产品前端集成方案,使用最新的前端技术栈,小于60kb的本地首屏js加载,已经做好大部分项目前期准备工作,并且带有大量示例代码,助...

Github标星超200K,这10个可视化面板你知道几个

在Github上有很多开源免费的后台控制面板可以选择,但是哪些才是最好、最受欢迎的可视化控制面板呢?今天就和大家推荐Github上10个好看又流行的可视化面板:1.AdminLTEAdminLTE是...

开箱即用的炫酷中后台前端开源框架第二篇

#头条创作挑战赛#1、SoybeanAdmin(1)介绍:SoybeanAdmin是一个基于Vue3、Vite3、TypeScript、NaiveUI、Pinia和UnoCSS的清新优...

搭建React+AntDeign的开发环境和框架

搭建React+AntDeign的开发环境和框架随着前端技术的不断发展,React和AntDesign已经成为越来越多Web应用程序的首选开发框架。React是一个用于构建用户界面的JavaScrip...

基于.NET 5实现的开源通用权限管理平台

??大家好,我是为广大程序员兄弟操碎了心的小编,每天推荐一个小工具/源码,装满你的收藏夹,每天分享一个小技巧,让你轻松节省开发效率,实现不加班不熬夜不掉头发,是我的目标!??今天小编推荐一款基于.NE...

StreamPark - 大数据流计算引擎

使用Docker完成StreamPark的部署??1.基于h2和docker-compose进行StreamPark部署wgethttps://raw.githubusercontent.com/a...

教你使用UmiJS框架开发React

1、什么是Umi.js?umi,中文可发音为乌米,是一个可插拔的企业级react应用框架。你可以将它简单地理解为一个专注性能的类next.js前端框架,并通过约定、自动生成和解析代码等方式来辅助...

简单在线流程图工具在用例设计中的运用

敏捷模式下,测试团队的用例逐渐简化以适应快速的发版节奏,大家很早就开始运用思维导图工具比如xmind来编写测试方法、测试点。如今不少已经不少利用开源的思维导图组件(如百度脑图...)来构建测试测试...

【开源分享】神奇的大数据实时平台框架,让Flink&amp;Spark开发更简单

这是一个神奇的框架,让Flink|Spark开发更简单,一站式大数据实时平台!他就是StreamX!什么是StreamX大数据技术如今发展的如火如荼,已经呈现百花齐放欣欣向荣的景象,实时处理流域...

聊聊规则引擎的调研及实现全过程

摘要本期主要以规则引擎业务实现为例,陈述在陌生业务前如何进行业务深入、调研、技术选型、设计及实现全过程分析,如果你对规则引擎不感冒、也可以从中了解一些抽象实现过程。诉求从硬件采集到的数据提供的形式多种...

【开源推荐】Diboot 2.0.5 发布,自动化开发助理

一、前言Diboot2.0.5版本已于近日发布,在此次发布中,我们新增了file-starter组件,完善了iam-starter组件,对core核心进行了相关优化,让devtools也支持对IAM...

微软推出Copilot Actions,使用人工智能自动执行重复性任务

IT之家11月19日消息,微软在今天举办的Ignite大会上宣布了一系列新功能,旨在进一步提升Microsoft365Copilot的智能化水平。其中最引人注目的是Copilot...

Electron 使用Selenium和WebDriver

本节我们来学习如何在Electron下使用Selenium和WebDriver。SeleniumSelenium是ThoughtWorks提供的一个强大的基于浏览器的开源自动化测试工具...

Quick &#39;n Easy Web Builder 11.1.0设计和构建功能齐全的网页的工具

一个实用而有效的应用程序,能够让您轻松构建、创建和设计个人的HTML网站。Quick'nEasyWebBuilder是一款全面且轻巧的软件,为用户提供了一种简单的方式来创建、编辑...