搭建企业资料网站wordpress 获取导航栏
2026/5/21 15:54:28 网站建设 项目流程
搭建企业资料网站,wordpress 获取导航栏,苏州免费网页制作模板,免费分销软件Vue3 高德地图(AMap) 实现平滑的实时轨迹回放与追踪 前言 在物联网、物流监控或安防调度系统中#xff0c;实时展示设备#xff08;如无人机、车辆、手环#xff09;的移动轨迹是一个常见需求。如果仅仅是简单的更新标记点位置#xff0c;视觉上会出现“跳变”现象#x…Vue3 高德地图(AMap) 实现平滑的实时轨迹回放与追踪前言在物联网、物流监控或安防调度系统中实时展示设备如无人机、车辆、手环的移动轨迹是一个常见需求。如果仅仅是简单的更新标记点位置视觉上会出现“跳变”现象体验很不流畅。本文将分享如何在Vue 3项目中利用高德地图 (AMap) JS API实现比较丝滑的实时轨效果。核心痛点平滑移动点位更新时Marker 需要从旧位置平滑过渡到新位置而不是瞬间跳过去。轨迹跟随随着 Marker 的移动轨迹线Polyline需要实时“生长”紧跟在 Marker 后面。增量更新后端通常返回完整的历史路径或当前状态前端需要计算出“新增的路径段”进行动画播放。实现逻辑解析核心是利用高德地图 API 的轨迹回放功能。虽然官方文档提供了基础的轨迹回放示例参考轨迹回放示例但官方示例通常是一次性加载完整路径例如先获取完整经纬度数组渲染出浅蓝色背景线再让小车沿着路径跑并画出浅绿色轨迹。我们的业务场景与官方示例的主要区别在于我们的路径数据是实时增量更新的。前端并没有一开始就拿到完整的路径而是通过 WebSocket 或轮询实时获取后端返回的最新路径数据。因此我们需要自行设计逻辑计算出每次更新的“增量片段”并让 Marker 平滑地走完这一段。核心思路前后端数据约定理想情况下后端最好直接返回“增量路径”即上一次位置到当前位置的坐标集合。但在实际项目中比如本案例后端接口返回的是当前时刻的完整累积路径。因此前端需要自行比对缓存的“上一次路径”和“最新路径”计算出增量部分。状态管理 (缓存实例)使用Map数据结构来缓存每个设备如无人机、手环的Marker图标和Polyline轨迹线实例。确保每个设备 ID 对应唯一的地图实例避免数据刷新时重复创建导致内存泄漏或闪烁。计算增量路径当新数据到达时通过对比新旧路径长度截取出新增的路径段。这段新增路径就是 Marker 接下来需要“平滑移动”的轨迹。平滑动画 (moveAlong)调用高德地图的marker.moveAlong()方法让 Marker 沿着新增路径平滑移动而不是瞬间跳变。实时绘制轨迹 (moving事件)监听 Marker 的moving事件。在移动过程中实时更新轨迹线Polyline的路径从而实现“边走边画”的效果。关键点为什么要在moving事件中更新总轨迹而不是在动画结束 (moveend) 后更新这是为了防止数据推送频率过快。如果等到动画结束再更新可能会出现“新的数据推送来了但上一次动画还没结束导致轨迹数据丢失或衔接不上”的问题。在moving过程中实时将passedPath已走过的路径拼接到总轨迹中是最稳妥的方式。动画结束清理动画结束时 (moveend)清理临时绘制的辅助线移除监听器防止内存泄漏。代码详解1. 状态管理与初始化我们使用Map来管理地图上的 Marker 和 Polyline 实例。/* by 01022.hk - online tools website : 01022.hk/zh/deencrypt.html */ // 存储 Marker 实例 (Key: 设备ID, Value: AMap.Marker) const uavMarkers ref(new Map()); // 存储轨迹线 Polyline 实例 (Key: 设备ID, Value: AMap.Polyline) const uavPaths ref(new Map());2. 核心处理函数refreshTempPoint这个函数负责处理单条设备数据的更新逻辑。/* by 01022.hk - online tools website : 01022.hk/zh/deencrypt.html */ // 刷新设备点位与轨迹 // item: 后端返回的设备数据对象 // position: 当前最新的坐标点 // type: 更新类型init 为初始化其他为增量更新 const refreshTempPoint async (item, position, type, marker, tempOverlay, pathOverlay) { // 1. 清理上一轮的临时覆盖物如临时路线、距离文本 tempOverlay?.clearOverlays(); if (item.coordinatesLine) { const coordinatesLine JSON.parse(item.coordinatesLine); // 解析后端返回的完整路径数组 // --- A. 初始化起点 Marker --- let tempMarker tempUavMarkers.value.get(item.id); if (!tempMarker) { // 如果是第一次出现渲染起点 tempMarker renderPoint(coordinatesLine[0], item, , pathOverlay); tempUavMarkers.value.set(item.id, tempMarker); } // --- B. 获取或创建历史轨迹线 (Polyline) --- let polyline uavPaths.value.get(item.id); if (!polyline) { polyline trajectoryLine(item, pathOverlay); // 创建新的线实例 uavPaths.value.set(item.id, polyline); } // 获取当前地图上已有的路径缓存的旧路径 const existingPath polyline.getPath() || []; if (type ! init) { // --- C. 增量更新逻辑 --- // 1. 计算增量路径从已有路径的最后一个点开始截取直到最新路径的末尾 const newPathSegment coordinatesLine.slice( existingPath.length ? existingPath.length - 1 : 0 ); // 2. 创建一条临时的“隐形”线段用于辅助计算或展示视需求而定 const newPolyline trajectoryLine(item, tempOverlay); // 3. 如果有新增路径开始动画 if (newPathSegment newPathSegment.length 0) { // 监听移动过程 marker.on(moving, function (e) { // e.passedPath 是 Marker 在当前动画片段中已经走过的路径 newPolyline.setPath(e.passedPath); // [关键] 实时将走过的路径拼接到历史总轨迹中 // 这样即使 WebSocket 推送频率很快也能保证轨迹数据的连续性 polyline.setPath([...existingPath, ...e.passedPath]); }); // 开始平滑移动 marker.moveAlong(newPathSegment, { duration: 1000, // 动画时长需根据 WebSocket 推送频率调整 autoRotation: true, // 车头自动对准路径方向 }); // 监听移动结束 marker.on(moveend, function () { // 动画结束清理临时覆盖物 tempOverlay?.clearOverlays(); // 更新距离文本等信息 if (item.distance) { renderText( coordinatesLine[Math.ceil(coordinatesLine.length - 2)], ${item.distance}米, tempOverlay ); } // 移除监听器防止重复绑定 marker.off(moveend); }); } else { // 如果没有新增路径位置没变仅更新文字信息 if (item.distance) { renderText(..., ${item.distance}米, tempOverlay); } } } else { // --- D. 初始化逻辑 --- // 如果是初始化加载直接设置完整路径不进行动画回放 if (item.distance) { renderText(..., ${item.distance}米, tempOverlay); } polyline.setPath(coordinatesLine); } } else { // --- E. 无轨迹数据时的降级处理 --- // 如果后端没有返回路径数据直接跳变到最新位置 marker.setPosition(position); // 清理相关的轨迹实例和缓存 let tempMarker tempUavMarkers.value.get(item.id); if (tempMarker) { tempMarker.setMap(null); pathOverlay pathOverlay.removeOverlay(tempMarker); tempUavMarkers.value.delete(item.id); } let polyline uavPaths.value.get(item.id); if (polyline) { polyline.setMap(null); pathOverlay pathOverlay.removeOverlay(polyline); uavPaths.value.delete(item.id); } } };}};// 无人机和手环轨迹暂时 const refreshAirMap async (type, data) { const res await getUavElement(); // console.log(无人机数据, res.result); res.result.map(async (item) { // data.map(async (item) { if (item.type 1) { let position JSON.parse(item.coordinates); // 获取或创建无人机标记 let marker uavMarkers.value.get(item.id); if (!marker) { // 创建无人机、手环点位。 marker renderPoint(position, item, , overlayGroups.value); uavMarkers.value.set(item.id, marker); }else{ if (item.elementType 2) { // 已有点位且是手环点位就更新电量 marker.setLabel({ content: div classmarker-label-container div classinfo-name${item.name}/div div classicon-placeholder/div ${batteryHtml(item)} /div, offset: new AMaps.value.Pixel(0, 0), direction: center, }); } } if (item.elementType 1) { // 无人机轨迹 refreshTempPoint( item, position, type, marker, pathOverlayGroups.value, pathsStartPointOverlayGroups.value ); } else if (item.elementType 2) { // 手环轨迹 refreshTempPoint( item, position, type, marker, rescuePathsOverlayGroups.value, rescueOverlayGroups.value ); } // 轨迹部分判断是否有轨迹 } }); // }); };说明一下哦我的项目中还需要实现其他功能像是手环电量展示点击按钮可隐藏无人机轨迹点击按钮可隐藏手环轨迹无人机和手环轨迹起始点也需要展示一个点位图标轨迹线上显示距离还考虑了第一次进入项目如无轨迹就只更新点位坐标等等这些无关的轨迹展示逻辑的各位观众老爷略过就好这篇文章主要是分享一下实时轨迹实现的逻辑把轨迹相关逻辑抽出来重新写一份代码我嫌麻烦嘻嘻。总结通过结合 增量路径计算moveAlong轨迹回放 以及moving事件监听我们实现了一个高性能且视觉流畅的实时轨迹追踪功能。这种方案特别适合无人机巡航、车辆实时定位等需要高频更新位置的场景。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询