使用 echarts 绘制 2020 疫情折线图
2020 年开始到现在的时间,真是发生了太多的事情,心情复杂,但又没有什么话好说,只能以有限的方式表达着对这些事件的思考。
最近买了个服务器,也正在着手搭建网站,利用一下我已经学到的前端知识,第一个应用性的内容,就以这次疫情的情况为出发点吧!
了解 echarts
ECharts,缩写来自 Enterprise Charts,商业级数据图表,是一个开源的数据可视化工具,一个纯 Javascript 的图表库,能够在 PC 端和移动设备上流畅运行,兼容当前绝大部分浏览器(IE6/7/8/9/10/11,chrome,firefox,Safari 等),底层依赖轻量级的 Canvas 库 ZRender,ECharts 提供直观,生动,可交互,可高度个性化定制的数据可视化图表。创新的拖拽重计算、数据视图、值域漫游等特性大大增强了用户体验,赋予了用户对数据进行挖掘、整合的能力。
需求确定
使用一个绘图库(这要怎么手画啊.jpg),完成 2020 疫情的数据收集,并绘制折线图。
准备零:工程建立
使用 IDE 新建一个 html5 工程,目录结构如下所示
其实里面的大部分文件与这次任务并无太大关系,这样直接新建只是为了省事。
准备一:绘图库
经过网上一番搜索和对比后,本次统计图绘图任务我选择用 echarts 来完成。它的首页是 https://www.echartsjs.com/zh/index.html,还蛮精美。
点击 “下载” 来到它的下载界面。或直接访问 https://www.echartsjs.com/zh/download.html。
网站提供了三种安装方式:
- 从下载的源代码或编译产物安装
- 从 npm 安装(
npm install echarts
) - 选择所需模块,在线定制安装
由于这次任务并不需要所有的图表支持,我选择第三种安装方式,点击 “在线定制” 即可进入定制页面。
选择需要的图表、坐标系、组件类型,在开发环境中我选择保留 IE8 的兼容性,同时不选择 “代码压缩” 以方便调试。
点击 “下载” 后,会出现一个编译界面,待到用到的所有组件加入完毕后,就可以下载生成的 js 文件了,默认文件名为 echarts.js
,我们把它保存在事先建好网页工程的 js 文件夹中。
同时生成一份 “代码压缩” 版的 js 以供线上利用,这个默认文件名为 echarts.min.js
下载好所需的 js 文件以后,就可以开始工作了~
-
建立一个空的 html 文档,填写必要的网页信息。
index.html 1
2
3
4
5
6
7
8
9
10
11
12
13
14
<html class="no-js" lang="zh">
<head>
<meta charset="utf-8">
<title>2020肺炎疫情数据 - 即时更新</title>
<!--省略其余meta相关代码-->
</head>
<body>
<!--省略jQuery相关代码-->
</body>
</html> -
引入
echarts.js
在线上环境时,我们将引入
echarts.min.js
index.html 1
2<!--head标签下-->
<script src="js/echarts.js"></script> -
为 Echarts 准备一个具有高宽的 DOM 容器
index.html 1
2
3
4<body>
<!-- 为 ECharts 准备一个具备大小(宽高)的 DOM -->
<div id="main" style="width: 600px;height:400px;"></div>
</body> -
通过
echarts.init
方法初始化一个 echarts 实例,并通过setOption
方法生成一个简单的统计图,大概是像这样的格式:index.html 1
2
3
4
5
6<script type="text/javascript">
// 基于准备好的dom,初始化echarts实例
var myChart = echarts.init(document.getElementById('main'));
var option = {...}; //这里做好设置和填充数据
myChart.setOption(option); //使用刚指定的配置项和数据显示图表
</script>-
options
中要怎么指定设置和数据呢?别急,我们先来看它的官方示例数据和它渲染出来的图:main.js 1
2
3
4
5
6
7
8
9
10
11
12
13option = {
xAxis: {
type: 'category',
data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
},
yAxis: {
type: 'value'
},
series: [{
data: [820, 932, 901, 934, 1290, 1330, 1320],
type: 'line'
}]
};-
option 中有 xAxis、yAxis、series 三个对象,它们都拥有 type 和 data 两个属性。对于这个简单的示例来说:
- xAxis 是横轴数据及类型描述
- yAxis 是纵轴数据及类型描述
- series 是系列描述,可以包含多组 “统计数据”
在 echarts 里,系列(series)是指:一组数值以及他们映射成的图。“系列” 这个词原本可能来源于 “一系列的数据”,而在 echarts 中取其扩展的概念,不仅表示数据,也表示数据映射成为的图。所以,一个 系列包含的要素至少有:一组数值、图表类型(series.type)、以及其他的关于这些数据如何映射成图的参数。
- 实际上,这也意味着可以在一个 echarts 对象上同时绘制多组类型各同或各异的统计数据(注意到 series 是一个数组!)!如果把以上例子的 series 数据稍作修改,就可以得到多折线的统计图,这也是本次数据处理任务的重要模型!
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19option = {
xAxis: {
type: 'category',
data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
},
yAxis: {
type: 'value'
},
series: [
{
data: [820, 932, 901, 934, 1290, 1330, 1320],
type: 'line'
},
{
data: [100,170,250,330,440,114,225],
type: 'line'
}
]
};
- 实际上,这也意味着可以在一个 echarts 对象上同时绘制多组类型各同或各异的统计数据(注意到 series 是一个数组!)!如果把以上例子的 series 数据稍作修改,就可以得到多折线的统计图,这也是本次数据处理任务的重要模型!
-
xAxis、yAxis、series 实质为统计图的 “组件”。在 echarts 中,各种内容都是被抽象为 “组件” 的,除了前述的 xAxis、yAxis、series 以外,还有 grid、polar、geo 等组件,与本次任务关系并不大。
- 所有的组件都在 option 对象中声明,可以是一个对象或者数组。
-
上层的 option 对象描述了图表的各种需求,包括:有什么数据、要画什么图表、图表长什么样子、含有什么组件、组件能操作什么事情等等。这些设置内容通过
setOption
函数绑定到 echarts 对象上。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26// 用 option 描述数据、数据如何映射成图形、交互行为等。
// option 是个大的 JavaScript 对象。
var option = {
// option 每个属性是一类组件。
legend: {...},
grid: {...},
tooltip: {...},
toolbox: {...},
dataZoom: {...},
visualMap: {...},
// 如果有多个同类组件,那么就是个数组。例如这里有三个 X 轴。
xAxis: [
// 数组每项表示一个组件实例,用 type 描述“子类型”。
{type: 'category', /*...*/},
{type: 'category', ...},
{type: 'value', ...}
],
yAxis: [{...}, {...}],
// 这里有多个系列,也是构成一个数组。
series: [
// 每个系列,也有 type 描述“子类型”,即“图表类型”。
{type: 'line', data: [['AA', 332], ['CC', 124], ['FF', 412], /_ ... _/ ]},
{type: 'line', data: [2231, 1234, 552, /* ... */ ]},
{type: 'line', data: [[4, 51], [8, 12], /* ... */ ]}
]
};-
数据都在
series.data
中,也可通过 dataset 来取得数据。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19var option = {
dataset: {
source: [
[121, "XX", 442, 43.11],
[663, "ZZ", 311, 91.14],
[913, "ZZ", 312, 92.12],
/* ... */
],
},
xAxis: {},
yAxis: {},
series: [
// 数据从 dataset 中取,encode 中的数值是 dataset.source 的维度 index(即第几列)
{ type: "bar", encode: { x: 1, y: 0 } },
{ type: "bar", encode: { x: 1, y: 2 } },
{ type: "scatter", encode: { x: 1, y: 3 } },
/* ... */
],
};
总的来说,option 表述了:数据、数据如何映射成图形、交互行为。
-
-
通过以上这几个步骤,我们可以绘制一个简单的统计图。
-
-
准备二:数据
本次使用的数据源为丁香园疫情实时播报网页。打开网站发现我们所要的数据在这里呈现:
右键或者 F12 打开开发者模式,发现数据来源为这段 JavaScript 代码
其中正好有我们需要的数据
1 | { |
使用 python requests 库爬下来,扔进 BeautifulSoup 解析一通,然后存到数据库中,其中数据库的结构为
1 | create table `2020_pneumonia` |
1 | req = requests.get(url='https://ncov.dxy.cn/ncovh5/view/pneumonia', headers=headers, verify=False) |
准备三:数据交互
编写一个 php 接口,借以从数据库中获取数据,其中接口行为设计如下:
1 | POST api.php HTTP /1.1 |
稍后会在 html 网页中用 jQuery ajax 技术调用这个接口。
编写主网页
-
在
body
标签中,准备一个div
容器,一会放我们的 echarts 图表。这个容器需要指明一个高度index.html 1
2
3<div id="chartbox" style="width:100%;height:100%;margin:0 auto">
<div id="main" style="width: 600px;height:calc(70% + 10px);margin:0 auto;"></div>
</div> -
在
div
容器中初始化 echarts 对象index.html 1
2var main = document.getElementById("main");
var chart = echarts.init(main, "light"); //设置echarts显示主题为亮色主题 -
图标样式设置(chartOptions)
main.js 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76chartoption = {
title: {
text: "2020肺炎疫情数据",
lineHeight: 40,
height: 40,
subtext: "数据来源:丁香园·丁香医生",
sublink: "https://ncov.dxy.cn/ncovh5/view/pneumonia",
},
dataZoom: [
{
id: "dataZoomX",
type: "inside",
xAxisIndex: [0],
filterMode: "empty",
},
{
id: "dataZoomY",
type: "inside",
yAxisIndex: [0],
filterMode: "empty",
},
],
dataset: {
dimensions: ["last_since", "proved", "uncertain", "died", "cured"],
source: [
// {last_since:xx(datetime),proved:xx(int),uncertain:xx(int),died:xx(int),cured:(int)}
],
},
tooltip: {
trigger: "axis",
},
toolbox: {
show: true,
orient: "vertical",
top: 20,
feature: {
dataZoom: {
yAxisIndex: "none",
},
// dataView: {
// readOnly: true
// },
restore: {},
saveAsImage: {
title: "保存为图片…",
},
},
},
legend: {
type: "scroll",
data: ["proved", "uncertain", "died", "cured"],
left: "right",
},
xAxis: {
type: "category",
// boundaryGap: false,
},
yAxis: {
type: "value",
},
series: [
{
type: "line",
},
{
type: "line",
},
{
type: "line",
},
{
type: "line",
},
],
animationEasing: "quarticOut",
};然后再将设置对象绑定到之前的 chart 对象上
main.js 1
chart.setOption(chartoption);
-
异步更新及获取数据,根据 echarts 官方文档的说法,只要在 jQuery 等工具异步获取完数据后,再调用 setOption 填入数据和配置项就行,非常方便
main.js 1
2
3
4
5
6
7
8
9$(document).ready(function () {
$.post("api.php", { action: "getfull" }, "json").done(function (data) {
chart.setOption({
dataset: {
source: data.contents, // 返回数据大致为{"success":true,"contents":[{},{},...]}
},
});
});
}); -
由于数据较多,加载时间较长,我们给 chart 对象添加一个简单的 loading 动画,在数据加载完成后,移除 loading 动画。
main.js 1
2
3
4
5chart.showLoading();
$(document).ready(function () {
//getting data with jQuery...
chart.hideLoading();
});
效果
-
经过以上步骤后,打开网页,现在显示效果如下,虽然不怎么酷炫,但是已经基本上满足了我们的需求,可以看到大概的数据走向。可以用鼠标滚轮实现局部放大效果。调一调样式,就可以嵌入到一般网页中去使用了。
后续的思考和启示
- 其实丁香园网页的开头还有几段 javascript 数据,多加研究的话还有可以值得提取和研究的东西,比如最近新加的国外疫情数据
- 原来页面真的可以全用 javascript 渲染出来…… 丁香园疫情网页应该用到了类似于 webpack 和客户端渲染的东西,网页
body
元素中全是 javascript 代码- 具体来讲,应该是在
script
标签中加载了一些 js 和 css 文件
- 具体来讲,应该是在
- 数据获取部分本来想用 php 一起写掉,但奈何 php 中想要爬虫,只有使用 curl 库来进行抓取,且后续正则提取麻烦,索性放弃了