本片博客旨在帮助后端开发者快速掌握 Echarts,进而实现数据可视化的需求。
- 本文从环境准备开始,详细介绍如何获取 Echarts 以及进行 Maven 配置,确保读者能够顺利地设置开发环境。
- 接着,文章通过案例分享部分深入讲解了饼状图、柱状图和词云图的具体配置方法,展示 Echarts 图表配置的强大功能和灵活性。
- 最后,本文将介绍如何接收后端图表数据,并利用 Thymleaf 渲染 HTML 模板,从而使前端能够展示动态的数据图表。
本文将以实用性和操作性为核心,为后端开发者介绍一个简单易学的数据可视化工具,使他们能够轻松地将复杂的数据转化为直观、易理解的图形。
环境准备
前端使用 Echarts 作为图表库,后端使用 Spring Boot + Thymleaf 渲染出 HTML 模板。项目结构如下:

src/main/resources/static/
为 SpringBoot 项目的默认静态资源目录,templates 子目录存放等待后端数据渲染的 HTML 模板。
获取Echarts
方式一:CDN 获取
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>Echarts案例介绍</title>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/echarts/5.4.3/echarts.min.js"></script>
</head>
<body>
</body>
方式二:curl 命令从 CDN 下载 js 文件后,放置在 static/scripts 目录下
$ curl https://cdnjs.cloudflare.com/ajax/libs/echarts/5.4.3/echarts.min.js -o echarts.min.js
Maven配置
本文基于 SpringBoot 框架构建后端项目,Maven 依赖配置如下,主要是引入 spring-boot 的 web 依赖以及 thymleaf 模板渲染引擎:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.9</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
</dependencies>
案例分享
饼状图
饼图主要用于表现不同类目的数据在总和中的占比,每个的弧度表示数据数量的比例。饼图的配置不需要配置坐标轴,仅需要把元素的名称(name) 和 值(value) 写在系列 series 中。
下面是一个最简单的 options 配置:
<!DOCTYPE html>
<html lang="zh-CN" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Echarts案例介绍</title>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/echarts/5.4.3/echarts.min.js"></script>
<script type="text/javascript" src="https://assets.pyecharts.org/assets/v5/echarts-wordcloud.min.js"></script>
</head>
<body>
<!-- (1) -->
<div id = "message-proportion" style="height: 800px;width: auto"></div>
<script >
<!-- (2) -->
let proportion_chart = echarts.init(document.getElementById('message-proportion'), 'white', {renderer: 'canvas'});
<!-- (3) -->
let proportion_data = [{name: '小王', value: 8000}, {name: '小李', value: '4000'}];
let proportion_option = {
<!-- (4) -->
series: [{
type: 'pie', // 饼状图
data: proportion_data
}]
};
<!-- (5) -->
proportion_chart.setOption(proportion_option);
</script>
</body>
我讲解下这段代码的流程:
(1):声明一个 div 容器用于显示 echarts 绘制的图像,并设置这个容器的高度和宽度。
(2):基于准备好的dom,初始化 echarts 实例
(3):渲染饼状图的数据,这里我先用静态数据展示,后面会演示如何从后端动态获取数据。注意到数据的类型为对象数组,每一个对象需要包含 name 和 value 属性,value 属性不必是百分比数据。
(4):series 中设置图表类型 type 和图表展示的数据 data。类型'pie'
表示饼图。
(5):设置 echarts 实例使用 proportion_option 配置。
饼图效果展示:
接下来,给 option 中加一些额外的配置,让饼图更美观、信息更详细。
- title:该属性配置饼图的标题;
- tooltip:提示框组件,当鼠标悬停在某一块区域上时,显示对应区块的名称和值。
- toolbox:工具栏组件,提供 数据视图、还原、保存图片等功能。
- legend:图例样式,包括 图例位置、水平 or 垂直、图例中元素的颜色、字体大小、图例背景等配置;
let proportion_option = {
tooltip: { show: true },
// 工具栏,提供 数据视图、还原、保存图片的功能
toolbox: {
show: true,
orient: 'vertical',
left: 'right',
top: 'center',
feature: {
mark: { show: true },
dataView: { show: true, readOnly: false },
restore: { show: true },
saveAsImage: { show: true }
}
},
// 图例样式
legend:{
left: '0',
// right: '0'
textStyle: {
color: '#000000',
fontSize: 16 // 设置字体大小
},
orient: 'vertical',
backgroundColor: 'rgba(255, 255, 255, 0.2)', // 为图例添加半透明的浅色背景
borderColor: '#FFFFFF', // 浅色边框颜色,这里使用白色
borderWidth: 1, // 边框宽度
padding: 10, // 图例内边距,根据需要调整
itemGap: 10, // 图例每项之间的间隔
borderRadius: 5 // 图例边框的圆角大小
},
series: [{
type: 'pie', // 饼状图
color: ['#ff0000', '#00ff00', '#ff00ff'],
data: proportion_data
}]
};
饼图中每个分块的提示仅包含名称小王
、小李
,且字体偏小,配置 series 进一步优化:
let proportion_option = {
// ...
series: [{
type: 'pie', // 饼状图
label: {
show: true, // 显示标签
textStyle: {
color: '#000000',
fontSize: 16 // 设置字体大小
},
formatter: function (params) {
return `${params.name} ${params.percent}%`
}
},
color: ['#ff0000', '#00ff00', '#ff00ff'],
data: proportion_data
}]
};
- label 配置饼图标签,标签中的文本格式通过 textStyle 属性配置,我选择字体颜色 color 为
#000000
(黑色)、字体大小 fontSize 为 16。 - formatter 配置标签中文本的格式化字符串,
params
为饼图中特定分块的数据(proportion_data
列表中的元素),该对象提供 name 名称、percent 比例等属性。 (注:echarts 会自动计算饼图中每个分块所占比例,percent 属性无需手动设置) - color:饼图中分块的颜色,比例越高的分块选择 color 数组中下标更考前的颜色,本例中最高比例的分块为
#ff0000
红色。
柱状图
柱状图(条形图)是一种通过柱形的长度来表现数据大小的一种常用图表类型。设置柱状图的方式,是将 series 的 type 设为'bar'
。
下面是一个关于月度统计的最简柱状图示例:
柱状图的横坐标为类目型的,需要在xAxis
中指定对应的值(本例中为yyyy-MM
格式化日期字符串);而纵坐标是数值型的,可以根据 series.data 自动生成坐标范围。
完整代码如下:
<body>
<div id = "chart-example" style="height: 800px;width: auto"></div>
</body>
<script type="text/javascript">
let monthly_stat_chart = echarts.init(document.getElementById('chart-example'), 'white', {renderer: 'canvas'});
let monthly_stat_data = [['2023-03','2023-04','2023-05','2023-06'],[100,200,350,158]];
let monthly_stat_option = {
xAxis:{
data: monthly_stat_data[0]
},
yAxis: {},
series: [
{
type: 'bar',
data: monthly_stat_data[1]
}
]
}
monthly_stat_chart.setOption(monthly_stat_option);
</script>
随后,我在基础柱状图上添加一些设置,美化展示效果:
let monthly_stat_option = {
title: {
text: '逐月统计消息数',
left: 'center'
},
tooltip: { show: true },
// 工具栏,提供 数据视图、还原、保存图片的功能
toolbox: {
show: true,
orient: 'vertical',
left: 'right',
top: 'center',
feature: {
mark: { show: true },
dataView: { show: true, readOnly: false },
restore: { show: true },
saveAsImage: { show: true }
}
},
visualMap: [{
type: 'continuous',
text: ['High', 'Low'],
realtime: true, // 实时更新显示图像
calculable: true, // 滑动调节数据显示范围
min: 100,
max: 350,
inRange: {
color: ['#EBE600', '#EB7001', '#EB0119']
}
}],
// ...
}
- title:该属性配置柱状图的标题;
- tooltip:提示框组件,当鼠标悬停在某根柱子上时,显示对应的 x 轴标签和数值。
- toolbox:工具栏组件,提供 数据视图、还原、保存图片等功能。
- visualMap:视觉映射组件,用于进行视觉编码。本例中,不同数值大小的柱子被映射为不同的背景颜色(黄色、橘色、红色),颜色越深代表分区数值越大。
- type:本例为
'continuous'
连续型视觉映射;也可选择piecewise
分段型数据映射。可以这么理解,continuous 会将某个区间的数值映射为连续颜色光谱,随着数值的改变,对应的颜色是渐变的;分段型数据映射则是将某一范围的数据等分为多个连续的区间,每个区间对应一个颜色。
- min、max:分别指定 visualMap 组件允许的最小值,[min, max] 形成视觉映射的定义域。
- inRange:定义 在选中范围中 的视觉元素,用户可以和 visualMap 组件交互,用鼠标或触摸选择范围。其中的 color 属性可以设置图元的颜色。
- realtime:拖拽时,是否实时更新。如果为 true,拖拽手柄过程中实时更新图表视图;如果为 false,拖拽结束才更新视图。
- type:本例为
美化后的图表效果如下:
词云图
- 首先引入 echarts 词云脚本:
<script type="text/javascript" src="https://assets.pyecharts.org/assets/v5/echarts-wordcloud.min.js"></script>
- 新建渲染词云图的区域标签
<body>
<div id="word_cloud" style="width: 900px;height:700px"></div>
</body>
<script>
块中对词云图进行配置,并渲染图表。
<script type="text/javascript">
var chart = echarts.init(document.getElementById('word_cloud'), 'white', {renderer: 'canvas'});
let data = [{"name": "汽车", "value": 349}, {"name": "妈妈", "value": 190}, {"name": "学校", "value": 315}, {"name": "苹果", "value": 7}, {"name": "电脑", "value": 345}, {"name": "书包", "value": 297}, {"name": "天气", "value": 238}, {"name": "音乐", "value": 59},
{"name": "足球", "value": 114}, {"name": "游戏", "value": 157}, {"name": "朋友", "value": 80}, {"name": "老师", "value": 304}, {"name": "家庭", "value": 296}, {"name": "动物", "value": 60}, {"name": "花园", "value": 280}, {"name": "图书馆", "value": 3},
{"name": "医院", "value": 63}, {"name": "电影", "value": 439}, {"name": "餐厅", "value": 44}, {"name": "旅游", "value": 164}, {"name": "工作", "value": 29}, {"name": "运动", "value": 175}, {"name": "健康", "value": 277}, {"name": "科技", "value": 367},
{"name": "艺术", "value": 113}, {"name": "设计", "value": 23}, {"name": "历史", "value": 309}, {"name": "文化", "value": 200}, {"name": "经济", "value": 262}, {"name": "政治", "value": 1}, {"name": "教育", "value": 452}, {"name": "环境", "value": 251},
{"name": "交通", "value": 168}, {"name": "安全", "value": 233}, {"name": "法律", "value": 150}, {"name": "信仰", "value": 54}, {"name": "节日", "value": 84}, {"name": "美食", "value": 155}, {"name": "时尚", "value": 231}, {"name": "爱好", "value": 231},
{"name": "计划", "value": 53}, {"name": "目标", "value": 112}, {"name": "挑战", "value": 42}, {"name": "解决", "value": 78}, {"name": "创新", "value": 279}, {"name": "发展", "value": 476}, {"name": "合作", "value": 234}, {"name": "交流", "value": 355},
{"name": "体验", "value": 255}, {"name": "管理", "value": 161}, {"name": "领导", "value": 309}, {"name": "成功", "value": 238}, {"name": "失败", "value": 100}, {"name": "机会", "value": 39}, {"name": "风险", "value": 472}, {"name": "模式", "value": 168},
{"name": "资源", "value": 125}, {"name": "能力", "value": 307}, {"name": "效率", "value": 487}, {"name": "质量", "value": 199}];
let option = {
tooltip: { show: true },
series: [{
name: '词云图',
type: 'wordCloud',
sizeRange: [15, 100],
size: ['100%', '100%'],
rotationRange: [-90, 90], // 旋转范围
rotationStep: 30, // 旋转步长
gridSize: 15,
drawOutOfBound: false,
shape: "diamond",
textStyle: {
color: function () {
return 'rgb(' + [
Math.round(Math.random() * 256),
Math.round(Math.random() * 256),
Math.round(Math.random() * 256)
].join(',') + ')';
}
},
// data 数组格式, 每个元素必须包含name和value属性
data: data,
}] //series结束
}; //option结束
chart.setOption(option)
</script>
我来介绍下代码中的关键配置项:
- tooltip:同柱状图和饼状图,这个属性配置了提示框组件,鼠标悬停时会提示词频。
- series 为数据系列及其配置,下面介绍的属性均为 series 列表中元素的属性。
- type:本例为
'wordCloud'
,表示图表类型为词云图。 - sizeRange:词云图中文字的字号范围,echarts 将根据词频对词语的字号进行渲染。
- rotationRange、rotationStep:前者为词云中词语的角度范围,本例中设置为 -90° 至 90°;后者设置旋转的步长为 30°。因此这个配置下,单词的旋转角度有:-90°、-60°、-30°、0°、30°、60°、90°。
- gridSize:词云文本之间的距离, 距离越大, 单词之间的间距越大, 单位像素;
- drawOutOfBound:是否允许词云在边界外渲染,设置为 false 放置词语重叠。
- shape:绘制词云的形状,值为回到函数 或 关键字,默认为圆形(
circle
)。其余关键字有:cardioid
心形、diamond
正方形、triangle
三角形、pentagon
五边形、star
星形。 - textStyle:词云文本格式配置。color 属性为回调函数,词云图中每个词语的颜色通过该函数生成,本例中利用了
Math.random()
生成随机颜色。 - data:词云中展示的词语列表,列表中的元素均包含
name
和value
属性。name 为词语,value 为词频。
正方形的词云效果图如下:
有时我们程序员的内心也会涌现一丝浪漫,例如给你深爱的 ta 绘制爱心状的词云,shape 中的关键词不满足要求,该 如何自定义词云图案呢?
- 找到一幅心形图案,保存到本地;

-
利用 转换工具,将图片转为 Base64 编码格式;
-
修改
<script>
中的代码,创建 Image 对象,该对象的 src 属性设置为心形图的 Base64 字符串。
<script type="text/javascript">
var chart = echarts.init(document.getElementById('word_cloud'), 'white', {renderer: 'canvas'});
let data = //...
var maskImg = new Image();
maskImg.src = /* 这里设置为图片Base64编码的字符串 */"";
let option = {
tooltip: { show: true },
series: [{
// series 配置同上
maskImage: maskImg
}] //series结束
}; //option结束
maskImg.onload = function () {
chart.setOption(option);
}
// chart.setOption(option)
</script>
注意:设置词云图 option 属性的操作需要在心形轮廓图加载完成后执行,因此代码示例中 maskImg.onload 的回调函数执行chart.setOption(option)
操作。
心形词云图效果展示:
接收后端的图表数据
案例分享章节主要介绍了饼状图、柱状图 以及 词云图的配置选项,美中不足的是图表展示的数据是静态的;而实际的场景下,图表数据大概率是由后端应用动态传递给前端展示。
本节将介绍,如何使用 thymeleaf 和 Spring Boot 完成后端数据生成 + 前端图表渲染。
Thymleaf 是一个Java库,用于在 Web 应用程序中处理 HTML、XML 文件等,允许将后端的数据模型动态地集成到前端页面中。
application.yml 配置:
spring:
thymeleaf:
prefix: classpath:/static/templates/
suffix: .html
mode: HTML5
encoding: UTF-8
test.html 进行如下修改:
th:inline="javascript"
:这是 Thymeleaf 的一个属性,用于指示 Thymeleaf 处理器解析并处理这个<script>
标签内的JavaScript 代码,从而能在 JavaScript 代码中直接使用 Thymleaf 表达式。/*[[${wordcloud_chart_data}]]*/ []
:这里的[[${wordcloud_chart_data}]]
为 Thymleaf 表达式,用于从后端模型中获取名为 wordcloud_chart_data 的数据。当页面渲染时,Thymeleaf 会将这个表达式替换成实际的数据。[]
:在 Thymeleaf 处理前,这个表达式外面的注释/* */
确保了如果 Thymeleaf 没有处理或者出现错误,JavaScript 代码仍然有效(虽然此时 data 被初始化为空数组)。一旦 Thymeleaf 替换了中间的表达式,注释将被实际的数据替换,data变量会被初始化为后端提供的 wordcloud_chart_data 数据。
<!DOCTYPE html>
<html lang="zh-CN" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Echarts案例介绍</title>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/echarts/5.4.3/echarts.min.js"></script>
<script type="text/javascript" src="https://assets.pyecharts.org/assets/v5/echarts-wordcloud.min.js"></script>
</head>
<body>
<div id="word_cloud" style="width: 900px;height:700px"></div>
</body>
<script type="text/javascript" th:inline="javascript">
var chart = echarts.init(document.getElementById('word_cloud'), 'white', {renderer: 'canvas'});
// ...
let data = /*[[${wordcloud_chart_data}]]*/[];
let option = {
series: [{
data: data
}] //series结束
}; //option结束
chart.setOption(option)
// ...
</script>
</body>
</html>
Spring Boot 项目中 Controller 方法入参设置为org.springframework.ui.Model
类型,Model 对象以 key-value 格式存放传递给 thymleaf 渲染的数据模型。下面为示例代码:
@GetMapping("/test")
public String test(Model model) {
// 词频map排序, 词云使用词频前100的词语
List<Map.Entry<String, Integer>> sortedEntries = textStatMetric.getAggWordCount().entrySet()
.stream()
.sorted(Map.Entry.<String, Integer>comparingByValue().reversed())
.collect(Collectors.toList());
List<WordCloudElem> elems = new ArrayList<>(100);
for(Map.Entry<String, Integer> entry: sortedEntries) {
if(elems.size() == 100) {
break;
}
elems.add(new WordCloudElem(entry.getKey(), entry.getValue()));
}
model.addAttribute("wordcloud_chart_data", elems);
return "test";
}
- 从文件统计对象
textStatMetric
中获取文本词频的聚合信息,根据词频逆序排列,返回词频 Top100 的词语列表。 - WordCloudElem 为词云图中 data 列表中元素的类型,包含 name 和 value 字段。
- elems 列表不断添加 WordCloudElem 实例,当元素个数达到 100 时,elems 列表被设置为名称为
wordcloud_chart_data
的数据模型,传递给 thymleaf 渲染 HTML 页面。