ECharts介绍

ECharts(Enterprise Charts)是一个由百度团队开源的可视化图表库,基于 JavaScript 开发,主要用于创建交互式、响应式的可视化图表。它以强大的功能、灵活的配置以及出色的性能在数据可视化领域非常受欢迎。

它具有以下一些很好的特性:

  1. 图表类型丰富。支持折线图、柱状图、散点图、饼图、雷达图、热力图、树图、关系图等多种图表类型。甚至支持三维(3D)图表、地图可视化等。
  2. 强交互性。支持缩放、拖拽、动态更新、工具提示(tooltip)、联动视图等交互功能。
  3. 高性能。基于 Canvas 和 WebGL 渲染,能够高效处理大规模数据,适合展示百万级别的数据。

更多的信息可以参见官网:ECharts官网

在Hexo博客中插入ECharts图表

ECharts基于JavaScript开发,理论上是可以无缝嵌入到html页面的。而Markdown是一种轻量级的标记语言,虽然支持一些html标签用来排版,但对于加入ECharts这样使用JavaScript的用例是无能为力的。

但Hexo是将Markdown转换为静态的html页面再进行展示的,如果进行适当的后处理,Hexo的博客页面中可以插入ECharts图表。

在本页面中,借助hexo-tag-echarts-new这个hexo插件来实现在hexo博客中插入图表。

首先使用:

1
npm install hexo-tag-echarts-new

进行安装。

之后,按照文档提示,通过以下格式来插入ECHarts图表:

1
2
3
{% echarts 100% 400 5.6.0 %}
\\TODO echarts option goes here
{% endecharts %}

其中 echarts 是标签名,不需要更改,400 是图表容器的高度,100% 是图表容器的相对宽度。而在 tag 之间的部分,则是需要自己填充的图表数据了。注意5.6.0是echarts的库版本,如果库版本太久远有可能cdn那就没备份了,注意使用较新一些的库版本。

感觉还是相当的符合直觉,安装完后可以测试以下是否可行。

测试用例

动态排序折线图

实现

使用了ECharts中比较炫酷的动态排序折线图来测试是否可用。在ECharts官网上截图效果如下:

Income of Germany and France since 1950 png

官网上这个示例是动态的,展示了ECharts图表强大的展示能力和交互性。通过hexo-tag-echarts-new的展示效果如下:

小结

通过询问ChatGPT费了一番功夫才弄好,主要原因在于这个图表加载了外部数据源,并且用JavaScript代码生成了各个国家的配置。而hexo-tag-echarts-new中仅支持配置静态的options,因此需要额外在JS中解包这个options并格式化。

虽然过程有些坎坷,但是最后还是能展现出来的。从这个示例可以看出来hexo-tag-echarts-new这个库的优点和缺点。优点当然是直观,然后对于基础用例来说足够简单,用户不必理会JS的细节。缺点就是灵活性较差,对于展示大批量的数据可能会费劲。毕竟不能使用JS写代码,那各种循环语句都得亲力亲为地写。总结来说就是基础用例是能用而且好用的,配合vscode中<!-- #region --><!-- #endregion -->能很好地折叠绘图区,从而专心编辑需要的区域。

用到的代码

记录一下用JS格式化options的代码,大部分由ChatGPT编写,我参考它写的进行了一些修改。

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
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
// 配置参数
const countriesToExtract = ["France", "Germany", ...]; // 要提取的国家
const yearRange = [1950, 2014]; // 提取的年份范围

// 提取数据
function extractData(rawData, countries, yearRange) {
const headers = rawData[0]; // 获取表头
const countryIndex = headers.indexOf("Country");
const yearIndex = headers.indexOf("Year");
const incomeIndex = headers.indexOf("Income");

// 过滤数据
const filteredData = rawData
.slice(1) // 去掉表头
.filter(
(row) =>
countries.includes(row[countryIndex]) &&
row[yearIndex] >= yearRange[0] &&
row[yearIndex] <= yearRange[1]
);

// 按国家分类数据
const result = {};
countries.forEach((country) => {
result[country] = filteredData
.filter((row) => row[countryIndex] === country)
.map((row) => ({
year: row[yearIndex],
income: row[incomeIndex],
}));
});

return result;
}

// 执行提取
const extractedData = extractData(rawData, countriesToExtract, yearRange);

// 格式化为 ECharts 格式
function formatForECharts(extractedData) {
const years = Array.from(
new Set(
Object.values(extractedData)
.flat()
.map((item) => item.year)
)
).sort((a, b) => a - b);

const series = Object.keys(extractedData).map((country) => {
const countryData = extractedData[country];
const data = years.map((year) => {
const entry = countryData.find((item) => item.year === year);
return entry ? entry.income : null;
});
return {
name: country,
type: "line",
data: data,
showSymbol: false,
endLabel: {
show: true,
formatter: "{a}: {c}",
},
labelLayout: {
moveOverlap: "shiftY",
},
emphasis: {
focus: "series",
},
};
});

return {
xAxis: {
type: "category",
data: years,
},
yAxis: {
type: "value",
name: "Income",
},
series: series,
};
}

// 转换为 ECharts 格式
const chartData = formatForECharts(extractedData);

// 输出 JSON 格式
console.log(JSON.stringify(chartData, null, 2));

渐变堆叠面积图

上面的示例可以看出来hexo-tag-echarts-new直观的地方和一些缺点。渐变堆叠面积图是hexo-tag-echarts-new这样的静态options没办法做到的,主要是因为渐变效果必须通过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
..., 
series: [
{
name: 'Line 1',
type: 'line',
stack: 'Total',
smooth: true,
lineStyle: {
width: 0
},
showSymbol: false,
areaStyle: {
opacity: 0.8,
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{
offset: 0,
color: 'rgb(128, 255, 165)'
},
{
offset: 1,
color: 'rgb(1, 191, 236)'
}
])
},
emphasis: {
focus: 'series'
},
data: [140, 232, 101, 264, 90, 340, 250]
},
...

为了在这种情况下也能正常显示echarts图表,查阅了hexo的文档,发现raw语法非常适合。被raw语法包裹的html代码hexo不会解释为Markdown处理,而是直接当作原始html处理的。那直接使用原始的echarts代码即可。但好像高度不太对,通过设置父元素的样式可以让高度恢复正常。

20250207更新:塞一大堆HTML代码进去让Typora编辑器几乎废了,一个更好地办法是引入可以折叠的HTML块,以便在编辑和浏览时不要显示它们。因此,参考这篇博客我决定使用HTML的折叠语句来支持对原生ECharts图表代码的折叠,以同时满足渲染显示和图表编辑两方面的需求。注意,因为这是HTML和Markdown混排,因此在HTML代码内部尽量不要出现连续换行符,以免干扰解析造成格式错误。

1
2
3
4
5
6
7
8
<details open>
<summary>展开查看图表</summary>
<pre style="background-color: #fff; color: #000; white-space: normal;">
<div style="height: 60vh; margin: 0; padding: 0;">
<!-- ECharts图表代码 -->
</div>
</pre>
</details>

这样设置后,需要JS介入的复杂图表也能有办法展示了:

展开查看图表

总结

简单地总结一下,hexo-tag-echarts-new插件对在hexo内插入echarts图表很友好,对于简单的图表配置好echarts的options就可以进行绘制。

对于很复杂需要JS介入的echarts图表,可以使用raw语法来进行HTML和JS的原生实现。

20250207更新hexo-tag-echarts-new插件会在原来的Markdown文档中插入大量代码,而且在Typora中没办法折叠。为了同时支持带ECharts图表博客的浏览和流畅编辑,之后的ECharts图表都将采用原生HTML代码+折叠块代码的形式实现。