地图轨迹回放是出行、跑步、物流类小程序的常见需求,比如跑步软件的运动记录回放、打车软件的行程回顾,都离不开map组件的polyline能力。不过官方文档对polyline的描述比较精简,真正动手做的时候会遇到各种细节问题:坐标数组怎么组织、颜色怎么分段、回放动画怎么控制节奏。这篇文章把轨迹回放和渐变色线条两个核心场景拆开讲透,代码可以直接复制到项目里跑。

polyline基础配置与常见坑
polyline是map组件的一个数组属性,每一个元素代表一条折线。核心字段包括points(坐标点数组)、color(线条颜色)、width(线宽)、arrowLine(是否带箭头)、borderWidth和borderColor(描边)等。先看一个最基础的写法:
Page({
data: {
latitude: 39.908,
longitude: 116.397,
polyline: [{
points: [
{ latitude: 39.908, longitude: 116.397 },
{ latitude: 39.908, longitude: 116.407 },
{ latitude: 39.912, longitude: 116.412 }
],
color: '#18AD00',
width: 6,
arrowLine: true
}]
}
})这里有几个容易踩的坑。第一个是color字段必须带透明度时要用8位HEX格式,比如#FF0000AA,前两位FF是透明度,这一点和CSS的rgba写法不同,直接写rgba字符串在某些版本的客户端上不生效。第二个是width的单位是像素,但在不同屏幕像素密度下,iOS和安卓的视觉粗细会有差异,建议实际效果以真机为准,模拟器上的线条渲染和真机区别不小。第三个是points数组如果超过一定数量(经验值在几千个点),setData的性能开销会明显上升,需要做抽稀处理。
另外,如果想要轨迹线有描边效果(类似地图App里白边包裹彩色线),可以用borderWidth加borderColor的组合,先画一条粗的白线,再叠一条细的彩色线,两条polyline的points保持一致即可,这是很实用的叠加技巧。
轨迹回放的完整实现思路
轨迹回放的本质是:把完整的轨迹点数组按时间切片,用定时器逐步把点追加到polyline的points里,同时移动一个代表车辆或人物的marker。整体流程分三步:准备数据、切片播放、控制交互。
假设从后端拿到一条完整的轨迹数据trackPoints,每个点带有经纬度。回放时不能一次性全部塞进polyline,而是用一个全局索引变量记录当前播放到第几个点,通过setInterval每次追加若干个点。示例代码如下:
Page({
data: {
polyline: [],
markers: [],
playIndex: 0
},
trackPoints: [],
onLoad() {
// 模拟后端返回的轨迹数据
this.trackPoints = [
{ latitude: 39.908, longitude: 116.397 },
{ latitude: 39.909, longitude: 116.401 },
{ latitude: 39.911, longitude: 116.405 },
{ latitude: 39.912, longitude: 116.412 },
{ latitude: 39.915, longitude: 116.418 }
];
},
startPlay() {
if (this.timer) return;
this.timer = setInterval(() => {
const next = this.data.playIndex + 1;
if (next >= this.trackPoints.length) {
this.stopPlay();
return;
}
const played = this.trackPoints.slice(0, next + 1);
this.setData({
playIndex: next,
polyline: [{
points: played,
color: '#1E90FF',
width: 5,
arrowLine: true
}],
markers: [{
id: 1,
latitude: this.trackPoints[next].latitude,
longitude: this.trackPoints[next].longitude,
iconPath: '/images/car.png',
width: 28,
height: 28,
rotate: this.getAngle(next)
}]
});
}, 200);
},
stopPlay() {
clearInterval(this.timer);
this.timer = null;
},
// 计算小车朝向角度
getAngle(index) {
if (index === 0) return 0;
const prev = this.trackPoints[index - 1];
const curr = this.trackPoints[index];
return Math.atan2(curr.latitude - prev.latitude,
curr.longitude - prev.longitude) * 180 / Math.PI;
}
})这段代码有几个关键点值得展开。首先是播放频率的选取,间隔200毫秒比较适中,如果轨迹点很密集可以每次追加多个点来加快速度,速度控制本质上就是调整每次追加的点数或者调整间隔时间。其次是marker的rotate计算,小车图标默认朝向是正右方,用相邻两点的经纬度算出方位角,图标才会沿着轨迹方向前进,这一步漏掉的话小车会一直横着走,观感很差。
还有一个体验细节:播放过程中地图视野最好跟着小车移动,可以在setData里同时更新map的latitude和longitude,让中心点始终是小车位置。不过要注意频率过高会触发地图频繁重绘,如果出现卡顿,可以用mapContext的translateMarker方法做平滑移动动画,它自带插值过渡,比手动逐帧跳位置流畅得多。
暂停和拖动进度条的实现也不复杂。暂停就是clearInterval并保留当前的playIndex;拖动进度条则是把playIndex设置为用户指定的位置,然后重新slice一次完整数据更新polyline。注意每次操作前都要清理旧的定时器,避免多个定时器叠加导致播放速度异常。
渐变色线条的分段实现方案
很多产品希望轨迹线能表达速度变化,比如跑步App里快的路段显示红色、慢的路段显示绿色。map组件的polyline并不支持CSS那种线性渐变,color字段只能接收单色值。解决办法是把一条轨迹拆成多条polyline,每一段用不同的颜色渲染,视觉上就形成了渐变过渡的效果。
具体做法是按相邻两个点切分线段,根据每段的速度或其他业务指标映射到颜色值,生成一个polyline数组。示例代码:
// points: 轨迹点数组,每个点带speed字段
function buildGradientPolylines(points) {
const result = [];
for (let i = 0; i < points.length - 1; i++) {
const speed = points[i].speed || 0;
// 根据速度映射颜色:慢为绿,快为红
const color = speedToColor(speed);
result.push({
points: [points[i], points[i + 1]],
color: color,
width: 6
});
}
return result;
}
function speedToColor(speed) {
// 速度区间0到10,映射到色相120(绿)到0(红)
const ratio = Math.min(speed / 10, 1);
const hue = 120 * (1 - ratio);
return `hsl(${hue}, 85%, 50%)`;
}
// 使用示例
this.setData({
polyline: buildGradientPolylines(this.trackPoints)
});这个方案里颜色映射用了HSL色彩空间,好处是可以按数值线性插值,从绿色平滑过渡到红色,代码简洁且效果自然。如果你的轨迹点很多,逐段生成polyline会导致数组过大,可以做一个优化:颜色相同的连续线段合并为一条polyline,段数能减少一个数量级。另外HSL格式在部分低版本客户端上支持不佳,稳妥起见可以手动把HSL转成8位HEX格式再赋值给color字段,兼容性最可靠。
分段方案的另一个注意点是线段衔接处会有细小的接缝,这是因为相邻线段的端点重合但渲染时抗锯齿处理导致的。缓解办法是让每段polyline的第二个点取下一段的第一个点再往后延伸一点,或者适当增加width抵消视觉缝隙。总体来说,这种分段着色方案虽然是曲线救国,但在真机上的表现足够好,滴滴、Keep等App的小程序轨迹展示基本也是这个思路。
最后补充一点性能建议:无论哪种方案,上万个轨迹点直接绘制都会拖垮setData。上线前建议对轨迹做抽稀,常用的算法是Douglas-Peucker,可以在后端做好再下发,也可以用js在端上实现,抽稀到几百个点通常视觉上已经看不出差别,回放动画也会流畅很多。
微信小程序map组件polyline轨迹修改时间:2026-09-04 02:18:51