由于最近的项目中使用echarts的部分比较多,因此把echarts的方法封装成了一个组件,方便调用。这算是我自己的项目总结吧,也希望能给刚接触echarts的小伙伴们一点帮助~
因为是在一个组件中处理多个图表,所以需要根据图表的类型进行判断。放在一个组件里处理的优点是方便调用,比较适合绘制风格差不多的简单图表;缺点是每个图表的自定义部分不多,如果需要增加一些特殊的功能,还需要逐个补充。
安装
项目搭建完成之后可以使用npm安装echarts:
npm i Echarts -S
组件
新建文件chart.vue并引入echarts:

props
由于这个组件要实现多种图表,因此需要从外部传入图表数据和图表类型,方便根据绘图类型生成相应的配置项。
所以需要从父组件传递进来的参数有:
props:{
domId: String,//图表容器的id
type: {type: String, default: 'bar'},//图的类型
chartData: Array,//数据
custom: {//自定义配置项
type: Object,
default: function () {
return {}
}
},
}
其中,绘图的容器id必须是唯一的。custom是自定义的样式,一般可以不传。以下是我定义的简单的custom:
{
title: String,//标题
xAxisname: String,//category轴的名字
yAxisname: String,//value轴的名字
color: Array,//颜色值数组
showLegend: Boolean,//是否显示图例
showRing: Boolean,//饼图显示成环形图
vertical: Boolean,//柱状图是否横向
rotate: Number, //坐标刻度标签的旋转角度
gridShow: Boolean,//是否显示网格区域
barMaxWidth: Number,//柱子的最大宽度
lineSymbol: String,//折线拐点图形
stack: Object,//堆叠值,例如{comment: 'stackname',share: 'stackname'}
clickParam: Function,//点击事件
clickGrid: Function,//网格点击事件
dataTypes: Object, //例如{like: '点赞',comment: '评论',share: '转发'}
types: Object, //多个图形对应的字段,例如{like: 'line',comment: 'bar',share: 'bar'}
}
调用画图的方法
mounted(){
// 初始化
let chartDom = document.getElementById(this.domId)
this.chart = echarts.init(chartDom)
// 调用绘图方法
this.getChartsById()
}
监听数据变化
如果数据变化了就重新绘图:
watch:{
chartData:{
handler: function (n) {
this.$nextTick(()=>{
this.getChartsById()
})
},
deep:true
}
}
监听窗口变化
如果容器的大小是随窗口变化,那么在容器变化时,也会重新调整图表的大小:
window.onresize = () => {
if(this.chart) this.chart.resize()
}
如果绘制多个图,那么在chart中resize只会改变最后一个绘图的大小。解决办法:在父组件中用数组push所有图的实例,然后循环resize。
在父组件中调用
组件的基础用法只用传id和数据:
<chart :chart-data="data" dom-id="chart1"></chart>
示例:


画图
定义要调用的方法:
getChartsById(){}
我们需要在getChartsById这个方法中,定义一些公用的变量,然后设置图表实例的配置项以及数据。 因此,需要做以下几件事:
- 初始化(这一步可以放在mounted中)
- 清空原来的图
this.chart.clear()
- 判断数据是否为空
let data = this.chartData
if(!data || data.length === 0) return
- 定义全局变量
//颜色
let color = this.custom.color||['#3aa1ff','#5cbd91','#ffbf63','#5683f5']
let options = {
color : color,
tooltip : {//提示框
trigger : this.type == 'pie'? 'item':'axis',
axisPointer : {
type : 'shadow',
},
}
}
//是否显示图例
if(this.custom.showLegend){
options.legend = this.getLegend()
}
//是否显示标题
if(this.custom.title){
options.title = {
text: this.custom.title
}
}
- 判断绘图类型
switch(this.type){
case 'bar':
case 'line':
//坐标系网格区域
options.grid = this.getGrid()
//横坐标
let axis = this.getxAxis()
if(this.custom.vertical){//纵向图
options.xAxis = axis.valueAxis
options.yAxis = axis.categoryAxis
}else{//横向图
options.xAxis = axis.categoryAxis
options.yAxis = axis.valueAxis
}
options.series = this.getBarOrLineSeries()
break;
case 'pie':
options.series = this.getPieSeries(color)
break;
case 'wordCloud':
options.series = this.getCloudSeries()
break;
default:
break;
}
if(this.type === 'bar'){
options.dataZoom = this.getDataZoom(color)
}
- 插入事件
点击图形事件:
if(this.custom.clickParam){
this.chart.on('click',param => {
this.custom.clickParam(param)
})
}
有时图形太小了难以点击,此时可以选择点击网格区域,获取点击位置:
if(this.custom.clickGrid){
this.chart.getZr().on('click',params => {
const pointInPixel = [params.offsetX, params.offsetY];
if (this.chart.containPixel('grid',pointInPixel)) {
let offset = this.chart.convertFromPixel({seriesIndex:0},[params.offsetX, params.offsetY]);
let xIndex = this.custom.vertical?offset[1]:offset[0];
/*事件处理代码书写位置*/
let item = data[xIndex]
this.custom.clickGrid(item);
}
});
}
- 绘图
this.chart.setOption(options)
需要坐标轴的图
坐标轴
折线图和柱状图需要获取坐标轴:
getxAxis(){
//坐标轴数据
let xAxisData = this.chartData.map(val=>val.name)
//坐标轴样式
let axisStyle = {
axisLine : {
lineStyle : {
color : '#cfcfcf'
}
},
}
let categoryAxis = Object.assign({}, axisStyle,{
type : "category",
data : xAxisData,
axisLabel : {
rotate : this.custom.rotate||0
},
name: this.custom.xAxisName||''
})
let valueAxis = Object.assign({}, axisStyle,{
type : "value",
name: this.custom.yAxisName||''
})
return {categoryAxis,valueAxis}
}
网格区域
getGrid(){
//坐标系网格区域
return {
show: this.custom.gridShow || false,
left : this.custom.gridLeft || 20,
right : this.custom.gridRight || 20,
top : this.custom.gridTop || 20,
bottom : this.custom.gridBottom || 20,
containLabel : true,
}
}
柱状图和折线图
柱状图和折线的区别其实不大,所以我们统一处理,先定义初始的样式:
getBarOrLineItem(type=this.type,title,stack){
//单个样式
let chartItem = {
name : title || this.custom.name,
type : type,
}
if(this.type === 'bar'){
chartItem.barMinHeight = 1
chartItem.barMaxWidth = this.custom.barMaxWidth||20 //柱子最大宽度
}else{
chartItem.symbolSize = 9
chartItem.symbol = this.custom.lineSymbol||'emptyCircle'
}
//堆叠图
stack && (chartItem.stack = stack)
return chartItem
}
有时候需要绘制多个柱子或者多个折线,绘制单个图形和组合图的数据会不一样。我定义的数据格式示例如下:
//单个数据
data = [{name:'张三',value:10},{name:'李四',value:20},,{name:'王五',value:30}]
//多个数据
datas = [{name:'张三',tweets:10,fans:1},{name:'李四',tweets:20,fans:10},{name:'王五',tweets:30,fans:5}]
一般我会在custom中传递一个dataTypes,以便区分不同的数据所对应的字段。例如:
dataTypes: {
tweets:'推文',
fans:'粉丝'
}
继续承接上面获取柱状图和折线图的方法。首先需要判断是要绘制多个图形还是单个图形,单个图形就直接赋值,多个图形的情况稍微复杂一些:
getBarOrLineSeries(type = this.type){
let series = []
if(this.custom.dataTypes){
// 多个图形
let dataObj = {}
let dataTypes = this.custom.dataTypes
for(let dataType in dataTypes){
//数据
dataObj[dataType] = this.chartData.map(item => {
return {name: item.name, value: item[dataType]||0}
})
//堆叠
let stack = this.custom.stack && this.custom.stack[dataType]
//多种类型。如果不传types,默认类型都是this.type
if(this.custom.types){
type = this.custom.types[dataType]
}
let newItem = this.getBarOrLineItem(type,dataType,stack)
newItem.data = dataObj[dataType]
series.push(newItem)
}
}else{
// 单个图
let chartItem = this.getBarOrLineItem()
chartItem.data = this.chartData
series.push(chartItem)
}
return series
}
不需要坐标轴的图
饼图
饼图的处理很简单:
getPieSeries(color){
//初始的饼图
let pieSeries = {
name : this.custom.name || '',
type : 'pie',
center : [ '50%', '50%' ], // 位置
radius : [ 0, '68%' ], // 半径
selectedMode : true,
hoverAnimation : true,
data : this.chartData,
color : color,
animationType : 'scale',
animationEasing : 'elasticOut',
animationDelay : function(idx) {
return Math.random() * 100;
}
}
//显示圆环图
this.custom.showRing && (pieSeries.radius = [ '37%', '68%' ])
return pieSeries
}
词云
由于echarts主要部分已经去掉了word-cloud词云图,如果要绘制词云图需要再安装一个插件:
npm i echarts-wordcloud -S
然后在echarts后面引入这个插件:
import wordcloud from 'echarts-wordcloud'
之后就可以照常使用了。
getCloudSeries(){
let wordSeries = {
type: 'wordCloud',
//文字大小
sizeRange: [11, 40],
rotationRange: [-45, 45],
autoSize: {
enable: true,
minSize: 6
},
//文字样式
textStyle: {
normal: {
color: function(param) {
return 'rgb(' + [
Math.round(Math.random() * 160),
Math.round(Math.random() * 160),
Math.round(Math.random() * 160)
].join(',') + ')';
},
},
emphasis: {
shadowBlur: 10,
shadowColor: '#666'
}
},
data: this.chartData
}
return wordSeries
}
其他
图例
有时候组合图和饼图需要显示图例:
getLegend(){
let legendData = []
//饼图
if(this.type === 'pie'){
legendData = this.chartData.map(val=>val.name)
}
//获取组合图的类型
else if(this.custom.dataTypes){
for(let t in this.custom.dataTypes){
legendData.push(this.custom.dataTypes[t])
}
}
let legend = {
data: legendData,
itemHeight: 10,
itemWidth: 12,
orient: 'horizontal',
right: 0,
textStyle: {
color:'#666'
},
}
return legend
}
滚动条
在柱状图数据很多的时候,可以显示滚动条:
getDataZoom(color){
let length = this.chartData.length
let dataZoom = {
type : 'slider',
show: length > 10,
bottom : 6,
startValue : 0,
endValue: 9,
height:10,
fillerColor: color[0],
borderColor: color[0],
handleIcon : 'M-292,322.2c-3.2,0-6.4-0.6-9.3-1.9c-2.9-1.2-5.4-2.9-7.6-5.1s-3.9-4.8-5.1-7.6c-1.3-3-1.9-6.1-1.9-9.3c0-3.2,0.6-6.4,1.9-9.3c1.2-2.9,2.9-5.4,5.1-7.6s4.8-3.9,7.6-5.1c3-1.3,6.1-1.9,9.3-1.9c3.2,0,6.4,0.6,9.3,1.9c2.9,1.2,5.4,2.9,7.6,5.1s3.9,4.8,5.1,7.6c1.3,3,1.9,6.1,1.9,9.3c0,3.2-0.6,6.4-1.9,9.3c-1.2,2.9-2.9,5.4-5.1,7.6s-4.8,3.9-7.6,5.1C-285.6,321.5-288.8,322.2-292,322.2z',
handleSize : 10,
}
// 横向图
if(this.custom.vertical){
let dom = document.getElementById(this.domId)
dataZoom.yAxisIndex = 0
dataZoom.height = dom.offsetHeight? dom.offsetHeight- 40 : 150
dataZoom.width = 10
}
return dataZoom
}
高亮事件
外部触发选中图形高亮:

highLightBar(list){
let newSeries = []
for(let i = 0; i < this.chart.getOption().series.length; i++){
newSeries.push({
itemStyle: {
normal: {
color: function (param){
return list.indexOf(param.name) === -1 ? param.color : '#fa423c';
}
},
}
})
}
this.chart.setOption({
series: newSeries
})
}
父组件中:
selectBar(){
this.$refs.chart1.highLightBar(this.selectNames)
},
最后
以上都是简单的图表样式,更炫酷的效果还得小伙伴们自己补充哈~
因为是初次在vue项目中使用echarts,所以还有一些不足,如果有改进意见麻烦给我留言鸭(´・ω・`)ノ