ECharts 自定義系列

2018-09-25 09:49 更新

什么是 ECharts 自定義系列?


自定義系列(custom series)是一種系列的類型,它能夠讓用戶定制渲染邏輯,在已有的坐標系中創(chuàng)造新的圖表;并且增強了極坐標柱狀圖、自定義維度映射、dataZoom 等 。

自定義系列(custom series)它把繪制圖形元素這一步留給開發(fā)者去做,從而開發(fā)者能在坐標系中自由繪制出自己需要的圖表。

ECharts 為什么會支持自定義系列?

由于圖表的類型多種多樣,要讓 ECharts 內(nèi)置支持所有類型的圖表是很難的,有很多小眾的需求 ECharts 并不能內(nèi)置的支持。那么就需要提供一種方式來讓開發(fā)者自己擴展。另一方面,所提供的擴展方式要盡可能得簡單,例如圖形元素創(chuàng)建和釋放、過渡動畫、tooltip、數(shù)據(jù)區(qū)域縮放(dataZoom)、視覺映射(visualMap)等功能,盡量在 ECharts 中內(nèi)置得處理,使開發(fā)者不必糾結于這些細節(jié)。綜上考慮形成了 自定義系列(custom series)。

例如,下面的例子使用自定義系列( custom series)擴展出了 x-range 圖:

ECharts 使用自定義系列擴展 x-range 圖

點擊編輯實例 》》

下面來介紹開發(fā)者怎么使用自定義系列(custom series)。

(一)renderItem 方法


通過書寫 renderItem 函數(shù)能夠讓開發(fā)者實現(xiàn)自定義的圖形元素渲染邏輯,例如:

var option = {
    ...,
    series: [{
        type: 'custom',
        renderItem: function (params, api) {
            // ...
        },
        data: data
    }]
}

在渲染階段,對于 series.data 中的每個數(shù)據(jù)項(為方便描述,這里稱為 dataItem),會調(diào)用此 renderItem 函數(shù)。

renderItem 函數(shù)的作用:返回一個(或者一組)圖形元素定義,圖形元素定義了圖形元素的類型、位置、尺寸、樣式等。

ECharts 會根據(jù)這些 圖形元素定義 來渲染出圖形元素。如下述所示:

var option = {
    ...,
    series: [{
        type: 'custom',
        renderItem: function (params, api) {
            // 對于 data 中的每個 dataItem,都會調(diào)用這個 renderItem 函數(shù)。
            // (但是注意,并不一定是按照 data 的順序調(diào)用)

            // 這里進行一些處理,例如,坐標轉(zhuǎn)換。
            // 這里使用 api.value(0) 取出當前 dataItem 中第一個維度的數(shù)值。
            var categoryIndex = api.value(0);
            // 這里使用 api.coord(...) 將數(shù)值在當前坐標系中轉(zhuǎn)換成為屏幕上的點的像素值。
            var startPoint = api.coord([api.value(1), categoryIndex]);
            var endPoint = api.coord([api.value(2), categoryIndex]);
            // 這里使用 api.size(...) 獲得 Y 軸上數(shù)值范圍為 1 的一段所對應的像素長度。
            var height = api.size([0, 1])[1] * 0.6;

            // 這里返回為這個 dataItem 構建的圖形元素定義。
            return {
                // 表示這個圖形元素是矩形。還可以是 'circle', 'sector', 'polygon' 等等。
                type: 'rect',
                // shape 屬性描述了這個矩形的像素位置和大小。
                // 其中特殊得用到了 echarts.graphic.clipRectByRect,意思是,
                // 如果矩形超出了當前坐標系的包圍盒,則剪裁這個矩形。
                shape: echarts.graphic.clipRectByRect({
                    // 矩形的位置和大小。
                    x: startPoint[0],
                    y: startPoint[1] - height / 2,
                    width: endPoint[0] - startPoint[0],
                    height: height
                }, {
                    // 當前坐標系的包圍盒。
                    x: params.coordSys.x,
                    y: params.coordSys.y,
                    width: params.coordSys.width,
                    height: params.coordSys.height
                }),
                // 用 api.style(...) 得到默認的樣式設置。這個樣式設置包含了
                // option 中 itemStyle 的配置和視覺映射得到的顏色。
                style: api.style()
            };
        },
        data: [
            [12, 44, 55, 60], // 這是第一個 dataItem
            [53, 31, 21, 56], // 這是第二個 dataItem
            [71, 33, 10, 20], // 這是第三個 dataItem
            ...
        ]
    }]
}

renderItem 函數(shù)中包含兩個參數(shù):

  • params:包含了當前數(shù)據(jù)信息(如 seriesIndex、dataIndex 等等)和坐標系的信息(如坐標系包圍盒的位置和尺寸)。
  • api:是一些開發(fā)者可調(diào)用的方法集合(如 api.value()、api.coord())。

renderItem 函數(shù)須返回根據(jù)此 dataItem 繪制出的圖形元素的定義信息,參見 renderItem.return。

一般來說,renderItem 函數(shù)的主要邏輯,是將 dataItem 里的值映射到坐標系上的圖形元素。這一般需要用到 renderItem.arguments.api 中的兩個函數(shù):

  • api.value(...),意思是取出 dataItem 中的數(shù)值。例如 api.value(0) 表示取出當前 dataItem 中第一個維度的數(shù)值。
  • api.coord(...),意思是進行坐標轉(zhuǎn)換計算。例如 var point = api.coord([api.value(0), api.value(1)]) 表示 dataItem 中的數(shù)值轉(zhuǎn)換成坐標系上的點。

有時候還需要用到 api.size(...) 函數(shù),表示得到坐標系上一段數(shù)值范圍對應的長度。

返回值中樣式的設置可以使用 api.style(...) 函數(shù),他能得到 series.itemStyle.normal 中定義的樣式信息,以及視覺映射的樣式信息。也可以用這種方式覆蓋這些樣式信息:api.style({fill: 'green', stroke: 'yellow'})。

renderItem 方法寫完后,我們就完成了自定義系列 90% 的工作,接下來該對工作進行優(yōu)化了。

(二)使坐標軸的范圍自適應數(shù)據(jù)范圍


直角坐標系(grid)和極坐標系(polar)中的坐標軸的刻度范圍需要自適應當前顯示出的數(shù)據(jù)的范圍,否則繪制出的圖形會超出去。

例如,在直角坐標系(grid)中,開發(fā)者如果使用自定義系列的話,就需要設定,data 中的哪些維度會對應到 x 軸上,哪些維度會對應到 y 軸上。

上述內(nèi)容通過 encode 來設定。例如:

option = {
    series: [{
        type: 'custom',
        renderItem: function () {
            ...
        },
        encode: {
            // data 中『維度1』和『維度2』對應到 X 軸
            x: [1, 2],
            // data 中『維度0』對應到 Y 軸
            y: 0
        },
        data: [
            // 維度0  維度1  維度2  維度3
            [   12,   44,   55,   60   ], // 這是第一個 dataItem
            [   53,   31,   21,   56   ], // 這是第二個 dataItem
            [   71,   33,   10,   20   ], // 這是第三個 dataItem
            ...
        ]
    }]
};

(三)設定 tooltip


當然,使用 tooltip.formatter 可以任意定制 tooltip 中的內(nèi)容。

但是,還有更簡單的方法,即通過 encode 和 dimensions 來設定:

option = {
    series: [{
        type: 'custom',
        renderItem: function () {
            ...
        },
        encode: {
            x: [1, 2],
            y: 0,
            // 表示『維度2』和『維度3』要顯示到 tooltip 中。
            tooltip: [2, 3]
        },
        // 表示給『維度2』和『維度3』分別取名為『年齡』和『滿意度』,顯示到 tooltip 中。
        dimensions: [null, null, '年齡', '滿意度'],
        data: [
            // 維度0  維度1  維度2  維度3
            [   12,   44,   55,   60   ], // 這是第一個 dataItem
            [   53,   31,   21,   56   ], // 這是第二個 dataItem
            [   71,   33,   10,   20   ], // 這是第三個 dataItem
            ...
        ]
    }]
};

(四)其他注意事項


(1)與 dataZoom 結合使用的時候,常常使用會設置 dataZoom.filterMode 為 'weakFilter'。這個設置的意思是:當 dataItem 部分超出坐標系邊界的時候,dataItem 不會整體被過濾掉。例如:

option = {
    dataZoom: {
        xAxisIndex: 0,
        filterMode: 'weakFilter'
    },
    series: [{
        type: 'custom',
        renderItem: function () {
            ...
        },
        encode: {
            // data 中『維度1』和『維度2』對應到 X 軸
            x: [1, 2],
            y: 0
        },
        data: [
            // 維度0  維度1  維度2  維度3
            [   12,   44,   55,   60   ], // 這是第一個 dataItem
            [   53,   31,   21,   56   ], // 這是第二個 dataItem
            [   71,   33,   10,   20   ], // 這是第三個 dataItem
            ...
        ]
    }]
};

在上述例子中,維度1維度2對應到 X 軸,dataZoom 組件控制 X 軸的縮放。

假如在縮放的過程中,某個 dataItem 的維度1超出了 X 軸的范圍,維度2還在 X 軸的范圍中,那么只要設置 dataZoom.filterMode = 'weakFilter',這個 dataItem 就不會被過濾掉,從而還能夠使用 renderItem 繪制圖形(可以使用上面提到過的 echarts.graphic.clipRectByRect 把圖形繪制成被坐標系剪裁過的樣子)。參見上面提到過的例子:Profile

(2)此外,開發(fā)者還應注意,renderItem.arguments.params 中的 dataIndex 和 dataIndexInside 是有區(qū)別的:

  • dataIndex 指的 dataItem 在原始數(shù)據(jù)中的 index。
  • dataIndexInside 指的是 dataItem 在當前數(shù)據(jù)窗口(參見 dataZoom)中的 index。

renderItem.arguments.api 中使用的參數(shù)都是 dataIndexInside 而非 dataIndex,因為從 dataIndex 轉(zhuǎn)換成 dataIndexInside 需要時間開銷。

參考閱讀


更多的自定義系列的例子參見:custom examples


以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號