备考数据可视化的那段时间,不知道为什么总喜欢在很晚的时候看文档。

窗外安静,屏幕亮着,翻着翻着就忽然觉得——ECharts 这个东西,好像不太一样。

它不像平时写 Java 或者 Python,不是在命令计算机”先做这个,再做那个”。更像是在填一张小表格。你把标题放进 title,把坐标轴放进 xAxis,把数据放进 series,然后图就出来了。很乖,不问多余的问题。

后来想通了:ECharts 不需要死背。先把骨架记住,再根据图表类型换零件就好。外套不一样,人还是那个人。


一、通用骨架

大部分图表都长这样:

1
2
3
4
5
6
7
8
9
10
11
12
var option = {
title: { text: '图表标题' },
tooltip: {},
legend: { data: ['系列名称'] },
xAxis: { type: 'category', data: ['A', 'B', 'C'] },
yAxis: { type: 'value' },
series: [{
name: '系列名称',
type: 'bar',
data: [10, 20, 30]
}]
};

需要记住的字段只有六个:

1
2
3
4
5
6
title    // 标题
tooltip // 悬停提示
legend // 图例
xAxis // x 轴
yAxis // y 轴
series // 图表主体

其中 series 是整张图的心脏。图是柱状图、折线图、饼图,还是散点图,就看 type 写什么。换一个词,图表就换了一副模样。


二、柱状图和折线图

这两个最省心,就像填空题。

1
2
3
4
5
6
7
8
9
10
11
xAxis: {
type: 'category',
data: ['一月', '二月', '三月']
},
yAxis: {
type: 'value'
},
series: [{
type: 'bar',
data: [120, 200, 150]
}]

categoryvalue 的区别,记这一句话就够:

1
2
category 是分类,自己写 data(月份、城市、科目这种)
value 是数值,ECharts 自己算刻度,不用写

如果想把柱状图和折线图叠在一起,在 series 里多放一个对象就行:

1
2
3
4
series: [
{ name: '销量', type: 'bar', data: [120, 200, 150] },
{ name: '增长', type: 'line', data: [100, 160, 130] }
]

一个 {} 一组图,想叠几个放几个。


三、两个 Y 轴

有时候两组数据的单位不一样,放在同一把尺子上会很奇怪。这时候可以写两个 Y 轴:

1
2
3
4
5
6
7
8
9
10
11
12
13
yAxis: [
{ type: 'value', name: '销量' },
{ type: 'value', name: '增长率' }
],
series: [
{ name: '销量', type: 'bar', data: [120, 200, 150] },
{
name: '增长率',
type: 'line',
yAxisIndex: 1,
data: [10, 20, 15]
}
]

重点是 yAxisIndex: 1——数组从 0 开始,所以 1 指的是第二个 Y 轴。

单位一样的话就别写两个了。能简单就简单。


四、tooltip,那个小纸条

tooltip 是鼠标悬停时弹出来的提示框。不写它图也能跑,但写了读图会舒服很多。

最懒的写法:

1
tooltip: {}

有坐标轴的图(柱状图、折线图)常用 trigger: 'axis',鼠标移到某个 x 轴位置,显示整列数据:

1
tooltip: { trigger: 'axis' }

没有坐标轴的图(饼图、漏斗图)用 trigger: 'item',鼠标移到哪个块,显示哪个块:

1
2
3
4
tooltip: {
trigger: 'item',
formatter: '{b}: {c}%'
}

{b} 是名字,{c} 是数值。记住这两个字母就够了。


五、散点图

散点图有点特别——坐标不写在 xAxis 里,而是直接藏在每个数据点里:

1
2
3
4
5
data: [
[10, 20],
[15, 35],
[8, 18]
]

每一个小数组是一个点:[x值, y值]

完整写法:

1
2
3
4
5
6
7
8
9
10
var option = {
title: { text: '散点图' },
tooltip: {},
xAxis: { type: 'value' },
yAxis: { type: 'value' },
series: [{
type: 'scatter',
data: [[10, 20], [15, 35], [8, 18]]
}]
};

注意 xAxisyAxistype 都是 'value',不需要 data


六、雷达图

雷达图有自己的一套,不走 xAxis / yAxis,多了一个字段叫 radar

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
var option = {
title: { text: '雷达图' },
tooltip: {},
radar: {
indicator: [
{ name: '学习', max: 100 },
{ name: '体育', max: 100 },
{ name: '社交', max: 100 },
{ name: '实践', max: 100 }
]
},
series: [{
type: 'radar',
data: [{
name: '样本A',
value: [80, 70, 90, 60]
}]
}]
};

indicator 定义有几根轴,value 填对应的数值,两边一一对应,数量要一样多。

1
2
radar.indicator 定义轴
series.data.value 填数值

七、漏斗图

漏斗图也不需要坐标轴,数据从大到小排列,形状自然就出来了:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
var option = {
title: { text: '漏斗图' },
tooltip: { trigger: 'item', formatter: '{b}: {c}%' },
series: [{
type: 'funnel',
data: [
{ value: 100, name: '访问' },
{ value: 80, name: '浏览' },
{ value: 60, name: '咨询' },
{ value: 40, name: '下单' },
{ value: 20, name: '支付' }
]
}]
};

适合表示”从多到少”的过程。越到后面越窄,像一只很认真在过滤东西的小漏斗。


八、树图

树图的数据是嵌套结构:

1
2
3
4
5
6
7
{
name: '根节点',
children: [
{ name: '子节点1' },
{ name: '子节点2' }
]
}

有一个坑:如果数据放在 JSON 文件里,不能直接用 <script> 引入,因为 <script> 只认 JS 文件。要用 fetch

1
2
3
4
5
6
7
8
9
10
11
12
13
fetch('data.json')
.then(res => res.json())
.then(data => {
var option = {
title: { text: '树图' },
tooltip: { trigger: 'item' },
series: [{
type: 'tree',
data: [data] // 注意是 [data],不是 data
}]
};
myChart.setOption(option);
});

data: [data] 这里要包一层数组——因为 series.data 要数组,但树图的根节点是对象,所以手动套一层中括号。

如果节点太多挤在一起,加这两个字段会舒展很多:

1
2
orient: 'LR',        // 从左到右展开
initialTreeDepth: 2 // 先展开两层

九、关系图

关系图看起来字段多,但核心只有两个:data 写节点,links 写连线。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
series: [{
type: 'graph',
layout: 'force',
roam: true,
label: { show: true },
edgeLabel: { show: true, formatter: '{c}' },
data: [
{ name: '节点A', symbolSize: 30 },
{ name: '节点B', symbolSize: 30 },
{ name: '节点C', symbolSize: 30 }
],
links: [
{ source: '节点A', target: '节点B', value: '关系1' },
{ source: '节点A', target: '节点C', value: '关系2' },
{ source: '节点B', target: '节点C', value: '关系3' }
]
}]

source 是起点,target 是终点,value 是线上显示的文字。edgeLabel.formatter: '{c}' 里的 {c} 就是这个 value

1
2
data 写点,links 连线
source 找 target,value 写关系

十、地图

地图这里要分清两件事:

第一件事,地图轮廓文件还是用 <script> 引入 JS 文件:

1
<script src="map/province/guangxi.js"></script>

这一步相当于先把”广西这张地图纸”拿出来。

第二件事,地图上要显示的数据,可以直接写数组,也可以用 fetch 读取 JSON 文件。

如果给的是普通数据数组,就这样放进 series.data

1
2
3
4
5
6
7
series: [{
type: 'map',
map: '广西',
roam: true,
label: { show: true },
data: data
}]

如果数据放在 guangxi_map.json 里,就用 fetch 读取:

1
2
3
4
5
6
7
8
9
10
11
12
13
fetch('guangxi_map.json')
.then(res => res.json())
.then(data => {
var option = {
series: [{
type: 'map',
map: '广西',
data: data
}]
};

myChart.setOption(option);
});

如果要按数值深浅显示颜色,加 visualMap

1
visualMap: { min: 100, max: 900 }

如果想在地图上叠加散点或航线,用 geo + coordinateSystem: 'geo'

1
2
3
geo: { map: '广西', roam: true }
// series 里加上:
coordinateSystem: 'geo'

十一、toolbox:导出效果图的小按钮

提交时,除了提交代码,还要提交效果图。

效果图可以用 ECharts 自带的 toolbox 导出,所以提交代码时要保留 toolbox 配置,不要删掉它哦。

最常用的是这个:

1
2
3
4
5
6
toolbox: {
show: true,
feature: {
saveAsImage: {}
}
}

放进 option 里就是:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
var option = {
title: { text: '柱状图' },
tooltip: {},
toolbox: {
show: true,
feature: {
saveAsImage: {}
}
},
xAxis: {
type: 'category',
data: ['一月', '二月', '三月']
},
yAxis: {
type: 'value'
},
series: [{
type: 'bar',
data: [120, 200, 150]
}]
};

页面右上角会出现一个保存图片的小按钮,点一下就能导出效果图。

简易记忆版:

1
toolbox: { feature: { saveAsImage: {} } }

最后,一张小卡片

考试前可以只背这一个骨架:

1
2
3
4
5
6
7
8
option = {
title: {},
tooltip: {},
legend: {},
xAxis: {},
yAxis: {},
series: [{ type: '类型', data: [] }]
}

再记住哪些图走的是”特殊通道”:

1
2
3
4
5
6
7
饼图 / 漏斗图  →  不要 xAxis、yAxis
雷达图 → 多一个 radar
树图 → data: [data],JSON 用 fetch 读
关系图 → data 写节点,links 写连线
地图 → map 指定地图名,fetch 读数据
toolbox → saveAsImage 导出效果图
散点图 → data 写 [[x, y], [x, y]]

其实 ECharts 没有那么多需要死记的东西。

它就是一只小抽屉,每个字段都有自己的位置。标题进 title,提示进 tooltip,数据进 series。一开始看会觉得字段很多,但放久了会发现,它们都很安分,不会乱跑。

考试的时候别慌。先把骨架写出来,判断图表类型,再把数据放进正确的位置。

一步一步来,图就亮起来了。