Apache ECharts 5.3.0 特性

Apache ECharts 5.3.0 在动画表现力、渲染性能和服务端渲染方面进行了重大改进。 它还添加了社区期待已久的功能,例如多轴刻度的自动对齐、提示框值格式化和地图投影。

关键帧动画

之前,ECharts 动画主要集中在创建、更新和删除元素的过渡动画,这些动画通常只有开始状态和结束状态。 为了表达更复杂的动画,我们在 5.3.0 中为自定义系列和图形组件引入了新的关键帧动画。

这是一个通过关键帧动画实现的简单呼吸动画效果

option = {
  graphic: {
    type: 'circle',
    shape: { r: 100 },
    left: 'center',
    top: 'center',
    keyframeAnimation: [
      {
        duration: 3000,
        loop: true,
        keyframes: [
          {
            percent: 0.5,
            easing: 'sinusoidalInOut',
            scaleX: 0.1,
            scaleY: 0.1
          },
          {
            percent: 1,
            easing: 'sinusoidalInOut',
            scaleX: 1,
            scaleY: 1
          }
        ]
      }
    ]
  }
};
实时

在关键帧动画中,您可以配置动画的持续时间、延迟、缓动、是否循环,以及每个关键帧的位置、缓动和图形属性。 您还可以同时为每个元素设置多个具有不同配置的关键帧动画。 灵活的配置使我们能够实现非常复杂的动画效果,以下是可以使用关键帧动画的几种场景。

自定义加载动画

ECharts 默认有一个内置的加载动画,可以通过调用 showLoading 来显示。 社区中经常会有人询问更多的加载动画效果。 现在,使用关键帧动画,我们可以使用带有关键帧动画的 graphic 组件来实现我们想要的任何加载动画效果。

这是一个文本描边动画的示例。

option = {
  graphic: {
    elements: [
      {
        type: 'text',
        left: 'center',
        top: 'center',
        style: {
          text: 'Apache ECharts',
          fontSize: 40,
          fontWeight: 'bold',
          lineDash: [0, 200],
          lineDashOffset: 0,
          fill: 'transparent',
          stroke: '#000',
          lineWidth: 1
        },
        keyframeAnimation: {
          duration: 3000,
          loop: true,
          keyframes: [
            {
              percent: 0.7,
              style: {
                fill: 'transparent',
                lineDashOffset: 200,
                lineDash: [200, 0]
              }
            },
            {
              // Stop for a while.
              percent: 0.8,
              style: {
                fill: 'transparent'
              }
            },
            {
              percent: 1,
              style: {
                fill: 'black'
              }
            }
          ]
        }
      }
    ]
  }
};
实时

或为柱子添加动画。

const columns = [];
for (let i = 0; i < 7; i++) {
  columns.push({
    type: 'rect',
    x: i * 20,
    shape: {
      x: 0,
      y: -40,
      width: 10,
      height: 80
    },
    style: {
      fill: '#5470c6'
    },
    keyframeAnimation: {
      duration: 1000,
      delay: i * 200,
      loop: true,
      keyframes: [
        {
          percent: 0.5,
          scaleY: 0.1,
          easing: 'cubicIn'
        },
        {
          percent: 1,
          scaleY: 1,
          easing: 'cubicOut'
        }
      ]
    }
  });
}
option = {
  graphic: {
    elements: [
      {
        type: 'group',
        left: 'center',
        top: 'center',
        children: columns
      }
    ]
  }
};
实时

扩展图表中更丰富的动画效果

带有动画效果的散点图一直是 ECharts 的一个特点。 开发人员可以使用 effectScatter 系列来实现带有涟漪效果的动态散点图,这使图表更有趣,也用于突出显示用户。 与加载动画一样,开发人员经常要求更多的动画效果。 现在,我们可以在 自定义 系列中使用关键帧动画来实现更复杂的效果。

例如,以下示例使用跳跃效果动画显示自定义系列在 SVG 地图上绘制的图钉,并带有涟漪动画。

fetch(
  'https://fastly.jsdelivr.net/gh/apache/echarts-website@asf-site/examples/data/asset/geo/Map_of_Iceland.svg'
)
  .then(response => response.text())
  .then(svg => {
    echarts.registerMap('iceland_svg', { svg: svg });
    option = {
      geo: {
        map: 'iceland_svg',
        left: 0,
        right: 0
      },
      series: {
        type: 'custom',
        coordinateSystem: 'geo',
        geoIndex: 0,
        zlevel: 1,
        data: [
          [488, 459, 100],
          [770, 757, 30],
          [1180, 743, 80],
          [894, 1188, 61],
          [1372, 477, 70],
          [1378, 935, 81]
        ],
        renderItem(params, api) {
          const coord = api.coord([
            api.value(0, params.dataIndex),
            api.value(1, params.dataIndex)
          ]);

          const circles = [];
          for (let i = 0; i < 5; i++) {
            circles.push({
              type: 'circle',
              shape: {
                cx: 0,
                cy: 0,
                r: 30
              },
              style: {
                stroke: 'red',
                fill: 'none',
                lineWidth: 2
              },
              // Ripple animation
              keyframeAnimation: {
                duration: 4000,
                loop: true,
                delay: (-i / 4) * 4000,
                keyframes: [
                  {
                    percent: 0,
                    scaleX: 0,
                    scaleY: 0,
                    style: {
                      opacity: 1
                    }
                  },
                  {
                    percent: 1,
                    scaleX: 1,
                    scaleY: 0.4,
                    style: {
                      opacity: 0
                    }
                  }
                ]
              }
            });
          }
          return {
            type: 'group',
            x: coord[0],
            y: coord[1],
            children: [
              ...circles,
              {
                type: 'path',
                shape: {
                  d:
                    'M16 0c-5.523 0-10 4.477-10 10 0 10 10 22 10 22s10-12 10-22c0-5.523-4.477-10-10-10zM16 16c-3.314 0-6-2.686-6-6s2.686-6 6-6 6 2.686 6 6-2.686 6-6 6z',
                  x: -10,
                  y: -35,
                  width: 20,
                  height: 40
                },
                style: {
                  fill: 'red'
                },
                // Jump animation.
                keyframeAnimation: {
                  duration: 1000,
                  loop: true,
                  delay: Math.random() * 1000,
                  keyframes: [
                    {
                      y: -10,
                      percent: 0.5,
                      easing: 'cubicOut'
                    },
                    {
                      y: 0,
                      percent: 1,
                      easing: 'bounceOut'
                    }
                  ]
                }
              }
            ]
          };
        }
      }
    };

    myChart.setOption(option);
  });
实时

加载 Lottie 动画

为了充分利用新的关键帧动画的功能,ECharts 团队的 Yi Shen 编写了一个 Lottie 动画解析库,该库可以将 Lottie 动画文件解析为 ECharts 图形格式以进行渲染。 结合 Lottie 的表现力,我们可以为我们的项目引入更多令人惊叹的动画。

图形组件过渡动画

我们在 5.0 中为自定义系列中返回的元素提供了更灵活的过渡动画配置。transitionenterFromleaveTo 配置项允许您配置每个元素的哪些属性将具有过渡动画,以及在创建和删除图形时如何进行动画处理。 这是一个例子。

function renderItem() {
  //...
  return {
    //...
    x: 100,
    // 'style', 'x', 'y' will be animated
    transition: ['style', 'x', 'y'],
    enterFrom: {
      style: {
        // Fade in
        opacity: 0
      },
      // Fly in from the left
      x: 0
    },
    leaveTo: {
      // Fade out
      opacity: 0
    },
    // Fly out to the right
    x: 200
  };
}

在 5.3.0 中,我们将这些过渡动画的配置扩展到了图形组件,并进行了额外的增强。

如果您不想写出每个要动画的属性,您现在可以直接配置 transition: 'all' 来动画所有属性。

我们还添加了 enterAnimationupdateAnimationleaveAnimation 来分别配置每个图形的进入、更新和退出动画的 durationdelayeasing。 渐变颜色现在也支持动画。

新的 SVG 渲染器

在 5.3.0 中,我们重构了我们的 SVG 渲染器,它提供了 2 倍 ~ 10 倍的性能提升,在某些特殊场景下甚至可以达到数十倍。

之前,我们直接从渲染队列到 DOM 更新 SVG 渲染器,但由于 zrender 的图形属性与 DOM 不是一一对应的,因此我们必须在中间实现非常复杂的 diff 逻辑,这很容易出错并且在某些情况下无法提供最佳性能。 在此版本中,我们首先将完整渲染重建为 VDOM,然后将 VDOM 补丁到 DOM 以完成渲染。 完整渲染避免了复杂的 diff 逻辑可能引起的潜在错误,并且 VDOM 和 DOM 之间的一一对应关系确保了在修补时更新最小,从而带来了巨大的性能提升。

此示例让您直观地了解性能的提升。 在 SVG 模式下拖动图表时,新版本比之前的版本流畅得多。

5.2.2 (之前) 5.3.0 (之后)
before after

除了性能提升之外,我们还可以使用渲染的 VDOM 做更多的事情,例如服务器端渲染,这将在下面描述。

零依赖的服务器端渲染

在之前的版本中,ECharts 也可以实现服务器端渲染,但它必须依赖 node-canvas,或者如果您使用 SVG 模式来模拟 DOM 环境,则依赖 JSDOM。 这些依赖不仅带来了额外的尺寸和使用要求,而且还影响了性能。

这个新的 SVG 渲染器允许我们从中间渲染的 VDOM 获取字符串,从而带来完全零依赖的服务器端渲染,并输出更精细的集成 CSS 动画的 SVG 字符串。

const echarts = require('echarts');

// In SSR mode the first parameter does not need to be passed in as a DOM object
const chart = echarts.init(null, null, {
  renderer: 'svg', // must use SVG mode
  ssr: true, // enable SSR
  width: 400, // need to specify height and width
  height: 300
});

// setOption as normal
chart.setOption({
  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'
    }
  ]
});

// Output string
const svgStr = chart.renderToSVGString();

自定义地图投影

地图一直是 ECharts 中非常广泛使用的组件。 通常,地图组件使用 GeoJSON 格式的数据,其中存储了纬度和经度。 然后,ECharts 计算适当的显示区域,并将纬度和经度线性映射到该区域。 这是投影地图的最简单方法。 但是,简单的线性投影不适用于复杂的地图场景,例如使用 Albers 投影来解决线性投影中的失真问题,或者将太平洋放在世界地图的中间等等。

因此,在 5.3.0 中,我们引入了扩展地图投影。 它通过 projectunproject 方法告诉 ECharts 如何投影坐标以及如何从投影坐标计算纬度和经度。 以下是使用墨卡托投影的简单示例。

series = {
  type: 'map',
  projection: {
    project: point => [
      (point[0] / 180) * Math.PI,
      -Math.log(Math.tan((Math.PI / 2 + (point[1] / 180) * Math.PI) / 2))
    ],
    unproject: point => [
      (point[0] * 180) / Math.PI,
      ((2 * 180) / Math.PI) * Math.atan(Math.exp(point[1])) - 90
    ]
  }
};

除了实现我们自己的投影公式外,我们还可以使用第三方库(如 d3-geo)提供的投影实现。

const projection = d3.geoConicEqualArea();
// ...
series = {
  type: 'map',
  projection: {
    project: point => projection(point),
    unproject: point => projection.invert(point)
  }
};

结合 5.2 中添加的新的全局过渡动画功能,我们可以动画显示不同投影效果之间的过渡:!

map-projection-animation

除了地图投影外,我们还在本版本中对地图进行了以下两项增强。

  • 为 GeoJSON 数据提供了 'LineString''MultiLineString' 支持。
  • 将默认标签位置的计算从边界框的中心更改为最大区域的质心,以获得更准确的结果。

多轴刻度对齐

多轴刻度对齐是社区中长期存在的要求,我们可以在社区中看到许多关于如何在 ECharts 中实现轴对齐的文章,这通常很麻烦并且有限制。

在 5.3.0 中,我们最终引入了对齐 'value''log' 轴上的刻度的功能。 您可以在需要对齐的轴中配置 alignTicks: true。 然后,轴将根据第一个轴的刻度调整其自身的刻度,从而实现自动对齐。

option = {
  tooltip: {
    trigger: 'axis'
  },
  legend: {},
  xAxis: [
    {
      type: 'category',
      data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'],
      axisPointer: {
        type: 'shadow'
      }
    }
  ],
  yAxis: [
    {
      type: 'value',
      name: 'Precipitation',
      alignTicks: true,
      axisLabel: {
        formatter: '{value} ml'
      }
    },
    {
      type: 'value',
      name: 'Temperature',
      axisLabel: {
        formatter: '{value} °C'
      }
    }
  ],
  series: [
    {
      name: 'Evaporation',
      type: 'bar',
      // prettier-ignore
      data: [2.0, 4.9, 7.0, 23.2, 25.6, 76.7, 135.6, 162.2, 32.6, 20.0, 6.4, 3.3]
    },
    {
      name: 'Precipitation',
      type: 'bar',
      // prettier-ignore
      data: [2.6, 5.9, 9.0, 26.4, 28.7, 70.7, 175.6, 182.2, 48.7, 18.8, 6.0, 2.3]
    },
    {
      name: 'Temperature',
      type: 'line',
      yAxisIndex: 1,
      data: [2.0, 2.2, 3.3, 4.5, 6.3, 10.2, 20.3, 23.4, 23.0, 16.5, 12.0, 6.2]
    }
  ]
};
实时

禁用高亮和选中状态

ECharts 中的 emphasis 状态在鼠标悬停在元素上时为用户提供反馈,但在包含大量元素的图表中,高亮动画可能会导致性能问题。 特别是,由 tooltiplegend 组件联动触发的高亮可以同时高亮显示多个元素。

因此,在此版本中,我们添加了 emphasis.disabled 配置。 如果您不需要高亮反馈并且您担心交互性,您可以使用此选项禁用 emphasis 状态。

对于 select 状态,我们还添加了 select.disabled。 此选项可用于配置某些数据为不可选。

支持选择整个系列

从 5.3.0 开始,我们支持将 selectedMode 配置为 'series' 以启用选择系列中的所有数据。

提示框中的值格式化

当用户悬停在数据项上时,提示框可用于显示有关数据项的更多详细信息。 ECharts 还提供了 formatter 回调函数,使开发人员可以更灵活地自定义提示框的内容。

但是,我们发现大多数情况下,开发人员只需要格式化提示框的值部分,例如固定精度、以 $ 为前缀等。 以前,为了格式化数字,开发人员必须使用 formatter 重写提示框的整个内容。 特别是在 5.0 之后,ECharts 提示框变得更加复杂和美观,因此重写它们变得成本高昂且难以实现默认结果。

因此,在此版本中,我们为提示框添加了 valueFormatter 配置,用于格式化值部分。

与轴对齐示例一样,我们可以在提示框的值部分添加 °C 和 ml 后缀。

option = {
  tooltip: {
    trigger: 'axis'
  },
  legend: {},
  xAxis: [
    {
      type: 'category',
      data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'],
      axisPointer: {
        type: 'shadow'
      }
    }
  ],
  yAxis: [
    {
      type: 'value',
      name: 'Precipitation',
      alignTicks: true,
      axisLabel: {
        formatter: '{value} ml'
      }
    },
    {
      type: 'value',
      name: 'Temperature',
      axisLabel: {
        formatter: '{value} °C'
      }
    }
  ],
  series: [
    {
      name: 'Evaporation',
      type: 'bar',
      tooltip: {
        valueFormatter: value => value + ' ml'
      },
      // prettier-ignore
      data: [2.0, 4.9, 7.0, 23.2, 25.6, 76.7, 135.6, 162.2, 32.6, 20.0, 6.4, 3.3]
    },
    {
      name: 'Precipitation',
      type: 'bar',
      tooltip: {
        valueFormatter: value => value + ' ml'
      },
      // prettier-ignore
      data: [2.6, 5.9, 9.0, 26.4, 28.7, 70.7, 175.6, 182.2, 48.7, 18.8, 6.0, 2.3]
    },
    {
      name: 'Temperature',
      type: 'line',
      yAxisIndex: 1,
      tooltip: {
        valueFormatter: value => value + ' °C'
      },
      data: [2.0, 2.2, 3.3, 4.5, 6.3, 10.2, 20.3, 23.4, 23.0, 16.5, 12.0, 6.2]
    }
  ]
};
实时

每个系列都可以根据自身的值格式配置自己的 valueFormatter

更灵活的扇形角半径

在 5.0 中,我们为扇形添加了圆角配置,这可以使饼图和旭日图更有趣。 之前,我们仅单独支持内半径和外半径,这次我们更进一步,支持配置扇形的四个角具有不同的角半径,从而带来更灵活的显示。

option = {
  tooltip: {
    trigger: 'item'
  },
  legend: {
    top: '5%',
    left: 'center'
  },
  series: [
    {
      name: 'Access From',
      type: 'pie',
      radius: ['30%', '70%'],
      roseType: 'angle',
      itemStyle: {
        borderRadius: [20, 5, 5, 10],
        borderColor: '#fff',
        borderWidth: 2
      },
      label: {
        show: false
      },
      data: [
        { value: 800, name: 'Search Engine' },
        { value: 735, name: 'Direct' },
        { value: 580, name: 'Email' },
        { value: 484, name: 'Union Ads' },
        { value: 400, name: 'Video Ads' }
      ]
    }
  ]
};
实时

饼图的复杂标签优化

饼图一直是 ECharts 中标签显示最复杂的图表之一。我们长期以来一直在优化饼图标签的布局和显示。

这次,我们对使用文本换行、背景颜色、富文本和其他复杂布局的饼图标签进行了深度优化。在自适应宽度、容器溢出、引导线计算等方面,都比以前有了更好的效果。

5.2.2 (之前) 5.3.0 (之后)
before after
before after

柱状图大型模式优化

在数据量较大(> 2k)的情况下,我们支持通过开启 large 模式来加速柱状图的渲染并提高交互性能。但之前,large 模式下柱状图的布局比较简单,不支持多个系列堆叠后的布局。在 5.3.0 中,我们优化了 large 模式的布局,使其与正常模式保持一致。通过开启 large,我们可以在更多场景下优化柱状图的性能。

此外,优化的柱状图布局还修复了在非线性轴(如对数轴)上堆叠不正确的错误。

重大变更

registerMapgetMap 方法需要在导入地图图表后才能使用

为了减小最小包的大小,我们从核心模块中移除了地图数据管理方法 getMapregisterMap

如果您是只导入必要的图表和组件,您需要确保在可以使用 registerMap 注册地图数据之前,已经导入了 GeoComponentMapChart

import * as echarts from 'echarts/core';
import { MapChart } from 'echarts/charts';

echarts.use([MapChart]);

// You must import the MapChart with the `use` method before you can register the map with registerMap
echarts.registerMap('world', worldJSON);

如果您使用 import * as echarts from 'echarts' 导入整个包,此更改不会对您产生任何影响。

移除折线图中默认的加粗强调效果

我们在 5.0 版本中为折线图引入了默认的加粗强调效果,但社区反馈说,在很多场景下,这种效果看起来并不好。因此,在这个版本中,我们将此效果从默认开启改为默认关闭。您可以通过以下方式启用它:

series = {
  type: 'line',
  //...
  emphasis: {
    lineStyle: {
      width: 'bolder'
    }
  }
};

完整更新日志

查看更新日志

贡献者 在 GitHub 上编辑此页面

Ovilia Oviliapissang pissangplainheart plainheart