自定义系列(custom series),是一种系列的类型。它把绘制图形元素这一步留给开发者去做,从而开发者能在坐标系中自由绘制出自己需要的图表。利用好自定义系列,你会觉得Echarts无所不能了。

简介

自定义系列主要是靠renderItem函数来实现,如下:

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

这个 renderItem 函数的职责,就是返回一个(或者一组)图形元素定义,图形元素定义 中包括图形元素的类型、位置、尺寸、样式等。echarts 会根据这些 图形元素定义 来渲染出图形元素。

var option = {    ...,    series: [{        type: 'custom',        renderItem: function (params, api) {            // 对于 data 中的每个 dataItem,都会调用这个 renderItem 函数。            // (但是注意,并不一定是按照 data 的顺序调用)            // 这里进行一些处理,例如,坐标转换。            // 这里使用 api.value(0) 取出当前 dataItem 中第一个维度的数值。            var categoryIndex = api.value(0);            // 这里使用 api.coord(...) 将数值在当前坐标系中转换成为屏幕上的点的像素值。            var startPoint = api.coord([api.value(1), categoryIndex]);            var endPoint = api.coord([api.value(2), categoryIndex]);            // 这里使用 api.size(...) 获得 Y 轴上数值范围为 1 的一段所对应的像素长度。            var height = api.size([0, 1])[1] * 0.6;            // shape 属性描述了这个矩形的像素位置和大小。            // 其中特殊得用到了 echarts.graphic.clipRectByRect,意思是,            // 如果矩形超出了当前坐标系的包围盒,则剪裁这个矩形。            // 如果矩形完全被剪掉,会返回 undefined.var rectShape = 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            });             // 这里返回为这个 dataItem 构建的图形元素定义。            return rectShape && {                // 表示这个图形元素是矩形。还可以是 'circle', 'sector', 'polygon' 等等。                type: 'rect',                shape: rectShape,                // 用 api.style(...) 得到默认的样式设置。这个样式设置包含了                // option 中 itemStyle 的配置和视觉映射得到的颜色。                style: api.style()            };        },        data: [            [12, 44, 55, 60], // 这是第一个 dataItem            [53, 31, 21, 56], // 这是第二个 dataItem            [71, 33, 10, 20], // 这是第三个 dataItem            ...        ]    }]}

renderItem 函数提供了两个参数:
params:包含了当前数据信息(如 seriesIndex、dataIndex 等等)和坐标系的信息(如坐标系包围盒的位置和尺寸)。
api:是一些开发者可调用的方法集合(如 api.value()、api.coord())。

实例一:echarts.graphic.clipRectByRect

介绍完成后,我们来看一个实例。
https://gallery.echartsjs.com...

option = null;var startTime = +new Date();var categories = ['categoryA', 'categoryB', 'categoryC', 'categoryD'];var step = 14399285;var data = [{    name: 'JS Heap',    value: [0, 1563000480298, (1563000480298 + step)],    itemStyle: {        normal: {            color: '#7b9ce1'        }    }}, {    name: 'Documents',    value: [1, 1563000484450, (1563000484450 + step - 3000000)],    itemStyle: {        normal: {            color: '#bd6d6c'        }    }}, {    name: 'Nodes',    value: [2, 1563000493753, (1563000493753 + step - 5000000)],    itemStyle: {        normal: {            color: '#75d874'        }    }}, {    name: 'Listeners',    value: [3, 1563000503740, (1563000503740 + step)],    itemStyle: {        normal: {            color: '#e0bc78'        }    }}, {    name: 'GPU Memory',    value: [2, 1563000506369, (1563000506369 + step - 8000000)],    itemStyle: {        normal: {            color: '#e0bc78'        }    }}, {    name: 'GPU',    value: [3, 1563000513841, (1563000513841 + step - 12000000)],    itemStyle: {        normal: {            color: '#72b362'        }    }}]function renderItem(params, api) {    var categoryIndex = api.value(0);    var start = api.coord([api.value(1), categoryIndex]);    var end = api.coord([api.value(2), categoryIndex]);    var height = api.size([0, 1])[1] * 0.6;    var rectShape = echarts.graphic.clipRectByRect({        x: start[0],        y: start[1] - height / 2,        width: end[0] - start[0],        height: height    }, {        x: params.coordSys.x,        y: params.coordSys.y,        width: params.coordSys.width,        height: params.coordSys.height    });    return rectShape && {        type: 'rect',        shape: rectShape,        style: api.style()    };}option = {    tooltip: {        formatter: function (params) {            return params.marker + params.name + ': ' + params.value[1] + ' ms';        }    },    title: {        text: 'Profile',        left: 'center'    },    dataZoom: [{        type: 'slider',        filterMode: 'weakFilter',        showDataShadow: false,        top: 400,        height: 10,        borderColor: 'transparent',        backgroundColor: '#e2e2e2',        handleIcon: 'M10.7,11.9H9.3c-4.9,0.3-8.8,4.4-8.8,9.4c0,5,3.9,9.1,8.8,9.4h1.3c4.9-0.3,8.8-4.4,8.8-9.4C19.5,16.3,15.6,12.2,10.7,11.9z M13.3,24.4H6.7v-1.2h6.6z M13.3,22H6.7v-1.2h6.6z M13.3,19.6H6.7v-1.2h6.6z', // jshint ignore:line        handleSize: 20,        handleStyle: {            shadowBlur: 6,            shadowOffsetX: 1,            shadowOffsetY: 2,            shadowColor: '#aaa'        },        labelFormatter: ''    }, {        type: 'inside',        filterMode: 'weakFilter'    }],    grid: {        height:300    },    xAxis: [{        min: startTime,        scale: true,        axisLabel: {            formatter: function (val) {                return Math.max(0, val - startTime) + ' ms';            }        }    }],    yAxis: [{        data: categories    }],    series: [{        type: 'custom',        renderItem: renderItem,        itemStyle: {            normal: {                opacity: 0.8            }        },        encode: {            x: [1, 2],            y: 0        },        data: data    }]};

这里最重要的一个函数就是renderItem, 其中里面的echarts.graphic.clipRectByRect更是其中的关键。

实例二:echarts.graphic.clipPointsByRect

echarts.graphic.clipRectByRect输入两个矩形,返回第二个矩形截取第一个矩形的结果。
除了echarts.graphic.clipRectByRect,还有一个echarts.graphic.clipPointsByRect, echarts.graphic.clipPointsByRect表示输入一组点,和一个矩形,返回被矩形截取过的点, 比如[[23, 44], [12, 15], …]

那我们再把上面的例子改善,绘制成多边形的形状。
https://gallery.echartsjs.com...

先看看效果图:

与上图不一样的就是右侧的形状,代码的不同之处也就是renderItem

function renderItem(params, api) {    var categoryIndex = api.value(0);    var start = api.coord([api.value(1), categoryIndex]);    var end = api.coord([api.value(2), categoryIndex]);    var height = api.size([0, 1])[1] * 0.6;    var width = end[0] - start[0];    var x = start[0];    var y = start[1] - height / 2;    var shape = echarts.graphic.clipPointsByRect([            [x, y],            [x + width - 10, y],            [x + width, y - height / 2],            [x + width - 10, y - height],            [x, y - height]        ], {        x: params.coordSys.x,        y: params.coordSys.y,        width: params.coordSys.width,        height: params.coordSys.height    });    return shape && {        type: 'polygon',        shape: {            points: shape        },        style: api.style()    }}

这里的绘制就是采用5个坐标的points绘制而成的,即上面的shape对象,所以echarts.graphic.clipPointsByRect真的是什么东西都能绘制出来。