ECharts 創建圖表
經過前面的學習,相信大家已經在自己的項目中繪制了一個基本的 ECharts 圖表,但是前面學習只是為了讓我們更好的了解安裝的流程。所以該小節,我們將會去更加詳細的了解到 ECharts 的 創建,更新,也會通過一個案例去跟蹤分析整個過程,所謂 萬事開頭難,在這個過程中也會有許許多多需要同學們注意的地方,希望大家在學習的時候也能靜下心來完成這重要的一步。
1. 簡介
ECharts 用法非常簡單,定義圖表的基本流程是實例化 echarts
后,調用 setOption
傳入配置對象,ECharts 根據配置對象的描述渲染各類圖表、組件。本節主要講解初始配置圖表的基本流程,以及實例化后動態更新圖表內容的方法。
2. 創建圖表
2.1 創建圖表的步驟
通常,創建一個 ECharts 圖表需要執行如下步驟:
- 定義 DOM 節點作為圖表的容器;
- 調用
echarts.init
方法實例化 ECharts 對象; - 調用
echartInstance.setOption
方法傳入圖表配置。
例如:
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width,initial-scale=1.0" />
<title>Echarts Example</title>
</head>
<body>
<!-- 第一步,設定圖表容器 DOM 節點 -->
<div id="main" style="width: 600px; height: 400px;"></div>
<script src="//cdn.bootcss.com/echarts/4.5.0/echarts.js"></script>
<script type="text/javascript">
// 第二步,調用 init 函數初始化echarts對象
const myChart = echarts.init(document.getElementById('main'));
const option = {
toolbox: { feature: { saveAsImage: {} } },
xAxis: {
type: 'category',
data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'],
},
yAxis: { type: 'value' },
series: [
{
type: 'line',
data: [820, 932, 901, 934, 1290, 1330, 1320],
},
],
};
// 第三步,調用 setOption 設定圖表配置
myChart.setOption(option);
</script>
</body>
</html>
示例效果:
2.1.1 定義容器
圖表容器通常使用 div
標簽定義,調用 init
函數后 ECharts 會在節點中插入多個 canvas 或 svg 標簽,用以渲染圖表。容器節點必須具備初始寬高,常用如下形式定義:
<!-- 使用像素定義寬高 -->
<div id="main" style="width: 600px; height:450px"></div>
<!-- 也支持使用百分比定義寬高 -->
<div id="main" style="width: 100%; height:450px"></div>
提示:
ECharts 3 之后支持直接使用 canvas 元素作為容器,這樣繪制完圖表可以直接將 canvas 作為圖片應用到其它地方,例如在 WebGL 中作為貼圖,這跟使用
echartsInstance.getDataURL
生成圖片鏈接相比可以支持圖表的實時刷新。但缺點是無法根據組件的
z
屬性將組件分離渲染到不同的 canvas 上,所以有一定的性能損失。
2.1.2 echarts.init
接口
echarts.init
函數用于創建 ECharts 對象,不能在同一個容器上多次調用,函數簽名:
(dom: HTMLDivElement|HTMLCanvasElement, theme?: Object|string, opts?: {
devicePixelRatio?: number
renderer?: string
width?: number|string
height? number|string
}) => ECharts
函數參數:
dom
: 指定容器節點,通常為div
標簽的 DOM 對象,例如上例中的document.getElementById('main')
;theme
: 圖表主題,支持傳入主題配置對象或主題名稱。配置對象可參考 light 主題源碼;ECharts 默認內置light
、dark
兩種主題,用戶也可以通過echarts.registerTheme
接口注冊自定義主題;opts
: 附加參數,支持配置屬性:
{
// 設備像素比,默認取瀏覽器的值 `window.devicePixelRatio`
devicePixelRatio: Number,
// 渲染器,支持 canvas 或 svg
renderer: String,
// 可顯式指定實例寬度,單位為像素。如果傳入值為 null/undefined/'auto',則取 dom(實例容器)的寬度。
width: Number|String|null|undefined,
// 可顯式指定實例高度,單位為像素。如果傳入值為 null/undefined/'auto',則取 dom(實例容器)的高度。
height: Number|String|null|undefined,
}
提示:
容器的寬高應通過節點的樣式定義,盡量避免通過
opts
屬性定義,這樣在調用init
前后,作為容器的 DOM 的尺寸才能保持一致。
2.1.3 echartInstance.setOption
接口
實例化 ECharts 對象后,需調用 echartInstance.setOption
接口傳入圖表配置,接口簽名:
(option: Object, notMerge?: boolean, lazyUpdate?: boolean) => void
// 或
(option: Object, opts?: Object) => void
第一種形態的參數為:
option
: 圖表實例配置;notMerge
: 可選,默認false
,用于設定是否與之前提供的配置合并;lazyUpdate
: 可選,默認false
,是否延遲更新。
第二種形態的 opts
參數接受如下對象:
{
// 默認 `false`,用于設定是否與之前提供的配置合并
notMerge: ...,
// 默認 `false`,是否延遲更新
lazyUpdate: ...,
// 默認 `false`,指定圖表更新時是否觸發事件
silent: ...
}
其中 option
對象是必不可少的,用于描述開發者對圖表的各項需求,包括使用什么組件、有什么數據、使用什么圖表、圖表有哪些操作等等。
2.2 配置對象概述
ECharts 是配置驅動的圖表框架,主要功能都以配置對象形式聲明,某種程度上可以說 ECharts 的應用都是圍繞配置對象展開的。配置對象結構如下:
{
title: object,
legend: object,
grid: object,
xAxis: object,
yAxis: object,
polar: object,
radiusAxis: object,
angleAxis: object,
radar: object,
dataZoom: array,
visualMap: array,
tooltip: object,
axisPointer: object,
toolbox: object,
brush: object,
geo: object,
parallel: object,
parallelAxis: object,
singleAxis: object,
timeline: object,
graphic: object,
calendar: object,
dataset: object,
aria: object,
series: array,
color: array,
backgroundColor: string,
textStyle: object,
animation: boolean,
animationThreshold: number,
animationDuration: number,
animationEasing: string,
animationDelay: number,
animationDurationUpdate: number,
animationEasingUpdate: string,
animationDelayUpdate: number,
blendMode: string,
hoverLayerThreshold: number,
useUTC: boolean,
}
ECharts 的使用方法絕大部分都圍繞著這些屬性展開。根據配置作用,可以將上述屬性劃分為以下幾類:
- 圖表配置,對應
series
項。該屬性接受一個數組值,每個數組項對應一個圖表,數組項至少需要提供type
屬性用于指定圖表類型; - 組件配置,包括
title
、radar
、toolbox
等項。ECharts 將圖表的常見的各項元素按功能組織為組件形式,組件與圖表通常是松耦合的,可以應用到多個圖表上; - 基礎配置,包括
textStyle
、color
、animation
等項。用以設定實例的基本運行邏輯。
2.2.1 圖表配置
ECharts 的圖表由 series
數組指定。series
數組項接受對象形式,每個數組項對應一個圖表,數組項通過 type
聲明圖表類型;通過 data
數組聲明圖表數據序列。比如:
series: [
{
data: [820, 932, 901, 934, 1290, 1330, 1320],
type: 'bar',
},
{
data: [620, 1032, 401, 624, 690, 730, 1420],
type: 'line',
},
],
上述配置會渲染出一個柱形圖,一個折線圖,效果:
不同類型圖表的配置結構有較大區別,詳情請參考 官網
Tips:
圖表大致上可以分為以下幾類:
- 直角坐標系圖表,包括
line
、bar
、scatter
、boxplot
等;- 極坐標系圖表,包括
line
、bar
、scatter
;- 日歷坐標系圖表,支持
heatmap
、scatter
、graph
;- 地圖坐標系圖表,包括
heatmap
、scatter
等;- 無坐標系,不依賴于坐標系環境的圖表,包括:
funnel
、pie
、treemap
、graph
等。上例圖表依賴于坐標系組件,必須同時提供圖表系列與坐標系的定義才能正常運行。對于這部分圖表,坐標系相當于一個容器,圖表的數據語義、渲染、邊界都由坐標系控制。第 5 類圖表則更加內聚,不依賴于其他組件。
2.2.2 組件配置
ECharts 組件大致可分為:
- 坐標系與坐標軸組件,這類組件直接參與到圖表定位、渲染過程中,包括
grid
、xAxis
、radar
、calendar
、polar
等; - 信息增強組件,用以傳達關于圖表的更多信息,包括
title
、tooltip
、axisPointer
; - 交互增強組件,為使用者提供改變圖表視圖的交互能力,包括
toolbox
、legend
、visualMap
、dataZoom
、brush
、timeline
。
Tips:
部分組件不能單獨使用,需要與關聯組件同時配置才能生效,比如grid
+xAxis
+yAxis
組合用于配置直角坐標系;polar
+angleAxis
+radiusAxis
組合用于配置極坐標系。
2.2.3 基礎配置
許多配置項在設計上具有多級繼承的特性,方法是在組件、圖表的配置上設定相同的同名屬性。運行時,ECharts 會沿著配置鏈路逐層向下 merge,比如 textStyle
屬性,在 title
配置項中有同名屬性 title.textStyle
,在下例中:
const option = {
textStyle: { color: '#fff', fontStyle: 'normal' },
title: {
textStyle: { color: '#eee' },
},
};
最終作用在 title
上的 textStyle
值等于 merge(option.textStyle, option.title.textStyle)
,結果為:
title.textStyle = {
// 屬性值來自 option.title.textStyle.color
color: '#eee',
// 屬性值來自 option.textStyle.fontStyle
fontStyle: 'normal',
};
Tips:
繼承能力能夠實現配置項的復用,當某些配置值能夠在多個組件、圖表上復用時,應該提出來在更上層作為基礎配置,可有效減少冗余配置項。
3. 圖表的更新
3.1 配置更新
在實例運行過程中可多次調用 setOption
接口,實現動態更新圖表。接口能夠對先后提供的多個 option
參數做 merge 操作,達到增量更新的效果,例如上面示例中,首次提供的配置項為:
const option = {
toolbox: { feature: { saveAsImage: {} } },
xAxis: {
type: 'category',
data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'],
},
yAxis: { type: 'value' },
series: [
{
type: 'line',
data: [820, 932, 901, 934, 1290, 1330, 1320],
},
],
};
myChart.setOption(option);
后續更新時,只需要提供發生變動的那一部分的配置項, setOption
會對前后配置對象做 merge 計算,例如:
const partialOption = {
series: [{ data: [1280, 762, 901, 934, 1290, 1330, 1320] }],
};
myChart.setOption(partialOption);
// merge 后的結果:
{
toolbox: { feature: { saveAsImage: {} } },
xAxis: {
type: 'category',
data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'],
},
yAxis: { type: 'value' },
series: [
{
type: 'line',
// 原始配置
// data: [820, 932, 901, 934, 1290, 1330, 1320],
// merge 后的series數組
data: [1280, 762, 901, 934, 1290, 1330, 1320]
},
],
}
Tips:
當lazyUpdate
為true
時,圖表不會立刻被更新渲染,ECharts 會等待下一幀發生時再嘗試執行更新,幀的調度由 zepto 實現,底層依賴requestAnimationFrame
接口,詳情可參考 源碼。
merge
特性實現了形式上的部分更新,ECharts 底層執行的實際上是清空圖表之后重新渲染所有組件、圖表 —— 即使我們只是修改了配置上的一小部分。這種處理模型的背后是 ECharts 將配置對象視作原子對象,每次調用 setOption
接口都被認為是一個全新的配置對象,不對之前的渲染結果做任何復用。ECharts 提供了另一個性能更佳,但功能受限的更新接口: appendData
,詳情請參考下一節。
3.2 數據增量更新
echartInstance.appendData
接口用于向已有的數據序列追加更多數據項,接口調用后不會改變任何已渲染的組件、圖表,只會在對應圖表上追加數據圖案,性能更佳。appendData
接口簽名:
(opts: {
// 要增加數據的系列序號。
seriesIndex?: string,
// 增加的數據。
data?: Array | TypedArray,
}) => void;
Tips:
官網文檔 顯示的返回值是string
,但實測幾個版本都返回undefined
,不知是不是接口與文檔沒有同步更新好。
基礎示例:
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width,initial-scale=1.0" />
<title>Echarts Example</title>
</head>
<body>
<div id="main" style="width: 600px; height: 400px;"></div>
<script src="//cdn.bootcss.com/echarts/4.5.0/echarts.js"></script>
<script type="text/javascript">
const myChart = echarts.init(document.getElementById('main'));
let len = 0;
const random = (min = 0, max = 180) => Math.round(Math.random() * (max - min) + min);
const range = (from, to) => {
const result = [];
for (let i = from; i <= to; i++) {
result.push(`X-${i}`);
}
return result;
};
const option = {
// appendData 接口不會更改初始渲染后的任何組件
// 所以需要預留足夠的空間來容納追加的圖案
xAxis: { type: 'category', data: range(1, 20) },
// 同樣的,在 y 軸上也需要設定好序列的最大值,以容納追加的圖案
yAxis: { type: 'value', max: 180 },
series: [
{
type: 'bar',
data: [random(), random()],
},
],
};
myChart.setOption(option);
setInterval(() => {
myChart.appendData({ seriesIndex: 0, data: [random()] });
}, 300);
</script>
</body>
</html>
示例中調用 setInterval
不斷追加數據項,效果:
appendData
有一個很大的限制 —— 它不會改變任何已經渲染好的圖形元素,比如上例在渲染追加圖表項時,即使坐標軸預定的數值范圍無法容納新增的數據,ECharts 也不會對坐標軸做任何變動,因此在上述示例需要在 xAxis
、yAxis
配置上預留足夠的空間來容納追加的數據。
Tips:
這個限制導致appendData
接口對坐標系圖表來說特別雞肋,實用性低,甚至在官網提供的實例也很少見到appendData
的用例。一個變通方法是混合使用setOption
與appendData
,例如在直角坐標系中,用額外的變量記錄當前 x、y 軸的最大最小值,如果新增的數值超出這個范圍的時候就通過setOption
更新圖表;否則盡量使用appendData
。
appendData
在地圖散點圖上表現的很好,但其他場景上限制多功能弱,帶來的問題多過便利,所以多數情況下都會退化為使用setOption
接口維護數據狀態。
此外,appendData
還有如下限制:
- 只能應用在少數圖表類型上,目前支持: 散點圖(scatter)、線圖(line)、柱形圖(bar);ECharts GL 版本的 散點圖(scatterGL)、線圖(linesGL) 和 可視化建筑群(polygons3D)。
- 不兼容 dataset,使用
appendData
時圖表的數據只能通過series.data
定義。
Tips:
除
setOption
、appendData
外,Echarts 沒有再提供其他維護數據內容的接口,數據的刪除、插入、更改都沒有官方推薦的方法,需要開發者自行處理。
3.3 寬度自適應
ECharts 圖表不具備響應式特性,初次渲染后不會因為容器尺寸的變化做自適應調節,需要用戶自行監聽屏幕尺寸的變化,并隨之調用 resize
函數,函數簽名:
(opts?: { width?: number | string, height?: number | string, silent?: boolean }) => ECharts;
參數:
width
: 顯式指定實例寬度,單位為像素。如果傳入值為null/undefined/'auto'
,則表示自動取 dom(實例容器)的寬度;height
: 顯式指定實例高度,單位為像素。如果傳入值為null/undefined/'auto'
,則表示自動取 dom(實例容器)的高度;silent
: 是否禁止拋出事件。
為了實現圖表元素響應屏幕尺寸的變化,通??梢约尤肴缦麓a片段:
window.addEventListener('resize', myChart.resize);
增加上述代碼片段后,在 SPA 場景下,當圖表隨頁面跳轉而析構后務必移除對應的事件監聽,否則 ECharts 實例對象會一直被事件系統保留引用,導致內存泄漏!但是 ECharts 并沒有暴露示例的析構事件,處理時機只能由開發者自行把握,以
vue
為例,推薦的用法:Vue.component('HelloWorld', { mounted() { this._ec = echarts.init(xxx); window.addEventListener('resize', this._ec.resize); }, beforeDestroy() { window.removeEventListener('resize', this._ec.resize); }, });
4. 小結
本節主要講述 Echarts 圖表常規的創建、配置、動態更新方法,這些是 Echarts 最常用的幾類接口,大多數應用都圍繞著他們展開,讀者應多加學習,為正式環境應用打下堅實基礎。