数据过渡

Apache EChartsTM 会在新增、更新、删除数据时在位置、缩放、形状上应用过渡动画。这让图表的变化更平滑,也能更好地展现数据之间的关系。通常开发者不需要担心如何使用这些动画,只需要通过 setOption 更新数据,ECharts 就会自动找出上一份数据和新数据的差异,并自动应用最合适的过渡动画。

例如,下例展示了定时更新饼图数据时的过渡动画。

function makeRandomData() {
  return [
    {
      value: Math.random(),
      name: 'A'
    },
    {
      value: Math.random(),
      name: 'B'
    },
    {
      value: Math.random(),
      name: 'C'
    }
  ];
}
option = {
  series: [
    {
      type: 'pie',
      radius: [0, '50%'],
      data: makeRandomData()
    }
  ]
};

setInterval(() => {
  myChart.setOption({
    series: {
      data: makeRandomData()
    }
  });
}, 2000);
在线示例

动画的配置

因为新增数据和更新数据通常需要不同的动画,比如我们可能希望数据更新的动画时间更短,所以 ECharts 区分了这两种动画的配置。

  • 对于新增的数据,我们会应用入场动画,分别使用 animationDurationanimationEasinganimationDelay 配置动画的时长、缓动函数和延迟。
  • 对于更新的数据,我们会应用更新动画,分别使用 animationDurationUpdateanimationEasingUpdateanimationDelayUpdate 配置动画的时长、缓动函数和延迟。

可以看到,更新动画的配置项就是入场动画的配置项加上了 Update 后缀。

在 ECharts 中每次使用 setOption,数据都会和上次更新的数据进行 diff,并根据 diff 结果对数据执行三种状态:新增、更新和删除。这个 diff 是基于数据的 name 进行的,比如上次更新有三个 name 分别是 'A''B''C',新的更新变成了 'B''C''D',那么数据 'B''C' 将被更新,数据 'A' 将被删除,数据 'D' 将被新增。如果是第一次 setOption,因为没有旧数据,所有数据都将是新增。根据这三种状态,ECharts 会分别应用入场动画、更新动画和离场动画。

所有这些配置都可以在 option 的顶层为所有系列和组件设置,也可以为每个系列单独设置。

如果我们想关闭动画,我们只需要把 option.animation 设置为 false

动画时长

animationDurationanimationDurationUpdate 用于设置动画的时长,单位是毫秒(ms)。设置一个更长的动画时长可以让用户更清楚地看到过渡动画的效果,但是我们也要注意时间过长可能会让用户在等待动画结束时失去耐心。

设置为 0 会关闭动画,当我们只想关闭入场动画或更新动画时,可以通过单独将对应的配置项设置为 0 来实现。

动画缓动

animationEasinganimationEasingUpdate 配置项用于设置动画的缓动函数,缓动函数是一个输入动画时间并输出动画进度的函数。

(t: number) => number;

ECharts 内置了常见的动画缓动函数,如 'cubicIn''cubicOut',可以直接使用。

内置缓动函数。

动画延迟

animationDelayanimationDelayUpdate 用于设置动画延迟开始的时间,通常我们会使用一个回调函数为不同的数据设置不同的延迟,以实现交错动画的效果。

var xAxisData = [];
var data1 = [];
var data2 = [];
for (var i = 0; i < 100; i++) {
  xAxisData.push('A' + i);
  data1.push((Math.sin(i / 5) * (i / 5 - 10) + i / 6) * 5);
  data2.push((Math.cos(i / 5) * (i / 5 - 10) + i / 6) * 5);
}
option = {
  legend: {
    data: ['bar', 'bar2']
  },
  xAxis: {
    data: xAxisData,
    splitLine: {
      show: false
    }
  },
  yAxis: {},
  series: [
    {
      name: 'bar',
      type: 'bar',
      data: data1,
      emphasis: {
        focus: 'series'
      },
      animationDelay: function(idx) {
        return idx * 10;
      }
    },
    {
      name: 'bar2',
      type: 'bar',
      data: data2,
      emphasis: {
        focus: 'series'
      },
      animationDelay: function(idx) {
        return idx * 10 + 100;
      }
    }
  ],
  animationEasing: 'elasticOut',
  animationDelayUpdate: function(idx) {
    return idx * 5;
  }
};
在线示例

动画的性能优化

当数据量特别大时,运行动画可能会有性能问题,所以我们可以设置 animation: false 来关闭动画。

对于数据量动态变化的图表,我们推荐使用 animationThreshold 配置,它允许 ECharts 在画布中的图形数量超过这个阈值时自动关闭动画,以提高绘图性能。这通常是一个经验值,ECharts 通常能够实时渲染数千个图形(我们的默认值也设置为2000),但如果你的图表很复杂,或者你的用户环境比较恶劣,页面上同时还有很多其他复杂的代码在运行,那么可能需要适当调低这个值,以保证整个应用的流畅性。

监听动画结束

有时我们想获取当前渲染的结果,如果不使用动画,ECharts 会在 setOption 后直接进行渲染,我们可以使用 getDataURL 方法同步获取渲染结果。

const chart = echarts.init(dom);
chart.setOption({
  animation: false
  //...
});
// can be executed directly and synchronously
const dataUrl = chart.getDataURL();

但如果图表有动画,马上执行 getDataURL 会得到动画开始时的画面,而不是最终的结果。所以我们需要知道动画什么时候结束,然后再执行 getDataURL 来获取结果。

如果你确定动画的时长,一个更简单粗暴的方法是根据动画时长用 setTimeout 延迟执行。

chart.setOption({
  animationDuration: 1000
  //...
});
setTimeout(() => {
  const dataUrl = chart.getDataURL();
}, 1000);

另外,我们可以使用 ECharts 提供的 rendered 事件来判断 ECharts 已经完成了动画并停止渲染。

chart.setOption({
  animationDuration: 1000
  //...
});

function onRendered() {
  const dataUrl = chart.getDataURL();
  // ...
  // This event will also be triggered if there is a subsequent interaction and the interaction is redrawn, so it needs to be removed when you're done using it
  chart.off('rendered', onRendered);
}
chart.on('rendered', onRendered);

贡献者 在 GitHub 上编辑此页

pissang