一、echarts了解
作为一个前端开发人员,对于echarts应该是十分熟悉的,在日常开发中,大多数关于数据的展示是通过图表实现的,而rcharts则是图表展示的首选js库。
 
二、实现思路
在本例中实现一个页面中按照4x3的布局排列12个echarts折线图,并且每个折线图是通过父组件传值给子组件通过for循环实现的。
 
二、Vue项目中echarts的实现
1.导入echarts并注册
在当前文件夹下输入命令:


npm i echarts -S
 
在main.js中导入模块并注册:


import echarts from 'echarts'

Vue.prototype.$echarts = echarts
2.在在components目录新建subcomponents目录存放子组件,并在其下新建SubEcharts.vue组件作为子组件实现echarts绘图和接受父组件的参数:
 
<template>

  <div :id="echartsId" class="echarts"></div>

</template>


<script>

export default {

  /**父组件传值 */

  props:{

    idCode: {

      type: Number,

      required: true

    }

  },


  /**数据 */

  data(){

    return{

      xAxisData: [1,2,3,4,5,6,7,8,9,10,11,12],

      dataTest:[5,9,3,7,8,12,45,25,11,6,9,20],

    }

  },


  /**计算属性 */

  computed:{

    echartsId(){

      return 'echarts' + this.idCode

    }

  },


  /**挂在函数 */

  mounted(){

    this.drowLine()

  },


  methods:{

    /**绘制折线图 */

    drowLine(){

      let echarts = this.$echarts.init(document.getElementById(this.echartsId));

      let echartsid = document.getElementById(this.echartsId)

      echarts.setOption({

        backgroundColor:'#000',

        title: {

          text: 'vue项目中echarts的使用',

          textStyle: {

            color:'#fff',

            fontSize:18,

            fontWeight:'bold',

          },

          subtext:'作者:微信-迟亦早(chiyizao),csdn-goodlovingz',

          subtextStyle: {

            color:'#ddd',

          },

        },

        legend:{

          bottom:'5%',

          textStyle: {

            color: '#fff',

            fontSize: 12,

          },

          data: ['测试数据'],

        },

        tooltip:{

          show:true,

          trigger: 'axis',

          axisPointer: {

            type:'cross',

            crossStyle:{

              color:'#ddd',

            },

          },

        }, 

        grid:{

          left:20,

          right:20,

          top:80,

          bottom:20,

          containLabel:true,

        },

        xAxis: {

          show:true,

          axisLabel:{

            interval:1,

            rotate:-20,

            margin: 30,

            textStyle:{

              color:'#ddd',

              align: 'center'

            },

          },

          axisTick:{

              alignWithLabel:true,

              lineStyle:{

                color:'#ddd',   

              },

          },

          data: this.xAxisData,

        },

        yAxis: [

          {

            type:'value',

            name:'(人/次)',

            nameTextStyle:{

                color:'#ddd',

            },

            axisLabel:{

            textStyle:{

                  color:'#ddd',

            },

            },

            axisTick:{

              alignWithLabel:true,

              lineStyle:{

                  color:'#ddd',

                  

              },

            },

            splitLine:{

                show:false,

            },

          },

        ],

        series: [

          {

            type: 'line',

            name:'测试',

            stack:'人数',

            data: this.dataTest,

            label: {

              normal: {

                show:true,

                position: 'insideTop',

                offset:[0,20],

                textStyle:{

                  color:'#000',

                },

              },

              emphasis: {

                textStyle:{

                  color:'#fff',

                }, 

              },

            },

          },

        ]

      });

  }

}

</script>

<style>

.echarts{

  width: 100%;

  height: 35vh;

  background-color: #fff;

  margin: 5

}

</style>
 
 
在子组件中要注意:子组件中的div映射到父组件中作为一个单独的div,所以要求div的id不能重复,否则所有的echarts图形会绘制在第一个绘图区重叠和覆盖。因此,在子组件中我们对div的id绑定计算属性<div :id="echartsId" class="echarts"></div>,并且将父组件中传进来的数据进行组合形成唯一的id防止重复
 
/**计算属性 */

  computed:{

    echartsId(){

      return 'echarts' + this.idCode

    }

  },



子组件中用于绘图的数据都写死了,所以在自己运用时可以通过axios获取数据并整理成绘图的格式传入xAxis:的data和series相应处即可。


3.在components目录下新建EchartsRoot.vue的echarts布局组件,也就是父组件,并在父组件中导入绘图子组件:


<template>

  <div class="echartsStyle">

    <el-row>

      <el-col span="6" v-for="i in 12" :key="i">

        <subEcharts 

          :idCode='i'>

        </subEcharts>

      </el-col>

    </el-row>

  </div>

</template>

<script>

import subEcharts from '@/components/subcomponents/SubEcharts'

export default {

  components:{

    subEcharts

  }


}

</script>

<style>

.body{

  width: 100%;

  height: 100vh;

}

.echartsStyle{

  background-color:#000;

  width: 100%;

  height: 100px;

  margin: 10px

}

</style>

 
运用element-ui的layout布局使用v-for循环将绘图子组件subEcharts包裹起来实现4x3的页面布局,并且传入父组件的数据 i 供子组件的props的 idCode 接收:


<el-col span="6" v-for="i in 12" :key="i">

        <subEcharts 

          :idCode='i'>

        </subEcharts>

    </el-col>
 
 
4.在Home.vue中添加跳转的按钮,跳转到echarts的展示组件EchartsRoot.vue
1)在路由router.js中实现home到echarts展示页面的路由的配置:
 
import Echarts from '@/components/EchartsRoot.vue'

{

            path: '/echarts',

            component: Echarts

        }



2)在home的template中添加html元素,并在methods中实现跳转的方法:


<el-button type="primary" @click="toEcharts">echarts图表</el-button>


/**跳转到echarts界面 */

    toEcharts(){

      this.$router.push('/echarts')

    }
 
四、初步效果及问题
到这里已经基本实现了想要的效果,但是当拖动屏幕大小的时候,4x3的布局的每一个echarts图大小不会随着屏幕自动的改变大小对屏幕进行适应,极大的降低了用户的体验。
 
五、echarts图表对屏幕大小自适应
1、如果一个界面只有一个或者每个echarts图都是单独写方法绘制的,那么进行自适应很简单,只需要调用echarts对象的**resize()**函数即可:
 
//1.通过id获取html元素,调用echarts的init()方法进行初始化

let echarts = this.$echarts.init(document.getElementById('html元素id');

//2.在绘图的option完成后或者之前调用window.onresize函数并在其中实现echarts.resize()即可

window.onresize = function () {

   echarts.resize()

}

2、对于上例通过v-for循环和父子组建结合,通过复用子组件的方式实现单页面多echarts图的方式这种方法明显行不通。因为在子组件中1方法只局限于当前循环,每个循环结束后子组件就会覆盖resize()方法,所以当图都加载完以后只有最后一个图能够自适应,其他图的resize()方法都在循环中一层一层的被覆盖了。
当然也可以按照其他思路实现所有图的自适应:在每一次for循环绘图结束时将此循环中的echarts对象传递到父组件一个数组或者列表中进行存放,当所有图都加载完以后在父组件中分别取出存放的每一个echarts对象调用resize()函数实现所有echarts图的自适应。(思路是这样的没问题,具体过程没有做!)
本例中采用另外的一种方法实现参考该博客:vue中使用echarts图表自适应的几种基本解决方案:在div上实现类似window的onresize 监听,这样既能实现图表的自适应,也不会因为页面跳转之后无法继续执行页面的onresize 方法!
 
1)在醒目的assets目录下新建esresize.js文件:
 
var EleResize = {

  _handleResize: function (e) {

    var ele = e.target || e.srcElement

    var trigger = ele.__resizeTrigger__

    if (trigger) {

      var handlers = trigger.__z_resizeListeners

      if (handlers) {

        var size = handlers.length

        for (var i = 0; i < size; i++) {

          var h = handlers[i]

          var handler = h.handler

          var context = h.context

          handler.apply(context, [e])

        }

      }

    }

  },

  _removeHandler: function (ele, handler, context) {

    var handlers = ele.__z_resizeListeners

    if (handlers) {

      var size = handlers.length

      for (var i = 0; i < size; i++) {

        var h = handlers[i]

        if (h.handler === handler && h.context === context) {

          handlers.splice(i, 1)

          return

        }

      }

    }

  },

  _createResizeTrigger: function (ele) {

    var obj = document.createElement('object')

    obj.setAttribute('style',

      'display: block; position: absolute; top: 0; left: 0; height: 100%; width: 100%; overflow: hidden;opacity: 0; pointer-events: none; z-index: -1;')

    obj.onload = EleResize._handleObjectLoad

    obj.type = 'text/html'

    ele.appendChild(obj)

    obj.data = 'about:blank'

    return obj

  },

  _handleObjectLoad: function (evt) {

    this.contentDocument.defaultView.__resizeTrigger__ = this.__resizeElement__

    this.contentDocument.defaultView.addEventListener('resize', EleResize._handleResize)

  }

}

if (document.attachEvent) { // ie9-10

  EleResize.on = function (ele, handler, context) {

    var handlers = ele.__z_resizeListeners

    if (!handlers) {

      handlers = []

      ele.__z_resizeListeners = handlers

      ele.__resizeTrigger__ = ele

      ele.attachEvent('onresize', EleResize._handleResize)

    }

    handlers.push({

      handler: handler,

      context: context

    })

  }

  EleResize.off = function (ele, handler, context) {

    var handlers = ele.__z_resizeListeners

    if (handlers) {

      EleResize._removeHandler(ele, handler, context)

      if (handlers.length === 0) {

        ele.detachEvent('onresize', EleResize._handleResize)

        delete ele.__z_resizeListeners

      }

    }

  }

} else {

  EleResize.on = function (ele, handler, context) {

    var handlers = ele.__z_resizeListeners

    if (!handlers) {

      handlers = []

      ele.__z_resizeListeners = handlers


      if (getComputedStyle(ele, null).position === 'static') {

        ele.style.position = 'relative'

      }

      var obj = EleResize._createResizeTrigger(ele)

      ele.__resizeTrigger__ = obj

      obj.__resizeElement__ = ele

    }

    handlers.push({

      handler: handler,

      context: context

    })

  }

  EleResize.off = function (ele, handler, context) {

    var handlers = ele.__z_resizeListeners

    if (handlers) {

      EleResize._removeHandler(ele, handler, context)

      if (handlers.length === 0) {

        var trigger = ele.__resizeTrigger__

        if (trigger) {

          trigger.contentDocument.defaultView.removeEventListener('resize', EleResize._handleResize)

          ele.removeChild(trigger)

          delete ele.__resizeTrigger__

        }

        delete ele.__z_resizeListeners

      }

    }

  }

}

export {EleResize}

2)在SubEcharts.vue中导入esresize.js
 
import {EleResize} from '../../assets/esresize.js'
 
3)在SubEcharts.vue的绘图方法中,首先获取html的div元素,然后调用esresize.js的.on()方法:
 
/**绘制折线图 */

    drowLine(){

      let echarts = this.$echarts.init(document.getElementById(this.echartsId));

      let echartsid = document.getElementById(this.echartsId)

      echarts.setOption({

        代码省略·····

      });

      

      let listener = function(){

        echarts.resize()

      }

      EleResize.on(echartsid,listener)

    },
 
至此,实现了多个方法不会覆盖的echarts图的自适应功能,效果图如下:
 
————————————————