在做项目的时候需要实现一个自适应的饼图。因为使用的饼图的圆环图,图例在饼图的右边。希望的效果是:屏幕宽度足够大的时候,图例成两列分布;屏幕宽度小的时候,图例成一列分布。同一个组件里有两个饼图,都需要随着屏幕大小的变化实时进行自适应。

实现后的效果图如下:

  • 这是屏幕宽度足够大的时候:
  • 这是屏幕宽度较小的时候:

1. 首先设置饼图基本配置

option = {
	title: false,  //不需要图标的标题
	color: ['#111','#222','#333','#444','#555','#666','#777'],  //图例及对应的饼图颜色(这里是自定义,从上到下的顺序)
    series: [   //series是数组,数组的内容是一个图标的对象
      {
        type: 'pie',   //生成饼图
        center: ['20%','50%'],  //设置饼图圆心所在的地方,比如这里是x轴靠左宽20%的地方,y轴居中
        radius: ['50%','70%'],  //当它是一个数组时,它的前一项表示内半径,后一项表示外半径,这样就形成了一个圆环图,这个属性可以控制圆环的‘厚度’
        data: [
          {
            value: 335,
            name: 'A'
          },
          {
            value: 234,
            name: 'B'
          },
      	  //...这里继续写
        ],
      }
    ]
};

2. 设置饼图图例的设置

图例是通过配置对象中的legend属性进行设置,legend属性本身是一个对象。

option = {
  legend: {
    orient: 'vertical',   //竖向排列图例
    itemGap: 5,  //每个图例的间隙
    left:this.legendLeft,  //图例距离左边的距离,根据浏览器的大小位置会改变,因此这里使用的是变量,后面会对这个变量进行操作
    height: this.height,   //图例距离左边的距离,同上
    formatter: name => {   //对图例的文案进行格式化,其中name就是data中的name
    	let _name = name;
		if(_name.length > 20){
			_name = _name.slice(0,15) + '...';   //将名字太长的图例截取前15个字...
		}
		let item = this.data.filter(item => item.name = name);   //获取该图例对应的data对象,为了获取data的个数展示在图例上
		// 注意这里fliter筛选到的是满足条件的数组,因此后面取值去要去item[0]

		return `{name|${_name}}{item|${typeof(item[0]['value']) !== 'undefined' ? item[0]['value'] + '个' : '-'}}`;  //这个方法配合rich使用,可以控制图例每个模块的样式。
		// 比如这里将图例分为name和item两个模块,name模块的值就是name
		// item模块的值先判定value是否有值,有的话展示value+‘个’,否则展示'-'
    },
    textStyle:{  //通过textStyle中的rich设置每个模块的样式
    	rich:{
    		name:{
    			width: 55,  // 宽度设置成55,比较长,这样图例的名字和个数之间会有一个空隙,并且实现了对齐小锅
    		},
    		item:{
    			width: 35,
    		}
    	}
    }
  },
}

到这里就是整个饼图的全部设置了,总的配置是需要把上面两个部分都放在option的对象中的哦

3.根据最开始的浏览器宽度,实现饼图的自适应展示

关于图例的换行,因为这里图例的方向是竖向的。legend.height这个属性是控制图例高度的,他不是控制一个图例的高度,而是所有的整个图例的总高度

何时让图例换成两行展示是图例自适应的关键。

什么情况下图例会换行呢?当图例实际的高度,超过legend.height规定的高度时,图例就会自动换行展示。因此当屏幕宽度大的时候,我们可以将legend.height设置的小一点,这样就很容易超过然后换行;屏幕宽度小的时候,反之。

我们假设当屏幕宽度超过1500px的时候,图例就换行。在组件挂载之后,就需要进行判断,保证组件展示的正确。

// 生命周期函数
mounted(){
	let widthScreen = document.body.clientWidth;  //获取屏幕宽度
	if(widthScreen  >= 1500){   //屏幕宽度很大
		this.height = '60%';    //图例高度设置小一点,可以自动换行
		this.legendLeft = '35%';    //因为图里有两行,比较宽,因此图例的left位置可以偏左一点,会美观一点
	}else{  //屏幕宽度小
		this.height = '120%';
		this.legendLeft = '50%';
	}
}

4. 生成饼图

mounted(){
	if(!ECharts || this.chart){
		return;
	}
	let chart = ECharts.init(this.$el,this.theme,this.option);
	this.chart = chart;
}

5. 绑定事件监听,实现浏览器大小改变时饼图自适应改变

此时饼图已经可以根据刚开始的浏览器的宽度进行展示,但是,当浏览器的大小进行改变的时候,饼图不会跟随改变。因此我们需要绑定事件监听函数。

mounted(){
	let _onWinResize = debounce(this.onResize,50);   //实现防抖
	window.addEventListener('resize',_onWinResize);
}

private onResize(){
	let widthScreen = document.body.clientWidth;  //获取屏幕宽度
	if(widthScreen  >= 1500){   //屏幕宽度很大
		this.height = '60%';    //图例高度设置小一点,可以自动换行
		this.legendLeft = '35%';    //因为图里有两行,比较宽,因此图例的left位置可以偏左一点,会美观一点
	}else{  //屏幕宽度小
		this.height = '120%';
		this.legendLeft = '50%';
	}
	this.chart.resize();
}

到这里就完成了项目的要求。

最后!有一个踩坑的地方想要说一下。
刚开始的时候,使用的是window.onresize的方式进行绑定的,结果两个饼图中只有最后一个会随着浏览器的改变而进行改变,第一个并不会改变。
问题:vue项目中多个echarts图表只有最后一个随浏览器变化改变大小。
后来解决方法就是通过window.addEventListener进行绑定,就没有问题啦。