首先比较下目前比较流行的几款图表库:
highcharts文档详细易懂,上手快捷,但highcharts依赖于jQuery库,而且Highcharts对个人免费但对企业收费
d3.js更自由些,更容易做出自己想要的效果,但学习起来需要更多的时间
echarts:开源软件,无私的为我们提供漂亮的图形界面;使用简单,默默的为我们封装了重要的js,只要会引用就会使用echarts;种类多,echarts为我们提供了各种图标,其中最具象征的就是地图了;兼容性好,基于html5动画渲染超棒
由于上述原因,也本着支持国产的理念,这里主要给大家介绍的是由百度开发并持续维护的图标库--ECharts
获取ECharts的路径有以下几种,请根据个人情况进行选择:
1.最直接的方法是在ECharts的官方网站中挑选适合的版本进行下载,不同的打包下载应用于不同的开发者功能与体积的需求,或者也可以直接下载完整版本;开发环境建议下载源代码版本,包含了常见的错误提示和警告
2.也可以在ECharts的GitHub上下载最新的release版本,解压出来的文件夹里的dist目录里可以找到最新版本的echarts库
3.或通过npm获取echarts,npm install echarts --save
4.由cdn引入,可以在cdnjs/npmcdn或国内的bootcdn上找到ECharts的最新版本
ECharts的引入方式变简单了,因为从ECharts3开始就不用使用AMD的方式引入,代码也不再内置AMD加载器,所以ECharts的引入方式就可以像JavaScript库一样用script标签引入;下面是Echarts引入的代码:
在绘图前我们需要为ECharts准备一个具备高宽的DOM容器
然后就可以通过echarts.init方法初始化一个echarts实例并通过setOption方法生成一个简单的柱状图,下面是完整代码
这样你的第一个图表就诞生了
Webpack是目前比较流行的模块打包工具,它可以分析你的项目结构,找到JavaScript模块及其它的一些浏览器不能直接运行的拓展语言(Scss,TypeScript等),并将其转换和打包为合适的格式供浏览器使用。
如果你已经对webpack有一定的了解,且能够很好的在项目中使用webpack,那么就可以学习怎么在使用webpack的项目中轻松的引入和打包Echarts了
Echarts3.1.1之后的版本和zrender的package在npm上是由官方EFE维护的,之前的版本不受官方维护;使用下述命令可以通过npm安装ECharts和zrender:
npm install echarts --save
Echarts和zrender以npm的方式安装之后会被存放在node_modules目录下,直接在项目中运行代码require('echarts')得到Echarts,具体操作如下:
var echarts = require('echarts');
var myChart = echarts.init(document.getElementById('main')); //基于准备好的dom,初始化echarts实例
myChart.setOption({ // 绘制图表
title: { text: 'ECharts示例一' },
tooltip: {},
xAxis: {
data: ["衬衫","羊毛衫","雪纺衫","裤子","高跟鞋","袜子"]
},
yAxis: {},
series: [{
name: '访问来源',
type: 'bar',
data: [5, 20, 36, 10, 10, 20]
}]
});
使用require('echarts')得到的Echarts包是包含了所有的图表和组件的,所以容量会比较大,对于一些要求体积小的项目可能是不友好的,那么我们可以利用下述的代码来根据需要引入相应模块;利用下述的代码来引入需要使用的柱状图/提示框/标题组件,这样可以有效的缩小打包后的体积,从400多KB减小到170多KB
var echarts = require('echarts/lib/echarts'); // 引入 ECharts 主模块
require('echarts/lib/chart/bar'); // 引入柱状图
require('echarts/lib/component/tooltip'); // 引入提示框和标题组件
require('echarts/lib/component/title');
var myChart = echarts.init(document.getElementById('main')); // 基于准备好的dom初始化echarts实例
myChart.setOption({ // 绘制图表
title: { text: 'ECharts示例一' },
tooltip: {},
xAxis: {
data: ["衬衫","羊毛衫","雪纺衫","裤子","高跟鞋","袜子"]
},
yAxis: {},
series: [{
name: '访问来源',
type: 'bar',
data: [5, 20, 36, 10, 10, 20]
}]
});
Browserify是另一个现今流行的模块打包工具,它可以让你使用类似于node的require()方式来组织浏览器端的Javascript代码,通过预编译让前端Javascript可以直接使用Node NPM安装的一些库,它的用法与以上描述的方法相同
Echarts可以自定义配置选项;Echarts设置数据图形的样式可以从三个层级入手,分别是:全局/系列/数据;下面我们使用Echarts来实现一个南丁格尔图:
与上面柱状图不同,现在要画的是饼图,饼图主要是通过扇形的弧度表现不同类目的数据在总和中的占比,它的数据格式比柱状图更简单,只有一维的数值,不需要给类目;因为不在直角坐标系上,所以也不需要xAxis/yAxis
下述代码可以绘制出一个简单的饼图:
myChart.setOption({
series:[{
name:'访问来源',
type:'pie',
radius:'55%', //图形在画布中占的比例
data:[
{value:235, name:'视频广告'},
{value:274, name:'联盟广告'},
{value:310, name:'邮件营销'},
{value:335, name:'直接访问'},
{value:100, name:'搜索引擎'}
]
}]
})
ECharts中的数据项既可以只设成数值,也可以设为一个包含有名称,该数据图形的样式配置/标签配置的对象;比如上述代码中的data属性值是一个包含name和value属性的对象,而不像柱状图里那样每一项都是单个数值;ECharts中的饼图也支持通过设置在series中的roseType显示成南丁格尔图;南丁格尔图会通过半径表示数据的大小:
roseType: 'angle'
建立好基本的南丁格尔图后,可以为其添加一些通用的样式,例如阴影/透明度/颜色/边框颜色/边框宽度等;这些样式通常都是在series系列的itemStyle中设置,例如阴影可以通过下面几个配置项设置:
itemStyle: {
normal: {
shadowBlur: 200, // 阴影的大小
shadowOffsetX: 0, // 阴影水平方向上的偏移
shadowOffsetY: 0, // 阴影垂直方向上的偏移
shadowColor: 'rgba(255, 0, 0, 0.5)' // 阴影颜色
}
}
itemStyle都会有normal(正常展示下的样式)和emphasis(鼠标hover时的高亮样式)两个选项,上面例子是正常样式下加阴影,但更多时候是hover的时候通过阴影突出
itemStyle: {
emphasis: {
shadowBlur: 200,
shadowColor: 'rgba(0, 0, 0, 0.5)'
}
}
接下来我们通过修改背景颜色和文本颜色来把上述实例的主题变为深色主题;因为背景色是全局的,所以直接在option下设置backgroundColor就可以了:
var option = { //指定图表的配置项和数据
backgroundColor: '#2c343c',
}
背景色设置完成后,同样将文本的样式设置为全局的textStyle:
var option = { //指定图表的配置项和数据
textStyle: {
color: 'rgba(255, 255, 255, 0.3)'
}
}
如果不想设置全局的文本样式,也可以分别对每个系列进行设置,每个系列的文本设置在series中label.normal.textStyle
label: {
normal: {
textStyle: {
color: 'rgba(255, 255, 255, 0.3)'
}
}
}
最后,我们将实例中的饼图的标签的视觉引导线的颜色设置为浅色
labelLine: {
normal: {
lineStyle: {
color: 'rgba(255, 255, 255, 0.3)'
}
}
}
跟itemStyle一样,label和labelLine的样式也有normal和emphasis两个状态
还可以根据实际需要对饼图中扇形的颜色进行设置,同样,扇形的颜色也是在itemStyle中设置
itemStyle: {
normal: { // 设置扇形的颜色
color: '#c23531',
shadowBlur: 200,
shadowColor: 'rgba(0, 0, 0, 0.5)'
}
}
ECharts中每个扇形的颜色可以通过分别设置data下的数据项实现
data:[
{value:235, name:'视频广告',itemStyle:{
normal: {
color: '#aaa'
}
}},
{value:274, name:'联盟广告',itemStyle:{
normal: {
color: '#abc'
}
}},
{value:310, name:'邮件营销',itemStyle:{
normal: {
color: '#123'
}
}},
{value:335, name:'直接访问',itemStyle:{
normal: {
color: '#1f3'
}
}},
{value:100, name:'搜索引擎',itemStyle:{
normal: {
color: '#2a4'
}
}},
],
接下来我们来实现图形的层次感和空间感,如果只有图像明暗度的变化,有一种快捷的方式是通过visualMap组件将数值的大小映射到明暗度
visualMap: {
show: false, // 不显示visualMap组件,只用于明暗度的映射,如果为true则会增加颜色条
min: 80, // 映射的最小值为80
max: 600, // 映射的最大值为600
inRange: {
colorLightness: [0, 1] //明暗度的范围是 0 到 1
}
},
上面例子中的数据都是在初始化后的setOption中直接填入的,但很多时候数据需要异步加载后再填入,ECharts中实现异步数据的更新非常简单,在图表初始化后的任何时候只要通过jQuery等工具异步获取数据后通过setOption填入数据和配置项就行
var myChart = echarts.init(document.getElementById('main'));
$.get('data.json').done(function (data) {
myChart.setOption({
title: {
text: '异步数据加载示例'
},
tooltip: {},
legend: {
data:['销量']
},
xAxis: {
data: ["衬衫","羊毛衫","雪纺衫","裤子","高跟鞋","袜子"]
},
yAxis: {},
series: [{
name: '销量',
type: 'bar',
data: [5, 20, 36, 10, 10, 20]
}]
});
});
或者先设置完其它的样式,显示一个空的直角坐标轴,然后获取并填入数据
var myChart = echarts.init(document.getElementById('main'));
myChart.setOption({ // 显示标题,图例和空的坐标轴
title: {
text: '异步数据加载示例'
},
tooltip: {},
legend: {
data:['销量']
},
xAxis: {
data: []
},
yAxis: {},
series: [{
name: '销量',
type: 'bar',
data: []
}]
});
$.get('data.json').done(function (data) { // 异步加载数据
myChart.setOption({ // 填入数据
xAxis: {
data: data.categories
},
series: [{
name: '销量', // 根据名字对应到相应的系列
data: data.data
}]
});
});
ECharts在更新数据时需要通过name属性对应到相应的系列,上例如果name不存在也可以根据系列的顺序正常更新,但更多时候推荐更新数据的时候加上系列的name数据
如果数据加载时间较长,一个空的坐标轴放在画布上会让用户觉得是产生bug了,因此需要一个loading动画来提示用户数据正在加载;ECharts默认提供了一个简单的加载动画,只需要调用showLoading方法显示,数据加载完成后再调用hideLoading方法隐藏加载动画
myChart.showLoading();
$.get('data.json').done(function (data) {
myChart.hideLoading();
myChart.setOption(...);
});
ECharts由数据驱动,数据的改变驱动图表展现的改变,因此动态数据的实现也变得异常简单;Echarts中所有数据的更新都通过setOption实现,我们只需要定时获取数据,然后使用setOption填入数据,而不用考虑数据到底产生了那些变化,ECharts会找到两组数据之间的差异然后通过合适的动画去表现数据的变化;ECharts3中移除了ECharts2中的addData方法,如果只需要加入单个数据,可以先data.push(value)后setOption
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);
除了图表外,ECharts中还提供了很多交互组件,例如:图例组件legend/标题组件title/视觉映射组件visualMap/数据区域缩放组件dataZoom/时间线组件timeline,下面以数据区域缩放组件dataZoom为例介绍如何加入这类组件
数据可视化的基本交互需求是:概览数据整体,按照需要关注数据细节;dataZoom组件完美的在直角坐标系(grid)/极坐标系(polar)中实现了这一功能;dataZoom组件是对数轴(axis)进行数据窗口缩放和数据窗口平移操作;可以通过dataZoom.xAxisIndex或dataZoom.yAxisIndex来指定dataZoom控制哪个或哪些数轴;dataZoom组件可同时存在多个,起到共同控制的作用,控制同一个数轴的组件,会自动联动;dataZoom的运行原理是通过数据过滤来达到数据窗口缩放的效果;数据过滤模式的设置不同,效果也不同;dataZoom的数据窗口范围的设置,目前支持两种形式:百分比形式,绝对数值形式
dataZoom组件支持的几种子组件:
内置型数据区域缩放组件(dataZoomInside):内置于坐标系中
滑动条型数据区域缩放组件(dataZoomSlider):有单独的滑动条操作
框选型数据区域缩放组件(dataZoomSelect):全屏的选框进行数据区域缩放,入口和配置项均在toolbox(辅助工具箱,辅助功能,如添加标线,框选缩放等)中
先只在单独的一个横轴上加dataZoom组件,代码示例如下:
option = {
xAxis: {
type: 'value'
},
yAxis: {
type: 'value'
},
dataZoom: [{ // 这个dataZoom组件,默认控制x轴
type: 'slider', // 这个 dataZoom 组件是 slider 型 dataZoom 组件
start: 10, // 左边在 10% 的位置
end: 60 // 右边在 60% 的位置
}],
series: [{
type: 'scatter', // 这是个『散点图』
itemStyle: {
normal: {
opacity: 0.8
}
},
symbolSize: function (val) {
return val[2] * 40;
},
data: [["14.616","7.241","0.896"],["3.958","5.701","0.955"],["2.768","8.971","0.669"],["9.051","9.710","0.171"],["14.046","4.182","0.536"],["12.295","1.429","0.962"],["4.417","8.167","0.113"],["0.492","4.771","0.785"],["7.632","2.605","0.645"],["14.242","5.042","0.368"]]
}]
}
接下来我们来实现在坐标系内拖动,以及用滚轮(或移动触屏上的两指滑动)进行缩放,方法很简单:再加上一个inside型的dataZoom组件即可;具体的实现是直接在option.dataZoom中增加:
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% 的位置。
}],
执行上述代码就能在坐标系中进行滑动,以及使用滚轮缩放了;如果想y轴也能够缩放,就在y轴上也加上dataZoom组件:
dataZoom: [{
type: 'slider',
xAxisIndex: 0,
start: 10,
end: 60
},{
type: 'inside',
xAxisIndex: 0,
start: 10,
end: 60
},{
type: 'slider',
yAxisIndex: 0,
start: 30,
end: 80
},{
type: 'inside',
yAxisIndex: 0,
start: 30,
end: 80
}],
用户使用Echarts工作的时候所需要用到的组件和系列都在指定高宽的DOM节点(容器)中,其中每个节点都可以由用户指定位置;Echarts图表库内部采用的是类似于绝对布局的易于理解的布局方式,因为实现DOM文档流布局是不合适的,但是图表库所采用的布局方式会受到容器尺寸的影响,出现组件重叠的情况;这就带来了一个问题:如果图表需要同时在PC端和移动端展示,该怎么解决内部组件的布局?上述问题需要Echarts内部组件能够自动地随着容器尺寸的改变而进行调整的能力,为了解决这个问题,ECharts完善了组件的定位设置,并且实现了类似CSS Media Query的自适应能力
Echarts中大部分组件和系列会遵循两种定位方式:
该定位方式中的六个量,每个量都可以当做绝对值/百分比/位置描述:
绝对值的单位是浏览器像素(px),用number形式书写(不写单位),例如{left:23,height:400};百分比表示占DOM容器高宽的百分之多少,用string形式书写;例如{right:'30%',bottom:'40%'};位置描述可以设置left:'center',表示水平居中;设置 top:'middle',表示垂直居中
这六个量的概念和CSS中六个量的概念类似:
left/right/top/bottom:表示距离DOM容器左/右/上/下边界的距离
width/height:表示宽度/高度
提示:
在表示横向的量的时候,由于组件的位置和大小可以由任意的两个量决定,所以在left/right/width三个量中,可以只提供两个量的值;至于取哪两个量的值就取决于用户,例如left和right或right和width都可以决定组件的位置和大小;同理,在表示纵向的量top/bottom/height时,取值与横向量相同
center是一个数组,表示[x,y],其中x/y可以是绝对值或百分比,含义与前面描述的相同;radius是一个数组,表示[内半径,外半径],其中内外半径可以是绝对值或百分比,含义与前面描述的相同
注意:在自适应容器大小时,百分比设置是很有用的
ECharts中像legend/visualMap/dataZoom(数据区域缩放,常用于展现大量数据时选择可视范围)/timeline(时间轴,常用于展现同一系列数据在时间维度上的多份数据)等狭长型的组件,大部分都有横向布局和纵向布局这两种选择;例如在宽少长多的移动端屏幕上,使用纵向布局更为合适;而在屏幕较宽的PC端上则要选择使用横向布局
横纵向布局的设置:一般在组件或系列的orient或layout配置项上,设置为'horizontal'或'vertical'
与ECharts2的兼容性:ECharts2中可以使用如x/x2/y/y2的命名方式,分别对应left/right/top/bottom;但是写成left/right/top/bottom会更为规范;为了兼容ECharts2,在描述位置的时候可以支持一些看起来略奇怪的设置,例如:left:'right',left:'left',top:'bottom',top:'top';这些语句分别等效于:right:0,left:0,bottom:0,top:0
Media Query提供了随着容器尺寸改变而改变的能力
在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
}
目前query支持三个属性:width/height/aspectRatio(长宽比),每个属性都可以加上min/max前缀;比如:minWidth:200
表示大于等于200px宽度,两个属性一起写表示且,例如:{minWidth:200,maxHeight:300}表示大于等于200px宽度且小于等于300px高度
media中的option既然是原子option,理论上可以写任何option的配置项;但是一般我们只写跟布局定位相关的,例如只截取一部分query option:
media: [
...,
{
query: {
maxAspectRatio: 1 // 当长宽比小于1时。
},
option: {
legend: { // legend 放在底部中间。
right: 'center',
bottom: 0,
orient: 'horizontal' // //布局方式,默认为水平布局,可选为:'horizontal' | 'vertical'
},
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同时被满足,会都被mergeOption,定义在后的后被merge(即优先级更高)
如果media中有某项不写query,则表示[默认值],即所有规则都不满足时采纳这个option
如果容器DOM节点需要能随着任意拖拽变化大小,那么目前使用时需要注意这件事:某个配置项,如果在某一个query option中出现,那么在其他query option中也必须出现,这样才能够回归到原来的状态(left/right/top/bottom/width/height不受这个限制)
也就是说,当第二(或三/四/五...)次chart.setOption(rawOption)时,如果rawOption是复合option(即包含media列表),那么新的rawOption.media列表不会和老的media列表进行merge,而是简单替代;当然,rawOption.baseOption仍然会正常和老的option进行merge;其实,很少有场景需要使用复合option来多次setOption,而我们推荐的做法是,使用mediaQuery时,第一次setOption使用复合option,后面setOption时仅使用原子option,也就是仅仅用setOption来改变baseOption
数据可视化指的是数据到视觉元素的映射过程,我们也可以将该过程称为视觉编码,把视觉元素当成视觉通道;Echarts帮助我们编程或免编程实现丰富的大数据可视化,它的每种图表都内置了上述的映射过程,例如:柱状图将数据映射到长度,折线图把数据映射到线;不止简单的图表,一些较为复杂的图表也会有内置的映射;ECharts提供了visualMap组件来提供通用的视觉映射,visualMap组件中可以使用的视觉元素有:图形类别(symbol)/图形大小(symbolSize)/颜色(color)/透明度(opacity)/颜色透明度(colorAlpha)/颜色明暗度(colorLightness)/颜色饱和度(colorSaturation)/色调(colorHue)
下面对visualMap组件的使用方式进行简要的介绍
ECharts的series.data一般用于存放数据,其中数据的具体形式会根据图表类型的不同而不同,可能是线性表/树/图等,但是不同类型的数据也有一个共性:都是数据项(dataItem)的集合,其中每个数据项包含了数据值(value)及其他需要用到的信息
注:每个数据值都可以是单一的数值(一维)或一个数组(多维)
例如:series.data最常见的形式,是线性表,即一个普通数组:
series: {
data: [
{ // 这里每一个项就是数据项(dataItem)
value: 2323, // 这是数据项的数据值(value)
itemStyle: {...}
},
1212, // 也可以直接是 dataItem 的 value,这更常见。
2323, // 每个 value 都是『一维』的。
4343,
3434
]
}
在图表中,往往默认把value的前一两个维度进行映射,比如取第一个维度映射到x轴,取第二个维度映射到y轴;可以借助visualMap把更多的维度展现出来;最常见的情况,气泡图(scatter)使用半径展现了第三个维度
series: {
data: [
{ // 这里每一个项就是数据项(dataItem)
value: [3434, 129, '圣马力诺'], // 这是数据项的数据值(value)
itemStyle: {...}
},
[1212, 5454, '梵蒂冈'], // 也可以直接是 dataItem 的 value,这更常见。
[2323, 3223, '瑙鲁'], // 每个 value 都是『三维』的,每列是一个维度。
[4343, 23, '图瓦卢'] // 假如是『气泡图』,常见第一维度映射到x轴,第二维度映射到y轴,第三维度映射到气泡半径(symbolSize)
]
}
visualMap组件的定义:把数据的哪个维度映射到什么视觉元素上;现在提供如下两种类型的visualMap组件,通过visualMap.type来区分;其定义结构例如:
option = {
visualMap: [ // 可以同时定义多个visualMap组件。
{ // 第一个visualMap组件
type: 'continuous', // 定义为连续型viusalMap
...
},
{ // 第二个 visualMap 组件
type: 'piecewise', // 定义为分段型visualMap
...
}
],
...
};
以下是分段型视觉映射组件(visualMapPiecewise)的三种模式:
连续型数据平均分段:依据visualMap-piecewise.splitNumber来自动平均分割成若干块
连续型数据自定义分段:依据visualMap-piecewise.pieces来定义每块范围
离散数据(类别性数据):类别定义在visualMap-piecewise.categories中
数据可视化是数据到视觉元素的映射,visualMap中可以指定数据的哪个维度映射到哪些视觉元素中
实例一:
option = {
visualMap: [{
type: 'piecewise'
min: 0,
max: 5000,
dimension: 3, // series.data 的第四个维度(即 value[3])被映射
seriesIndex: 4, // 对第四个系列进行映射。
inRange: { // 选中范围中的视觉配置
color: ['blue', '#121122', 'red'], // 定义了图形颜色映射的颜色列表,数据最小值映射到'blue'上,最大值映射到'red'上,其余自动线性计算
symbolSize: [30, 100] // 定义了图形尺寸的映射范围,数据最小值映射到30上,最大值映射到100上,其余自动线性计算
},
outOfRange: { // 选中范围外的视觉配置
symbolSize: [30, 100]
}
},
...
]
};
实例二:
option = {
visualMap: [
{
...,
inRange: { // 选中范围中的视觉配置
\
colorLightness: [0.2, 1], // 映射到明暗度上,也就是对本来的颜色进行明暗度处理;本来的颜色可能是从全局色板中选取的颜色,visualMap组件并不关心
symbolSize: [30, 100]
},
...
},
...
]
};
用户在操作Echarts的图表时会触发相应的事件,这些事件由开发者监听,然后回调函数做出相应的处理,可以弹出一个对话框/跳转到一个地址/做数据下钻等;ECharts3中绑定事件跟Echarts2一样都是通过on方法,不同的是事件名称更加简单;ECharts3中,事件名称对应DOM事件名称,均为小写的字符串,如下是一个绑定点击操作的示例:
myChart.on('click', function(params){
console.log(params.name); // 控制台打印数据的名称
});
在ECharts中有两种事件类型:
1.用户鼠标操作点击或hover图表的图形时触发的事件
2.用户在使用可以交互的组件后触发的行为事件,例如在切换图例开关时触发的'legendselectchanged'事件(这里需要注意切换图例开关是不会触发'legendselected'事件的),数据区域缩放时触发的'datazoom'事件等
ECharts支持常规的鼠标事件类型,包括click/dblclick/mousedown/mousemove/mouseup/mouseover/mouseout事件;先来看一个简单的点击柱状图后打开相应的百度搜索页面的示例:
var myChart = echarts.init(document.getElementById('main')); // 基于准备好的dom,初始化ECharts实例
var option = { // 指定图表的配置项和数据
xAxis: {
data: ["衬衫","羊毛衫","雪纺衫","裤子","高跟鞋","袜子"]
},
yAxis: {},
series: [{
name: '销量',
type: 'bar',
data: [5, 20, 36, 10, 10, 20]
}]
};
myChart.setOption(option); // 使用刚指定的配置项和数据显示图表
myChart.on('click', function (params) { // 处理点击事件并且跳转到相应的百度搜索页面
window.open('https://www.baidu.com/s?wd=' + encodeURIComponent(params.name));
});
所有的鼠标事件包含参数params,这是一个包含点击图形的数据信息的对象,格式如下:
{
componentType: string, // 当前点击的图形元素所属的组件名称,其值如series/markLine/markPoint/timeLine等
seriesType: string, // 系列类型,值可能为:line/bar/pie等,当componentType为'series'时有意义
seriesIndex: number, // 系列在传入的option.series中的index,当componentType为'series'时有意义
seriesName: string, // 系列名称,当componentType为'series'时有意义
name: string, // 数据名,类目名
dataIndex: number, // 数据在传入的data数组中的index
data: Object, // 传入的原始数据项
dataType: string, //sankey/graph等图表同时含有nodeData和edgeData两种data,dataType值会是node或edge,表示当前点击在node或edge上;其他大部分图表只有一种data,dataType无意义
value: number|Array // 传入的数据值
color: string // 数据图形的颜色,当componentType为'series'时有意义
}
怎么区分鼠标点击到了哪里:
myChart.on('click', function (params) {
if (params.componentType === 'markPoint') { // 点击到了 markPoint 上
if (params.seriesIndex === 5) { // 点击到了 index 为 5 的 series 的 markPoint 上
}
}
else if (params.componentType === 'series') {
if (params.seriesType === 'graph') {
if (params.dataType === 'edge') { // 点击到了 graph 的 edge(边)上
......
}
else { // 点击到了 graph 的 node(节点)上
......
}
}
}
});
你可以在回调函数中获得这个对象中的数据名/系列名称后在自己的数据仓库中索引得到其它的信息候更新图表,显示浮层等,如下代码:
myChart.on('click', function (parmas) {
$.get('detail?q=' + params.name, function (detail) {
myChart.setOption({
series: [{
name: 'pie',
data: [detail.data] // 通过饼图表现单个柱子中的数据分布
}]
});
});
});
在ECharts中基本上所有的组件交互行为都会触发相应的事件,下面是监听一个图例开关的示例:
myChart.on('legendselectchanged', function (params) { // 图例开关的行为只会触发 legendselectchanged 事件
var isSelected = params.selected[params.name]; // 获取点击图例的选中状态
console.log((isSelected ? '选中了' : '取消选中了') + '图例' + params.name); // 在控制台中打印
console.log(params.selected); // 打印所有图例的状态
});
通过前面的描述我们知道组件交互的行为会触发诸如'legendselectchanged'事件,除用户的交互操作,有时也会需要在程序里调用方法触发图表的行为,诸如显示tooltip时选中图例;ECharts2.x通过myChart.component.tooltip.showTip这种形式调用相应的接口触发图表行为,入口很深,而且涉及到内部组件的组织,而在ECharts3里改为通过调用myChart.dispatchAction({type:''})触发图表行为,统一管理所有动作,也可以方便地根据需要去记录用户的行为路径;下例演示了如何通过dispatchAction去轮流高亮饼图的每个扇形
自定义系列(custom series)是一种系列的类型,它能够让用户定制渲染逻辑,在已有的坐标系中创造新的图表;并且增强了极坐标柱状图/自定义维度映射/dataZoom等;自定义系列(custom series)把绘制图形元素这一步留给开发者去做,从而开发者能在坐标系中自由绘制出自己需要的图表
由于图表的类型多种多样,有些小众需求ECharts不能内置的支持,那就需要提供一种方式来让开发者自己扩展;另外所提供的扩展方式要尽可能简单,例如图形元素创建和释放/过渡动画/tooltip/数据区域缩放(ataZoom)/视觉映射(visualMap)等功能,自定义系列(custom series)使开发者不必纠结于这些细节
下面来介绍开发者怎么使用自定义系列(custom series)
通过书写renderItem函数能够让开发者实现自定义的图形元素渲染逻辑
var option = {
...,
series: [{
type: 'custom',
renderItem: function (params, api) {
// ...
},
data: data
}]
}
渲染阶段,对于series.data中的每个数据项(为方便描述,这里称为dataItem),会调用此renderItem函数;renderItem函数的作用:返回一个(或一组)图形元素定义,图形元素定义了图形元素的类型/位置/尺寸/样式等;ECharts会根据这些图形元素定义来渲染出图形元素,如下述所示:
var option = {
...,
series: [{
type: 'custom',
renderItem: function (params, api) {
var categoryIndex = api.value(0); // 对于data中的每个dataItem都会调用这个renderItem函数(但并不一定是按照data的顺序调用),这里进行一些处理,如坐标转换
var startPoint = api.coord([api.value(1), categoryIndex]); //用api.value(0)取出当前dataItem中第一个维度的数值
var endPoint = api.coord([api.value(2), categoryIndex]); //用api.coord(...)将数值在当前坐标系中转换成为屏幕上的点的像素值
var height = api.size([0, 1])[1] * 0.6; //用api.size(...)获得Y轴上数值范围为1的一段所对应的像素长度
return { // 这里返回为这个dataItem构建的图形元素定义
type: 'rect', // 表示这个图形元素是矩形,还可以是circle/sector/polygon等
shape: echarts.graphic.clipRectByRect({ // shape属性描述了这个矩形的像素位置和大小,其中特殊的用到了echarts.graphic.clipRectByRect,意思是:如果矩形超出了当前坐标系的包围盒,则剪裁这个矩形
x: startPoint[0], // 矩形的位置和大小
y: startPoint[1] - height / 2,
width: endPoint[0] - startPoint[0],
height: height
}, {
x: params.coordSys.x, // 当前坐标系的包围盒
y: params.coordSys.y,
width: params.coordSys.width,
height: params.coordSys.height
}),
style: api.style() // 用api.style(...)得到默认的样式设置,这个样式设置包含了option中itemStyle的配置和视觉映射得到的颜色
};
},
data: [
[12, 44, 55, 60], // 这是第一个 dataItem
[53, 31, 21, 56], // 这是第二个 dataItem
[71, 33, 10, 20], // 这是第三个 dataItem
...
]
}]
}
renderItem函数中包含两个参数:
1.params:包含当前数据信息(如seriesIndex/dataIndex等)和坐标系的信息(如坐标系包围盒的位置和尺寸)
2.api:是一些开发者可调用的方法集合(如api.value()/api.coord())
renderItem函数须返回根据此dataItem绘制出的图形元素的定义信息;一般renderItem函数的主要逻辑是将dataItem里的值映射到坐标系上的图形元素,这一般需要用到renderItem.arguments.api中的两个函数:
1.api.value(...):意思是取出dataItem中的数值,如api.value(0):表示取出当前dataItem中第一个维度的数值
2.api.coord(...):意思是进行坐标转换计算,如var point=api.coord([api.value(0),api.value(1)])表示dataItem中的数值转换成坐标系上的点。
有时候还要用到api.size(...)函数,表示得到坐标系上一段数值范围对应的长度;返回值中样式的设置可以使用api.style(...)函数,他能得到series.itemStyle.normal中定义的样式信息及视觉映射的样式信息,也可以用这种方式覆盖这些样式信息:api.style({fill: 'green', stroke: 'yellow'})
renderItem方法写完后,我们就完成了自定义系列90%的工作,接下来该对工作进行优化了
直角坐标系(grid)和极坐标系(polar)中坐标轴的刻度范围需要自适应当前显示出的数据范围,否则绘制出的图形会超出去;例如,在直角坐标系(grid)中,开发者如果使用自定义系列的话,就需要设定:data中的哪些维度会对应到x轴上,哪些维度会对应到y轴上;上述内容通过encode来设定
option = {
series: [{
type: 'custom',
renderItem: function () {
...
},
encode: {
x: [1, 2], // data 中『维度1』和『维度2』对应到 X 轴
y: 0 // data 中『维度0』对应到 Y 轴
},
data: [ // 维度0 维度1 维度2 维度3
[ 12, 44, 55, 60 ], // 这是第一个 dataItem
[ 53, 31, 21, 56 ], // 这是第二个 dataItem
[ 71, 33, 10, 20 ], // 这是第三个 dataItem
...
]
}]
};
使用tooltip.formatter可以任意定制tooltip中的内容;但是还有更简单的方法,即通过encode和dimensions来设定:
option = {
series: [{
type: 'custom',
renderItem: function () {
...
},
encode: {
x: [1, 2],
y: 0,
tooltip: [2, 3] // 表示『维度2』和『维度3』要显示到 tooltip 中
},
dimensions: [null, null, '年龄', '满意度'], // 表示给『维度2』和『维度3』分别取名为『年龄』和『满意度』,显示到 tooltip 中
data: [ // 维度0 维度1 维度2 维度3
[ 12, 44, 55, 60 ], // 这是第一个 dataItem
[ 53, 31, 21, 56 ], // 这是第二个 dataItem
[ 71, 33, 10, 20 ], // 这是第三个 dataItem
...
]
}]
};
1.与dataZoom结合使用的时候,常会设置dataZoom.filterMode为'weakFilter',这个设置的意思是:当dataItem部分超出坐标系边界的时候,dataItem不会整体被过滤掉
option = {
dataZoom: {
xAxisIndex: 0,
filterMode: 'weakFilter'
},
series: [{
type: 'custom',
renderItem: function () {
...
},
encode: {
x: [1, 2], // data 中『维度1』和『维度2』对应到 X 轴
y: 0
},
data: [ // 维度0 维度1 维度2 维度3
[ 12, 44, 55, 60 ], // 这是第一个 dataItem
[ 53, 31, 21, 56 ], // 这是第二个 dataItem
[ 71, 33, 10, 20 ], // 这是第三个 dataItem
...
]
}]
};
上例中,维度1和维度2对应到X轴,dataZoom组件控制X轴的缩放;假如在缩放的过程中某个dataItem的维度1超出了X轴的范围,维度2还在X轴的范围中,那只要设置dataZoom.filterMode='weakFilter'这个dataItem就不会被过滤掉,从而还能够使用renderItem绘制图形(可以使用提到过的echarts.graphic.clipRectByRect把图形绘制成被坐标系剪裁过的样子)
2.此外,开发者还应注意,renderItem.arguments.params中的dataIndex和dataIndexInside是有区别的:
dataIndex指的dataItem在原始数据中的index;dataIndexInside指的是dataItem在当前数据窗口中的index;renderItem.arguments.api中使用的参数都是dataIndexInside而非dataIndex,因为从dataIndex转换成dataIndexInside需要时间开销
ECharts富文本标签可以应用在许多地方
其实,富文本标签是在EChartsv3.7以后才增加的功能;在EChartsv3.7之前的版本中,只能对整个块进行统一样式的设置,而且只可以设置字体和颜色,不易于制作表达能力更强的文字描述信息
富文本标签的主要功能:
能够定制文本块整体的样式(如背景/边框/阴影等),位置,旋转等;能够对文本块中个别片段定义样式(如颜色/字体/高宽/背景/阴影等),对齐方式等;能够在文本中使用图片做小图标或背景
特定组合以上的规则,可以做出简单表格/分割线等效果
文本块(Text Block):文本标签块整体
文本片段(Text Fregment):文本标签块中的部分文本
ECharts提供了丰富的文本标签配置项,包括:
字体基本样式设置:fontStyle/fontWeight/fontSize/fontFamily;文字颜色:color;文字描边:textBorderColor/textBorderWidth;文本块或文本片段大小:lineHeight/width/height/padding;文本块或文本片段的对齐:align/verticalAlign;文本块或文本片段的边框/背景(颜色或图片):backgroundColor/borderColor/borderWidth/borderRadius;文本块或文本片段的阴影:shadowColor/shadowBlur/shadowOffsetX/shadowOffsetY;文本块的位置和旋转:position/distance/rotate;可以在各处的rich属性中定义文本片段样式,如:series-bar.label.normal.rich
label: {
normal: {
formatter: [ // 在文本中可以对部分文本采用rich中定义样式,这里需要在文本中使用标记符号:`{styleName|text content text content}` 标记样式名;换行仍是使用'\n','{a|这段文本采用样式a}','{b|这段文本采用样式b}这段用默认样式{x|这段用样式x}'
].join('\n'),
color: '#333', // 这里是文本块的样式设置
fontSize: 5,
fontFamily: 'Arial',
borderWidth: 3,
backgroundColor: '#984455',
padding: [3, 10, 10, 5],
lineHeight: 20,
rich: { // rich里是文本片段的样式设置
a: {
color: 'red',
lineHeight: 10
},
b: {
backgroundColor: {
image: 'xxx/xxx.jpg'
},
height: 40
},
x: {
fontSize: 18,
fontFamily: 'Microsoft YaHei',
borderColor: '#449933',
borderRadius: 4
},
...
}
}
}
注意:如果不定义rich,不能指定文字块的width和height
文本/文本框/文本片段的基本样式和装饰:
每个文本可以设置基本的字体样式:fontStyle/fontWeight/fontSize/fontFamily;可以设置文字的颜色color和边框的颜色textBorderColor,textBorderWidth;文本框可以设置边框和背景的样式:borderColor/borderWidth/backgroundColor/padding;文本片段也可以设置边框和背景的样式:borderColor/borderWidth/backgroundColor/padding
对于折线图/柱状图/散点图等,均可使用label来设置标签;标签相对于图形元素的位置,一般使用label[normal|emphasis].position/label[normal|emphasis].distance来配置
注意:position在不同的图中可取值有所不同,distance并不是在每个图中都支持
某些图中,为了能有足够长的空间来显示标签,需要对标签进行旋转;可以结合align和verticalAlign来对标签位置进行了调整
注意:在结合align和verticalAlign的时候要遵循先使用align和verticalAlign定位,然后再旋转的处理逻辑
可以将ECharts文本片段的排版方式想象成CSS中的inline-block,在文档流中按行进行放置;每个文本片段的内容盒尺寸(content box size)默认是根据文字大小决定的,但也可以设置width/height来强制指定,虽然一般不会这么做;文本片段的边框盒尺寸(border box size)由上述本身尺寸加上文本片段的padding来得到;只有'n'是换行符,能导致换行;一行内会有多个文本片段,每行的实际高度由lineHeight最大的文本片段决定,文本片段的lineHeight可直接在rich中指定,也可以在rich的父层级中统一指定,如果采用到rich的所有项中都不指定则取文本片段的边框盒尺寸(border box size)
在一行的lineHeight被决定后,一行内,文本片段的竖直位置由文本片段的verticalAlign来指定(这里和CSS中的规则稍有不同):'bottom':文本片段的盒的底边贴住行底;'top':文本片段的盒的顶边贴住行顶;'middle':居行中
文本块的宽度直接由文本块的width指定,否则,由最长的行决定;宽度决定后,文本片段的align决定了文本片段在行中的水平位置:首先,从左向右连续紧靠放置align为'left'的文本片段盒;然后,从右向左连续紧靠放置align为'right'的文本片段盒;最后,剩余的没处理的文本片段盒,紧贴着,在中间剩余的区域中居中放置
关于文字在文本片段盒中的位置:如果align为'center',则文字在文本片段盒中是居中的;如果align为'left',则文字在文本片段盒中是居左的如果align为'right',则文字在文本片段盒中是居右的
文本片段的backgroundColor可以指定为图片后就可以在文本中使用图标了,具体操作:
rich: {
Sunny: {
backgroundColor: { // 这样设定 backgroundColor 就可以是图片了。
image: './data/asset/img/weather/sunny_128.png'
},
height: 30 // 可以只指定图片的高度,从而图片的宽度根据图片的长宽比自动得到。
}
}
分割线实际是用border实现的:
rich: {
hr: {
borderColor: '#777',
width: '100%', // width设为'100%'表示分割线的长度充满文本块;注意,这里是文本块内容盒(content box)的100%而不包含padding;虽然这和CSS相关的定义有所不同,但在这类场景中更加方便
borderWidth: 0.5,
height: 0
}
}
标题块是使用backgroundColor实现的:
formatter: '{titleBg|Left Title}', // 标题文字居左
rich: {
titleBg: {
backgroundColor: '#000',
height: 30,
borderRadius: [5, 5, 0, 0],
padding: [0, 10, 0, 10],
width: '100%',
color: '#eee'
}
}
formatter: '{tc|Center Title}{titleBg|}', // 标题文字居中;这个实现有些tricky,但能够不引入更复杂的排版规则而实现这个效果
rich: {
titleBg: {
align: 'right',
backgroundColor: '#000',
height: 30,
borderRadius: [5, 5, 0, 0],
padding: [0, 10, 0, 10],
width: '100%',
color: '#eee'
}
}
简单表格的设定,其实就是给不同行上纵向对应的文本片段设定同样的宽度就可以了
名词 | 描述 |
---|---|
chart | 指一个完整的图表,如折线图/饼图等'基本'图表类型或由基本图表组合而成的'混搭'图表,可能包括坐标轴/图例等 |
axis | 直角坐标系中的一个坐标轴,可分为类目型,数值型或时间型 |
grid | 直角坐标系中除坐标轴外的绘图网格,用于定义直角系整体布局 |
dataRange | 值域选择,常用于展现地域数据时选择值域范围 |
roamController | 缩放漫游组件,搭配地图使用 |
名词 | 描述 |
---|---|
line | 折线图,堆积折线图,区域图,堆积区域图 |
bar | 柱形图(纵向),堆积柱形图,条形图(横向),堆积条形图 |
scatter | 散点图,气泡图;散点图至少需要横纵两个数据,更高维度数据加入时可以映射为颜色或大小,当映射到大小时则为气泡图 |
k | K线图,蜡烛图;常用于展现股票交易数据 |
pie | 饼图,圆环图;饼图支持两种(半径/面积)南丁格尔玫瑰图模式 |
radar | 雷达图,填充雷达图;高维度数据展现的常用图表 |
chord | 和弦图;常用于展现关系数据,外层为圆环图,可体现数据占比关系,内层为各个扇形间相互连接的弦,可体现关系数据 |
force | 力导布局图;常用于展现复杂关系网络聚类布局 |
map | 地图;内置世界地图,中国及中国34个省市自治区地图数据,可通过标准GeoJson扩展地图类型;支持svg扩展类地图应用,如室内地图/运动场/物件构造等 |
heatmap | 热力图;用于展现密度分布信息,支持与地图/百度地图插件联合使用 |
gauge | 仪表盘;用于展现关键指标数据,常见于BI类系统 |
funnel | 漏斗图;用于展现数据经过筛选/过滤等流程处理后发生的数据变化,常见于BI类系统 |
evnetRiver | 事件河流图;常用于展示具有时间属性的多个事件,以及事件随时间的演化 |
treemap | 矩形式树状结构图,简称:矩形树图;用于展示树形数据结构,优势是能最大限度展示节点的尺寸特征 |
venn | 韦恩图;用于展示集合以及它们的交集 |
tree | 树图;用于展示树形数据结构各节点的层级关系 |
wordCloud | 词云;词云是关键词的视觉化描述,用于汇总用户生成的标签或一个网站的文字内容 |
感悟:三思而后行,少说不可怕可怕的是说出去的就收不回来了