数据可视化不再难:后端开发者的Echarts快速上手指南


本片博客旨在帮助后端开发者快速掌握 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,拖拽结束才更新视图。

美化后的图表效果如下
在这里插入图片描述


词云图

  1. 首先引入 echarts 词云脚本:
<script type="text/javascript" src="https://assets.pyecharts.org/assets/v5/echarts-wordcloud.min.js"></script>
  1. 新建渲染词云图的区域标签
<body>
    <div id="word_cloud"  style="width: 900px;height:700px"></div>
</body>
  1. <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:词云中展示的词语列表,列表中的元素均包含namevalue属性。name 为词语,value 为词频。


正方形的词云效果图如下


有时我们程序员的内心也会涌现一丝浪漫,例如给你深爱的 ta 绘制爱心状的词云,shape 中的关键词不满足要求,该 如何自定义词云图案呢

  1. 找到一幅心形图案,保存到本地;
  1. 利用 转换工具,将图片转为 Base64 编码格式;

  2. 修改<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 进行如下修改:
  1. th:inline="javascript":这是 Thymeleaf 的一个属性,用于指示 Thymeleaf 处理器解析并处理这个<script>标签内的JavaScript 代码,从而能在 JavaScript 代码中直接使用 Thymleaf 表达式。
  2. /*[[${wordcloud_chart_data}]]*/ []:这里的[[${wordcloud_chart_data}]]为 Thymleaf 表达式,用于从后端模型中获取名为 wordcloud_chart_data 的数据。当页面渲染时,Thymeleaf 会将这个表达式替换成实际的数据
  3. []:在 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 页面。
  • 11
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值