Apache ECharts 5.2.0 中的新特性

通用过渡

自然流畅的过渡动画一直是 Apache ECharts 中的一个重要特性。通过避免数据更新造成的突兀变化,它不仅改善了视觉效果,还提供了表达数据关联和演变的可能性。因此,在 5.2.0 中,我们进一步增强了这种动画能力。接下来,我们将看到这个通用过渡如何为图表增加表现力和叙事力量。

在之前的版本中,过渡动画有一定的局限性:它们只能用于相同形状的位置、大小变化,并且只能在相同类型的系列上工作。例如,以下示例通过饼图中扇形形状的变化来反映数据百分比的变化。

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);
在线示例

从 5.2.0 开始,我们引入了通用过渡,这是一个更强大的动画特性。有了它,过渡不再局限于相同类型的系列之间。现在,我们可以使用这种跨系列的变形在任何类型的系列和任何类型的形状之间进行动画。

这有多酷?让我们看看!

跨系列变形过渡

通过设置 universalTransition: true 来启用通用过渡特性,从饼图切换到柱状图,或者从柱状图切换到散点图,甚至在更复杂的图表(如旭日图和矩形树图)之间切换,都可以自然变形。

如下所示,在饼图和柱状图之间切换。

const dataset = {
  dimensions: ['name', 'score'],
  source: [
    ['Hannah Krause', 314],
    ['Zhao Qian', 351],
    ['Jasmin Krause ', 287],
    ['Li Lei', 219],
    ['Karle Neumann', 253],
    ['Mia Neumann', 165],
    ['Böhm Fuchs', 318],
    ['Han Meimei', 366]
  ]
};
const pieOption = {
  dataset: [dataset],
  series: [
    {
      type: 'pie',
      // associate the series to be animated by id
      id: 'Score',
      radius: [0, '50%'],
      universalTransition: true,
      animationDurationUpdate: 1000
    }
  ]
};
const barOption = {
  dataset: [dataset],
  xAxis: {
    type: 'category'
  },
  yAxis: {},
  series: [
    {
      type: 'bar',
      // associate the series to be animated by id
      id: 'Score',
      // Each data will have a different color
      colorBy: 'data',
      encode: { x: 'name', y: 'score' },
      universalTransition: true,
      animationDurationUpdate: 1000
    }
  ]
};

option = barOption;

setInterval(() => {
  option = option === pieOption ? barOption : pieOption;
  // Use the notMerge form to remove the axes
  myChart.setOption(option, true);
}, 2000);
在线示例

更多常见图表之间的过渡。

这种过渡不再仅限于基本的折线图、柱状图和饼图,还可以在柱状图和地图之间进行。

或者在旭日图和矩形树图之间,甚至是非常灵活的自定义系列之间进行过渡。

请注意,您需要配置系列 ID,以确保需要动画过渡的系列之间存在一一对应的关系。

最小化捆绑包需要手动导入此特性。

import { UniversalTransition } from 'echarts/features';
echarts.use([UniversalTransition]);

数据拆分和合并动画

除了常见的数据值更新之外,有时我们还会遇到交互后数据聚合、向下钻取和其他更新,此时我们不能直接应用一对一的过渡,而是需要使用更多动画效果(如拆分和合并)来表达数据的转换。

为了能够表达数据之间可能的 多对多 连接,在 5.2.0 中,我们引入了一个新的概念 groupId。我们可以通过 series.dataGroupId 将组设置为整个系列,或者通过 series.data.groupId 更精细地将组设置为每个数据所属的组。如果您使用 dataset 来管理数据,则可以使用 encode.itemGroupId 来指定一个编码为 groupId 的维度,这就更简单了。

例如,如果我们想为柱状图实现向下钻取动画,我们可以将向下钻取后的整个系列的数据设置为相同的 groupId,然后与向下钻取前的数据对应。

option = {
  xAxis: {
    data: ['Animals', 'Fruits', 'Cars']
  },
  yAxis: {},
  dataGroupId: '',
  animationDurationUpdate: 500,
  series: {
    type: 'bar',
    id: 'sales',
    data: [
      {
        value: 5,
        groupId: 'animals'
      },
      {
        value: 2,
        groupId: 'fruits'
      },
      {
        value: 4,
        groupId: 'cars'
      }
    ],
    universalTransition: {
      enabled: true,
      divideShape: 'clone'
    }
  }
};

const drilldownData = [
  {
    dataGroupId: 'animals',
    data: [
      ['Cats', 4],
      ['Dogs', 2],
      ['Cows', 1],
      ['Sheep', 2],
      ['Pigs', 1],
      ['Cows', 1],
      ['Sheep', 2],
      ['Pigs', 1]
    ]
  },
  {
    dataGroupId: 'fruits',
    data: [
      ['Apples', 4],
      ['Oranges', 2],
      ['Oranges', 2]
    ]
  },
  {
    dataGroupId: 'cars',
    data: [
      ['Toyota', 4],
      ['Opel', 2],
      ['Volkswagen', 2],
      ['Volkswagen', 2]
    ]
  }
];

myChart.on('click', event => {
  if (event.data) {
    const subData = drilldownData.find(data => {
      return data.dataGroupId === event.data.groupId;
    });
    if (!subData) {
      return;
    }
    myChart.setOption({
      xAxis: {
        data: subData.data.map(item => {
          return item[0];
        })
      },
      series: {
        type: 'bar',
        id: 'sales',
        dataGroupId: subData.dataGroupId,
        data: subData.data.map(item => {
          return item[1];
        }),
        universalTransition: {
          enabled: true,
          divideShape: 'clone'
        }
      },
      graphic: [
        {
          type: 'text',
          left: 50,
          top: 20,
          style: {
            text: 'Back',
            fontSize: 18
          },
          onclick: function() {
            myChart.setOption(option, true);
          }
        }
      ]
    });
  }
});
在线示例

使用 groupId,我们还可以实现更丰富的聚合和向下钻取动画。

数据聚合。

将单个系列向下钻取到两个系列

通用过渡将表达数据关系和演变的能力提升到一个新的水平,为您的可视化创作灵感插上翅膀。

此时,我们知道您已经迫不及待地想尝试一下了。但别担心,5.2.0 中还有更多值得一看的新特性。

调色板颜色选取策略

在上面的通用过渡示例中,您可能已经注意到我们使用了之前版本中没有的 colorBy 配置。此配置也是我们在本版本中添加的新特性,用于配置系列的不同粒度的调色板颜色选取。此配置目前支持两种策略。

  • 'series' 按系列分配调色板中的颜色,使同一系列中的所有数据都使用相同的颜色。
  • 'data' 根据数据项分配调色板中的颜色,每个数据项使用不同的颜色。

以前,我们为每种类型的系列固定了此策略,例如,柱状图固定为 'series',饼图固定为 'data'

现在,有了这个新特性,我们可以为柱状图中的每个数据项分配不同的颜色。

option = {
  xAxis: {
    type: 'category',
    data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
  },
  yAxis: {
    type: 'value'
  },
  series: [
    {
      data: [120, 200, 150, 80, 70, 110, 130],
      type: 'bar',
      colorBy: 'data'
    }
  ]
};
在线示例

或者在饼图中统一使用一种颜色。

option = {
  series: {
    type: 'pie',
    colorBy: 'series',
    radius: [0, '50%'],
    itemStyle: {
      borderColor: '#fff',
      borderWidth: 1
    },
    data: [
      {
        value: 335,
        name: 'Direct Visit'
      },
      {
        value: 234,
        name: 'Union Ad'
      },
      {
        value: 1548,
        name: 'Search Engine'
      }
    ]
  }
};
在线示例

此配置使我们无需逐个查找调色板颜色并进行设置,并且可以在特定场景中提供便利。我们稍后将进一步增强此配置,以提供更多策略。

极坐标柱状图的标签

在此版本中,我们实现了极坐标上柱状图的标签,并支持丰富的标签定位配置。以下是一个在起始点显示标签的进度图。

option = {
  angleAxis: {
    show: false,
    max: 10
  },
  radiusAxis: {
    show: false,
    type: 'category',
    data: ['AAA', 'BBB', 'CCC', 'DDD']
  },
  polar: {},
  series: [
    {
      type: 'bar',
      data: [3, 4, 5, 6],
      colorBy: 'data',
      roundCap: true,
      label: {
        show: true,
        // Try changing it to 'insideStart'
        position: 'start',
        formatter: '{b}'
      },
      coordinateSystem: 'polar'
    }
  ]
};
在线示例

更多标签位置配置。

这种灵活的标签位置配置项极大地丰富了极坐标柱状图的表现力,使文本能够清晰地匹配相应的数据。

空数据饼图样式

在之前的版本中,如果饼图中没有数据,则屏幕可能会完全空白。由于没有视觉元素,用户可能会怀疑是否有错误。

为了解决这个问题,在此版本中,当没有数据时,我们将默认显示一个灰色占位符圆圈,以防止屏幕完全空白。我们可以使用 emptyCircleStyle 配置此占位符圆圈的样式。

option = {
  series: [
    {
      type: 'pie',
      data: [],
      // showEmptyCircle: false,
      emptyCircleStyle: {
        // change the style to empty circle
        color: 'transparent',
        borderColor: '#ddd',
        borderWidth: 1
      }
    }
  ]
};
在线示例

如果您不想显示此灰色圆圈,还可以设置 showEmptyCircle: false 将其关闭。

高维数据性能增强

自 4.0 版本以来,我们引入了 dataset 来管理图表数据。但是,在某些具有特别高维度(> 100)数据的极端场景中,我们可能会遇到一些显著的性能下降,例如以下通过一千个系列可视化一千维数据的场景(#11907),甚至可能导致卡顿。

const indices = Array.from(Array(1000), (_, i) => {
  return `index${i}`;
});
const option = {
  xAxis: { type: 'category' },
  yAxis: {},
  dataset: {
    // dimension: ['date', . . indices],
    source: Array.from(Array(10), (_, i) => {
      return {
        date: i,
        ... .indices.reduce((item, next) => {
          item[next] = Math.random() * 100;
          return item;
        }, {})
      };
    })
  },
  series: indices.map(index => {
    return { type: 'line', name: index };
  })
};

此性能问题的原因是,我们根据需要在每个系列的底部处理高维数据集,并保存一份已处理的数据副本和有关数据维度的元信息。这意味着必须在示例中处理和保存 1000 x 1000 个维度,这对内存和垃圾回收造成了巨大压力,导致高维度的性能急剧下降。

在新版本中,我们优化了这个问题,以便所有系列尽可能共享数据集存储(是否共享取决于系列如何使用数据)。此优化确保内存不会随着数据集维度和系列的增长而爆炸,从而显著提高了此极端场景中的初始化性能。刚才描述的示例的渲染时间也已缩短到可接受的 300 毫秒或更短。

不仅仅是高维场景受益于此优化。当使用大量数据的 dataset 时,多个系列由于数据共享只处理一次数据,因此也可以带来显著的性能提升。

自定义系列类型优化

自定义系列提供了一种非常灵活的方式来创建系列图。与其他系列相比,自定义系列的入门门槛可能有点高。因此,在此版本中,我们进一步优化了自定义系列中核心方法 renderItem 的类型,对 renderItem 的参数和返回值类型进行了更精确的推断,从而可以根据返回的类型推断出可以设置元素的哪些属性。

series = {
  type: 'custom',
  renderItem(params) {
    return {
      type: 'group',
      // The group type uses children to store children of other types
      children: [
        {
          type: 'circle',
          // circle has the following configurable shape attributes
          shape: { r: 10, cx: 0, cy: 0 },
          // Configurable styles
          style: { fill: 'red' }
        },
        {
          type: 'rect',
          // rect has the following configurable shape properties
          shape: { x: 0, y: 0, width: 100, height: 100 }
        },
        {
          type: 'path',
          // Custom path shapes
          shape: { d: '...' }
        }
      ]
    };
  }
};

总结

如果您对 5.2.0 中的某些特性和优化感兴趣,您可能需要更新到最新版本的 Apache ECharts 并亲自尝试一下。

如果您对 Apache ECharts 的未来发展感兴趣,您还可以关注我们在 GitHub 里程碑 中的开发计划。欢迎您加入我们的贡献者行列(在 Wiki 中了解更多信息)。

完整更新日志

查看 更新日志

贡献者 在 GitHub 上编辑此页面

pissang pissangOvilia Ovilia