
需求分析
前情提要
同心多扇形图,用于表示树形数据结构以及每一层级节点在该层总量中占比。
原始数据结构:
{ "name": "师资队伍", "value": 19, "children": [ { "name": "基本情况", "value": 3, "children": [ { "name": "师资规模", "value": 2 }, { "name": "进修情况", "value": 1 } ] }, { "name": "师资结构", "value": 6, "children": [ { "name": "职称结构", "value": 2 }, { "name": "学历结构", "value": 1 }, { "name": "学缘结构", "value": 3 } ] }, { "name": "师资水平", "value": 10, "children": [ { "name": "资深学术权威", "value": 2 }, { "name": "中年领军专家", "value": 2 }, { "name": "青年拔尖英才", "value": 4 }, { "name": "国际知名学者", "value": 2 } ] } ] }
以上仅显示了一个根节点,其他根节点的数据结构类似。
PS: 子节点值相加,和等于父节点的值。
需求定义
- 绘制同心多级扇形图,可视化显示节点数值以及在同深度占比情况。
- 当某一根节点legend点击隐藏或显示时,动态更新其关联的所有子节点。
echarts
简介
echarts是一款JS可视化图表生成工具,由百度提供.
GitHub:
https://github.com/ecomfe/echarts
官网:
http://echarts.baidu.com/index.html
相比于其他图表生成工具:
等, echarts 的风格对国内开发者来说, 显然更加友好。
获取引入
NPM:
npm install echarts --save
下载地址:
http://echarts.baidu.com/download.html
CDN:
https://cdnjs.cloudflare.com/ajax/libs/echarts/3.8.5/echarts.min.js
情景分析
通过查看echarts官方实例(CV大法),我们可以轻松实现 要求1 :
绘制同心多级扇形图,可视化显示节点数值以及在同深度占比情况。
http://echarts.baidu.com/demo.html#pie-nest
然而即使翻遍文档也没有可拿的代码来实现 要求2:
当点击legend 控制节点数据的隐藏或显示时,动态更新其关联的所有子节点。
PS: CV大法虽好使,可不要过度依赖!
需求实现
演变数据结构
显然,原始数据结构在这里很畸形,我们需要解析并更改使之更适用于现在这种情形。
无论使用循环还是递归来演变(这里省略了这一过程,线性组合),我们需要的最终数据结构如下:
{ "level1": [ { "name": "师资队伍", "color": "#2196F3", "category": 0, "value": 19 } ], "level2": [ { "name": "基本情况", "color": "#2196F3", "category": 0, "value": 3 }, { "name": "师资结构", "color": "#E3F2FD", "category": 0, "value": 6 }, { "name": "师资水平", "color": "#BBDEFB", "category": 0, "value": 10 } ], "level3": [ { "name": "师资规模", "color": "#2196F3", "category": 0, "value": 2 }, { "name": "进修情况", "color": "#E3F2FD", "category": 0, "value": 1 }, { "name": "职称结构", "color": "#BBDEFB", "category": 0, "value": 2 }, { "name": "学历结构", "color": "#90CAF9", "category": 0, "value": 1 }, { "name": "学缘结构", "color": "#64B5F6", "category": 0, "value": 3 }, { "name": "资深学术权威", "color": "#42A5F5", "category": 0, "value": 2 }, { "name": "中年领军专家", "color": "#2196F3", "category": 0, "value": 2 }, { "name": "青年拔尖英才", "color": "#1E88E5", "category": 0, "value": 4 }, { "name": "国际知名学者", "color": "#1976D2", "category": 0, "value": 2 } ] }
数据联动
一切关联问题的解决, 都只在于一个唯一标识的确立,这里我们使用的是 category (根节点数组下标)。
万事俱备,只欠一个 触发事件(国内外知名插件对于事件的封装和处理都很到位,API也是极尽简单)。
翻阅文档: http://echarts.baidu.com/api.html#events
legend点选事件:
myChart.on('legendselectchanged', function (params) { console.log(params); });
具体实现可在 源码 中查看。
PS
别问我color怎么来的,我会告诉你吗?
如果翻墙了,可以参考这个网站(专业配色):
https://material.io/guidelines/style/color.html#color-color-palette
部分颜色截图:

我把这些组成了一个JSON以备不时之需, 如下所示:
{ "data": [ { "category": "蓝色", "color": "#2196F3", "sub": ["#2196F3","#E3F2FD", "#BBDEFB", "#90CAF9", "#64B5F6", "#42A5F5","#2196F3", "#1E88E5", "#1976D2", "#1565C0", "#0D47A1","#82B1FF", "#448AFF", "#2979FF", "#2962FF"] }, { "category": "红色", "color": "#F44336", "sub": ["#F44336","#FFEBEE", "#FFCDD2", "#EF9A9A", "#E57373", "#EF5350","#F44336", "#E53935", "#D32F2F", "#C62828", "#B71C1C","#FF8A80", "#FF5252", "#FF1744", "#D50000"] }, { "category": "绿色", "color": "#4CAF50", "sub": ["#4CAF50","#E8F5E9", "#C8E6C9", "#A5D6A7", "#81C784", "#66BB6A","#4CAF50", "#43A047", "#388E3C", "#2E7D32", "#1B5E20"] }, { "category": "橘色", "color": "#FF9800", "sub": ["#FFF3E0","#FFE0B2", "#FFCC80", "#FFB74D", "#FFA726", "#FF9800","#FB8C00", "#F57C00", "#EF6C00", "#E65100", "#FFD180","#FFAB40", "#FF9100", "#FF6D00"] }, { "category": "紫色", "color": "#9C27B0", "sub": ["#F3E5F5","#E1BEE7", "#CE93D8", "#BA68C8", "#AB47BC", "#9C27B0","#8E24AA", "#7B1FA2", "#6A1B9A", "#4A148C", "#EA80FC","#E040FB", "#D500F9", "#AA00FF"] } ] }
源码
我将这一段代码单独提取出来放在这里, 本地新建.html文件, 复制粘贴即可在浏览器查看:
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>chart</title> </head> <body> <div id="graph" style="height: 600px;"></div> <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/echarts/3.8.5/echarts.min.js"></script> <script type="text/javascript"> (function(config){ let chart = echarts.init(document.getElementById("graph")); /** * @params params * params.name 标识当前点击的legend * params.selected 标识当前选中的legend集合 */ chart.on('legendselectchanged', function (params) { let options = this.getOption(), keys = Object.keys(params.selected), selected = []; // 获取选中目录的 category 值 for(let item of keys) { if(params.selected[item]) { for(let term of config.level1) if(item == term.name) selected.push(term.category); } } // 根据 category 获取 series 中 data 数据项 options.series[1].data = config.level2.filter(item => { return selected.includes(item.category); }); options.series[2].data = config.level3.filter(item => { return selected.includes(item.category); }); this.setOption(options); }); chart.setOption({ tooltip: { trigger: 'item', formatter: "{a} <br/>{b}: {c} ({d}%)" }, legend: { orient: 'vertical', x: 'left', data:['师资队伍','学科建设','人才培养','科学研究','国际化程度'] }, series: [ { name:'一级指标', type:'pie', label: { normal: { show: false, } }, itemStyle: { normal: { /** * 自定义饼图 扇形 颜色 * @param params */ color(params) { return params.data.color; } } }, radius: ['15%', '30%'], data: config.level1 }, { name:'二级指标', type:'pie', label: { normal: { show: false, } }, itemStyle: { normal: { color(params) { return params.data.color; } } }, radius: ['40%', '55%'], data: config.level2 }, { name:'三级指标', type:'pie', radius: ['65%', '80%'], itemStyle: { normal: { color(params) { return params.data.color; } } }, data: config.level3 } ] }); })({ "level1": [ { name: "师资队伍", color: "#2196F3", category: 0, value: 19 }, { name: "学科建设", color: "#F44336", category: 1, value: 10 }, { name: "人才培养", color: "#4CAF50", category: 2, value: 21 }, { name: "科学研究", color: "#FF9800", category: 3, value: 35 }, { name: "国际化程度", color: "#9C27B0", category: 4, value: 6 } ], "level2": [ { name: "基本情况", color: "#2196F3", category: 0, value: 3 }, { name: "师资结构", color: "#E3F2FD", category: 0, value: 6 }, { name: "师资水平", color: "#BBDEFB", category: 0, value: 10 }, { name: "学科基础", color: "#F44336", category: 1, value: 7 }, { name: "学科水平", color: "#FFEBEE", category: 1, value: 3 }, { name: "基本情况", color: "#4CAF50", category: 2, value: 4 }, { name: "培养条件", color: "#E8F5E9", category: 2, value: 7 }, { name: "培养结果", color: "#C8E6C9", category: 2, value: 10 }, { name: "科研条件", color: "#FFF3E0", category: 3, value: 2 }, { name: "科研项目", color: "#FFE0B2", category: 3, value: 10 }, { name: "科研论文", color: "#FFCC80", category: 3, value: 11 }, { name: "科研获奖", color: "#FFB74D", category: 3, value: 12 }, { name: "国际化人员", color: "#F3E5F5", category: 4, value: 2 }, { name: "合作教学", color: "#E1BEE7", category: 4, value: 2 }, { name: "国际化成果", color: "#CE93D8", category: 4, value: 2 } ], "level3": [ { name: "师资规模", color: "#2196F3", category: 0, value: 2 }, { name: "进修情况", color: "#E3F2FD", category: 0, value: 1 }, { name: "职称结构", color: "#BBDEFB", category: 0, value: 2 }, { name: "学历结构", color: "#90CAF9", category: 0, value: 1 }, { name: "学缘结构", color: "#64B5F6", category: 0, value: 3 }, { name: "资深学术权威", color: "#42A5F5", category: 0, value: 2 }, { name: "中年领军专家", color: "#2196F3", category: 0, value: 2 }, { name: "青年拔尖英才", color: "#1E88E5", category: 0, value: 4 }, { name: "国际知名学者", color: "#1976D2", category: 0, value: 2 }, { name: "建设基础", color: "#F44336", category: 1, value: 2 }, { name: "建设强度", color: "#FFEBEE", category: 1, value: 2 }, { name: "学科层次", color: "#FFCDD2", category: 1, value: 3 }, { name: "学科排名", color: "#EF9A9A", category: 1, value: 2 }, { name: "学科竞争力", color: "#E57373", category: 1, value: 1 }, { name: "研究生培养规模", color: "#4CAF50", category: 2, value: 3 }, { name: "培养层次", color: "#E8F5E9", category: 2, value: 1 }, { name: "课程资源", color: "#C8E6C9", category: 2, value: 4 }, { name: "教授任课情况", color: "#A5D6A7", category: 2, value: 3 }, { name: "教学成果", color: "#81C784", category: 2, value: 5 }, { name: "毕业生就业情况", color: "#66BB6A", category: 2, value: 1 }, { name: "造就学术人才", color: "#4CAF50", category: 2, value: 4 }, { name: "科研经费", color: "#FFF3E0", category: 3, value: 2 }, { name: "重大重点项目", color: "#FFE0B2", category: 3, value: 6 }, { name: "一般青年项目", color: "#FFCC80", category: 3, value: 4 }, { name: "中文期刊论文", color: "#FFB74D", category: 3, value: 1 }, { name: "中文顶尖期刊论文", color: "#FFA726", category: 3, value: 1 }, { name: "国际期刊论文", color: "#FF9800", category: 3, value: 5 }, { name: "国际顶尖期刊论文", color: "#FB8C00", category: 3, value: 2 }, { name: "专著专利", color: "#F57C00", category: 3, value: 2 }, { name: "国际三大科技奖", color: "#EF6C00", category: 3, value: 7 }, { name: "社会科学重大成果", color: "#E65100", category: 3, value: 4 }, { name: "国际权威奖项", color: "#FFD180", category: 3, value: 1 }, { name: "国际化教师", color: "#F3E5F5", category: 4, value: 1 }, { name: "国际化学生", color: "#E1BEE7", category: 4, value: 1 }, { name: "合作教学", color: "#CE93D8", category: 4, value: 2 }, { name: "国际合作论文", color: "#BA68C8", category: 4, value: 2 } ] }); </script> </body> </html>