一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第16天,点击查看活动详情。
echarts是一个很庞大的图标库,有非常多的配置项和API,每次使用都要反复查询文档,因此非常有必要对echarts的使用在整体上做了一个梳理。
图标容器
在 HTML 中定义有宽度和高度的父容器(推荐)
通常来说,需要在 HTML 中先定义一个 <div> 节点,并且通过 CSS 使得该节点具有宽度和高度。初始化的时候,传入该节点,图表的大小默认即为该节点的大小。
<div id="main" style="width: 600px;height:400px;"></div>
<script type="text/javascript">
var myChart = echarts.init(document.getElementById('main'));
</script>
监听图表容器的大小并改变图表大小
在有些场景下,我们希望当容器大小改变时,图表的大小也相应地改变。
var myChart = echarts.init(document.getElementById('main'));
window.onresize = function() {
myChart.resize();
};
容器节点被销毁以及被重建时
假设页面中存在多个tabs,每个标签页都包含一些图表。当选中一个标签页的时候,其他标签页的内容在 DOM 中被移除了。这样,当用户再选中这些标签页的时候,就会发现图表"不见"了。
本质上,这是由于图表的容器节点被移除导致的。即使之后该节点被重新添加,图表所在的节点也已经不存在了。
正确的做法是,在图表容器被销毁之后,调用 echartsInstance.dispose 销毁实例,在图表容器重新被添加后再次调用 echarts.init 初始化。
在容器节点被销毁时,总是应调用 echartsInstance.dispose 以销毁实例释放资源,避免内存泄漏。
封装一个vue的图标组件
- class: 'echarts'设置为height: 100% width: 100%,这样就继承父级的宽高,父级一定要设置一个宽高,否则图标就没有宽高了
- 这里需要保证有一个唯一的class,因为这个组件是一个通用组件,当一个页面中有很多图标的时候,如果class都是一样的,那么就会出现异响不到的bug
<template>
<div :class="[className, 'echarts']"></div>
</template>
<script>
import { watch, onMounted } from 'vue'
// 全量引入
import * as echarts from 'echarts'
import { v4 as uuidv4 } from 'uuid'
export default {
name: 'VueEcharts',
props: {
options: Object,
theme: [String, Object]
},
setup(props) {
let dom
let chart
// 因为是通用组件,所以必须要满足有一个唯一的class
let className = `echarts${uuidv4()}`
const initChart = () => {
if (!chart) {
dom = document.getElementsByClassName(className)[0]
chart = echarts.init(dom, props.theme)
}
if (props.options) {
chart.setOption(props.options)
}
}
onMounted(() => {
initChart()
})
watch(
() => props.options,
() => {
initChart()
}
)
return {
className
}
}
}
</script>
<style lang="scss" scoped>
.echarts {
width: 100%;
height: 100%;
}
</style>
使用:
<vue-echarts :options="options" />
样式设置
颜色主题(Theme)
最简单的更改全局样式的方式,是直接采用颜色主题(theme)。ECharts5 除了一贯的默认主题外,还内置了'dark'主题。可以像这样切换成深色模式:
var chart = echarts.init(dom, 'dark');
其他的主题,没有内置在 ECharts 中,需要自己加载。这些主题可以在 主题编辑器 里访问到,具体使用参见文档。
调色盘
调色盘,可以在 option 中设置。它给定了一组颜色,图形、系列会自动从其中选择颜色。 可以设置全局的调色盘,也可以设置系列自己专属的调色盘。
option = {
// 全局调色盘。
color: [
'#c23531',
'#2f4554',
'#61a0a8',
'#d48265'
],
series: [
{
type: 'bar',
// 此系列自己的调色盘。
color: [
'#dd6b66',
'#759aa0',
'#e69d87',
]
}
]
比如下面我们对柱状图设置了一个调色盘:
const color = ['rgb(116,166,49)', 'rgb(190,245,99)', 'rgb(202,252,137)', 'rgb(251,253,142)']
options = {
color: color
}
柱状图的颜色就是上面设置的颜色:
直接的样式设置 itemStyle, lineStyle, areaStyle, label, ...
直接的样式设置是比较常用设置方式。纵观 ECharts 的 option 中,很多地方可以设置 itemStyle、lineStyle、areaStyle、label 等等。这些的地方可以直接设置图形元素的颜色、线宽、点的大小、标签的文字、标签的样式等等。
一般来说,ECharts 的各个系列和组件,都遵从这些命名习惯,虽然不同图表和组件中,itemStyle、label 等可能出现在不同的地方。
高亮的样式:emphasis
在鼠标悬浮到图形元素上时,一般会出现高亮的样式。默认情况下,高亮的样式是根据普通样式自动生成的。但是高亮的样式也可以自己定义,主要是通过 emphasis 属性来定制。emphsis 中的结构,和普通样式的结构相同,例如:
option = {
series: {
type: 'scatter',
// 普通样式。
itemStyle: {
// 点的颜色。
color: 'red'
},
label: {
show: true,
// 标签的文字。
formatter: 'This is a normal label.'
},
// 高亮样式。
emphasis: {
itemStyle: {
// 高亮时点的颜色。
color: 'blue'
},
label: {
show: true,
// 高亮时标签的文字。
formatter: 'This is a emphasis label.'
}
}
}
};
注意:在 ECharts4 以前,高亮和普通样式的写法,是这样的:
option = {
series: {
type: 'scatter',
itemStyle: {
// 普通样式。
normal: {
// 点的颜色。
color: 'red'
},
// 高亮样式。
emphasis: {
// 高亮时点的颜色。
color: 'blue'
}
}
};
这种写法 仍然被兼容,但是,不再推荐。
数据集
数据集(dataset)是专门用来管理数据的组件。虽然每个系列都可以在 series.data 中设置数据,但是从 ECharts4 支持 数据集 开始,更推荐使用 数据集 来管理数据。因为这样,数据可以被多个组件复用,也方便进行 “数据和其他配置” 分离的配置风格。毕竟,在运行时,数据是最常改变的,而其他配置大多并不会改变。
在系列中设置数据
如果数据设置在 系列(series)中,例如:
option = {
xAxis: {
type: 'category',
data: ['Matcha Latte', 'Milk Tea', 'Cheese Cocoa', 'Walnut Brownie']
},
series: [
{
type: 'bar',
name: '2015',
data: [89.3, 92.1, 94.4, 85.4]
}
]
};
这种方式的优点是,适于对一些特殊的数据结构(如“树”、“图”、超大数据)进行一定的数据类型定制。 但是缺点是,常需要用户先处理数据,把数据分割设置到各个系列(和类目轴)中。此外,不利于多个系列共享一份数据,也不利于基于原始数据进行图表类型、系列的映射安排。
在数据集中设置数据
数据设置在 数据集(dataset)中,会有这些好处:
- 能够贴近数据可视化常见思维方式:(I)提供数据,(II)指定数据到视觉的映射,从而形成图表。
- 数据和其他配置可以被分离开来。数据常变,其他配置常不变。分开易于分别管理。
- 数据可以被多个系列或者组件复用,对于大数据量的场景,不必为每个系列创建一份数据。
- 支持更多的数据的常用格式,例如二维数组、对象数组等,一定程度上避免使用者为了数据格式而进行转换。
下面是一个最简单的
dataset的例子:
option = {
dataset: {
// 提供一份数据。
source: [
['product', '2015', '2016', '2017'],
['Matcha Latte', 43.3, 85.8, 93.7],
['Milk Tea', 83.1, 73.4, 55.1],
['Cheese Cocoa', 86.4, 65.2, 82.5],
['Walnut Brownie', 72.4, 53.9, 39.1]
]
},
// 声明一个 X 轴,类目轴(category)。默认情况下,类目轴对应到 dataset 第一列。
xAxis: { type: 'category' },
// 声明一个 Y 轴,数值轴。
yAxis: {},
// 声明多个 bar 系列,默认情况下,每个系列会自动对应到 dataset 的每一列。
series: [{ type: 'bar' }, { type: 'bar' }, { type: 'bar' }]
};
或者也可以使用常见的“对象数组”的格式:
option = {
legend: {},
tooltip: {},
dataset: {
// 用 dimensions 指定了维度的顺序。直角坐标系中,如果 X 轴 type 为 category,
// 默认把第一个维度映射到 X 轴上,后面维度映射到 Y 轴上。
// 如果不指定 dimensions,也可以通过指定 series.encode
dimensions: ['product', '2015', '2016', '2017'],
source: [
{ product: 'Matcha Latte', '2015': 43.3, '2016': 85.8, '2017': 93.7 },
{ product: 'Milk Tea', '2015': 83.1, '2016': 73.4, '2017': 55.1 },
{ product: 'Cheese Cocoa', '2015': 86.4, '2016': 65.2, '2017': 82.5 },
{ product: 'Walnut Brownie', '2015': 72.4, '2016': 53.9, '2017': 39.1 }
]
},
xAxis: { type: 'category' },
yAxis: {},
series: [{ type: 'bar' }, { type: 'bar' }, { type: 'bar' }]
};
把数据集( dataset )的行或列映射为系列(series)
有了数据表之后,使用者可以灵活地配置:数据如何对应到轴和图形系列。
用户可以使用 seriesLayoutBy 配置项,改变图表对于行列的理解。seriesLayoutBy 可取值:
- 'column': 默认值。系列被安放到
dataset的列上面。 - 'row': 系列被安放到
dataset的行上面。
option = {
legend: {},
tooltip: {},
dataset: {
source: [
['product', '2012', '2013', '2014', '2015'],
['Matcha Latte', 41.1, 30.4, 65.1, 53.3],
['Milk Tea', 86.5, 92.1, 85.7, 83.1],
['Cheese Cocoa', 24.1, 67.2, 79.5, 86.4]
]
},
xAxis: [
{ type: 'category', gridIndex: 0 },
{ type: 'category', gridIndex: 1 }
],
yAxis: [{ gridIndex: 0 }, { gridIndex: 1 }],
grid: [{ bottom: '55%' }, { top: '55%' }],
series: [
// 这几个系列会出现在第一个直角坐标系中,每个系列对应到 dataset 的每一行。
{ type: 'bar', seriesLayoutBy: 'row' },
{ type: 'bar', seriesLayoutBy: 'row' },
{ type: 'bar', seriesLayoutBy: 'row' },
// 这几个系列会出现在第二个直角坐标系中,每个系列对应到 dataset 的每一列。
{ type: 'bar', xAxisIndex: 1, yAxisIndex: 1 },
{ type: 'bar', xAxisIndex: 1, yAxisIndex: 1 },
{ type: 'bar', xAxisIndex: 1, yAxisIndex: 1 },
{ type: 'bar', xAxisIndex: 1, yAxisIndex: 1 }
]
};
维度的概念
常用图表所描述的数据大部分是"二维表"结构,上述的例子中,我们都使用二维数组来容纳二维表。现在,当我们把系列(series)对应到"列"的时候,那么每一列就称为一个"维度(dimension)",而每一行称为数据项(item)。反之,如果我们把系列(series)对应到表行,那么每一行就是"维度( dimension )",每一列就是数据项( item )(维度的概念不是很好理解)。比如下面的数据集:
source: [
['product', '2012', '2013', '2014', '2015'],
['Matcha Latte', 41.1, 30.4, 65.1, 53.3],
['Milk Tea', 86.5, 92.1, 85.7, 83.1],
['Cheese Cocoa', 24.1, 67.2, 79.5, 86.4]
]
如果系列对应的是列,那么每一列就是一个维度,维度有'product', '2012', '2013', '2014', '2015',也就是我们看数据就从这几个维度来分析数据。比如,我从2012年这个维度来分析产品的销量情况。
如果系列对应的是行,那么每一行就是一个维度,维度有'product', 'Matcha Latte', 'Milk Tea', 'Cheese Cocoa',那么分析数据的时候就从这几个维度来分析,比如,我从Matcha Latte这个产品的维度来分析2012 2013 2014等这几年的销售情况。
维度的定义,也可以使用单独的 dataset.dimensions 或者 series.dimensions 来定义,这样可以同时指定维度名,和维度的类型( dimension type )。大多数情况下,我们并不需要去设置维度类型,因为 ECharts 会自动尝试判断。
数据到图形的映射( series.encode )
了解了维度的概念后,我们就可以使用 series.encode 来做映射。总体是这样的感觉:
var option = {
dataset: {
source: [
['score', 'amount', 'product'],
[89.3, 58212, 'Matcha Latte'],
[57.1, 78254, 'Milk Tea'],
[74.4, 41032, 'Cheese Cocoa'],
[50.1, 12755, 'Cheese Brownie'],
[89.7, 20145, 'Matcha Cocoa'],
[68.1, 79146, 'Tea'],
[19.6, 91852, 'Orange Juice'],
[10.6, 101852, 'Lemon Juice'],
[32.7, 20112, 'Walnut Brownie']
]
},
xAxis: {},
yAxis: { type: 'category' },
series: [
{
type: 'bar',
encode: {
// 将 "amount" 列映射到 X 轴。
x: 'amount',
// 将 "product" 列映射到 Y 轴。
y: 'product'
}
}
]
};
series.encode 声明的基本结构如下。其中冒号左边是坐标系、标签等特定名称,如 'x', 'y', 'tooltip' 等,冒号右边是数据中的维度名(string 格式)或者维度的序号(number 格式,从 0 开始计数),可以指定一个或多个维度(使用数组)。可以参考示例,可以看看series.encode是如何写的。
多个 dataset 以及如何引用他们
可以同时定义多个 dataset。系列可以通过 series.datasetIndex 来指定引用哪个 dataset。例如:
var option = {
dataset: [
{
// 序号为 0 的 dataset。
source: []
},
{
// 序号为 1 的 dataset。
source: []
},
{
// 序号为 2 的 dataset。
source: []
}
],
series: [
{
// 使用序号为 2 的 dataset。
datasetIndex: 2
},
{
// 使用序号为 1 的 dataset。
datasetIndex: 1
}
]
};
注意:ECharts 4 之前一直以来的数据声明方式仍然被正常支持,如果系列已经声明了 series.data, 那么就会使用 series.data 而非 dataset。
其实,series.data 也是种会一直存在的重要设置方式。一些特殊的非 table 格式的图表,如 treemap、graph、lines 等,现在仍不支持在 dataset 中设置,仍然需要使用 series.data。另外,对于巨大数据量的渲染(如百万以上的数据量),需要使用 appendData 进行增量加载,这种情况不支持使用 dataset。
数据转换
Apache ECharts5 开始支持了"数据转换"( data transform )功能。在 echarts 中,"数据转换"这个词指的是,给定一个已有的"数据集"和一个"转换方法",echarts 能生成一个新的"数据集",然后可以使用这个新的"数据集"绘制图表,这些工作都可以声明式地完成。
抽象地来说,数据转换是这样一种公式:outData = f(inputData)。f 是转换方法,例如:filter、sort、regression、boxplot、cluster、aggregate(todo) 等等。有了数据转换能力后,我们就至少可以做到这些事情:
- 把数据分成多份用不同的饼图展现。
- 进行一些数据统计运算,并展示结果。
- 用某些数据可视化算法处理数据,并展示结果。
- 数据排序。
- 去除或直选择数据项。
数据转换基础使用
在 echarts 中,数据转换是依托于数据集来实现的. 我们可以设置dataset.transform来表示,此 dataset 的数据,来自于此 transform 的结果。
var option = {
dataset: [
{
// 这个 dataset 的 index 是 `0`。
source: [
['Product', 'Sales', 'Price', 'Year'],
['Cake', 123, 32, 2011],
['Cereal', 231, 14, 2011],
['Tofu', 235, 5, 2011],
['Dumpling', 341, 25, 2011],
['Biscuit', 122, 29, 2011],
['Cake', 143, 30, 2012],
['Cereal', 201, 19, 2012],
['Tofu', 255, 7, 2012],
['Dumpling', 241, 27, 2012],
['Biscuit', 102, 34, 2012],
['Cake', 153, 28, 2013],
['Cereal', 181, 21, 2013],
['Tofu', 395, 4, 2013],
['Dumpling', 281, 31, 2013],
['Biscuit', 92, 39, 2013],
['Cake', 223, 29, 2014],
['Cereal', 211, 17, 2014],
['Tofu', 345, 3, 2014],
['Dumpling', 211, 35, 2014],
['Biscuit', 72, 24, 2014]
]
// id: 'a'
},
{
// 这个 dataset 的 index 是 `1`。
// 这个 `transform` 配置,表示,此 dataset 的数据,来自于此 transform 的结果。
transform: {
type: 'filter',
config: { dimension: 'Year', value: 2011 }
}
// 我们还可以设置这些可选的属性: `fromDatasetIndex` 或 `fromDatasetId`。
// 这些属性,指定了,transform 的输入,来自于哪个 dataset。例如,
// `fromDatasetIndex: 0` 表示输入来自于 index 为 `0` 的 dataset 。又例如,
// `fromDatasetId: 'a'` 表示输入来自于 `id: 'a'` 的 dataset。
// 当这些属性都不指定时,默认认为,输入来自于 index 为 `0` 的 dataset 。
},
{
// 这个 dataset 的 index 是 `2`。
// 同样,这里因为 `fromDatasetIndex` 和 `fromDatasetId` 都没有被指定,
// 那么输入默认来自于 index 为 `0` 的 dataset 。
transform: {
// 这个类型为 "filter" 的 transform 能够遍历并筛选出满足条件的数据项。
type: 'filter',
// 每个 transform 如果需要有配置参数的话,都须配置在 `config` 里。
// 在这个 "filter" transform 中,`config` 用于指定筛选条件。
// 下面这个筛选条件是:选出维度( dimension )'Year' 中值为 2012 的所有
// 数据项。
config: { dimension: 'Year', value: 2012 }
}
},
{
transform: {
type: 'filter',
config: { dimension: 'Year', value: 2013 }
}
}
],
series: [
{
type: 'pie',
radius: 50,
center: ['25%', '50%'],
// 这个饼图系列,引用了 index 为 `1` 的 dataset 。也就是,引用了上述
// 2011 年那个 "filter" transform 的结果。
datasetIndex: 1
},
{
type: 'pie',
radius: 50,
center: ['50%', '50%'],
datasetIndex: 2
},
{
type: 'pie',
radius: 50,
center: ['75%', '50%'],
datasetIndex: 3
}
]
};
具体transform的使用请参照使用 transform 进行数据转换
坐标轴
x 轴、y 轴
x 轴和 y 轴都由轴线、刻度、刻度标签、轴标题四个部分组成。部分图表中还会有网格线来帮助查看和计算数据。
x 轴常用来标示数据的维度,维度一般用来指数据的类别,是观察数据的角度,例如"销售时间" "销售地点" "产品名称"等。y 轴常常用来标示数据的数值,数值是用来具体考察某一类数据的数量值,也是我们需要分析的指标,例如"销售数量"和"销售金额"等。
option = {
xAxis: {
type: 'time',
name: '销售时间'
// ...
},
yAxis: {
type: 'value',
name: '销售数量'
// ...
},
dataZoom: [
// ...
]
};
当 x 轴(水平坐标轴)跨度很大,可以采用区域缩放方式dataZoom灵活显示数据内容。
一个坐标系有多个轴
option = {
tooltip: {
trigger: 'axis',
axisPointer: { type: 'cross' }
},
legend: {},
xAxis: [
{
type: 'category',
axisTick: {
alignWithLabel: true
},
data: [
'1月',
'2月',
'3月',
...
'10月',
'11月',
'12月'
]
}
],
yAxis: [
{
type: 'value',
name: '降水量',
min: 0,
max: 250,
// 执行轴的摆放位置
position: 'right',
axisLabel: {
formatter: '{value} ml'
}
},
{
type: 'value',
name: '温度',
min: 0,
max: 25,
position: 'left',
axisLabel: {
formatter: '{value} °C'
}
}
],
series: [
{
name: '降水量',
type: 'bar',
// 对应哪个y轴
yAxisIndex: 0,
data: [6, 32, 70, 86, 68.7, 100.7, 125.6, 112.2, 78.7, 48.8, 36.0, 19.3]
},
{
name: '温度',
type: 'line',
smooth: true,
yAxisIndex: 1,
data: [
6.0,
10.2,
10.3,
11.5,
10.3,
...
5.2
]
}
]
};
多个坐标轴
var sizeValue = '57%';
grid: [
{ right: sizeValue, bottom: sizeValue },
{ left: sizeValue, bottom: sizeValue },
{ right: sizeValue, top: sizeValue },
{ left: sizeValue, top: sizeValue }
],
xAxis: [
{
type: 'value',
gridIndex: 0,
},
{
type: 'category',
gridIndex: 1,
},
...
],
series: [
{
type: 'scatter',
symbolSize: symbolSize,
xAxisIndex: 0,
yAxisIndex: 0,
}
...
]
视觉映射
ECharts 的每种图表本身就内置了这种映射过程,比如折线图把数据映射到“线”,柱状图把数据映射到“长度”。一些更复杂的图表,如关系图、事件河流图、树图也都会做出各自内置的映射。
此外,ECharts 还提供了 visualMap 组件 来提供通用的视觉映射。visualMap 组件中可以使用的视觉元素有:
- 图形类别(symbol)、图形大小(symbolSize)
- 颜色(color)、透明度(opacity)、颜色透明度(colorAlpha)、
- 颜色明暗度(colorLightness)、颜色饱和度(colorSaturation)、色调(colorHue)
visualMap多用在多维度的数据展示中。在图表中,往往默认把dataset的前一两个维度进行映射,比如取第一个维度映射到 x 轴,取第二个维度映射到 y 轴。如果想要把更多的维度展现出来,可以借助 visualMap。最常见的情况,散点图(scatter)使用半径展现了第三个维度。
visualMap 组件定义了把数据的哪个维度映射到什么视觉元素上。
事件与行为
在 Apache ECharts 的图表中用户的操作将会触发相应的事件。开发者可以监听这些事件,然后通过回调函数做相应的处理,比如跳转到一个地址,或者弹出对话框,或者做数据下钻等等。
在 ECharts 中事件分为两种类型,一种是用户鼠标操作点击,或者 hover 图表的图形时触发的事件,还有一种是用户在使用可以交互的组件后触发的行为事件,例如在切换图例开关时触发的 'legendselectchanged' 事件,数据区域缩放时触发的 'datazoom' 事件等等。
以上就是对echarts的整体脉络的一个梳理,对echarts整体有个了解后,我们就可以比较高效的开发数据可视化项目了。