动画与过渡
deck.gl 没有内置时间轴,动画靠每帧更新属性实现。 能交给 transitions 的插值就不要自己算,能用着色器完成的位移就不要在 JS 里改数据。
属性过渡
transitions 声明哪些访问器的结果需要平滑过渡,值是一个时长(毫秒)。
触发条件是 updateTriggers 里的值发生变化,或者数据引用变了。
var selected = 'BJ-001';
function buildStations(activeId) {
return new deck.ScatterplotLayer({
id: 'stations',
data: stations,
getPosition: function (d) { return [d.lng, d.lat]; },
getRadius: function (d) { return d.id === activeId ? 16 : 7; },
getFillColor: function (d) {
return d.id === activeId ? [255, 180, 0, 240] : [40, 140, 255, 200];
},
transitions: {
getRadius: 600,
getFillColor: 600
},
updateTriggers: {
getRadius: activeId,
getFillColor: activeId
}
});
}
var first = buildStations(selected);
过渡的是数值,不是状态。颜色会按通道从旧值插到新值,半径线性变化。 如果访问器每次都返回新数组,插值仍能工作,但会产生大量临时对象。
| 动画需求 | 手段 | 备注 |
|---|---|---|
| 选中放大、变色 | transitions + updateTriggers | 最省事,由 deck.gl 插值 |
| 轨迹沿线移动 | TripsLayer 的 currentTime | 位移在着色器中完成 |
| 柱体高度起伏 | 每帧改 elevationScale | 不要重建数据数组 |
| 图层淡入淡出 | 改 opacity,或增删图层 | 配合 transitions 用不了,需自己缓动 |
用时钟驱动轨迹
TripsLayer 接收带时间戳的路径,按 currentTime
决定每条路径画到哪一段。推进时间只需要改这一个参数,数据完全不动。
var tripData = [
{
vendor: 1,
path: [
[116.20, 39.80, 0], [116.30, 39.88, 0], [116.41, 39.91, 0],
[116.52, 39.96, 0], [116.60, 40.02, 0]
],
timestamps: [0, 40, 90, 140, 200]
},
{
vendor: 2,
path: [
[116.35, 40.05, 0], [116.38, 39.96, 0], [116.44, 39.90, 0], [116.50, 39.86, 0]
],
timestamps: [0, 60, 120, 190]
}
];
var tripsLayer = new deck.TripsLayer({
id: 'trips',
data: tripData,
getPath: function (d) { return d.path; },
getTimestamps: function (d) { return d.timestamps; },
getColor: function (d) { return d.vendor === 1 ? [255, 180, 0] : [0, 180, 255]; },
widthMinPixels: 3,
trailLength: 30,
currentTime: 0
});
path 与 timestamps 必须等长。trailLength
是尾迹覆盖的时间长度,不是点数,与 timestamps 使用同一套单位。
逐帧推进
用一个循环更新 currentTime。关键点是复用图层对象:
每次只调用 setProps,不要每帧 new 一个 Layer。
var deckgl = new deck.Deck({
container: 'map',
initialViewState: { longitude: 116.40, latitude: 39.93, zoom: 10.5, pitch: 45 },
controller: true,
layers: [tripsLayer]
});
var clock = 0;
var last = performance.now();
function frame(now) {
var dt = (now - last) / 1000;
last = now;
clock = (clock + dt * 8) % 200; // 8 倍速,200 是时间上限
tripsLayer.setProps({ currentTime: clock });
requestAnimationFrame(frame);
}
requestAnimationFrame(frame);
setProps 只更新变化的属性,图层的着色器与缓冲保持不动。
每帧新建 TripsLayer 会触发完整重建,数据量大时表现为每秒几百毫秒的卡顿。
页面切到后台时停掉循环。requestAnimationFrame
在隐藏标签页会自动暂停,但用 setInterval 驱动的动画不会,
切回页面时会出现时间跳变。
注意事项
transitions只对访问器的返回值生效,opacity、elevationScale这类图层属性需要手动缓动。- 过渡期间
updateTriggers继续变化,会打断当前插值并从当前值重新开始。 TripsLayer的时间单位自定义,但对所有路径必须统一,混用秒和毫秒会让部分路径永远画不出来。- 开启
picking时动画图层每帧都要重建拾取缓冲,交互与动画并存时优先关闭拾取。 - 系统开启「减少动态效果」时,应提供静止模式,直接显示最终状态而不是循环播放。
经纬志(2026)。《动画与过渡》。https://www.jingweizhi.com/deckgl/tutorial/animation.html(访问日期 2026-11-03)。