下定决心,好好过一天 ~
ECharts
ECharts 异步加载数据
ECharts 通常数据设置在 setOption 中,如果我们需要异步加载数据,可以配合 jQuery等工具,在异步获取数据后通过 setOption 填入数据和配置项就行。
如果异步加载需要一段时间,我们可以添加 loading 效果,ECharts 默认有提供了一个简单的加载动画。只需要调用 showLoading 方法显示。数据加载完成后再调用 hideLoading 方法隐藏加载动画:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>第一个 ECharts 实例</title>
<script src="https://cdn.staticfile.org/jquery/2.2.4/jquery.min.js"></script>
<!-- 引入 echarts.js -->
<script src="https://cdn.staticfile.org/echarts/4.3.0/echarts.min.js"></script>
</head>
<body>
<!-- 为ECharts准备一个具备大小(宽高)的Dom -->
<div id="main" style="width: 600px;height:400px;"></div>
<script type="text/javascript">
// 基于准备好的dom,初始化echarts实例
var myChart = echarts.init(document.getElementById('main'));
myChart.showLoading(); // 开启 loading 效果
$.get('https://www.runoob.com/static/js/echarts_test_data.json', function (data) {
alert("可以看到 loading 字样。"); // 测试代码,用于查看 loading 效果
myChart.hideLoading(); // 隐藏 loading 效果
myChart.setOption({
series : [
{
name: '访问来源',
type: 'pie', // 设置图表类型为饼图
radius: '55%', // 饼图的半径,外半径为可视区尺寸(容器高宽中较小一项)的 55% 长度。
data:data.data_pie
}
]
})
}, 'json');
</script>
</body>
数据的动态更新
ECharts 由数据驱动,数据的改变驱动图表展现的改变,因此动态数据的实现也变得异常简单。
所有数据的更新都通过 setOption 实现,你只需要定时获取数据,setOption 填入数据,而不用考虑数据到底产生了那些变化,ECharts 会找到两组数据之间的差异然后通过合适的动画去表现数据的变化。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>第一个 ECharts 实例</title>
<script src="https://cdn.staticfile.org/jquery/2.2.4/jquery.min.js"></script>
<!-- 引入 echarts.js -->
<script src="https://cdn.staticfile.org/echarts/4.3.0/echarts.min.js"></script>
</head>
<body>
<!-- 为ECharts准备一个具备大小(宽高)的Dom -->
<div id="main" style="width: 600px;height:400px;"></div>
<script type="text/javascript">
var base = +new Date(2014, 9, 3);
var oneDay = 24 * 3600 * 1000;
var date = [];
var data = [Math.random() * 150];
var now = new Date(base);
function addData(shift) {
now = [now.getFullYear(), now.getMonth() + 1, now.getDate()].join('/');
date.push(now);
data.push((Math.random() - 0.4) * 10 + data[data.length - 1]);
if (shift) {
date.shift();
data.shift();
}
now = new Date(+new Date(now) + oneDay);
}
for (var i = 1; i < 100; i++) {
addData();
}
option = {
xAxis: {
type: 'category',
boundaryGap: false,
data: date
},
yAxis: {
boundaryGap: [0, '50%'],
type: 'value'
},
series: [
{
name:'成交',
type:'line',
smooth:true,
symbol: 'none',
stack: 'a',
areaStyle: {
normal: {}
},
data: data
}
]
};
setInterval(function () {
addData(true);
myChart.setOption({
xAxis: {
data: date
},
series: [{
name:'成交',
data: data
}]
});
}, 500);
// 基于准备好的dom,初始化echarts实例
var myChart = echarts.init(document.getElementById('main'));
myChart.setOption(option)
</script>
</body>
ECharts 数据集(dataset)
ECharts 使用 dataset 管理数据。
dataset 组件用于单独的数据集声明,从而数据可以单独管理,被多个组件复用,并且可以基于数据指定数据到视觉的映射。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>第一个 ECharts 实例</title>
<!-- 引入 echarts.js -->
<script src="https://cdn.staticfile.org/echarts/4.3.0/echarts.min.js"></script>
</head>
<body>
<!-- 为ECharts准备一个具备大小(宽高)的Dom -->
<div id="main" style="width: 600px;height:400px;"></div>
<script type="text/javascript">
// 基于准备好的dom,初始化echarts实例
var myChart = echarts.init(document.getElementById('main'));
// 指定图表的配置项和数据
var option = {
legend: {},
tooltip: {},
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]
]
//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}
]
},
// 声明一个 X 轴,类目轴(category)。默认情况下,类目轴对应到 dataset 第一列。
xAxis: {type: 'category'},
// 声明一个 Y 轴,数值轴。
yAxis: {},
// 声明多个 bar 系列,默认情况下,每个系列会自动对应到 dataset 的每一列。
series: [
{type: 'bar'},
{type: 'bar'},
{type: 'bar'}
]
};
// 使用刚指定的配置项和数据显示图表。
myChart.setOption(option);
</script>
</body>
</html>
数据到图形的映射
我们可以在配置项中将数据映射到图形中。
我们可以使用 series.seriesLayoutBy 属性来配置 dataset 是列(column)还是行(row)映射为图形系列(series),默认是按照列(column)来映射。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>第一个 ECharts 实例</title>
<!-- 引入 echarts.js -->
<script src="https://cdn.staticfile.org/echarts/4.3.0/echarts.min.js"></script>
</head>
<body>
<!-- 为ECharts准备一个具备大小(宽高)的Dom -->
<div id="main" style="width: 600px;height:400px;"></div>
<script type="text/javascript">
// 基于准备好的dom,初始化echarts实例
var myChart = echarts.init(document.getElementById('main'));
// 指定图表的配置项和数据
var 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}
]
}
// 使用刚指定的配置项和数据显示图表。
myChart.setOption(option);
</script>
</body>
</html>
常用图表所描述的数据大部分是"二维表"结构,我们可以使用 series.encode 属性将对应的数据映射到坐标轴(如 X、Y 轴):
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>第一个 ECharts 实例</title>
<!-- 引入 echarts.js -->
<script src="https://cdn.staticfile.org/echarts/4.3.0/echarts.min.js"></script>
</head>
<body>
<!-- 为ECharts准备一个具备大小(宽高)的Dom -->
<div id="main" style="width: 600px;height:400px;"></div>
<script type="text/javascript">
// 基于准备好的dom,初始化echarts实例
var myChart = echarts.init(document.getElementById('main'));
// 指定图表的配置项和数据
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']
]
},
grid: {containLabel: true},
xAxis: {},
yAxis: {type: 'category'},
series: [
{
type: 'bar',
encode: {
// 将 "amount" 列映射到 X 轴。
x: 'amount',
// 将 "product" 列映射到 Y 轴。
y: 'product'
}
}
]
};
// 使用刚指定的配置项和数据显示图表。
myChart.setOption(option);
</script>
</body>
</html>
encode 声明的基本结构如下,其中冒号左边是坐标系、标签等特定名称,如 ‘x’, ‘y’, ‘tooltip’ 等,冒号右边是数据中的维度名(string 格式)或者维度的序号(number 格式,从 0 开始计数),可以指定一个或多个维度(使用数组)。通常情况下,下面各种信息不需要所有的都写,按需写即可。
下面是 encode 支持的属性:
// 在任何坐标系和系列中,都支持:
encode: {
// 使用 “名为 product 的维度” 和 “名为 score 的维度” 的值在 tooltip 中显示
tooltip: ['product', 'score']
// 使用 “维度 1” 和 “维度 3” 的维度名连起来作为系列名。(有时候名字比较长,这可以避免在 series.name 重复输入这些名字)
seriesName: [1, 3],
// 表示使用 “维度2” 中的值作为 id。这在使用 setOption 动态更新数据时有用处,可以使新老数据用 id 对应起来,从而能够产生合适的数据更新动画。
itemId: 2,
// 指定数据项的名称使用 “维度3” 在饼图等图表中有用,可以使这个名字显示在图例(legend)中。
itemName: 3
}
// 直角坐标系(grid/cartesian)特有的属性:
encode: {
// 把 “维度1”、“维度5”、“名为 score 的维度” 映射到 X 轴:
x: [1, 5, 'score'],
// 把“维度0”映射到 Y 轴。
y: 0
}
// 单轴(singleAxis)特有的属性:
encode: {
single: 3
}
// 极坐标系(polar)特有的属性:
encode: {
radius: 3,
angle: 2
}
// 地理坐标系(geo)特有的属性:
encode: {
lng: 3,
lat: 2
}
// 对于一些没有坐标系的图表,例如饼图、漏斗图等,可以是:
encode: {
value: 3
}
视觉通道(颜色、尺寸等)的映射
我们可以使用 visualMap 组件进行视觉通道的映射。
视觉元素可以是:
1.ymbol: 图元的图形类别。
2.symbolSize: 图元的大小。
3.color: 图元的颜色。
4.colorAlpha: 图元的颜色的透明度。
5.opacity: 图元以及其附属物(如文字标签)的透明度。
6.colorLightness: 颜色的明暗度。
7.colorSaturation: 颜色的饱和度。
8.colorHue: 颜色的色调。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>ECharts 实例</title>
<!-- 引入 echarts.js -->
<script src="https://cdn.staticfile.org/echarts/4.3.0/echarts.min.js"></script>
</head>
<body>
<!-- 为ECharts准备一个具备大小(宽高)的Dom -->
<div id="main" style="width: 600px;height:400px;"></div>
<script type="text/javascript">
// 基于准备好的dom,初始化echarts实例
var myChart = echarts.init(document.getElementById('main'));
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']
]
},
grid: {containLabel: true},
xAxis: {name: 'amount'},
yAxis: {type: 'category'},
visualMap: {
orient: 'horizontal',
left: 'center',
min: 10,
max: 100,
text: ['High Score', 'Low Score'],
// Map the score column to color
dimension: 0,
inRange: {
color: ['#D7DA8B', '#E15457']
}
},
series: [
{
type: 'bar',
encode: {
// Map the "amount" column to X axis.
x: 'amount',
// Map the "product" column to Y axis
y: 'product'
}
}
]
};
myChart.setOption(option);
</script>
</body>
</html>
ECharts 交互组件
ECharts 提供了很多交互组件:例组件 legend、标题组件 title、视觉映射组件 visualMap、数据区域缩放组件 dataZoom、时间线组件 timeline。
dataZoom
dataZoom 组件可以实现通过鼠标滚轮滚动,放大缩小图表的功能。
默认情况下 dataZoom 控制 x 轴,即对 x 轴进行数据窗口缩放和数据窗口平移操作。
dataZoom: [
{ // 这个dataZoom组件,默认控制x轴。
type: 'slider', // 这个 dataZoom 组件是 slider 型 dataZoom 组件
start: 10, // 左边在 10% 的位置。
end: 60 // 右边在 60% 的位置。
},
{ // 这个dataZoom组件,也控制x轴。
type: 'inside', // 这个 dataZoom 组件是 inside 型 dataZoom 组件
start: 10, // 左边在 10% 的位置。
end: 60 // 右边在 60% 的位置。
}
],
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>ECharts 实例</title>
<!-- 引入 echarts.js -->
<script src="https://cdn.staticfile.org/echarts/4.3.0/echarts.min.js"></script>
</head>
<body>
<!-- 为ECharts准备一个具备大小(宽高)的Dom -->
<div id="main" style="width: 600px;height:400px;"></div>
<script type="text/javascript">
// 基于准备好的dom,初始化echarts实例
var myChart = echarts.init(document.getElementById('main'));
// 指定图表的配置项和数据
var data1 = [];
var data2 = [];
var data3 = [];
var random = function (max) {
return (Math.random() * max).toFixed(3);
};
for (var i = 0; i < 500; i++) {
data1.push([random(15), random(10), random(1)]);
data2.push([random(10), random(10), random(1)]);
data3.push([random(15), random(10), random(1)]);
}
option = {
animation: false,
legend: {
data: ['scatter', 'scatter2', 'scatter3']
},
tooltip: {
},
xAxis: {
type: 'value',
min: 'dataMin',
max: 'dataMax',
splitLine: {
show: true
}
},
yAxis: {
type: 'value',
min: 'dataMin',
max: 'dataMax',
splitLine: {
show: true
}
},
dataZoom: [
{
type: 'slider',
show: true,
xAxisIndex: [0],
start: 1,
end: 35
},
{
type: 'slider',
show: true,
yAxisIndex: [0],
left: '93%',
start: 29,
end: 36
},
{
type: 'inside',
xAxisIndex: [0],
start: 1,
end: 35
},
{
type: 'inside',
yAxisIndex: [0],
start: 29,
end: 36
}
],
series: [
{
name: 'scatter',
type: 'scatter',
itemStyle: {
normal: {
opacity: 0.8
}
},
symbolSize: function (val) {
return val[2] * 40;
},
data: data1
},
{
name: 'scatter2',
type: 'scatter',
itemStyle: {
normal: {
opacity: 0.8
}
},
symbolSize: function (val) {
return val[2] * 40;
},
data: data2
},
{
name: 'scatter3',
type: 'scatter',
itemStyle: {
normal: {
opacity: 0.8,
}
},
symbolSize: function (val) {
return val[2] * 40;
},
data: data3
}
]
}
// 使用刚指定的配置项和数据显示图表。
myChart.setOption(option);
</script>
</body>
</html>
ECharts 响应式
ECharts 图表显示在用户指定高宽的 DOM 节点(容器)中。
有时候我们希望在 PC 和 移动设备上都能够很好的展示图表的内容,实现响应式的设计,为了解决这个问题,ECharts 完善了组件的定位设置,并且实现了类似 CSS Media Query 的自适应能力。
ECharts 组件的定位和布局
大部分『组件』和『系列』会遵循两种定位方式。
left/right/top/bottom/width/height 定位方式
这六个量中,每个量都可以是『绝对值』或者『百分比』或者『位置描述』。
绝对值
单位是浏览器像素(px),用 number 形式书写(不写单位)。例如 {left: 23, height: 400}。
百分比
表示占 DOM 容器高宽的百分之多少,用 string 形式书写。例如 {right: ‘30%’, bottom: ‘40%’}。
位置描述
可以设置 left: ‘center’,表示水平居中。
可以设置 top: ‘middle’,表示垂直居中。
这六个量的概念,和 CSS 中六个量的概念类似:
left:距离 DOM 容器左边界的距离。
right:距离 DOM 容器右边界的距离。
top:距离 DOM 容器上边界的距离。
bottom:距离 DOM 容器下边界的距离。
width:宽度。
height:高度。
在横向,left、right、width 三个量中,只需两个量有值即可,因为任两个量可以决定组件的位置和大小,例如 left 和 right 或者 right 和 width 都可以决定组件的位置和大小。 纵向,top、bottom、height 三个量,和横向类同不赘述。
center / radius 定位方式
1.center
是一个数组,表示 [x, y],其中,x、y可以是『绝对值』或者『百分比』,含义和前述相同。
2.radius
是一个数组,表示 [内半径, 外半径],其中,内外半径可以是『绝对值』或者『百分比』,含义和前述相同。
在自适应容器大小时,百分比设置是很有用的。
横向(horizontal)和纵向(vertical)
ECharts的『外观狭长』型的组件(如 legend、visualMap、dataZoom、timeline等),大多提供了『横向布局』『纵向布局』的选择。例如,在细长的移动端屏幕上,可能适合使用『纵向布局』;在PC宽屏上,可能适合使用『横向布局』。
横纵向布局的设置,一般在『组件』或者『系列』的 orient 或者 layout 配置项上,设置为 ‘horizontal’ 或者 ‘vertical’。
要在 option 中设置 Media Query 须遵循如下格式:
option = {
baseOption: { // 这里是基本的『原子option』。
title: {...},
legend: {...},
series: [{...}, {...}, ...],
...
},
media: [ // 这里定义了 media query 的逐条规则。
{
query: {...}, // 这里写规则。
option: { // 这里写此规则满足下的option。
legend: {...},
...
}
},
{
query: {...}, // 第二个规则。
option: { // 第二个规则对应的option。
legend: {...},
...
}
},
{ // 这条里没有写规则,表示『默认』,
option: { // 即所有规则都不满足时,采纳这个option。
legend: {...},
...
}
}
]
};
上面的例子中,baseOption、以及 media 每个 option 都是『原子 option』,即普通的含有各组件、系列定义的 option。而由『原子option』组合成的整个 option,我们称为『复合 option』。baseOption 是必然被使用的,此外,满足了某个 query 条件时,对应的 option 会被使用 chart.mergeOption() 来 merge 进去。
query
{
minWidth: 200,
maxHeight: 300,
minAspectRatio: 1.3
}
现在支持三个属性:width、height、aspectRatio(长宽比)。每个属性都可以加上 min 或 max 前缀。比如,minWidth: 200 表示『大于等于200px宽度』。两个属性一起写表示『并且』,比如:{minWidth: 200, maxHeight: 300} 表示『大于等于200px宽度,并且小于等于300px高度』。
option
media中的 option 既然是『原子 option』,理论上可以写任何 option 的配置项。但是一般我们只写跟布局定位相关的,例如截取上面例子中的一部分 query option:
media: [
...,
{
query: {
maxAspectRatio: 1 // 当长宽比小于1时。
},
option: {
legend: { // legend 放在底部中间。
right: 'center',
bottom: 0,
orient: 'horizontal' // legend 横向布局。
},
series: [ // 两个饼图左右布局。
{
radius: [20, '50%'],
center: ['50%', '30%']
},
{
radius: [30, '50%'],
center: ['50%', '70%']
}
]
}
},
{
query: {
maxWidth: 500 // 当容器宽度小于 500 时。
},
option: {
legend: {
right: 10, // legend 放置在右侧中间。
top: '15%',
orient: 'vertical' // 纵向布局。
},
series: [ // 两个饼图上下布局。
{
radius: [20, '50%'],
center: ['50%', '30%']
},
{
radius: [30, '50%'],
center: ['50%', '75%']
}
]
}
},
...
]
多个 query 被满足时的优先级
注意,可以有多个 query 同时被满足,会都被 mergeOption,定义在后的后被 merge(即优先级更高)。
默认 query
如果 media 中有某项不写 query,则表示『默认值』,即所有规则都不满足时,采纳这个option。
容器大小实时变化时的注意事项
在不少情况下,并不需要容器DOM节点任意随着拖拽变化大小,而是只是根据不同终端设置几个典型尺寸。
但是如果容器DOM节点需要能任意随着拖拽变化大小,那么目前使用时需要注意这件事:某个配置项,如果在某一个 query option 中出现,那么在其他 query option 中也必须出现,否则不能够回归到原来的状态。(left/right/top/bottom/width/height 不受这个限制。)
『复合 option』 中的 media 不支持 merge
也就是说,当第二(或三、四、五 …)次 chart.setOption(rawOption) 时,如果 rawOption 是 复合option(即包含 media 列表),那么新的 rawOption.media 列表不会和老的 media 列表进行 merge,而是简单替代。当然,rawOption.baseOption 仍然会正常和老的 option 进行merge。 其实,很少有场景需要使用『复合 option』来多次 setOption,而我们推荐的做法是,使用 mediaQuery 时,第一次setOption使用『复合 option』,后面 setOption 时仅使用 『原子 option』,也就是仅仅用 setOption 来改变 baseOption。
下定决心,好好过一天 ~