ECharts饼图和折线图的组合图

1,280 阅读5分钟

背景

最近在ECharts官网上看到了一个日历饼图(效果图如下),觉得这样的组合图比一般单个图(如日历图、饼图)的功能要多,而且它能够表达的信息量也比单个图要多一点。再加上它在web网页上占据的面积也与日历图一样。这对于设计可视化系统来说,显然更有优势。 image.png

目的

设计实现具有同样效果的组合图,比如饼图+折线图的组合图。

转换过程

仔细研究了该图的实现代码。发现它的实现过程主要是通过下面这个函数完成的。

function getPieSeries(scatterData, chart) {
  return scatterData.map(function (item, index) {
    // convertToPixel方法接受两个参数:被转换的坐标系 和 被转换的值
    // 第一个参数calendar:代表被转换的是日历坐标系
    // 第二个参数item:代表被转换的是一个散点数据 [date, value]
    var center = chart.convertToPixel('calendar', item);
    return {
      id: index + 'pie',
      type: 'pie',
      center: center,
      label: {
        formatter: '{c}',
        position: 'inside'
      },
      radius: pieRadius,
      data: [
        { name: '工作', value: Math.round(Math.random() * 24) },
        { name: '娱乐', value: Math.round(Math.random() * 24) },
        { name: '睡觉', value: Math.round(Math.random() * 24) }
      ]
    };
  });
}

上面这个函数的作用就是将每个散点格式的数据映射成饼图格式的数据。比如本题就是将日历坐标系上的每个散点数据[date, value] 转换成 饼图格式的数据。对应的数据格式如下:

散点数据饼图数据
image.pngimage.png

实现原理

它的实现原理就是:首先通过echartsInstance的convertToPixel方法将日历坐标系上的每个散点坐标转换成对应的像素坐标值,即饼图的中心点center。然后再以center为中心构造饼图数据。这样就完成了上面所说的散点数据转换成饼图数据。

使用echartsInstance的convertToPixel方法不仅能够实现日历饼图,还能完成其他任意多个图的组合。

( 
    // finder 用于指示『使用哪个坐标系进行转换』。 
    // 通常地,可以使用 index 或者 id 或者 name 来定位。 
    finder: { 
        seriesIndex?: number, 
        seriesId?: string, 
        seriesName?: string, 
        geoIndex?: number, 
        geoId?: string, 
        geoName?: string, 
        xAxisIndex?: number, 
        xAxisId?: string, 
        xAxisName?: string, 
        yAxisIndex?: number, 
        yAxisId?: string, 
        yAxisName?: string, 
        gridIndex?: number, 
        gridId?: string, 
        gridName?: string 
    }, 
    // 要被转换的值。 
    value: Array|number 
    // 转换的结果为像素坐标值,以 echarts 实例的 dom 节点的左上角为坐标 [0, 0] 点。 
) => Array|number

例:

  • 在地理坐标系(geo)上,把某个点的经纬度坐标转换成为像素坐标:
// [128.3324, 89.5344] 表示 [经度,纬度]。
// 使用第一个 geo 坐标系进行转换:
chart.convertToPixel('geo', [128.3324, 89.5344]); // 参数 'geo' 等同于 {geoIndex: 0}

// 使用第二个 geo 坐标系进行转换:
chart.convertToPixel({geoIndex: 1}, [128.3324, 89.5344]);

// 使用 id 为 'bb' 的 geo 坐标系进行转换:
chart.convertToPixel({geoId: 'bb'}, [128.3324, 89.5344]);
  • 在直角坐标系(cartesian,grid)上,把某个点的坐标转换成为像素坐标:
// [300, 900] 表示该点 x 轴上对应刻度值 300,y 轴上对应刻度值 900。
// 注意,一个 grid 可能含有多个 xAxis 和多个 yAxis,任何一对 xAxis-yAxis 形成一个cartesian。
// 使用第三个 xAxis 和 id 为 'y1' 的 yAxis 形成的 cartesian 进行转换:
chart.convertToPixel({xAxisIndex: 2, yAxisId: 'y1'}, [300, 900]);

// 使用 id 为 'g1' 的 grid 的第一个 cartesian 进行转换:
chart.convertToPixel({gridId: 'g1'}, [300, 900]);
  • 把某个坐标轴的点转换成像素坐标:
// id 为 'x0' 的 xAxis 的刻度 3000 位置所对应的横向像素位置:
chart.convertToPixel({xAxisId: 'x0'}, 3000); // 返回一个 number。

// 第二个 yAxis 的刻度 600 位置所对应的纵向像素位置:
chart.convertToPixel({yAxisIndex: 1}, 600); // 返回一个 number。
  • 把关系图(graph)的点转换成像素坐标:
// 因为每个 graph series 自己持有一个坐标系,所以我们直接在 finder 中指定 series:
chart.convertToPixel({seriesIndex: 0}, [2000, 3500]);
chart.convertToPixel({seriesId: 'k2'}, [100, 500]);
  • 在某个系列所在的坐标系(无论是 cartesian、geo、graph 等)中,转换某点成像素坐标:
// 使用第一个系列对应的坐标系:
chart.convertToPixel({seriesIndex: 0}, [128.3324, 89.5344]);

// 使用 id 为 'k2' 的系列所对应的坐标系:
chart.convertToPixel({seriesId: 'k2'}, [128.3324, 89.5344]);

饼图+折线图的组合图实现

代码

懂了上述方法后,就可以开始实现多个图的组合了。首先想到的就是折线图+饼图的组合。下面直接贴实现代码了。

var minPieRadius = 20;
var base = 10000;
// 根据y轴数值得到饼图半径大小
function getPieRadius(value) {
    var pieRadius = Math.floor(value / (1.1 * base) * 40) > minPieRadius ? Math.floor(value / base * 40) : minPieRadius;
    return pieRadius;
}
// 得到虚拟折线数据
function getVirtulData() {
    var date = +echarts.number.parseDate('2022-01-01');
    var end = +echarts.number.parseDate('2022-01-10');
    var dayTime = 3600 * 24 * 1000;
    var data = [];
    for (var time = date; time < end; time += dayTime) {
        data.push([
            echarts.format.formatTime('yyyy-MM-dd', time),
            Math.floor((Math.random() + 0.1) * base)
        ]);
    }
    return data;
}
// 根据虚拟数据得到饼图数据
function getPieSeries(lineData, chart) {
    return echarts.util.map(lineData, function (item, index) {
        // 其实主要就是修改这个地方的参数: {seriesIndex: 0} 改为 'grid' 也可以
        // 因为是在直角坐标系上,把某个点的坐标转换成为像素坐标
        var center = chart.convertToPixel({seriesIndex: 0}, item);
        return {
            id: index + 'pie',
            type: 'pie',
            center: center,
            label: {
                normal: {
                    formatter: '{c}',
                    position: 'inside'
                }
            },
            zlevel: 1,
            radius: getPieRadius(item[1]),
            data: [
                {name: '工作', value: Math.round(Math.random() * 24)},
                {name: '娱乐', value: Math.round(Math.random() * 24)},
                {name: '睡觉', value: Math.round(Math.random() * 24)}
            ]
        };
    });
}

var lineData = getVirtulData();

option = {
    tooltip : {},
    legend: {
        data: ['工作', '娱乐', '睡觉'],
        bottom: 20
    },
    grid: {
        left: '10%',
        right: '10%',
        top:'10%',
        bottom:'20%',
    },
    xAxis: {
        type: 'category',
        name: '日',
        nameGap: 5,
        axisLabel: {
            color: '#000',
            textStyle: {
                fontSize: '13'
            },
        },
        axisTick: {
            show: true,
        },
        axisLine: {
            lineStyle: {
                color: '#000',
            }
        },
        splitLine: {
            show: false
        }
    },
    yAxis: {
        type: 'value',
        name: '步数',
        nameGap: 10,
        // scale: true,
        nameTextStyle: {
            fontSize: 13,
        },
        axisLabel: {
            textStyle: {
                fontSize: '13'
            },
        },
        axisTick: {
            show: true,
        },
        axisLine: {
            show: true,
            lineStyle: {
                color: '#000',
            }
        },
        splitLine: {
            show: false,
        }
    },
    series: [{
        type: 'line',
        symbolSize: 0,
        lineStyle: {
            color: '#000a',
            width: 1,
        },
        data: lineData
    }]
};

setTimeout(function () {
  myChart.setOption({
    series: getPieSeries(lineData, myChart)
  });
}, 10);

效果图

image.png

总结

通过echartsInstance的convertToPixel方法能够完成多种图表的组合。如关系图+饼图的组合图,地图+饼图的组合图等等。

此外,学习ECharts官网上的一些API的方法,可以实现很多有意思的想法。