编写折线图/柱状图组件vue+echarts

前言:因为公司需要做一款报表类app专供boss查询,且敲定了用vue实现,图标类就用自己比较熟悉的e charts。所以就有了下面的实现,封装一个vue+echarts组件。

实现效果:

实现单个图表实例中存在多组y轴数据(同一x轴,多组y轴;本文中直规范实现了前两组数据格式,如果插入超过两组的数据,实现的样式为echarts默认样式),点击切换天数,实现多组数据实时更新。如果数据较多可左右滑动;也可动态加载一起展示。

想要达到的效果是只需要传入3个数据:ydata为导入的y轴数据,数组格式,数组中的每个对象为一组数据。例 [{name:"销售额(元)",type:"bar",data:[21000, 4000.9, ...]}]name为图例名称,type为图例表现方式,同echarts,data为数据;
xdata为导入的x轴数据,数组格式,例 xdata=['2018-01-01','2018-02-01',...];
total为当前选中的按钮,数字,为天数

<echart-container :ydata="ydata" :xdata="xdata" :total="nowtab"></echart-container>
效果预览
  • 组件编写,初始化(标签命名echart-container,接收参数ydata,xdata,total)
var charts = Vue.component('echart-container', {
  props: {
    total:Number,
    ydata:Array,
    xdata:Array,
  },
  template: '',
  mounted:function() {},
  watch:{},
  methods:{}
})
  • 编写模版(我当时拿到的UI图有图表的背景及x轴的阴影效果,所以我直接定义了图表高度,在图表外部直接添加效果。当然echarts内部也可以编辑图表的背景,但是我试验之后觉得效果没有在外面直接用css做的方便。不需要的可以直接去掉,即以下代码的2&3行),放入template中
var temp = '<div class="echart-wrap">';
temp += '<img class="echart-back" src="https://img.~.png"/>';
temp += '<div class="wb-shadow-up"></div>';
temp += '<div class="echart-contain">';
temp += '<div id="container"></div>';
temp += '</div></div>';
  • 编写图表初始化方法,参考echarts官网,下面贴上的是我写的初始化代码(根据公司UI编写,可以根据想要的效果替换)我写的规范了前2组y轴数据的样式,如果超过2组,后面的数据会是默认样式;如果有需要可以继续增加。
    这个配置是实现左右可滑动效果,若是不需要可以去掉相关配置进度条以及x轴进度条配置,就可以直接展示所有数据,若要动态添加可使用setInterval&clearInterval方法,具体就不贴了,可参考echarts官网示例http://echarts.baidu.com/examples/editor.html?c=dynamic-data
initChart:function(){
    var myChart=echarts.init(document.getElementById("container"))//初始化
    var app = {};
    var option = null;//配置
    var legend=[];//图例
    var yset=[];//y轴设置
    
    for (var i =0; i<this.ydata.length; i++) {
        //图例
        legend[i]={
            name: this.ydata[i].name,//图例名称
            icon: 'roundRect',//图例图标
            textStyle:{//图例文字样式
                fontSize:8,
                color:'#ccc'
            }
        }
        //图例的图片设置
        if (i==1&&this.ydata[1].type=='line') {
            legend[1].icon='image://https://img.~.png'//图例图标
        }
        //y轴数据对齐方式
        var yAlign='';
        if (i==0) {yAlign='left';} else if(i==1) {yAlign='right';}
        //y轴数据设置
        yset[i]={
            type: 'value',//坐标轴类型,默认
            position: i==0?'left':"right",//位置
            axisLine: {show:false},//y轴线设置不显示
            axisTick: {show:false},//y轴刻度设置不显示
            axisLabel: { //刻度显示
                formatter: function(value){
                        if (value==0) {
                            return ''
                        } else if (value<=1&&value>0) {
                            return value*100+'%'
                        } else if (value>=1000) {
                            return value/10000+'w'
                        } else {
                            return value;
                    }
                        
                },
                fontSize:10,
                fontFamily:'DINEngschrift',
                fontWeight:'bold',
                color:'#D8D8D8',
                align:yAlign
            },
            splitLine: {show:false}//y轴分割线设置
        }
        
        this.ydata[i].yAxisIndex=i;//使用的 y 轴的 index,在单个图表实例中存在多个y轴的时候有用
        if (this.ydata[i].type=='line') {
            this.ydata[i].smooth = true;//折线流畅
            this.ydata[i].lineStyle={
                width:3, //折线粗细
                shadowColor: 'rgba(250,52,101, 0.3)',//阴影颜色
                shadowBlur: 4,//阴影粗细
                shadowOffsetY:8//阴影垂直偏移
            }
            this.ydata[i].symbolSize=6
            this.ydata[i].hoverAnimation= false
             this.ydata[i].animation= false
        }
        if (this.ydata[i].type=='bar') {
            this.ydata[i].barWidth = 6;//柱状图粗细
            this.ydata[i].barGap = 0.5;//柱状图间隔
            this.ydata[i].itemStyle={//圆角
                 barBorderRadius: [1,1,0,0]
            } 
        }
        
        if (i==0) {//第一个图表颜色
            if (this.ydata[0].itemStyle) {
                this.ydata[0].itemStyle.color = "#4992F5"
            } else {
                this.ydata[0].itemStyle={
                    color: "#4992F5"
                }
            }
        } else if (i==1) {//第二个图表颜色,为bar,黄色;line玫红
            if (this.ydata[1].type=='bar') {
                if (this.ydata[1].itemStyle) {
                    this.ydata[1].itemStyle.color = "#F7BB42"
                } else {
                    this.ydata[1].itemStyle={
                        color: "#F7BB42"
                    }
                }
            } else if (this.ydata[1].type=='line'){
                if (this.ydata[1].itemStyle) {
                    this.ydata[1].itemStyle.color = "#FA3465"
                } else {
                    this.ydata[1].itemStyle={
                        color: "#FA3465"
                    }
                }
            }
        }
    }
    var yDt=[]; //完全深度复制对象this.ydata
    for (var i =0; i<this.ydata.length; i++) {
        yDt[i]={};
        for(key in this.ydata[i]) {
            if (typeof (this.ydata[i][key]) == 'Object') {
                yDt[i][key]=[];
                for (var j=0; j<this.ydata[i][key].length; j++) {
                    yDt[i][key][j]=this.ydata[i][key][j];
                }
            } else {
                yDt[i][key]=this.ydata[i][key];
            }
        }
    }
    //控制传入的数据长度
    for (var i =0; i<yDt.length; i++) {
        yDt[i].data=yDt[i].data.slice(0,this.total)
        for (var t=0 ;t<yDt[i].data.length; t++) {
            yDt[i].data[t]<1?yDt[i].data[t]*100:yDt[i].data[t];
        }
    }
    
    var endPercent = this.total>7?30:100;//进度条长度控制
    option = {
        tooltip: {//鼠标点击的提示
            trigger: 'axis',
            axisPointer: {
                type: 'shadow',
                shadowStyle:{
                    color:'rgba(73,146,245, 0.1)'
                }
            },
            position: function (point,params) {// 固定在顶部
                    if (params.length==1) return [point[0]-80,-40];
                    if (params.length==2) return [point[0]-80,-60];
            },
            backgroundColor:'transparent',
            triggerOn:'click',//提示框触发的条件
            formatter: function(params){//提示内容
                var str='<div class="tooltip-wrap">';
                for (var k=0; k< params.length; k++){
                    var val=params[k].data;
                    var valStr='';
                    if (val<=1&&val>0) {
                        valStr=val*100+'%'
                    } else {
                        valStr=val
                    }
                    str+='<p>'+params[k].seriesName+':<span>'+valStr+'</span></p>'
                }
                str+='</div>'
                return str;
            }
        },
        grid: { //图表位置
          top:20,
          left:15,
          right:15,
          bottom: 82,//距离
        },
        legend: {//设置图例
          itemWidth: 7,
          itemHeight: 7,
          bottom:0,//图例位置
          data:legend
        },
        
        dataZoom: [//给x轴设置滚动条  
           {  
           show: false,
           start:0,//默认为0  
           end: endPercent,  
           type: 'slider',  
           xAxisIndex: [0],  
           handleSize: 0,//滑动条的 左右2个滑动条的大小  
           },  
           //下面这个属性是里面拖到  
           {  
               type: 'inside', 
               preventDefaultMouseMove:false
           },  
        ],

        xAxis: [
            {
                type: 'category',//x轴类型,默认
                axisTick: {show:false},//x轴刻度设置不显示
                axisLine: {show:false}, //x轴线设置显示
                data: this.xdata.slice(0,this.total),
                axisLabel:{rotate:-60,fontSize:9,color:'#999',padding:[10,0,-10,-5]}, //x轴标签设置
            }
        ],
        yAxis: yset,
        series: yDt
    };
    if (option && typeof option === "object") {
        myChart.setOption(option, true);
    }
}
  • 动态监听数据变化(我做的是x轴监听,代码里提到的changeperiod方法是我做的数据增减的动画效果,大家可以自己写,我这里就不放了)
xdata:function(val,old){
    if (val[0]!=old[0]) {
        this.initChart();
        return;
    }
    if (val.length!=old.length) {
        this.changeperiod(val.length,old.length);
        return;
    }
    if (val[0]==old[0]&&val.length==old.length) {
        this.initChart();
        return;
    }
},

以上,就是我封装的vue+echarts组件,除了效果图的外部css这边没有附上,别的基本都有了。只需要引入vue.js及echarts.js和echarts.css后就可以用了。

这是我的第一篇技术笔记,还需要完善。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,802评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,109评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,683评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,458评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,452评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,505评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,901评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,550评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,763评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,556评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,629评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,330评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,898评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,897评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,140评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,807评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,339评论 2 342

推荐阅读更多精彩内容