本篇文章数据来源为各大电商网站公开数据(如有侵权,联系删除),使用到的截图为 Echarts Demo 示例。
本篇文章介绍 Apache ECharts
可视化图表库,包括雷达图 radar
,柱状图 bar
,散点图 scatter
,旭日图 sunburst
,词云图 wordcloud
,折线图 line
,饼图 pie
的使用要点,全是摘自实际项目中使用到的示例。
1. 雷达图 radar
雷达图使用 radar 组件作为其坐标系,雷达图主要用于表现多变量的数据。
雷达图是表现多个维度的数据,所以,如果只有一条数据,则展示为一个点,两条数据展示为一条线,三个及以上数据才展示为多个维度的雷达图。
// 完整示例代码
option = {
"legend": {
"left": "left",
"orient": "vertical"
},
"tooltip": {
"trigger": "item"
},
"radar": {
"indicator": [
{
"name": "零配件设计",
"max": 20
},
{
"name": "质量/品质",
"max": 20
},
{
"name": "物流配送",
"max": 20
}
]
},
"grid": {
"top": 16,
"left": 20,
"right": 20,
"bottom": 16,
"containLabel": true
},
"series": [
{
"type": "radar",
"data": [
{
"value": [
15,
9,
10
],
"name": "品牌1",
"colorBy": "data",
"itemStyle": {
"color": "#E01737"
}
},
{
"value": [
6,
13,
10
],
"name": "品牌2",
"colorBy": "data",
"itemStyle": {
"color": "#005FBF"
}
}
]
}
]
};
1.1. 一个维度(一条数据)
只有一条数据时,只出现一个点,以及雷达图状背景。
option = {
"legend": {
"left": "left",
"orient": "vertical"
},
"tooltip": {
"trigger": "item"
},
"radar": {
"indicator": [
{
"name": "零配件设计",
"max": 20
},
]
},
"grid": {
"top": 16,
"left": 20,
"right": 20,
"bottom": 16,
"containLabel": true
},
"series": [
{
"type": "radar",
"data": [
{
"value": [
15,
],
"name": "品牌1",
"colorBy": "data",
"itemStyle": {
"color": "#E01737"
}
}
]
}
]
};
1.2. 二个维度(二条数据)
只有二条数据时,只出现一条线,以及雷达图状背景。
option = {
legend: {
left: 'left',
orient: 'vertical'
},
tooltip: {
trigger: 'item'
},
radar: {
indicator: [
{
name: '零配件设计',
max: 20
},
{
name: '质量/品质',
max: 20
}
]
},
grid: {
top: 16,
left: 20,
right: 20,
bottom: 16,
containLabel: true
},
series: [
{
type: 'radar',
data: [
{
value: [15, 9],
name: '品牌1',
colorBy: 'data',
itemStyle: {
color: '#E01737'
}
}
]
}
]
};
1.3. 雷达图的指示器必填
雷达图的指示器 indicator
,用来指定雷达图中的多个变量(维度),必填。如果不填将会报错。
如上完整示例代码设置 indicator
为空数组:
radar: {
indicator: []
}
不设置表示没有维度,没有意义。所以当没有数据时,需要兼容处理,让代码健壮一些:
radar: {
indicator: [{name: ''}]
}
1.4. 指示器建议设置的最大值
设置指示器的最大值后,其他数值和这个最大值的比值,为图中占比。如果不设置,不容易看出哪个 legend 是最大值。建议设置为同类型中最大那个数值。
如上完整示例代码设置 indicator
为空数组:
radar: {
indicator: [
{
name: '零配件设计',
max: 15
},
{
name: '质量/品质',
max: 13
},
{
name: '物流配送',
max: 10
}
]
},
series: [
{
type: 'radar',
data: [
{
value: [15, 9, 10],
name: '品牌1',
colorBy: 'data',
itemStyle: {
color: '#E01737'
}
},
{
value: [6, 13, 10],
name: '品牌2',
colorBy: 'data',
itemStyle: {
color: '#005FBF'
}
}
]
}
],
...
2. 柱状图 bar
柱状图(或称条形图)是一种通过柱形的高度(横向的情况下则是宽度)来表现数据大小的一种常用图表类型。
很简单的柱状图,我们其实可以通过自己实现来表现数据趋势,以横向的情况为例:
实现原理:
- 为每一条数据定义两种颜色,一种背景色,用来表示总量,一种前景色,用来表示占比。
- 柱状图用高低起伏的条形,来表示不同数据之间的差异,需要体现值大的数据更高(宽),值小的数据更低(窄)。
<div class="card-content">
<dl class="positive">
<dt><span class="emotion">正面</span> TOP10指标 </dt>
<dd class="top10">
<div title="机器运行噪音" class="top10-name">机器运行噪音</div>
<div class="top10-box">
<div style="width: 75%; background: rgb(108, 204, 122);"></div>
<div class="top10-value">11</div>
</div>
</dd>
<dd class="top10">
<div title="信任度" class="top10-name">信任度</div>
<div class="top10-box">
<div style="width: 61.3636%; background: rgb(108, 204, 122);"></div>
<div class="top10-value">9</div>
</div>
</dd>
</dl>
<dl class="negative">
<dt><span class="emotion">负面</span> TOP10指标 </dt>
<dd class="top10">
<div title="客服态度" class="top10-name">客服态度</div>
<div class="top10-box">
<div style="width: 75%; background: rgb(234, 123, 123);"></div>
<div class="top10-value">1</div>
</div>
</dd>
<dd class="top10">
<div title="机器内部异味大小" class="top10-name">机器内部异味大小</div>
<div class="top10-box">
<div style="width: 75%; background: rgb(234, 123, 123);"></div>
<div class="top10-value">1</div>
</div>
</dd>
</dl>
</div>
dl,
dt,
dd {
margin: 0;
}
.card-content {
display: flex;
flex-direction: row;
align-items: flex-start;
& > dl {
width: calc(50% - 25px);
&:last-of-type {
margin-left: 50px;
}
dt {
margin: 0 0 16px;
font-size: 12px;
line-height: 1.5em;
font-weight: 400;
color: #919399;
.emotion {
font-size: 14px;
font-weight: 500;
color: #273849;
margin-right: 8px;
}
}
.top10 {
height: 16px;
line-height: 16px;
display: flex;
margin-bottom: 12px;
&:hover {
cursor: pointer;
.top10-name {
color: #000;
font-weight: 600;
}
.top10-box {
background-color: #b1b3b7;
}
}
.top10-name {
width: 100px;
margin-right: 10px;
font-size: 12px;
font-weight: 400;
color: #273849;
text-align: end;
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
word-break: break-all;
transition: all 0.4s ease 0s;
}
.top10-box {
flex: 1;
background-color: #f5f7fa;
display: flex;
transition: all 0.4s ease 0s;
.top10-value {
font-size: 12px;
font-weight: 400;
color: #273849;
margin-left: 5px;
}
}
}
}
}
说明:
以上 width
宽度属性值计算规则为 width: (item.value / (positiveItems[0].value / 0.75)) * 100 + '%'
。
- 先将动态返回的数据按照数值大小降序排序。
- 取第一条数据的值(最大的值)除以 0.75 作为背景颜色的宽度。这时候最大的数值只占了 75% 的宽,留下 25% 用来显示具体的数值。
- 用当前数据的值除以背景颜色的宽度,得到的比值就是自身的宽度。
3. 散点图 scatter
散点(气泡)图,是一组分散在坐标轴区域内的点,用来展现数据的 x,y 之间的关系。
3.1. 设置组件合并模式
这不仅仅是散点图需要设置,其他图如果没有按照预期正确渲染,都可以设置合并模式 setOption notMerge
。将其设置为 true
,表示所有组件都会被删除,然后根据新 option
创建所有新组件。不设置时为 false
。
如下图,重复计算了散点,没有把之前的清空。
将 notMerge
设置为 true
就会恢复正常。
3.2. 散点图画线
散点图是分散在坐标系中的点状图,通常需要画出横坐标和纵坐标来表示点的平均值,体现点和平均值的比较关系。
图表标线 可以在坐标区域中画线。只需要找到横坐标线和纵坐标线的位置即可。以点的横坐标、纵坐标的平均值为例作为画线的位置为例:
const xAverage = Number((xCount / seriesDataLen).toFixed(2)); // xCount 为 x 坐标的值的总和,seriesDataLen 为 top10 坐标个数(最多10个,不足 10 个有多少算多少)
const yAverage = Number((yCount / seriesDataLen).toFixed(2)); // yCount 为 y 坐标的值的总和
const top10Data = satisfaction.filter((item, index) => index < 10); // top10 数据
const xMax = Math.max(...top10Data.map((item) => Number(item.x_axis))); // 取 top10 数据 x 轴最大值
const yMax = Math.max(...top10Data.map((item) => Number(item.y_axis))); // 取 top10 数据 y 轴最大值
const markLineData = {
data: [
{
yAxis: yAverage,
lineStyle: {
color: 'rgba(233, 236, 242, 1)'
}
},
{
xAxis: xAverage,
lineStyle: {
color: 'rgba(233, 236, 242, 1)'
}
}
]
};
...
if (this.satisfactionOption.series.length > 0) {
this.satisfactionOption.series[0].markLine = markLineData;
}
3.2. 散点图画区域
图表标域。区域按象限区分,分为第一象限、第二象限、第三象限、第四象限,分别对应的位置是右上角区域、左上角区域、左下角区域、右下角区域。
// 第一象限区域
const markAreaData1 = {
silent: true,
itemStyle: {
color: 'rgba(111, 191, 26, 0.05)',
borderWidth: 0,
borderType: 'solid'
},
label: {
show: true,
position: 'insideBottomLeft',
formatter: '优势区域', // 第一象限区域标识的 label 文本
color: '#aaa',
distance: 10 // 距离中心点 10 (根号10)距离
},
data: [
// 该区域只需要知道左下角、右上角的点便可连成区域
[
{
xAxis: xAverage,
yAxis: yAverage
},
{
xAxis: Math.ceil(xMax * 2) + 20, // 在 xMax 的基础上在 * 2 , +20 是因为有些点刚好是最大值,区域画得更远,能保证全部点都在坐标轴内
yAxis: Math.ceil(yMax * 2) + 20
}
]
]
};
// 第二象限区域
const markAreaData2 = {
silent: true,
itemStyle: {
color: 'rgba(111, 191, 26, 0.05)',
borderWidth: 0,
borderType: 'solid'
},
label: {
show: true,
position: 'insideBottomRight',
formatter: '保持区域', // 第二象限区域标识的 label 文本
color: '#aaa',
distance: 10
},
data: [
[
{
xAxis: 0,
yAxis: yAverage
},
{
xAxis: xAverage,
yAxis: Math.ceil(yMax * 2) + 20
}
]
]
};
// 第三象限区域
const markAreaData3 = {
silent: true,
itemStyle: {
color: 'rgba(238, 81, 68, 0.05)',
borderWidth: 0,
borderType: 'solid'
},
label: {
show: true,
position: 'insideTopRight',
formatter: '退出区域', // 第三象限区域标识的 label 文本
color: '#aaa',
distance: 10
},
data: [
[
{
xAxis: 0,
yAxis: 0
},
{
xAxis: xAverage,
yAxis: yAverage
}
]
]
};
// 第四象限区域
const markAreaData4 = {
silent: true,
itemStyle: {
color: 'rgba(238, 81, 68, 0.05)',
borderWidth: 0,
borderType: 'solid'
},
label: {
show: true,
position: 'insideTopLeft',
formatter: '改进区域', // 第四象限区域标识的 label 文本
color: '#aaa',
distance: 10
},
data: [
[
{
xAxis: xAverage,
yAxis: 0
},
{
xAxis: Math.ceil(xMax * 2) + 20,
yAxis: yAverage
}
]
]
};
...
if (this.satisfactionOption.series.length > 3) {
this.satisfactionOption.series[0].markArea = markAreaData1;
this.satisfactionOption.series[1].markArea = markAreaData2;
this.satisfactionOption.series[2].markArea = markAreaData3;
this.satisfactionOption.series[3].markArea = markAreaData4;
}
上面,少于 4 个点将不会画区域,因为不存在四象限概念。
3.3. 散点图区域设置文本
如上画区域示例,markAreaData1.label.formatter
是区域的标识文本。
4. 旭日图 sunburst
旭日图
sunburst
由多层的环形图组成,在数据结构上,内圈是外圈的父节点。因此,它既能像饼图一样表现局部和整体的占比,又能像矩形树图一样表现层级关系。
旭日图数据结构呈现父子关系结构。
// yapi 文档
{
"type": "array",
"items": {
"type": "object",
"properties": {
"name": {
"type": "string",
"description": "父级名,如:\"关系\""
},
"children": {
"type": "array",
"items": {
"type": "object",
"properties": {
"name": {
"type": "string",
"description": "子级名,如:\"夫妻\""
},
"value": {
"type": "number",
"description": "子级值,如:34"
}
},
"description": "子级对象",
"required": [
"name",
"value"
]
},
"description": "子级对象集合"
}
},
"required": [
"name"
]
}
}
4.1. 设置最小角度 minAngle
minAngle当某个扇形块的角度小于该值(角度制)时,扇形块对应的文字不显示。该值用以隐藏过小扇形块中的文字。
外层的扇形和内层的扇形角度一样大,可是外层的区域更大,能显示更多文本。所以设置最小值时可以将外层的 minAngle
设置得更大些,将内层的 minAngle
设置得更小些。
this.option.series.data = value.map((item) => {
return {
...item,
label: {
minAngle: 14
},
children: item.children.map((elem) => {
return {
...elem,
label: {
minAngle: 6
}
};
})
};
});
5. 词云图 wordcloud
词云图,可用于的场景有很多,比如年度热词,可以根据出现的频次高低显示不同的字号,明确当前热门和人们最关切的词或事件。
5.1. 根据词数量设置词大小
做词云图,有一个很重要的点,需要根据词数量来设置词的字号大小。因为当词很少且字号很小时,显示得就看不清。
所以,需要根据词的数量来设置,词太少时,将字号设置得大一些。
6. 折线图 line
折线图是用折线将各个数据点标志连接起来的图表,用于展现数据的变化趋势。
6.1. 设置数据堆叠
数据堆叠,同个类目轴上系列配置相同的 stack 值后,后一个系列的值会在前一个系列的值上相加。
如上图,在 2022-01-25 这一时间,“客服会话”本来数值为 0,结果在图中显示为 147 了,就是因为“客服会话”的值在“Udesk”和“电商评论”的基础上叠加了。并且“客服会话”把“电商评论”的点给覆盖了。
7. 饼图 pie
饼图主要用于表现不同类目的数据在总和中的占比。每个的弧度表示数据数量的比例。
7.1. 标题的妙用
如上图, 193总声量,是用标题组件生成的。
option = {
title: {
text: '',
left: 'center',
top: 'center',
textStyle: {
fontSize: 16,
align: 'center',
verticalAlign: 'middle'
}
}
},
const count = Number(total).toLocaleString();
this.option.title.text = `${count}\n总声量`;
欢迎写出你的看法,一起成长!