地图轨迹回放时如何平滑移动Marker?

迷人的思佳 阅读 36

我在用高德地图做轨迹回放功能,现在是每隔1秒更新一次Marker的位置,但看起来特别卡顿,像“瞬移”一样。试过用marker.setPosition()直接设置新坐标,也试过加CSS transition,但都没用。

查了文档说可以用moveAlong,但我的轨迹点是动态加载的,不是一开始就全有的。有没有办法让Marker在两个点之间平滑移动?比如用requestAnimationFrame自己算插值?

function replayTrack(points) {
  let index = 0;
  const timer = setInterval(() => {
    if (index >= points.length) {
      clearInterval(timer);
      return;
    }
    marker.setPosition(points[index]);
    index++;
  }, 1000);
}
我来解答 赞 5 收藏
二维码
手机扫码查看
2 条解答
萌新.怡硕
要实现平滑的Marker移动,关键在于在两个坐标点之间进行插值计算,而不是直接跳到下一个位置。requestAnimationFrame确实是个好选择,因为它能根据显示器刷新率来优化动画。

首先我们得抛弃setInterval这种粗糙的定时器,改用requestAnimationFrame。然后在每次更新时,根据当前时间和目标位置来计算中间位置。

下面是一个改进版的代码示例:

function replayTrack(points) {
let index = 0;
let startTime = null;
const duration = 1000; // 每段移动持续时间1秒

function step(timestamp) {
if (!startTime) startTime = timestamp;
const elapsed = timestamp - startTime;

if (index >= points.length) return;

// 获取当前和下一个坐标
const currentPos = points[index];
const nextPos = points[index + 1] || currentPos; // 防止越界

// 计算比例
const t = Math.min(1, elapsed / duration);

// 线性插值计算新位置
const lat = currentPos.lat + t * (nextPos.lat - currentPos.lat);
const lng = currentPos.lng + t * (nextPos.lng - currentPos.lng);

marker.setPosition({ lat, lng });

if (t < 1) {
requestAnimationFrame(step);
} else {
index++;
startTime = null; // 准备下一段动画
if (index < points.length) {
requestAnimationFrame(step);
}
}
}

requestAnimationFrame(step);
}


这个方案有几个要点:
- 使用线性插值来计算中间位置,这样看起来更自然
- 把每段动画限制在1秒内完成(duration变量)
- 注意处理最后一个点的情况,避免数组越界
- 每次动画结束后再开始新的动画周期

比起简单的setPosition,这种方法能让Marker看起来是在连续移动,而不是瞬间跳跃。当然,实际应用中可能还需要考虑更多细节,比如不同路段的速度变化之类的。但这个基础框架应该能满足大部分需求了。

说实话,轨迹回放这种需求看似简单,真要做好还挺费劲的,尤其要考虑地图本身的缩放和平移操作对动画的影响。不过慢慢调调参数总能搞定的。
点赞
2026-03-31 10:04
Mr-子诺
Mr-子诺 Lv1
问题在于你每秒直接setPosition肯定瞬移啊,高德Marker本身就有moveTo方法自带动画,或者自己用requestAnimationFrame做插值也行。

用高德自带的moveTo最省事:

function replayTrack(points) {
let index = 0;

function moveNext() {
if (index >= points.length - 1) return;

marker.moveTo(points[index + 1], {
duration: 1000,
delay: 0
});
index++;
}

// 监听移动结束事件,继续下一段
marker.on('moveend', moveNext);
moveNext();
}


如果你非要自己撸插值,用requestAnimationFrame也行,就是麻烦点:

function smoothMove(marker, from, to, duration) {
const startTime = performance.now();

function animate(currentTime) {
const elapsed = currentTime - startTime;
const progress = Math.min(elapsed / duration, 1);

const lng = from.lng + (to.lng - from.lng) * progress;
const lat = from.lat + (to.lat - from.lat) * progress;

marker.setPosition([lng, lat]);

if (progress < 1) {
requestAnimationFrame(animate);
}
}

requestAnimationFrame(animate);
}


动态加载的点也无所谓,每次拿到新点调moveTo就行,不用一开始全量加载。
点赞 2
2026-03-01 01:19