第三个故事 - 交互式图表间的联动

「概览数据整体,按需关注数据细节」是数据可视化的基本需求。前文讲到的两类图表都是静态图表,数据结构往往只有一层。当你的数据具有多个层次(树形结构),如数据按男女划分之后,对应每个性别,会有进一步的年龄层次的划分,对于各年龄层次,可能还会有城乡户籍、受教育程度的区分,多层次的信息很难在单一的静态图表中展示。

本例中笔者将使用旭日图作为核心图表,展示如何可视化多层次的数据。并且在最后,笔者会做一个扩展,演示如何联动两类型图表,更直观地反映数据特征,这一部分可能会涉及较复杂的编码内容,大家可以选择性学习。

本例的数据由笔者随机生成,最终的页面可以点击链接查看。

回收片头

正式开始

  1. 数据获取

    本例的数据也将直接给出,并且这部分数据将不单独保存在其他文件中,而直接写入到代码。本例采用该方法,是因为旭日图的同一层次显示类似于扇形图,数据量并不大,写入代码中不会导致结构过于混乱。本手册面向的是非编码职业的读者,我们采用这个不专业的方法,也是为了省去额外的解释内容,将重点放在如何可视化数据上。

    不过,即便数据量不大,该部分仍旧占用了大量的空间。为了节省篇幅,除了最一开始导入数据的步骤,以及最终展示代码全貌外,其余的内容将略去数据部分,望读者注意,避免错误的粘贴导致图表出错。

  2. 数据分析

    旭日图所需的数据结构,是字典型(数据对的集合)。我们展示一部分的数据,如下所示:

    var data = [{
        name: '正式平台',
        value: 9487,
        children: [{
            name: '有地址',
            value: 9357,
            children: [{
                name: '6选3',
                value: 2851,
                children: [{
                    name: '北京市',
                    value: 2336
                },{
                    name: '山东省',
                    value: 425                        
                },{
                    name: '天津市',
                    value: 83                        
                },{
                    name: '上海市',
                    value: 7                        
                }]
            },{
                         name:'3+1+2',
                         value:5321
                 }]
         }]
    }]
    

    如果读者觉得直接看这个不甚明了,可以结合下方的图示理解。

    graph TB
    正式平台-->有地址-->6选3
    6选3-->北京市
    6选3-->山东省
    6选3-->天津市
    6选3-->上海市
    有地址-->3+1+2
    

    对于任意一个节点,都包含三个信息:[name],[value],[children],分别代表节点名称、节点的值、子节点,其中子节点中又是包含有这三个信息的节点排列。

    一个节点如果没有子节点,则不需要填入[children]信息。特别的,对于Echarts,如果一个节点是父节点(存在子节点),可不为其填入[value]值。在绘制图表时,value值将取子节点[value]值之和。

    需要注意的是,父节点的[value]值可能不等于子节点的[value]值之和(可能存在数据缺失),所以出现该情况时,务必录入父节点的[value]值,保证图表的正确性。

    数据准备全部完成,接下来我们准备绘制初始图表。

  1. 初始图表:

    有了前两个案例的学习,读者应当了解了Echarts代码的一些特点。本节将直接给出最初始图表(带有简单解释),然后逐步美化图表显示,最后增加交互效果。进度会稍有加快,但会更加凸显重点。

    1. 初始的图表。我们直接给出代码,读者可以将其粘贴到[txt]文件中,保存后修改后缀为[.html],直接打开即可看到当前效果。

      <!DOCTYPE html>
      <html>
      <head>
          <meta charset="utf-8">
          <title>ECharts</title>
          <!-- 引入 echarts.js -->
          <script type="text/javascript" src="https://cdn.jsdelivr.net/npm/echarts/dist/echarts.min.js"></script>
          <script type="text/javascript" src="https://cdn.jsdelivr.net/npm/echarts-gl/dist/echarts-gl.min.js"></script>
          <script type="text/javascript" src="https://cdn.jsdelivr.net/npm/echarts-stat/dist/ecStat.min.js"></script>
          <script type="text/javascript" src="https://cdn.jsdelivr.net/npm/echarts/dist/extension/dataTool.min.js"></script>
          <script type="text/javascript" src="https://cdn.jsdelivr.net/npm/echarts/map/js/china.js"></script>
          <script type="text/javascript" src="https://cdn.jsdelivr.net/npm/echarts/map/js/world.js"></script>
          <script type="text/javascript" src="https://cdn.jsdelivr.net/npm/echarts/dist/extension/bmap.min.js"></script>
          <script type="text/javascript" src="https://www.jb51.net/jslib/jquery/jquery.min.js"></script>
      </head>
      <body>
          <!-- 为ECharts准备一个具备大小(宽高)的Dom -->
          <div id="sun1" style="width:100%;height:900px;float:left;"></div>
      
          <script type="text/javascript">
              // 基于准备好的dom,初始化echarts实例
              var myChartSun1 = echarts.init(document.getElementById('sun1'));
      
              //旭日图所需数据结构
              var data = [{
                  name: '正式平台',
                  value: 9487,
                  children: [{
                      name: '有地址',
                      value: 9357,
                      children: [{
                          name: '6选3',
                          value: 2851,
                          children: [{
                              name: '北京市',
                              value: 2336
                          },{
                              name: '山东省',
                              value: 425                        
                          },{
                              name: '天津市',
                              value: 83                        
                          },{
                              name: '上海市',
                              value: 7                        
                          }]
                      }, {
                          name: '3+1+2',
                          value: 5321,
                          children: [{
                              name: '广东省',
                              value: 2068
                          },{
                              name: '福建省',
                              value: 1582,
                              //数据下可设置格式
                              // itemStyle: {
                              //     shadowBlur: 10,
                              //     shadowColor: '#99ffff'
                              // },                        
                          },{
                              name: '重庆市',
                              value: 718                        
                          },{
                              name: '江苏省',
                              value: 301                        
                          },{
                              name: '湖北省',
                              value: 257                        
                          },{
                              name: '河北省',
                              value: 222                        
                          },{
                              name: '湖南省',
                              value: 116                        
                          },{
                              name: '辽宁省',
                              value: 57                        
                          }]
                      }, {
                          name: '7选3-浙江',
                          value: 28
                      }, {
                          name: '未改革',
                          value: 1157 ,
                          children:[{
                              name:'安徽省',
                              value: 460
                          },{
                              name:'陕西省',
                              value:286
                          },{
                              name:'四川省',
                              value:126
                          },{
                              name:'新疆',
                              value:114
                          },{
                              name:'云南省',
                              value:66
                          },{
                              name:'广西',
                              value:34
                          },{
                              name:'江西省',
                              value:27
                          },{
                              name:'甘肃省',
                              value:17
                          },{
                              name:'河南省',
                              value:14
                          },{
                              name:'山西省',
                              value:13
                          }]
                      }]
                  }, {
                      name: '无地址',
                      value: 130
                  }]
              }, {
                  name: '培训平台',
                  value: 3215,
                  children: [{
                      name: '智校测试校',
                      children: [{
                          name: '培训',
                          value: 1449
                      }, {
                          name: '排课',
                          value: 1325
                      }]
                  },{
                      name:'新课改培训',
                      value: 441
                  }]
              }, {
                  name: '测试+演示平台',
                  value: 1046,
                  children: [{
                      name: '测试',
                      value: 464
                      }, {
                          name: '演示',
                          value: 582
                      }
                  ]
              }, {
                  name: '职教+旧平台',
                  value: 1239
              }];
      
              var option = {
                  backgroundColor:'#000',
                  tooltip: {
                      trigger: 'item',
                      formatter: function (params) {
                          return params.name + ' : ' + params.value;
                      }
                  },
                  visualMap: {
                      min: 0,
                      max: 2000,
                      calculable: true,
                      inRange: {
                          color: ['#50a3ba', '#eac736', '#d94e5d']
                      },
                      textStyle: {
                          color: '#fff'
                      }
                  },
                  series: [{
                      type: 'sunburst',
                      id:'sunburst1',
                      // highlightPolicy: 'ancestor',
                      data: data,
                      center:['50%','50%'],
                        //旭日图半径90%,中心预留10像素
                      radius: [10, '90%'],
                      //高亮当前及其子区间
                      //highlightPolicy: 'descendant',
                      label: {
                          rotate: 'radial',
                          fontSize: 15
                      }
                  }]
              };    
              // 使用刚指定的配置项和数据显示图表。
              myChartSun1.setOption(option);
      
          </script>
      </body>
      </html>
      

      代码中几乎没有新的内容,希望读者可以举一反三,自行了解下每部分代码的效果。当前图表如下:

      image

      旭日图本身支持数据下钻,点击任意环形块,都会呈现出以点击节点为根节点(初始节点)的新图:

      image

      当前的图表不分主次地呈现了全部数据,如果你将其展示给你的老板,你应该很难凸显出图表想要表达的数据特点。实际上这样的一张图表,大多数人都无法理解它想表达的内容。

      接下来笔者将介绍如何美化这张图表,使其能够更鲜明地体现数据。

  1. 逐步美化图表:

    1. 读者应该能注意到,这张旭日图分四个层次,当前各层次的厚度都相同。通常我们会减少最外层环的厚度(降维效果,将面积信息转化为长度信息),并且增加中心空白(背景黑色)的半径,代码在[series]中添加,新增的代码如下:

      series: [{
          type: 'sunburst',
          id:'sunburst1',
          // highlightPolicy: 'ancestor',
          data: data,
          center:['50%','50%'],
          radius: [10, '90%'],
          //高亮当前及其子区间
          //highlightPolicy: 'descendant',
          label: {
              rotate: 'radial',
              fontSize: 15
          },
          //levels设置各层次环形宽度
          levels: [{}, {
              //内圈半径
              r0: '10%',
              //外圈半径
              r: '30%',
              itemStyle: {
              //边框宽度
                     borderWidth: 2
              }
          }, {
              r0: '30%',
              r: '50%',
              itemStyle: {
                     borderWidth: 2
              }                    
          }, {
              r0: '50%',
              r: '70%',
              itemStyle: {
                  borderWidth: 2
              }                     
          }, {
              r0: '70%',
              r: '75%',
              label: {
                  //文字显示在外侧
                  position: 'outside',
                  //距离环形的距离
                  padding: 3,
              },
              itemStyle: {
                  borderWidth: 3,
                  //阴影半径
                  shadowBlur: 10,
                  //阴影颜色,当前是亮色
                  shadowColor: '#99ffff'
              }
          }]
      }]
      

      我们通过设置内圈外圈的半径,限制每一层环形的宽度。最外层环形仅有5%的宽度,无法容纳文字,所以我们选择将文字内容悬浮于图形之外。并且为了凸显外圈内容,我们增加了阴影[shadow]效果,并且选择亮色,使其能在黑色背景下高亮最外圈内容。

      新增代码后的图表效果如下:

      image
    2. 旭日图支持数据下钻,但除了点击操作外,其他任何操作都不会影响图表显示。我们接下来新增几个焦点动画,使得鼠标悬浮于节点之上时,可以强调当前节点。代码在[series]中添加,我们首先新增能强调当前节点的部分代码:

      //选定区域
      emphasis: {
          itemStyle: {
                     //环形颜色
              color: 'red',
              //边框颜色
                     borderColor: '#fff',
              shadowBlur: 10,
              shadowColor: '#99ffff'
             }
      },
      //高亮区域
      highlight: {
          itemStyle: {
              color: 'orange',
              borderColor: '#fff',
              shadowBlur: 10,
              shadowColor: '#99ffff'
          }
      },
      

      我们分别设置了当前选定区域和高亮区域的焦点效果。你可能会有疑问,这两者的区别在哪里?读者已经清楚了解到,旭日图的数据是树状的层级结构,当用户选中父节点时,Echarts同时会高亮子节点。也就是说,[emphasis]设置的是当前选中区域的样式,[highlight]设置的是子节点区域的样式。

      读者可以自行观察交互效果,更加便于理解。当前效果如下图:

      image
    3. 平心而论,你真的能很快注意到我们当前选定的内容么?很显然是不能的。因为非选定的区域也带有多种颜色,一般读者很难将两者区分开。所以除了强调选定节点,非选定的节点我们需要弱化其显示效果。代码在[series]中添加,新增的代码如下:

      //非选定区域
      downplay: {
          itemStyle: {
              color: '#1e1e1e',
              shadowBlur: 0,
              //透明度
              opacity: 0.3
          },
          label: {
              //透明度
              opacity: 0.3
          }
      },
      

      如代码所示,我们降低了非选定区域内容的透明度,调整了区域背景和边框颜色,使得这部分内容不容易被注意到。调整后的图形如下:

      image

      我们选择了相同的区域,读者可以对比前后两张图片。很显然,调整后的图片更加凸显重点。并且得益于Echarts出色的动画渐变效果,切换选择区域不会给用户带来很突兀的感觉。

      现在给出最终的完整代码,读者可以根据官方文档自行修改,或者使用自己的数据,生成自己的专属图表:

      <!DOCTYPE html>
      <html>
      <head>
          <meta charset="utf-8">
          <title>ECharts</title>
          <!-- 引入 echarts.js -->
          <script type="text/javascript" src="https://cdn.jsdelivr.net/npm/echarts/dist/echarts.min.js"></script>
          <script type="text/javascript" src="https://cdn.jsdelivr.net/npm/echarts-gl/dist/echarts-gl.min.js"></script>
          <script type="text/javascript" src="https://cdn.jsdelivr.net/npm/echarts-stat/dist/ecStat.min.js"></script>
          <script type="text/javascript" src="https://cdn.jsdelivr.net/npm/echarts/dist/extension/dataTool.min.js"></script>
          <script type="text/javascript" src="https://cdn.jsdelivr.net/npm/echarts/map/js/china.js"></script>
          <script type="text/javascript" src="https://cdn.jsdelivr.net/npm/echarts/map/js/world.js"></script>
          <script type="text/javascript" src="https://cdn.jsdelivr.net/npm/echarts/dist/extension/bmap.min.js"></script>
          <script type="text/javascript" src="https://www.jb51.net/jslib/jquery/jquery.min.js"></script>
      </head>
      <body>
          <!-- 为ECharts准备一个具备大小(宽高)的Dom -->
          <div id="sun1" style="width:100%;height:900px;float:left;"></div>
      
          <script type="text/javascript">
              // 基于准备好的dom,初始化echarts实例
              var myChartSun1 = echarts.init(document.getElementById('sun1'));
      
              //旭日图所需数据结构
              var data = [{
                  name: '正式平台',
                  value: 9487,
                  children: [{
                      name: '有地址',
                      value: 9357,
                      children: [{
                          name: '6选3',
                          value: 2851,
                          children: [{
                              name: '北京市',
                              value: 2336
                          },{
                              name: '山东省',
                              value: 425                        
                          },{
                              name: '天津市',
                              value: 83                        
                          },{
                              name: '上海市',
                              value: 7                        
                          }]
                      }, {
                          name: '3+1+2',
                          value: 5321,
                          children: [{
                              name: '广东省',
                              value: 2068
                          },{
                              name: '福建省',
                              value: 1582,
                              //数据下可设置格式
                              // itemStyle: {
                              //     shadowBlur: 10,
                              //     shadowColor: '#99ffff'
                              // },                        
                          },{
                              name: '重庆市',
                              value: 718                        
                          },{
                              name: '江苏省',
                              value: 301                        
                          },{
                              name: '湖北省',
                              value: 257                        
                          },{
                              name: '河北省',
                              value: 222                        
                          },{
                              name: '湖南省',
                              value: 116                        
                          },{
                              name: '辽宁省',
                              value: 57                        
                          }]
                      }, {
                          name: '7选3-浙江',
                          value: 28
                      }, {
                          name: '未改革',
                          value: 1157 ,
                          children:[{
                              name:'安徽省',
                              value: 460
                          },{
                              name:'陕西省',
                              value:286
                          },{
                              name:'四川省',
                              value:126
                          },{
                              name:'新疆',
                              value:114
                          },{
                              name:'云南省',
                              value:66
                          },{
                              name:'广西',
                              value:34
                          },{
                              name:'江西省',
                              value:27
                          },{
                              name:'甘肃省',
                              value:17
                          },{
                              name:'河南省',
                              value:14
                          },{
                              name:'山西省',
                              value:13
                          }]
                      }]
                  }, {
                      name: '无地址',
                      value: 130
                  }]
              }, {
                  name: '培训平台',
                  value: 3215,
                  children: [{
                      name: '智校测试校',
                      children: [{
                          name: '培训',
                          value: 1449
                      }, {
                          name: '排课',
                          value: 1325
                      }]
                  },{
                      name:'新课改培训',
                      value: 441
                  }]
              }, {
                  name: '测试+演示平台',
                  value: 1046,
                  children: [{
                      name: '测试',
                      value: 464
                      }, {
                          name: '演示',
                          value: 582
                      }
                  ]
              }, {
                  name: '职教+旧平台',
                  value: 1239
              }];
      
              var option = {
                  backgroundColor:'#000',
                  tooltip: {
                      trigger: 'item',
                      formatter: function (params) {
                          return params.name + ' : ' + params.value;
                      }
                  },
                  visualMap: {
                      min: 0,
                      max: 2000,
                      calculable: true,
                      inRange: {
                          color: ['#50a3ba', '#eac736', '#d94e5d']
                      },
                      textStyle: {
                          color: '#fff'
                      }
                  },
                  series: [{
                      type: 'sunburst',
                      id:'sunburst1',
                      // highlightPolicy: 'ancestor',
                      data: data,
                      center:['50%','50%'],
                      radius: [10, '90%'],
                      //高亮当前及其子区间
                      //highlightPolicy: 'descendant',
                      label: {
                          rotate: 'radial',
                          fontSize: 15
                      },
                      //选定区域
                      emphasis: {
                          itemStyle: {
                              //环形颜色
                              color: 'red',
                              //边框颜色
                              borderColor: '#fff',
                              shadowBlur: 10,
                              shadowColor: '#99ffff'
                          }
                      },
                      //高亮区域
                      highlight: {
                          itemStyle: {
                              color: 'orange',
                              borderColor: '#fff',
                              shadowBlur: 10,
                              shadowColor: '#99ffff'
                          }
                      },
                      //非选定区域
                      downplay: {
                          itemStyle: {
                              color: '#1e1e1e',
                              shadowBlur: 0,
                              //透明度
                              opacity: 0.3
                          },
                          label: {
                              //透明度
                              opacity: 0.3
                          }
                      },
                      //levels设置各层次环形宽度
                      levels: [{}, {
                          //内圈半径
                          r0: '10%',
                          //外圈半径
                          r: '30%',
                          itemStyle: {
                              //边框宽度
                              borderWidth: 2
                          }
                      }, {
                          r0: '30%',
                          r: '50%',
                          itemStyle: {
                              borderWidth: 2
                          }                    
                      }, {
                          r0: '50%',
                          r: '70%',
                          itemStyle: {
                              borderWidth: 2
                          }                     
                      }, {
                          r0: '70%',
                          r: '75%',
                          label: {
                              //文字显示在外侧
                              position: 'outside',
                              //距离环形的距离
                              padding: 3,
                          },
                          itemStyle: {
                              borderWidth: 3,
                              //阴影半径
                              shadowBlur: 10,
                              //阴影颜色,当前是亮色
                              shadowColor: '#99ffff'
                          }
                      }]
                  }]
              };    
              // 使用刚指定的配置项和数据显示图表。
              myChartSun1.setOption(option);
      
          </script>
      </body>
      </html>
      
  2. 动画交互(选学,但无疑是最吸引人的部分):

    接下来笔者将介绍本章节最核心的部分。虽然是选学,但是笔者强烈希望读者可以了解这部分内容,并且将其应用于自己的图表中(即使不了解,你也可以通过修改数据使用它)。

    旭日图类似于扇形图,实际情况中很多人对这种扇形的面积信息很不敏感,无法从图形中获得各层次节点的数值分布。对于这个需求,直方图更加适合。于是笔者考虑,能否将两个图表组合起来,当用户选择某节点时,通过直方图将子节点的数值信息展示出来。

    在本节中,笔者将分三步介绍交互效果的实现过程:

    1. 在旭日图旁创建一个新的直方图,显示旭日图第一层级的数据;
    2. 新建一个[mouseover]事件,当用户鼠标悬浮于某节点时,直方图的数据更新为其子节点的[value]值;
    3. 引入新维度的数据。读者可以结合业务需求选择性学习该部分内容。

    接下来我们正式开始。

    1. 创建直方图:

      创建直方图的方式与前文所述的散点图类似,区别是直方图的x轴类目相对较少,这部分不再赘述。我们主要是调整旭日图的显示位置,即令旭日图的容器宽度减半且左移,再右侧新建一个容器,以便展示直方图。此时的完整代码如下:

      <!DOCTYPE html>
      <html>
      <head>
          <meta charset="utf-8">
          <title>ECharts</title>
          <!-- 引入 echarts.js -->
          <script type="text/javascript" src="https://cdn.jsdelivr.net/npm/echarts/dist/echarts.min.js"></script>
          <script type="text/javascript" src="https://cdn.jsdelivr.net/npm/echarts-gl/dist/echarts-gl.min.js"></script>
          <script type="text/javascript" src="https://cdn.jsdelivr.net/npm/echarts-stat/dist/ecStat.min.js"></script>
          <script type="text/javascript" src="https://cdn.jsdelivr.net/npm/echarts/dist/extension/dataTool.min.js"></script>
          <script type="text/javascript" src="https://cdn.jsdelivr.net/npm/echarts/map/js/china.js"></script>
          <script type="text/javascript" src="https://cdn.jsdelivr.net/npm/echarts/map/js/world.js"></script>
          <script type="text/javascript" src="https://cdn.jsdelivr.net/npm/echarts/dist/extension/bmap.min.js"></script>
          <script type="text/javascript" src="https://www.jb51.net/jslib/jquery/jquery.min.js"></script>
      </head>
      <body>
          <!-- 为ECharts准备一个具备大小(宽高)的Dom -->
          <!-- float设置浮动方式,保证两个div显示在同一排(因为我们的容器宽度是自适应的) -->
          <div id="sun1" style="width:50%;height:900px;float:left;"></div>
          <div id="bar1" style="width:50%;height:900px;float:left;"></div>
          <script type="text/javascript">
              // 基于准备好的dom,初始化echarts实例
              var myChartSun1 = echarts.init(document.getElementById('sun1'));
              var myChartBar1 = echarts.init(document.getElementById('bar1'));
              //旭日图所需数据结构
              var data = [{
                  name: '正式平台',
                  value: 9487,
                  children: [{
                      name: '有地址',
                      value: 9357,
                      children: [{
                          name: '6选3',
                          value: 2851,
                          children: [{
                              name: '北京市',
                              value: 2336
                          },{
                              name: '山东省',
                              value: 425                        
                          },{
                              name: '天津市',
                              value: 83                        
                          },{
                              name: '上海市',
                              value: 7                        
                          }]
                      }, {
                          name: '3+1+2',
                          value: 5321,
                          children: [{
                              name: '广东省',
                              value: 2068
                          },{
                              name: '福建省',
                              value: 1582,
                              //数据下可设置格式
                              // itemStyle: {
                              //     shadowBlur: 10,
                              //     shadowColor: '#99ffff'
                              // },                        
                          },{
                              name: '重庆市',
                              value: 718                        
                          },{
                              name: '江苏省',
                              value: 301                        
                          },{
                              name: '湖北省',
                              value: 257                        
                          },{
                              name: '河北省',
                              value: 222                        
                          },{
                              name: '湖南省',
                              value: 116                        
                          },{
                              name: '辽宁省',
                              value: 57                        
                          }]
                      }, {
                          name: '7选3-浙江',
                          value: 28
                      }, {
                          name: '未改革',
                          value: 1157 ,
                          children:[{
                              name:'安徽省',
                              value: 460
                          },{
                              name:'陕西省',
                              value:286
                          },{
                              name:'四川省',
                              value:126
                          },{
                              name:'新疆',
                              value:114
                          },{
                              name:'云南省',
                              value:66
                          },{
                              name:'广西',
                              value:34
                          },{
                              name:'江西省',
                              value:27
                          },{
                              name:'甘肃省',
                              value:17
                          },{
                              name:'河南省',
                              value:14
                          },{
                              name:'山西省',
                              value:13
                          }]
                      }]
                  }, {
                      name: '无地址',
                      value: 130
                  }]
              }, {
                  name: '培训平台',
                  value: 3215,
                  children: [{
                      name: '智校测试校',
                      children: [{
                          name: '培训',
                          value: 1449
                      }, {
                          name: '排课',
                          value: 1325
                      }]
                  },{
                      name:'新课改培训',
                      value: 441
                  }]
              }, {
                  name: '测试+演示平台',
                  value: 1046,
                  children: [{
                      name: '测试',
                      value: 464
                      }, {
                          name: '演示',
                          value: 582
                      }
                  ]
              }, {
                  name: '职教+旧平台',
                  value: 1239
              }];
      
              var option = {
                  backgroundColor:'#000',
                  tooltip: {
                      trigger: 'item',
                      formatter: function (params) {
                          return params.name + ' : ' + params.value;
                      }
                  },
                  visualMap: {
                      min: 0,
                      max: 2000,
                      calculable: true,
                      inRange: {
                          color: ['#50a3ba', '#eac736', '#d94e5d']
                      },
                      textStyle: {
                          color: '#fff'
                      }
                  },
                  series: [{
                      type: 'sunburst',
                      id:'sunburst1',
                      // highlightPolicy: 'ancestor',
                      data: data,
                      center:['50%','50%'],
                      radius: [10, '90%'],
                      //高亮当前及其子区间
                      //highlightPolicy: 'descendant',
                      label: {
                          rotate: 'radial',
                          fontSize: 15
                      },
                      //选定区域
                      emphasis: {
                          itemStyle: {
                              //环形颜色
                              color: 'red',
                              //边框颜色
                              borderColor: '#fff',
                              shadowBlur: 10,
                              shadowColor: '#99ffff'
                          }
                      },
                      //高亮区域
                      highlight: {
                          itemStyle: {
                              color: 'orange',
                              borderColor: '#fff',
                              shadowBlur: 10,
                              shadowColor: '#99ffff'
                          }
                      },
                      //非选定区域
                      downplay: {
                          itemStyle: {
                              color: '#1e1e1e',
                              shadowBlur: 0,
                              //透明度
                              opacity: 0.3
                          },
                          label: {
                              //透明度
                              opacity: 0.3
                          }
                      },
                      //levels设置各层次环形宽度
                      levels: [{}, {
                          //内圈半径
                          r0: '10%',
                          //外圈半径
                          r: '30%',
                          itemStyle: {
                              //边框宽度
                              borderWidth: 2
                          }
                      }, {
                          r0: '30%',
                          r: '50%',
                          itemStyle: {
                              borderWidth: 2
                          }                    
                      }, {
                          r0: '50%',
                          r: '70%',
                          itemStyle: {
                              borderWidth: 2
                          }                     
                      }, {
                          r0: '70%',
                          r: '75%',
                          label: {
                              //文字显示在外侧
                              position: 'outside',
                              //距离环形的距离
                              padding: 3,
                          },
                          itemStyle: {
                              borderWidth: 3,
                              //阴影半径
                              shadowBlur: 10,
                              //阴影颜色,当前是亮色
                              shadowColor: '#99ffff'
                          }
                      }]
                  }]
              };
             //另一个option,设置直方图的图表信息
              var option1 = {
                  backgroundColor:'#000',
                  tooltip: {
                      trigger: 'item',
                      formatter: function (params) {
                          return params.name + ' : ' + params.value;
                      }
                  },
                  visualMap: {
                      min: 0,
                      max: 2000,
                      calculable: true,
                      show: false,
                      seriesIndex:0,
                      inRange: {
                          color: ['#50a3ba', '#eac736', '#d94e5d']
                      },
                      textStyle: {
                          color: '#fff'
                      }
                  },
                  //柱形图设置
                  legend:{
                      data:['调用次数','学校数','真实学校数'],
                      textStyle:{
                          color:'#fff'
                      },
                      top: '1%',
                      selected:{
                          '调用次数':true,
                          '学校数':false,
                          '真实学校数':false                    
                      }
                  },
                  xAxis: {
                      type: 'category',
                      data:['正式平台','培训平台','职教+旧平台','测试+演示平台'],
      
                      axisLine:{
                          lineStyle:{
                              type:'solid',
                              color:'#fff',
                              width:1
                          }
                      },
                      //旋转90度显示
                      // axisLabel:{
                      //     rotate:90
                      // }
                  },
                  yAxis: [{
                      name:'调用次数',
                      type: 'value',
                      //坐标内分割线
                      splitLine:{
                          lineStyle:{
                              type:'dashed'
                          }
                      },
                      axisLine:{
                          lineStyle:{
                              color:'#fff',
                              width:1,//这里是为了突出显示加上的
                          }
                      }
                  },{//是否显示数值分割线
                      splitLine:{
                          show:false
                      },
                      //max:100,
                      axisLine:{
                          lineStyle:{
                              color:'#fff',
                              width:1,//这里是为了突出显示加上的
                          }
                      }
                  }],
                  grid: {
                      left: '10%',
                      right: '10%',
                      bottom: '7%',
                      containLabel: true
                  },            
                  series: {
                      name:'调用次数',
                      id: 'bar1',
                      type: 'bar',
                      //柱形图添加边框及阴影
                      itemStyle:{
                          normal:{
                              borderWidth: 1,
                              borderColor: '#fff',                        
                              shadowBlur: 5,
                              shadowColor: '#99ffff'
                          }
                      },
                      data: data
      
                  }
              };    
              // 使用刚指定的配置项和数据显示图表。
              myChartSun1.setOption(option);
              myChartBar1.setOption(option1);
          </script>
      </body>
      </html>
      

      我们创建了初始的直方图,显示在旭日图右方。

      Echarts更新动画的方式为重新绘制画布,而重新绘制画布会导致旭日图当前下钻的效果被重置。如果将两个图表放在一个容器中,每次更新直方图,旭日图也会被重置为最初的状态。只有将两者分开,旭日图的下钻状态(根节点为某个子节点)才会被保留。

      当前数据取自旭日图,自动显示第一层的内容。关于直方图的坐标轴美化方法,可以参考第一个故事中的散点图的设置。现在的图表如下所示:

      image

      好了,我们成功拼接了两个图表。接下来我们需要做的,是将两个图表联动起来。

    2. 新建[mouseover]事件:

      为了实现两个图表的联动,我们需要新建一个[mouseover](鼠标悬浮)事件。当鼠标悬浮在旭日图的某个节点上时,若其有子节点,则根据其子节点数据重新绘制直方图。

      我们在<script>代码块中编码,写入该事件,完整代码如下:

      <!DOCTYPE html>
      <html>
      <head>
          <meta charset="utf-8">
          <title>ECharts</title>
          <!-- 引入 echarts.js -->
          <script type="text/javascript" src="https://cdn.jsdelivr.net/npm/echarts/dist/echarts.min.js"></script>
          <script type="text/javascript" src="https://cdn.jsdelivr.net/npm/echarts-gl/dist/echarts-gl.min.js"></script>
          <script type="text/javascript" src="https://cdn.jsdelivr.net/npm/echarts-stat/dist/ecStat.min.js"></script>
          <script type="text/javascript" src="https://cdn.jsdelivr.net/npm/echarts/dist/extension/dataTool.min.js"></script>
          <script type="text/javascript" src="https://cdn.jsdelivr.net/npm/echarts/map/js/china.js"></script>
          <script type="text/javascript" src="https://cdn.jsdelivr.net/npm/echarts/map/js/world.js"></script>
          <script type="text/javascript" src="https://cdn.jsdelivr.net/npm/echarts/dist/extension/bmap.min.js"></script>
          <script type="text/javascript" src="https://www.jb51.net/jslib/jquery/jquery.min.js"></script>
      </head>
      <body>
          <!-- 为ECharts准备一个具备大小(宽高)的Dom -->
          <!-- float设置浮动方式,保证两个div显示在同一排(因为我们的容器宽度是自适应的) -->
          <div id="sun1" style="width:50%;height:900px;float:left;"></div>
          <div id="bar1" style="width:50%;height:900px;float:left;"></div>
          <script type="text/javascript">
              // 基于准备好的dom,初始化echarts实例
              var myChartSun1 = echarts.init(document.getElementById('sun1'));
              var myChartBar1 = echarts.init(document.getElementById('bar1'));
              //旭日图所需数据结构
              var data = [{
                  name: '正式平台',
                  value: 9487,
                  children: [{
                      name: '有地址',
                      value: 9357,
                      children: [{
                          name: '6选3',
                          value: 2851,
                          children: [{
                              name: '北京市',
                              value: 2336
                          },{
                              name: '山东省',
                              value: 425                        
                          },{
                              name: '天津市',
                              value: 83                        
                          },{
                              name: '上海市',
                              value: 7                        
                          }]
                      }, {
                          name: '3+1+2',
                          value: 5321,
                          children: [{
                              name: '广东省',
                              value: 2068
                          },{
                              name: '福建省',
                              value: 1582,
                              //数据下可设置格式
                              // itemStyle: {
                              //     shadowBlur: 10,
                              //     shadowColor: '#99ffff'
                              // },                        
                          },{
                              name: '重庆市',
                              value: 718                        
                          },{
                              name: '江苏省',
                              value: 301                        
                          },{
                              name: '湖北省',
                              value: 257                        
                          },{
                              name: '河北省',
                              value: 222                        
                          },{
                              name: '湖南省',
                              value: 116                        
                          },{
                              name: '辽宁省',
                              value: 57                        
                          }]
                      }, {
                          name: '7选3-浙江',
                          value: 28
                      }, {
                          name: '未改革',
                          value: 1157 ,
                          children:[{
                              name:'安徽省',
                              value: 460
                          },{
                              name:'陕西省',
                              value:286
                          },{
                              name:'四川省',
                              value:126
                          },{
                              name:'新疆',
                              value:114
                          },{
                              name:'云南省',
                              value:66
                          },{
                              name:'广西',
                              value:34
                          },{
                              name:'江西省',
                              value:27
                          },{
                              name:'甘肃省',
                              value:17
                          },{
                              name:'河南省',
                              value:14
                          },{
                              name:'山西省',
                              value:13
                          }]
                      }]
                  }, {
                      name: '无地址',
                      value: 130
                  }]
              }, {
                  name: '培训平台',
                  value: 3215,
                  children: [{
                      name: '智校测试校',
                      children: [{
                          name: '培训',
                          value: 1449
                      }, {
                          name: '排课',
                          value: 1325
                      }]
                  },{
                      name:'新课改培训',
                      value: 441
                  }]
              }, {
                  name: '测试+演示平台',
                  value: 1046,
                  children: [{
                      name: '测试',
                      value: 464
                      }, {
                          name: '演示',
                          value: 582
                      }
                  ]
              }, {
                  name: '职教+旧平台',
                  value: 1239
              }];
      
              var option = {
                  backgroundColor:'#000',
                  tooltip: {
                      trigger: 'item',
                      formatter: function (params) {
                          return params.name + ' : ' + params.value;
                      }
                  },
                  visualMap: {
                      min: 0,
                      max: 2000,
                      calculable: true,
                      inRange: {
                          color: ['#50a3ba', '#eac736', '#d94e5d']
                      },
                      textStyle: {
                          color: '#fff'
                      }
                  },
                  series: [{
                      type: 'sunburst',
                      id:'sunburst1',
                      // highlightPolicy: 'ancestor',
                      data: data,
                      center:['50%','50%'],
                      radius: [10, '90%'],
                      //高亮当前及其子区间
                      //highlightPolicy: 'descendant',
                      label: {
                          rotate: 'radial',
                          fontSize: 15
                      },
                      //选定区域
                      emphasis: {
                          itemStyle: {
                              //环形颜色
                              color: 'red',
                              //边框颜色
                              borderColor: '#fff',
                              shadowBlur: 10,
                              shadowColor: '#99ffff'
                          }
                      },
                      //高亮区域
                      highlight: {
                          itemStyle: {
                              color: 'orange',
                              borderColor: '#fff',
                              shadowBlur: 10,
                              shadowColor: '#99ffff'
                          }
                      },
                      //非选定区域
                      downplay: {
                          itemStyle: {
                              color: '#1e1e1e',
                              shadowBlur: 0,
                              //透明度
                              opacity: 0.3
                          },
                          label: {
                              //透明度
                              opacity: 0.3
                          }
                      },
                      //levels设置各层次环形宽度
                      levels: [{}, {
                          //内圈半径
                          r0: '10%',
                          //外圈半径
                          r: '30%',
                          itemStyle: {
                              //边框宽度
                              borderWidth: 2
                          }
                      }, {
                          r0: '30%',
                          r: '50%',
                          itemStyle: {
                              borderWidth: 2
                          }                    
                      }, {
                          r0: '50%',
                          r: '70%',
                          itemStyle: {
                              borderWidth: 2
                          }                     
                      }, {
                          r0: '70%',
                          r: '75%',
                          label: {
                              //文字显示在外侧
                              position: 'outside',
                              //距离环形的距离
                              padding: 3,
                          },
                          itemStyle: {
                              borderWidth: 3,
                              //阴影半径
                              shadowBlur: 10,
                              //阴影颜色,当前是亮色
                              shadowColor: '#99ffff'
                          }
                      }]
                  }]
              };
              var option1 = {
                  backgroundColor:'#000',
                  tooltip: {
                      trigger: 'item',
                      formatter: function (params) {
                          return params.name + ' : ' + params.value;
                      }
                  },
                  visualMap: {
                      min: 0,
                      max: 2000,
                      calculable: true,
                      show: false,
                      seriesIndex:0,
                      inRange: {
                          color: ['#50a3ba', '#eac736', '#d94e5d']
                      },
                      textStyle: {
                          color: '#fff'
                      }
                  },
                  //柱形图设置
                  legend:{
                      data:['调用次数','学校数','真实学校数'],
                      textStyle:{
                          color:'#fff'
                      },
                      top: '1%',
                      selected:{
                          '调用次数':true,
                          '学校数':false,
                          '真实学校数':false                    
                      }
                  },
                  xAxis: {
                      type: 'category',
                      data:['正式平台','培训平台','职教+旧平台','测试+演示平台'],
      
                      axisLine:{
                          lineStyle:{
                              type:'solid',
                              color:'#fff',
                              width:1
                          }
                      },
                      //旋转90度显示
                      // axisLabel:{
                      //     rotate:90
                      // }
                  },
                  yAxis: [{
                      name:'调用次数',
                      type: 'value',
                      //坐标内分割线
                      splitLine:{
                          lineStyle:{
                              type:'dashed'
                          }
                      },
                      axisLine:{
                          lineStyle:{
                              color:'#fff',
                              width:1,//这里是为了突出显示加上的
                          }
                      }
                  },{//是否显示数值分割线
                      splitLine:{
                          show:false
                      },
                      //max:100,
                      axisLine:{
                          lineStyle:{
                              color:'#fff',
                              width:1,//这里是为了突出显示加上的
                          }
                      }
                  }],
                  grid: {
                      left: '10%',
                      right: '10%',
                      bottom: '7%',
                      containLabel: true
                  },            
                  series: {
                      name:'调用次数',
                      id: 'bar1',
                      type: 'bar',
                      //柱形图添加边框及阴影
                      itemStyle:{
                          normal:{
                              borderWidth: 1,
                              borderColor: '#fff',                        
                              shadowBlur: 5,
                              shadowColor: '#99ffff'
                          }
                      },
                      data: data
      
                  }
              };
              //新建mouseover事件,且仅当对象为series时启发,并传入回调函数parmas,parmas包含子节点的信息
              myChartSun1.on('mouseover','series', function (params) {
                  //仅当选取对象为旭日图seriesType===sunburst时生效            
                  if(params.seriesType === 'sunburst' ){
                      //新建一个空的横坐标类目的列表
                      var xAxisData = [];
                      //存在子元素时,直方图显示子元素结果    
                      if(params.data.children){
                          //将横坐标类目push入 xAxisData 中
                          for(var i = 0; i < params.data.children.length; i++){
                              xAxisData.push(params.data.children[i].name);                    
                          };
                          //重新绘制直方图
                          myChartBar1.setOption({
                              xAxis:{
                                  data:xAxisData
                              },
                              series:[{
                                  id:'bar1',
                                  data: params.data.children
      
                              }]
                          })
                      }
                  }
              });    
              // 使用刚指定的配置项和数据显示图表。
              myChartSun1.setOption(option);
              myChartBar1.setOption(option1);
          </script>
      </body>
      </html>
      

      笔者用通俗语言简单解释一下代码内容。这段代码完成的工作相当简单,就是当事件触发时,将当前节点的全部子节点 [name] 信息保存,并传入给直方图的横坐标,并且将 [data] 替换为全部子节点的 [value] 值。根据这些调整,Echarts将自动选择合理的动画效果,展示新的直方图。

      最终的图表效果如下:

      image
    3. 引入新维度的数据:

      所谓引入新的数据,实际上是扩充直方图每个类目下数据的项数。如下图所示,对于每个类目,除了显示引擎的调用次数外,我们还新增了[学校数]和[真实学校数]两项,用以比较三个数据间的比例情况。

      这里笔者不再给出完整代码,具体的实现方式读者可以自己思考。

      image
  1. 图表的意义-它述说了怎样的故事

    数据本身是虚拟出来的,不过我们可以尝试分析下通过图表能够获得的信息。

    1. 目前使用引擎的地区中,政策为‘3+1+2’的地区使用次数最多,‘6选3’次之,‘未改革’地区最少,说明我们的排课引擎主要的用户还是新高考改革地区。

      ‘3+1+2’地区虽然是改革的第一年,但很快就超越了‘6选3’地区。之后的引擎改进,应当把‘3+1+2’地区的政策特征作为主要考虑的对象(之前主要满足‘6选3’地区的需求);

    2. 引擎的使用集中于几个省份;

    3. 读者可以自己思考下,通过图表还可以获得哪些信息(建议读者更换自己的真实数据进行分析)。

故事结束

第三个故事主要讲解旭日图的使用,以及如何联动两个图表。

我们都知道,扇形图(旭日图)主要体现比例的分配,而直方图主要体现具体的数值(包括最大值最小值),以及数值的分布。显然,两个图表在数据的抽象化表达上是互补的。我们将两个图表结合,并且提供一定的联动效果,用户通过自己的操作,可以获得相当多的信息。

这种图表组合其实代表了一种新的数据展示方式。我们不再生成静态图表,而是引导用户和图表进行交互。

这种做法保持了图表的直观性,同时又允许读者按自己的方法来探索数据。

是的,在这里我们不仅扮演了数据导游的身份,还将数据本身“玩具化”。任何一个用户,不需要专业的知识,只通过简单的操作就可以去探索数据。这种独特的体验,一般的静态图表是无法带来的。

当然,动态的图表无法被引用在ppt(或者所有只能引用静态图片的软件)中。要么把这个图表作为一个单独的h5页面,以超链接的形式引用。要么,可以了解下hype4,应用这个高保真原型绘制工具,将图表与h5页面有机组合在一起(笔者有hype的操作手册编写计划,但是时间未定)。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,456评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,370评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,337评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,583评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,596评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,572评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,936评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,595评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,850评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,601评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,685评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,371评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,951评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,934评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,167评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,636评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,411评论 2 342