uniapp微信小程序定位跑步的实现

本文介绍了如何利用微信小程序的API开发一个校园跑应用,包括使用`wx.getLocation`获取用户位置、绘制运动轨迹、实时计算速度等功能,并设置了定时器以控制更新频率。
摘要由CSDN通过智能技术生成

最近写了一个类似校园跑小程序,分享一下

效果如下:

先附上完整代码:

<template>
	<view class="text-area">
		<!-- <button @tap="getwz">点我获取当前位置</button> -->
		<map class="map" :longitude="longitude" :latitude="latitude" :markers="marker" :polyline="polyline"></map>
		<view class="block">
			<view class="but" @click="startRun" :class="{ 'run-flag-active': runFlag }">
				{{ content }}
			</view>
			<view class="msg">
				<view class="speed">
					平均速度:{{speed}} m/s
				</view>
				<view class="kilo">
					公里:{{kilo}} km
				</view>
			</view>
		</view>
	</view>
</template>


<script>
	export default {
		data() {
			return {
				longitude: 107.701594,
				latitude: 29.615453,
				marker: [],
				polyline: [{ //指定一系列坐标点,从数组第一项连线至最后一项
					points: [{
						longitude: 107.701594,
						latitude: 29.615453
					}, {
						longitude: 107.70048,
						latitude: 29.617
					}],
					color: "#0000AA", //线的颜色
					width: 1, //线的宽度
					//     dottedLine:true,//是否虚线
				}],
				// 跑步标致,为true时停止跑步
				runFlag: true,
				content: "开始运动",
				speed: 0,
				kilo: 0,
				startTime: null, // 运动开始时间
				totalElapsedTime: 0, // 累计行驶时间(单位:秒)

			}
		},
		onLoad() {
			var that = this
			wx.authorize({
				scope: 'scope.userLocation',
				success(res) {
					console.log(res)
					if (res.errMsg == 'authorize:ok') {
						wx.getLocation({
							type: 'gcj02',
							success(res) {
								console.log(res) //此时里面有经纬度
								// 设置中心点位
								that.longitude = res.longitude;
								that.latitude = res.latitude;
								// 添加至标记点位
								that.marker.push({
									id: 0, // 保证ID唯一
									longitude: res.longitude,
									latitude: res.latitude,
									iconPath: '../../static/img/map/marker_checked.png',
									rotate: 0,
									width: 10,
									height: 20,
									title: '当前位置',
								});
							}
						})
					}
				},
				fail(err) {
					console.log(err)
				}
			})
		},
		methods: {
			getwz() {
				var that = this
				wx.getLocation({
					type: 'gcj02',
					isHighAccuracy: true,
					highAccuracyExpireTime: 3500,
					success(res) {
						console.log(res);
						// 设置中心点位
						that.longitude = res.longitude;
						that.latitude = res.latitude;
						// 修改标记点位
						that.marker[0].longitude = res.longitude;
						that.marker[0].latitude = res.latitude;
						// 加入轨迹折线
						that.polyline[0].points.push({
							longitude: res.longitude,
							latitude: res.latitude,
						});

						// 计算与上一点的距离并累加到总路程
						// 计算距离和速度
						const distance = that.calculateDistance(
							that.polyline[0].points[that.polyline[0].points.length - 2], {
								longitude: res.longitude,
								latitude: res.latitude
							}
						);
						// 更新总路程(保留三位有效数字)
						that.kilo = (parseFloat(that.kilo) + distance).toFixed(3);
						// 设置速度的实时显示(基于单次位置更新的瞬时速度)
						// 判断速度是否有效(非Infinity和NaN),有效则更新速度显示
						var nowTime = new Date().getTime(); // 获取当前时间
						var timeSpace = nowTime - that.startTime; // 单位:毫秒
						var speedRes = that.kilo * 1000 * 1000 / timeSpace; // 单位:m/s
						if (!isNaN(speedRes) && isFinite(speedRes)) {
							that.speed = speedRes.toFixed(3)
						}
					},
					fail(info) {
						console.log(info);
					}
				})
			},

			// 开始运动
			startRun() {
				// ... 原有startRun方法内容 ...
				var that = this
				if (that.runFlag) {
					wx.showModal({
						content: '是否开始运动',
						success(res) {
							if (res.confirm) {
								// console.log('用户点击确定')
								// 开始运动
								that.content = "停止运动"
								that.runFlag = !that.runFlag;

								// 获取位置
								wx.getLocation({
									type: 'gcj02',
									isHighAccuracy: true,
									highAccuracyExpireTime: 3500,
									success(res) {
										// 设置中心点位
										that.longitude = res.longitude;
										that.latitude = res.latitude;
										// 修改标记点位
										that.marker[0].longitude = res.longitude;
										that.marker[0].latitude = res.latitude;
										// 修改初始轨迹折线起点
										that.polyline[0].points[0].longitude = res.longitude,
											that.polyline[0].points[0].latitude = res.latitude,
											that.polyline[0].points[1].longitude = res.longitude,
											that.polyline[0].points[1].latitude = res.latitude
									},
									fail(info) {
										console.log(info);
									}
								});

								// 或当前时间
								that.startTime = new Date().getTime();

								// 定时器跑步
								that.locationTimer = setInterval(() => {
									that.getwz();
								}, 8000); // 开始跑步时重新开启定时器
							} else if (res.cancel) {
								// console.log('用户点击取消')
							}
						}
					})

				} else {
					wx.showModal({
						content: '是否停止运动',
						success(res) {
							if (res.confirm) {
								// console.log('用户点击确定')
								// 停止运动
								that.content = "开始运动"
								that.runFlag = !that.runFlag;

								clearInterval(that.locationTimer); // 结束跑步时清除定时器
							} else if (res.cancel) {
								// console.log('用户点击取消')
							}
						}
					})
				}
			},


			// 计算路径长度
			calculateDistance(prevPoint, currentPoint) {
				const R = 6371; // 地球平均半径,单位为公里
				const dLat = (currentPoint.latitude - prevPoint.latitude) * Math.PI / 180;
				const dLon = (currentPoint.longitude - prevPoint.longitude) * Math.PI / 180;
				const a =
					Math.sin(dLat / 2) * Math.sin(dLat / 2) +
					Math.cos(prevPoint.latitude * Math.PI / 180) *
					Math.cos(currentPoint.latitude * Math.PI / 180) *
					Math.sin(dLon / 2) * Math.sin(dLon / 2);
				const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
				let distance = R * c;

				// 将距离保留三位小数
				distance = parseFloat(distance.toFixed(3));
				return distance;

				// 计算当前时间并累计行驶时间
				const now = new Date().getTime();

				// 计算平均速度(单位:m/s)
				let speed = distance * 1000 / elapsedTime;
				speed = parseFloat(speed.toFixed(3)); // 保留三位小数
			},
		},

		// 不再使用时,记得清除定时器,防止内存泄漏
		onUnload() {
			clearInterval(this.locationTimer);
		},
	}
</script>


<style lang="scss">
	.map {
		width: 100%;
		height: 650rpx;
	}

	.block {
		width: 95%;
		height: 300rpx;
		// background-color: #3c9cff;
		border: 2rpx solid #3c9cff;
		border-radius: 20rpx;
		margin: 20rpx 20rpx;

		display: flex;
		align-items: center;

		.but {
			width: 50%;
			height: 100%;
			background-color: red;
			border-radius: 20rpx;
			border-right: 2rpx solid white;
			display: flex;
			align-items: center;
			justify-content: center;
			text-align: left;
			font-size: 50rpx;
			font-weight: 700;
			color: white;
		}

		/* 当runFlag为true时应用此样式 */
		.but.run-flag-active {
			background-color: #3c9cff;
		}

		.msg {
			width: 50%;
			height: 100%;
			display: flex;
			flex-direction: column;
			justify-content: space-between;

			.speed {
				width: 90%;
				height: 50%;
				background-color: white;
				border-bottom: 2rpx solid #3c9cff;
				padding-left: 30rpx;
				display: flex;
				flex-direction: column;
				justify-content: center;
				align-items: flex-start;
				text-align: left;
				font-size: 40rpx;
				font-weight: 550;
			}

			.kilo {
				width: 90%;
				height: 50%;
				background-color: white;
				padding-left: 30rpx;
				display: flex;
				flex-direction: column;
				justify-content: center;
				align-items: flex-start;
				text-align: left;
				font-size: 40rpx;
				font-weight: 550;
			}
		}
	}
</style>

总体来说,代码还是很简单的。

使用到wx.getLocation接口(如果项目上线的话需要去官网申请,不上线可以随便用),获取精确的当前定位信息。

代码的简单讲解:

小程序官方的地图组件。

各个属性讲解:longitude是经度、latitude是纬度,这里的经纬度是当前地图定位的中心点位;markers是标记点位,也就是经过的点位;polyline是一系列的点位,用于绘制运动轨迹。

<map class="map" :longitude="longitude" :latitude="latitude" :markers="marker" :polyline="polyline"></map>

也就是说,运动轨迹是由很多标记点组成的,接口请求越快,点位就越密集,绘制出的轨迹就越准确。

具体实现分析:

第一次点击“开始运动”后,要将中心点位定位到当前位置,后续运动时,用户移动改变位置,需要一次次地调用接口获取当前位置信息,设置到中心地位、标记点、轨迹绘制中。

【注意】用户运动的当前位置在轨迹上表现就是轨迹的末端。

此外,关于定位信息的授权需要在manifest.json中进行说明,否则在运行时会闪退。下面进行代码详解:

data中要特别注意polyline起始数值必须包含两个起始的点位,否则在小程序中运行会报错。

data() {
			return {
				longitude: 107.701594,
				latitude: 29.615453,
				marker: [],
				polyline: [{ //指定一系列坐标点,从数组第一项连线至最后一项
					points: [{
						longitude: 107.701594,
						latitude: 29.615453
					}, {
						longitude: 107.70048,
						latitude: 29.617
					}],
					color: "#0000AA", //线的颜色
					width: 1, //线的宽度
					//     dottedLine:true,//是否虚线
				}],
				// 跑步标致,为true时停止跑步
				runFlag: true,
				content: "开始运动",
				speed: 0,
				kilo: 0,
				startTime: null, // 运动开始时间
				totalElapsedTime: 0, // 累计行驶时间(单位:秒)

			}
		},

在onLoad中提示用户获取定位权限,并获取用户当前初始的中心点位、加入标记点。

onLoad() {
			var that = this
			wx.authorize({
				scope: 'scope.userLocation',
				success(res) {
					console.log(res)
					if (res.errMsg == 'authorize:ok') {
						wx.getLocation({
							type: 'gcj02',
							success(res) {
								console.log(res) //此时里面有经纬度
								// 设置中心点位
								that.longitude = res.longitude;
								that.latitude = res.latitude;
								// 添加至标记点位
								that.marker.push({
									id: 0, // 保证ID唯一
									longitude: res.longitude,
									latitude: res.latitude,
									iconPath: '../../static/img/map/marker_checked.png',
									rotate: 0,
									width: 10,
									height: 20,
									title: '当前位置',
								});
							}
						})
					}
				},
				fail(err) {
					console.log(err)
				}
			})
		},

mehods里的:开始运动和正式跑步的函数,开始跑步需要再定位一次,因为用户可能打开应用后不会立即进行跑步。开始跑步后也是发请求、对那三个数据赋值:中心点位、标记点、轨迹折线。

getwz() {
				var that = this
				wx.getLocation({
					type: 'gcj02',
					isHighAccuracy: true,
					highAccuracyExpireTime: 3500,
					success(res) {
						console.log(res);
						// 设置中心点位
						that.longitude = res.longitude;
						that.latitude = res.latitude;
						// 修改标记点位
						that.marker[0].longitude = res.longitude;
						that.marker[0].latitude = res.latitude;
						// 加入轨迹折线
						that.polyline[0].points.push({
							longitude: res.longitude,
							latitude: res.latitude,
						});

						// 计算与上一点的距离并累加到总路程
						// 计算距离和速度
						const distance = that.calculateDistance(
							that.polyline[0].points[that.polyline[0].points.length - 2], {
								longitude: res.longitude,
								latitude: res.latitude
							}
						);
						// 更新总路程(保留三位有效数字)
						that.kilo = (parseFloat(that.kilo) + distance).toFixed(3);
						// 设置速度的实时显示(基于单次位置更新的瞬时速度)
						// 判断速度是否有效(非Infinity和NaN),有效则更新速度显示
						var nowTime = new Date().getTime(); // 获取当前时间
						var timeSpace = nowTime - that.startTime; // 单位:毫秒
						var speedRes = that.kilo * 1000 * 1000 / timeSpace; // 单位:m/s
						if (!isNaN(speedRes) && isFinite(speedRes)) {
							that.speed = speedRes.toFixed(3)
						}
					},
					fail(info) {
						console.log(info);
					}
				})
			},

			// 开始运动
			startRun() {
				// ... 原有startRun方法内容 ...
				var that = this
				if (that.runFlag) {
					wx.showModal({
						content: '是否开始运动',
						success(res) {
							if (res.confirm) {
								// console.log('用户点击确定')
								// 开始运动
								that.content = "停止运动"
								that.runFlag = !that.runFlag;

								// 获取位置
								wx.getLocation({
									type: 'gcj02',
									isHighAccuracy: true,
									highAccuracyExpireTime: 3500,
									success(res) {
										// 设置中心点位
										that.longitude = res.longitude;
										that.latitude = res.latitude;
										// 修改标记点位
										that.marker[0].longitude = res.longitude;
										that.marker[0].latitude = res.latitude;
										// 修改初始轨迹折线起点
										that.polyline[0].points[0].longitude = res.longitude,
											that.polyline[0].points[0].latitude = res.latitude,
											that.polyline[0].points[1].longitude = res.longitude,
											that.polyline[0].points[1].latitude = res.latitude
									},
									fail(info) {
										console.log(info);
									}
								});

								// 或当前时间
								that.startTime = new Date().getTime();

								// 定时器跑步
								that.locationTimer = setInterval(() => {
									that.getwz();
								}, 8000); // 开始跑步时重新开启定时器
							} else if (res.cancel) {
								// console.log('用户点击取消')
							}
						}
					})

				} else {
					wx.showModal({
						content: '是否停止运动',
						success(res) {
							if (res.confirm) {
								// console.log('用户点击确定')
								// 停止运动
								that.content = "开始运动"
								that.runFlag = !that.runFlag;

								clearInterval(that.locationTimer); // 结束跑步时清除定时器
							} else if (res.cancel) {
								// console.log('用户点击取消')
							}
						}
					})
				}
			},

在跑步中,我还加入了实时的计算路程和平均速度的函数,如下:

// 计算路径长度
			calculateDistance(prevPoint, currentPoint) {
				const R = 6371; // 地球平均半径,单位为公里
				const dLat = (currentPoint.latitude - prevPoint.latitude) * Math.PI / 180;
				const dLon = (currentPoint.longitude - prevPoint.longitude) * Math.PI / 180;
				const a =
					Math.sin(dLat / 2) * Math.sin(dLat / 2) +
					Math.cos(prevPoint.latitude * Math.PI / 180) *
					Math.cos(currentPoint.latitude * Math.PI / 180) *
					Math.sin(dLon / 2) * Math.sin(dLon / 2);
				const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
				let distance = R * c;

				// 将距离保留三位小数
				distance = parseFloat(distance.toFixed(3));
				return distance;

				// 计算当前时间并累计行驶时间
				const now = new Date().getTime();

				// 计算平均速度(单位:m/s)
				let speed = distance * 1000 / elapsedTime;
				speed = parseFloat(speed.toFixed(3)); // 保留三位小数
			},
		},

【特别注意】getLocation接口可能有一定的请求频率限制,为了展示的效果合理,我在调用包含该接口的getwz()时,设置了一个定时器,总之....效果就是差不多8秒更新一下最新的位置信息....哈哈,我的也不用于实际使用,只是比赛的作品,大佬们如果有其他解决方法可以评论一下。

uniapp是一款跨平台的开发框架,支持同时开发微信小程序App、H5等多个平台的应用程序。在uniapp中开发微信小程序定位功能非常简单,可以通过调用uniapp提供的API实现。 首先,在uniapp的项目中,我们可以使用uni.getLocation方法来获取用户的位置信息。这个方法会弹出微信小程序的位置授权提示框,用户可以选择是否授权获取位置信息。我们可以在页面的生命周期钩子函数中调用这个方法,例如在mounted钩子函数中。 在调用uni.getLocation方法之前,我们需要先引入uniapp提供的API,可以通过在页面的script标签中添加import语句来实现。引入之后,就可以在页面的方法中调用uni.getLocation方法了。 调用uni.getLocation方法后,会返回一个Promise对象,我们可以通过.then方法来处理获取位置信息成功的情况,并进行相应的处理操作,例如展示地理位置的信息。如果获取位置信息失败,可以使用.catch方法来处理。 通过以上的步骤,我们就可以在uniapp微信小程序实现定位功能的demo了。当然,在实际开发中,我们还可以通过uni.showLoading等方法来优化用户体验,例如在获取位置信息期间显示一个加载动画。 总结一下,uniapp微信小程序定位demo的实现步骤如下:引入uniapp提供的API,调用uni.getLocation方法获取位置信息,处理获取位置信息成功或失败的情况,并进行相应的操作。希望以上的回答对您有所帮助!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值