高德轨迹更新效果
基于高德地图 JSAPI 2.0 开发的轨迹回放系统,支持实时轨迹点加载、平滑动画播放、轨迹可视化等功能。
主要功能
- 🎯 轨迹回放动画 - 平滑的移动动画,自动旋转,地图跟随
图片效果
代码附上:
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="initial-scale=1.0, user-scalable=no, width=device-width">
<title>轨迹回放</title>
<link rel="stylesheet" href="https://a.amap.com/jsapi_demos/static/demo-center/css/demo-center.css"/>
<style>
html, body, #container {
height: 100%;
width: 100%;
}
</style>
</head>
<body>
<div id="container"></div>
<script type="text/javascript" src="https://webapi.amap.com/maps?v=2.0&key=YOUR_API_KEY"></script>
<script>
// JSAPI2.0 使用覆盖物动画必须先加载动画插件
AMap.plugin('AMap.MoveAnimation', function(){
// 完整的轨迹点数据(模拟远程数据源)
var allTrajectoryPoints = [
[116.478935,39.997761],
[116.478939,39.997825],
[116.478912,39.998549],
[116.478912,39.998549],
[116.478998,39.998555],
[116.478998,39.998555],
[116.479282,39.99856],
[116.479658,39.998528],
[116.480151,39.998453],
[116.480784,39.998302],
[116.480784,39.998302],
[116.481149,39.998184],
[116.481573,39.997997],
[116.481863,39.997846],
[116.482072,39.997718],
[116.482362,39.997718],
[116.483633,39.998935],
[116.48367,39.998968],
[116.484648,39.999861]
];
var marker;
var currentPath = []; // 当前已加载的轨迹点
var loadedIndex = 0; // 已加载的轨迹点索引
var batchSize = 3; // 每次加载的轨迹点数量
var loadInterval = null; // 定时器
var isAnimating = false; // 标记是否正在动画中
var map = new AMap.Map("container", {
resizeEnable: true,
center: [116.397428, 39.90923],
zoom: 17
});
marker = new AMap.Marker({
map: map,
position: allTrajectoryPoints[0],
icon: "https://a.amap.com/jsapi_demos/static/demo-center-v2/car.png",
offset: new AMap.Pixel(-13, -26),
});
// 只保留已走过轨迹线
var passedPolyline = new AMap.Polyline({
map: map,
strokeColor: "#FF8000", //线颜色
strokeWeight: 6, //线宽
});
// 维护一个累积的已走过路径数组,确保轨迹线一直显示
var accumulatedPassedPath = [allTrajectoryPoints[0]];
var lastRecordedPosition = allTrajectoryPoints[0]; // 上次记录的位置
// 初始化时设置已走过轨迹为起点
passedPolyline.setPath(accumulatedPassedPath);
currentPath.push(allTrajectoryPoints[0]);
loadedIndex = 1;
marker.on('moving', function (e) {
// 获取当前marker的位置
var currentPos = e.target.getPosition();
var currentPosArray = [currentPos.lng, currentPos.lat];
// 计算与上次记录位置的距离
var dist = Math.sqrt(
Math.pow(currentPosArray[0] - lastRecordedPosition[0], 2) +
Math.pow(currentPosArray[1] - lastRecordedPosition[1], 2)
);
// 如果移动距离超过阈值(避免重复添加太近的点),添加到累积路径
if (dist > 0.00001) {
accumulatedPassedPath.push(currentPosArray);
lastRecordedPosition = currentPosArray;
// 更新轨迹线显示
passedPolyline.setPath(accumulatedPassedPath);
}
map.setCenter(currentPos, true);
});
marker.on('moveend', function(e) {
isAnimating = false;
// 确保最后一个位置被记录
var finalPos = e.target.getPosition();
var finalPosArray = [finalPos.lng, finalPos.lat];
// 检查最后一个位置是否已经记录
var lastPoint = accumulatedPassedPath[accumulatedPassedPath.length - 1];
var dist = Math.sqrt(
Math.pow(finalPosArray[0] - lastPoint[0], 2) +
Math.pow(finalPosArray[1] - lastPoint[1], 2)
);
if (dist > 0.00001) {
accumulatedPassedPath.push(finalPosArray);
passedPolyline.setPath(accumulatedPassedPath);
}
lastRecordedPosition = finalPosArray;
});
// 模拟远程加载轨迹点的函数
function loadTrajectoryPoints() {
if (loadedIndex >= allTrajectoryPoints.length) {
// 所有轨迹点已加载完成,清除定时器
if (loadInterval) {
clearInterval(loadInterval);
loadInterval = null;
}
console.log('所有轨迹点已加载完成');
return;
}
// 计算本次要加载的轨迹点
var newPoints = [];
var endIndex = Math.min(loadedIndex + batchSize, allTrajectoryPoints.length);
for (var i = loadedIndex; i < endIndex; i++) {
newPoints.push(allTrajectoryPoints[i]);
}
console.log('加载新的轨迹点:', newPoints);
// 将新点添加到当前路径
currentPath = currentPath.concat(newPoints);
loadedIndex = endIndex;
// 获取marker当前位置
var currentPosition = marker.getPosition();
var currentPosArray = [currentPosition.lng, currentPosition.lat];
// 如果marker正在移动,需要暂停并重新规划路径
if (isAnimating) {
marker.pauseMove();
// 找到当前位置在currentPath中最接近的点索引
var closestIndex = 0;
var minDist = Infinity;
for (var k = 0; k < currentPath.length; k++) {
var dist = Math.sqrt(
Math.pow(currentPath[k][0] - currentPosition.lng, 2) +
Math.pow(currentPath[k][1] - currentPosition.lat, 2)
);
if (dist < minDist) {
minDist = dist;
closestIndex = k;
}
}
// 构建从当前位置开始的新路径
// 如果当前位置不在路径点上,先添加当前位置
var newPath = [];
if (minDist > 0.0001) {
newPath.push(currentPosArray);
}
// 添加从最近点之后的所有点(包括新加载的点)
for (var m = closestIndex + 1; m < currentPath.length; m++) {
newPath.push(currentPath[m]);
}
// 如果还有剩余路径,继续移动
if (newPath.length > 0) {
isAnimating = true;
marker.moveAlong(newPath, {
duration: 500,
autoRotation: true,
});
}
} else {
// 如果marker没有在移动,找到当前位置在路径中的位置,从那里继续
var startPath = [];
var foundIndex = -1;
// 查找当前位置在路径中的位置
for (var n = 0; n < currentPath.length; n++) {
var dist = Math.sqrt(
Math.pow(currentPath[n][0] - currentPosition.lng, 2) +
Math.pow(currentPath[n][1] - currentPosition.lat, 2)
);
if (dist < 0.0001) {
foundIndex = n;
break;
}
}
if (foundIndex >= 0 && foundIndex < currentPath.length - 1) {
// 从找到的位置之后开始
startPath = currentPath.slice(foundIndex + 1);
} else if (foundIndex === -1) {
// 如果没找到,从当前位置开始,包含所有剩余点
startPath = [currentPosArray].concat(currentPath.slice(1));
} else {
// 已经在最后一个点,使用新加载的点
startPath = currentPath.slice(-newPoints.length);
}
if (startPath.length > 0) {
isAnimating = true;
marker.moveAlong(startPath, {
duration: 500,
autoRotation: true,
});
}
}
}
// 确保地图渲染完成后再开始移动动画
map.on('complete', function() {
console.log('地图渲染完成,开始加载轨迹点');
// 加载第一批数据
var firstBatch = [];
var firstEndIndex = Math.min(loadedIndex + batchSize, allTrajectoryPoints.length);
for (var i = loadedIndex; i < firstEndIndex; i++) {
firstBatch.push(allTrajectoryPoints[i]);
}
currentPath = currentPath.concat(firstBatch);
loadedIndex = firstEndIndex;
console.log('加载第一批轨迹点:', firstBatch);
// 开始第一次移动
if (currentPath.length > 1) {
isAnimating = true;
marker.moveAlong(currentPath, {
duration: 500,
autoRotation: true,
});
}
// 每3秒加载一次新数据
loadInterval = setInterval(function() {
loadTrajectoryPoints();
}, 3000);
});
map.setFitView();
});
</script>
</body>
</html>