前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >受到宿爽大神视频分享中 Message Center 的启发,图表交叉交互的解耦实现

受到宿爽大神视频分享中 Message Center 的启发,图表交叉交互的解耦实现

作者头像
ZXand618
发布2022-04-10 10:07:58
2540
发布2022-04-10 10:07:58
举报

最近有幸学习了宿爽大神的「浅入浅出 ECharts 源码 - 资深架构师独家揭秘 ECharts 源码架构」,受益良多。

尤其是其中「Message Center」的设计,简直醍醐灌顶~

之前一直苦于图表间的复杂交互的实现(一大堆有交叉的 on、dispatchAction、setOption),感觉脑子特别不够用……做出来的效果总是和自己预想的不一样,然后只能一边改一边试,一遍遍地慢慢改

直到看到宿爽大神讲到「Message Center」,讲到如何为这些交叉的监听解耦,才突然意识到原来有更好的方法。

插一句,顺便再推荐一个 ECharts 核心开发者羡辙大神的分享:

想法&思路

为了验证自己学到的东西,打算做一个简易的 Demo,即 3 个图表交叉作用,点其中任意一个图表,其他两个会变化:

  • 3 个图表分别从 3 个维度展示统计结果
  • 点击任意一个图表,另外两个图表会通过各自的维度展示被点击的数据

思路大致就是:

  • 用一个对象记录每个图表的数据过滤规则
  • 通过另一个对象配置图表间点击事件的响应关系(这里是除了被点击图表外,全都响应)
  • 当一个图表被点击时,形成被点击数据的过滤规则,覆盖到另外两张图表的规则配置中
  • 通过新的规则配置刷新图表

主要代码

准备一些示意数据

代码语言:javascript
复制
srcData = [
    //['维度1', '维度2', '维度3', '度量'],
    ['a1', 'b1', 'c1', 1],
    ['a1', 'b1', 'c2', 1],
    ['a1', 'b1', 'c3', 1],
    ['a1', 'b2', 'c1', 1],
    ['a1', 'b2', 'c2', 1],
    ['a1', 'b2', 'c3', 1],
    ['a1', 'b3', 'c1', 1],
    ['a1', 'b3', 'c2', 1],
    ['a1', 'b3', 'c3', 1],

    ['a2', 'b1', 'c1', 2],
    ['a2', 'b1', 'c2', 2],
    ['a2', 'b1', 'c3', 2],
    ['a2', 'b2', 'c1', 2],
    ['a2', 'b2', 'c2', 2],
    ['a2', 'b2', 'c3', 2],
    ['a2', 'b3', 'c1', 2],
    ['a2', 'b3', 'c2', 2],
    ['a2', 'b3', 'c3', 2],

    ['a3', 'b1', 'c1', 3],
    ['a3', 'b1', 'c2', 3],
    ['a3', 'b1', 'c3', 3],
    ['a3', 'b2', 'c1', 3],
    ['a3', 'b2', 'c2', 3],
    ['a3', 'b2', 'c3', 3],
    ['a3', 'b3', 'c1', 3],
    ['a3', 'b3', 'c2', 3],
    ['a3', 'b3', 'c3', 3],
];

用到了一年多以前,自己瞎写的二维数组过滤、求和(类似 sum ... group by ...)的函数

代码语言:javascript
复制
var ecCalc = new Object({});

/**聚合计算
*  @alias  module:ecCalc/groupCalc
*  @param  {Array}  source  输入,二维数组
*  @param  {boolean}  hasColumnName  第一行(source[0])是否是列名
*  @param  {Array}  dimensions  纬度,一维数组,传入列id列表,例如[0,2,3],列表长度至少为1
*  @param  {Object}  measures  度量,json数组,例如[{"id": 2, "method": "count"}, {"id": 3, "method": "count"}],id用于
source[measures[id]],method用于聚合方法
*  @return  {Array}  输出,二维数组,新列与原列对应关系为dimensions.concat(measures)  
*/
ecCalc.groupCalc = function(source, hasColumnName, dimensions, measures) {
  ...
}

/**条件筛选
* @alias module:ecCalc/filter
* @param {Array} source 输入,二维数组
* @param {boolean} hasColumnName 第一行(source[0])是否是列名
* @param {function} filterCondition 筛选(判断)函数
                   (@param {Array} source[i]作为输入; 
                    @return {boolean} 输出保留/筛掉的布尔值) 
         {string} filterCondition 条件表达式,用item代表source[i],
                    传入例如"(item[0] - item[1] > 0) && (item[0] + item[1] > 0)"的,
                    结果为boolean的表达式 
* @return {Array} 输出,二维数组*/
ecCalc.filter = function(source, hasColumnName, filterCondition) {
  ...
}

数据过滤、求和的规则配置和通过该规则转换数据的函数。

代码语言:javascript
复制
convertConfig = [
    {
        dimensions: [0],
        methods: [{id: 3, method: "sum"}],
        filter: 'true',
    },
    {
        dimensions: [1],
        methods: [{id: 3, method: "sum"}],
        filter: 'true',
    },
    {
        dimensions: [2],
        methods: [{id: 3, method: "sum"}],
        filter: 'true',
    },
];

function dataConvert(config){
    let res = [];
    
    for (let obj of convertConfig){
        let tmp = ecCalc.filter(srcData, false, obj.filter);
        
        res.push(ecCalc.groupCalc(tmp, false, obj.dimensions, obj.methods));
        
    }
    return res;
};

定义 option(放了 line、bar、pie 三个图)

代码语言:javascript
复制
option = {
    title: {
        text: '受到宿爽大神视频分享中 Message Center 的启发,图表联动的交叉实现'
    },
    tooltip:{},
    grid: [{
        bottom: '55%'
    }, {
        top: '55%',
        right: '55%'
    }],
    xAxis: [{
        type: 'category'
    }, {
        type: 'category',
        gridIndex: 1
    }],
    yAxis: [{}, {
        gridIndex: 1
    }],
    series: [{
        id: 'line',
        type: 'line',
        label: {
            show: true
        },
        name: convertConfig[0].filter,
        data: convertedData[0]
    }, {
        id: 'bar',
        type: 'bar',
        xAxisIndex: 1,
        yAxisIndex: 1,
        label: {
            show: true
        },
        name: convertConfig[1].filter,
        data: convertedData[1]
    }, {
        id: 'pie',
        type: 'pie',
        center: ['75%', '75%'],
        radius: '25%',
        label: {
            show: true,
            formatter: '{b}:{c}',
        },name: convertConfig[2].filter,
        data: convertedData[2].map(function(item){
            return {name:item[0],value:item[1]};
        })
    }]
};

图表交互的对应(响应)关系

代码语言:javascript
复制
actionConfig = [{
    //from: 0,
    to: [1, 2]
},{
    to: [0, 2]
},{
    to: [0, 1]
}];

on(click)、setOption 部分

代码语言:javascript
复制
myChart.on('click', function(params){
    
    let actionSeries = actionConfig[params.seriesIndex].to;
    
    for (let i of actionSeries){
        
        convertConfig[i].filter = convertConfig[params.seriesIndex].filter + ' && item[' + params.seriesIndex + '] == "' + params.name + '"';
        
    }

    convertedData = dataConvert(convertConfig);
    
    myChart.setOption({
        series:[{
            id: 'line',
            name: convertConfig[0].filter,
            data: convertedData[0]
        },{
            id: 'bar',
            name: convertConfig[1].filter,
            data: convertedData[1]
        },{
            id: 'pie',
            name: convertConfig[2].filter,
            data: convertedData[2].map(function(item){
                return {name:item[0],value:item[1]};
            })
        }]
    });
})

以上就是一个简易、粗糙的例子,估计有不少欠考虑的地方……另外引用之前瞎写的函数时,把部分 var 改成了 let(没改全,有点乱)。算了,主要是验证思路(自我安慰),哈哈哈哈~~

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2019-12-31,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 ZXand618的ECharts之旅 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档