ECharts 自定義系列

2018-09-25 09:49 更新

什么是 ECharts 自定義系列?


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

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

ECharts 為什么會(huì)支持自定義系列?

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

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

ECharts 使用自定義系列擴(kuò)展 x-range 圖

點(diǎn)擊編輯實(shí)例 》》

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

(一)renderItem 方法


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

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

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

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

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

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

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

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

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

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

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

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

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

有時(shí)候還需要用到 api.size(...) 函數(shù),表示得到坐標(biāo)系上一段數(shù)值范圍對(duì)應(yīng)的長(zhǎng)度。

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

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

(二)使坐標(biāo)軸的范圍自適應(yīng)數(shù)據(jù)范圍


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

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

上述內(nèi)容通過(guò) encode 來(lái)設(shè)定。例如:

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

(三)設(shè)定 tooltip


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

但是,還有更簡(jiǎn)單的方法,即通過(guò) encode 和 dimensions 來(lái)設(shè)定:

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   ], // 這是第一個(gè) dataItem
            [   53,   31,   21,   56   ], // 這是第二個(gè) dataItem
            [   71,   33,   10,   20   ], // 這是第三個(gè) dataItem
            ...
        ]
    }]
};

(四)其他注意事項(xiàng)


(1)與 dataZoom 結(jié)合使用的時(shí)候,常常使用會(huì)設(shè)置 dataZoom.filterMode 為 'weakFilter'。這個(gè)設(shè)置的意思是:當(dāng) dataItem 部分超出坐標(biāo)系邊界的時(shí)候,dataItem 不會(huì)整體被過(guò)濾掉。例如:

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

在上述例子中,維度1維度2對(duì)應(yīng)到 X 軸,dataZoom 組件控制 X 軸的縮放。

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

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

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

renderItem.arguments.api 中使用的參數(shù)都是 dataIndexInside 而非 dataIndex,因?yàn)閺?nbsp;dataIndex 轉(zhuǎn)換成 dataIndexInside 需要時(shí)間開銷。

參考閱讀


更多的自定義系列的例子參見(jiàn):custom examples


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

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)