经纬志
首页 / deck.gl / 教程

动画与过渡

先看结论

deck.gl 没有内置时间轴,动画靠每帧更新属性实现。 能交给 transitions 的插值就不要自己算,能用着色器完成的位移就不要在 JS 里改数据。

最后验证 2026-11-03 · deck.gl 9.0 · 难度:进阶 · 核验 2026-10-29 · 引用本文

属性过渡

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)。