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 系列實現帶有漣漪特效的動態散點圖,使圖表更有趣,也起到突出高亮的作用。和載入動畫一樣,開發者也經常提出需要更多的動畫效果。現在我們可以透過在 custom 自定義系列中使用關鍵幀動畫來實現更復雜的效果。

例如,下面的例子用跳動效果和漣漪動畫,為自定義系列在 SVG 地圖上繪製的圖釘添加了動畫。

fetch(
  'https://echarts.apache.tw/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 團隊的沈毅編寫了一個 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 倍的效能提升,在一些特殊場景下甚至能達到幾十倍。

以前,我們從渲染佇列直接更新 SVG 渲染器到 DOM,但由於 ZRender 的圖形屬性與 DOM 不是一一對應的,我們不得不在中間實現非常複雜的 diff 邏輯,這很容易出錯,並且在某些場景下效能也不是最好的。在這個版本中,我們首先將完整渲染重構為 VDOM,然後將 VDOM patch 到 DOM 來完成渲染。完整渲染避免了複雜 diff 邏輯可能帶來的潛在錯誤,而 VDOM 和 DOM 之間的一一對應關係確保了在 patch 時更新是最小的,從而帶來了巨大的效能提升。

這個例子讓你對效能提升有一個直觀的印象。新版本在 SVG 模式下拖動圖表時比以前的版本要流暢得多。

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

除了效能提升,我們還可以用渲染出的 VDOM 做更多的事情,比如下面要介紹的服務端渲染。

零依賴的服務端渲染

在以前的版本中,ECharts 也可以實現服務端渲染,但必須依賴 node-canvas,或者如果你使用 SVG 模式則需要 JSDOM 來模擬 DOM 環境。這些依賴不僅帶來了額外的體積和使用要求,也影響了效能。

這個新的 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 計算出合適的顯示區域,並將經緯度線性地對映到該區域。這是最簡單的地圖投影方式。然而,簡單的線性投影對於複雜的地圖場景並不適用,比如使用阿爾伯斯投影來解決線性投影中的畸變問題,或者讓世界地圖以太平洋為中心等等。

因此,在 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]
    }
  ]
};
線上示例

停用高亮(emphasis)和選中(select)狀態

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

柱狀圖 large 模式最佳化

在資料量很大(> 2k)的情況下,我們支援柱狀圖透過開啟 large 模式來加快渲染速度,提升互動效能。但以前 large 模式下柱狀圖的佈局很簡單,不支援多個系列堆疊後的佈局。在 5.3.0 中,我們優化了 large 模式的佈局,使其與普通模式保持一致。我們可以透過開啟 large 來在更多場景下最佳化柱狀圖的效能。

此外,最佳化後的柱狀圖佈局還修復了在對數軸等非線性軸上堆疊不正確的錯誤。

不相容改動

registerMap 和 getMap 方法需要在引入地圖圖表後才能使用

為了減小最小打包體積,我們從核心模組中移除了地圖資料管理方法 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 上編輯此頁

Oviliapissangplainheart