UniApp 跑步路线规划开发指南
uni-app中提供的专门监听用户位置变化的api:https://uniapp.dcloud.net.cn/api/location/location-change.html
以下是 UniApp 跑步路线规划功能的实现思路,采用 分阶段表格 形式呈现核心逻辑流程:
实现思路流程表
阶段 | 实现思路 | 技术要点 |
---|---|---|
1. 权限初始化 | 确保用户授予定位权限,处理授权拒绝场景 | - uni.authorize 请求权限- uni.getSetting 检查权限状态- uni.openSetting 引导用户开启权限 |
2. 定位服务启动 | 开启高精度定位监听,设置定位参数 | - uni.startLocationUpdate 启动监听- type: 'gcj02' 坐标系类型- isHighAccuracy: true 高精度模式 |
3. 轨迹数据采集 | 持续接收位置变化事件,存储坐标点并计算运动数据 | - uni.onLocationChange 监听位置变化- 哈弗辛公式计算点间距 - 时间差计算配速和卡路里 |
4. 地图渲染更新 | 动态绘制用户运动轨迹,标记起点终点 | - map 组件绑定 polyline 属性- markers 数组管理标记点- setData 触发视图层更新 |
5. 数据持久化 | 本地存储运动记录,支持云端同步 | - uni.setStorageSync 本地缓存- uniCloud.uploadFile 上传轨迹文件- 结构化数据存储(JSON 格式) |
6. 异常处理 | 处理定位中断、权限变化、设备兼容等问题 | - uni.onLocationChangeError 监听错误- try...catch 包裹核心逻辑- 多端条件编译处理 API 差异 |
7. 性能优化 | 平衡定位频率与资源消耗,优化渲染性能 | - throttle 节流控制数据更新- 轨迹点采样(如每 5 秒存一点) - 使用 v-slot:markers 优化标记渲染 |
核心流程箭头图
(文字版逻辑链表示)
权限检查 → 启动定位 → 监听坐标变化 → 采集轨迹点
↓ ↓ ↓
失败处理 → 计算运动数据 → 更新地图渲染
↓ ↓ ↓
引导授权 → 持久化存储 → 异常监控
扩展优化方向
通过该结构化思路,开发者可快速定位各阶段实现要点,并根据实际需求选择技术方案。建议结合具体业务场景补充细节(如电子围栏、海拔变化分析等)。
一、核心功能解析
-
实时位置追踪
- 通过
uni.startLocationUpdate
监听用户位置变化 - 使用
uni.onLocationChange
接收位置更新事件 - 权限处理流程:
// 授权请求 uni.authorize({ scope: 'scope.userLocation', success: startTracking, fail: showPermissionGuide })
- 通过
-
轨迹绘制原理
- 持续收集经纬度坐标存入数组
- 通过
map
组件的polyline
属性绘制连线 - 坐标数据结构:
const path = [ { latitude: 39.909, longitude: 116.39742 }, { latitude: 39.908, longitude: 116.396 } ]
-
运动数据计算
- 距离计算:哈弗辛公式(高精度)或坐标差累加(近似值)
- 配速计算:
速度 = 距离 / 时间
- 卡路里估算:
体重(kg) × 距离(km) × 1.036
二、优化版代码实现
1. 核心逻辑封装
将记录用户位置的方法代码 进行封装
// utils/sportTracker.js
export class SportTracker {
constructor() {
this.path = [] // 用于存储运动轨迹的路径点
this.startTime = null // 开始记录的时间戳
this.isTracking = false // 标记是否正在记录运动轨迹
this.totalDistance = 0 // 总距离(初始化为0)
}
// 开始记录运动轨迹
async start() {
await this.checkPermission() // 检查并请求定位权限
this.startTime = Date.now() // 记录开始时间
this.isTracking = true // 设置为正在记录状态
// 开始监听位置更新
uni.startLocationUpdate({
success: () => {
// 监听位置变化事件
uni.onLocationChange(res => {
this.handleNewPosition(res) // 处理新的位置信息
})
}
})
}
// 处理新坐标点
handleNewPosition(res) {
const newPoint = {
latitude: res.latitude, // 新点的纬度
longitude: res.longitude, // 新点的经度
timestamp: Date.now() // 新点的时间戳
}
// 如果路径中已经有至少一个点,计算与上一个点的距离
if (this.path.length > 0) {
const last = this.path[this.path.length - 1] // 获取路径中的最后一个点
const distance = this.calcDistance(last, newPoint) // 调用距离计算函数
this.totalDistance += distance // 累加到总距离
}
this.path.push(newPoint) // 将新点加入路径
}
// 使用哈弗辛公式计算两点之间的距离
calcDistance(p1, p2) {
const R = 6371e3 // 地球半径(单位:米)
const φ1 = p1.latitude * Math.PI / 180 // 将纬度转换为弧度
const φ2 = p2.latitude * Math.PI / 180 // 将纬度转换为弧度
const Δφ = (p2.latitude - p1.latitude) * Math.PI / 180 // 纬度差(弧度)
const Δλ = (p2.longitude - p1.longitude) * Math.PI / 180 // 经度差(弧度)
// 哈弗辛公式的核心计算部分
const a = Math.sin(Δφ / 2) ** 2 +
Math.cos(φ1) * Math.cos(φ2) *
Math.sin(Δλ / 2) ** 2
return R * 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)) // 返回两点之间的距离
}
// 检查定位权限
async checkPermission() {
const { authSetting } = await uni.getSetting() // 获取用户的授权设置
if (!authSetting['scope.userLocation']) { // 如果没有定位权限
await uni.authorize({ scope: 'scope.userLocation' }) // 请求定位权限
}
}
}
2. 组件集成
<script setup>
import { ref, onUnmounted } from 'vue'
import { SportTracker } from './utils/sportTracker'
// 创建一个 SportTracker 实例,用于记录运动轨迹
const tracker = new SportTracker()
// 定义一个响应式变量 mapData,用于存储地图的绘制数据
const mapData = ref({
polyline: [{ // 定义多段线(轨迹线)的样式和数据
points: [], // 轨迹点数组,初始为空
color: '#4CAF50', // 轨迹线颜色
width: 6 // 轨迹线宽度
}]
})
// 开始运动
const startRun = async () => {
await tracker.start() // 调用 SportTracker 的 start 方法开始记录运动轨迹
// 每隔 1 秒更新一次地图轨迹
setInterval(() => {
mapData.value.polyline[0].points = tracker.path // 将轨迹点数据更新到地图的绘制数据中
}, 1000)
}
// 结束运动
const endRun = () => {
uni.stopLocationUpdate() // 停止监听位置更新
uni.offLocationChange() // 移除位置变化事件监听
saveToStorage() // 将运动数据保存到存储中(saveToStorage 函数未在代码中定义,可能是外部定义的)
}
// 在组件卸载时调用 endRun 方法,确保停止监听位置更新和事件监听
onUnmounted(endRun)
</script>
三、关键知识点
1. 定位精度优化
- 参数配置:
uni.startLocationUpdate({ type: 'gcj02', // 坐标系类型 interval: 2000, // 更新间隔 isHighAccuracy: true // 高精度模式 })
- 定位漂移处理:
- 设置移动速度阈值过滤异常点
- 使用卡尔曼滤波算法平滑轨迹
2. 性能优化策略
-
数据采样:
每5秒存储一个关键点,减少数据量let lastSave = 0 if (Date.now() - lastSave > 5000) { saveCriticalPoint() lastSave = Date.now() }
-
渲染优化:
使用throttle
控制地图更新频率import { throttle } from 'lodash-es' const updateMap = throttle(points => { mapData.value.polyline[0].points = points }, 1000)
3. 多端兼容处理
// 平台判断
const isWeapp = ref(process.env.UNI_PLATFORM === 'mp-weixin')
// 微信小程序专用API
if (isWeapp.value) {
wx.onCompassChange(res => {
// 处理指南针数据
})
}
四、最佳实践
-
数据持久化方案
// 本地缓存 const saveToStorage = () => { uni.setStorageSync('running_data', { path: tracker.path, distance: tracker.totalDistance, duration: Date.now() - tracker.startTime }) } // 云存储 const uploadToCloud = async () => { await uniCloud.uploadFile({ fileContent: JSON.stringify(tracker.path), cloudPath: `tracks/${Date.now()}.json` }) }
-
异常处理机制
uni.onLocationChangeError(err => { console.error('定位错误:', err) if (err.errCode === 2) { uni.showToast({ title: '请检查定位服务是否开启' }) } })
-
后台持续运行
// manifest.json 配置 "mp-weixin": { "requiredBackgroundModes": ["location"] }
五、扩展功能示例
1. 语音播报实现
const playAudio = (text) => {
const innerAudioContext = uni.createInnerAudioContext()
innerAudioContext.src = `https://tts-api.com/synthesize?text=${encodeURIComponent(text)}`
innerAudioContext.play()
}
// 每公里触发
if (totalDistance > nextMilestone) {
playAudio(`您已跑步${nextMilestone}公里`)
nextMilestone += 1
}
2. 轨迹分享功能
<button @click="shareTrajectory">
分享轨迹
</button>
<script>
const shareTrajectory = () => {
uni.share({
type: 'image',
imageUrl: generateTrajectorySnapshot(),
success: () => console.log('分享成功')
})
}
</script>
通过以上实现方案,可构建出专业级的跑步轨迹记录功能。建议结合具体业务需求:
- 添加海拔变化分析
- 实现分段配速统计
- 集成社交分享能力
- 对接健康数据API(如微信运动)