echarts堆叠柱状图优化,解决较小值的bar被较大值bar遮盖的问题

需求场景

我现在需要做一个这样的柱状图,一共3个系列的数据,分别为3个会议类型。从设计图上看,每种类型的会议数量在每个x轴刻度处都是堆叠在一起,可以直观看到每个会议类型在不同月份的数据,也可以看每个月份不同类型的会议的数量比较。

初步思路

通常我会按照正常的写法,除了给echarts的option按照设计图上的样式进行设置,还需要准备3个series项,对应3个类型的会议数量bar。

测试数据和代码如下:

let data={
				data:['1月','2月','3月','4月','5月','6月','7月','8月','9月','10月','11月','12月'],
		        series:[
					{name:"党委会",value:[0,0,1,1,2,0,4,0,0,0,0,0]},
	                {name:'董事会',value:[0,0,0,1,1,1,4,0,0,0,0,0]},
					{name:'总经理办公会',value:[0,1,0,1,0,0,2,0,0,0,0,0]}
				]
			},
			colorList=['#6AC4FF', '#4B93C3', '#597789'],
            axisData=data.data,
            series=[]
			data.series.forEach((e, i) => {
				let total = 0;
				e.value.forEach(e => {
					total += e;
				});
				series.push({
					name: e.name + `(${total})`,
					type: 'bar',
					barWidth: '20',
					label: {
						show: false
					},
					itemStyle: {
						color: colorList[i],
						borderRadius: [4, 4, 0, 0]
					},
					barGap: '-100%',
					data: e.value.map((ele, index) => {
						return {
							name: axisData[index],
							value: ele
						};
					})
				});
			});
			let option = {
				grid: {
					top: '15%',
					left: '50',
					right: '30',
					bottom: '10%'
					// containLabel:true
				},
				xAxis: {
					type: 'category',
					// boundaryGap: false,
					axisLabel: {
						// rotate: 45,
						color: '#333333',
						fontSize: '14px',
						interval: 0
					},
					axisLine: {
						show: true,
						lineStyle: {
							color: '#EFEFEF'
						}
					},
					axisTick: {
						show: false
					},
					splitLine: {
						show: false
					},
					data: axisData
				},
				yAxis: {
					type: 'value',
					// minInterval: 1,
					nameTextStyle: {
						color: '333',
						padding: [0, 34, 0, 0],
						fontSize: 14
					},
					min: 0,
					axisLine: {
						show: false
					},
					axisLabel: {
						color: '#333',
						fontSize: '14px'
					},
					splitLine: {
						show: true // 是否展示分割线条
					}
				},
				legend: {
					show: true,
					left: 'center',
					top: 5,
					selectedMode: false,
					itemWidth: 12,
					itemHeight: 12,
					data: series.map((e, i) => {
						return {
							name: e.name,
							itemStyle: {
								color: this.colorList[i]
							}
						};
					})
				},
				emphasis: {
					disabled: true
				},
				tooltip: {
					formatter: res => {
						let str = '';
						data.series.forEach((e, i) => {
							str +=
								'<div style="display: flex;align-items: center">' +
								`<span style=\"display:inline-block;margin-right:4px;border-radius:10px;width:10px;height:10px;background-color:${this.colorList[i]}\"></span>` +
								e.name +
								`<span style=\" padding-left:10px\">${e.value[res.dataIndex]}</span>` +
								'</div>';
						});
						return str;
					}
				},
				series
			};

效果:

1月tooltip

5月tooltip

7月tooltip

 问题

可以看到,option.series这里数组中,如果不给每个series数组项设置z或者zlevel,echarts会默认让后面bar的图层在前面bar之上,也就是会盖住前面series项对应的bar。但我们想要的效果是设计图上那种,数值小的bar盖住数值大的bar,这样有层次地展示,但是我们的数据又不可能永远是第一个series项的每月数值都是最大,然后下一个series项的每月数值都是第二大,第三个series项的每月数值都是第三大,这是不现实的。所以我打算换一种思路

新的思路

我们只修改series,其他代码基本不变,原来我们每个series项对应的是每种不同的会议类型,现在我们改成第一个series项里的data全是每月的最大数值,第二个series项里的data全是每月的第二大数值,以此类推。

不管这个数值是哪个类型的。因为我们对类型的展示只是bar的颜色,而bar的颜色可以通过series项的data数组中的每一数组项来单独设置

我先写一个函数用于返回需要的series

//现在写一个优化重叠的函数
		seriesFun(data) {
			let series = [],
			colorList: ['#6AC4FF', '#4B93C3', '#597789'],
				z = data.series.length + 2;
			data.series.forEach((e, i) => {
				let total = 0;
				e.value.forEach(e => {
					total += e;
				});
				//一个一个地push进去,先push的就是在同一投影下较大的集合
                //这个total是用于图例处显示总数
				series.push({
					name: e.name + `(${total})`,
					type: 'bar',
					barWidth: '20',
					z: z++,
					label: {
						show: false
					},
					itemStyle: {
						borderRadius: [4, 4, 0, 0]
					},
					barGap: '-100%',
					data: e.value.map((ele, index) => {
						let item = data.series
							.map((sortE, sortI) => {
								return {
									value: sortE.value[index],
									name: sortE.name,
									index: sortI
								};
							})
							.sort((a, b) => {
								return b.value - a.value;
							})[i];
						let value = item.value;

						return {
							name: data.data[index],
							value,
							itemStyle: {
								color: colorList[item.index]
							}
						};
					})
				});
			});
			return series;
		}

然后在刚才的option中,series等于这个函数的返回值

而且这里有个需求是,如果数值相等,优先显示党委会,所以在排序函数处加一些条件即可,其他的优先级显示需求也是同理

//现在写一个优化重叠的函数
		seriesFun(data) {
			let series = [],
			colorList: ['#6AC4FF', '#4B93C3', '#597789'],
				z = data.series.length + 2;
			data.series.forEach((e, i) => {
				let total = 0;
				e.value.forEach(e => {
					total += e;
				});
				//一个一个地push进去,先push的就是在同一投影下较大的集合
                //这个total是用于图例处显示总数
				series.push({
					name: e.name + `(${total})`,
					type: 'bar',
					barWidth: '20',
					z: z++,
					label: {
						show: false
					},
					itemStyle: {
						borderRadius: [4, 4, 0, 0]
					},
					barGap: '-100%',
					data: e.value.map((ele, index) => {
						let item = data.series
							.map((sortE, sortI) => {
								return {
									value: sortE.value[index],
									name: sortE.name,
									index: sortI
								};
							})
							.sort((a, b) => {
//这里是个特殊的规则,即如果党委会和其他的类型数量相等,则党委会永远盖住其他的
								//所以,如果value相等,则name为党委会的要排后面,其余情况不管,正常排即可
								if (
									a.value == b.value &&
									a.value > 0 &&
									(a.name == '党委会' || b.name == '党委会')
								) {
									return a.name == '党委会' ? 1 : -1;
								}
								return b.value - a.value;
							})[i];
						let value = item.value;

						return {
							name: data.data[index],
							value,
							itemStyle: {
								color: colorList[item.index]
							}
						};
					})
				});
			});
			return series;
		}

完整代码

function seriesFun(data) {
			let series = [],
			colorList: ['#6AC4FF', '#4B93C3', '#597789'],
				z = data.series.length + 2;
			data.series.forEach((e, i) => {
				let total = 0;
				e.value.forEach(e => {
					total += e;
				});
				series.push({
					name: e.name + `(${total})`,
					type: 'bar',
					barWidth: '20',
					z: z++,
					label: {
						show: false
					},
					itemStyle: {
						borderRadius: [4, 4, 0, 0]
					},
					barGap: '-100%',
					data: e.value.map((ele, index) => {
						let item = data.series
							.map((sortE, sortI) => {
								return {
									value: sortE.value[index],
									name: sortE.name,
									index: sortI
								};
							})
							.sort((a, b) => {								if (
									a.value == b.value &&
									a.value > 0 &&
									(a.name == '党委会' || b.name == '党委会')
								) {
									return a.name == '党委会' ? 1 : -1;
								}
								return b.value - a.value;
							})[i];
						let value = item.value;

						return {
							name: data.data[index],
							value,
							itemStyle: {
								color: colorList[item.index]
							}
						};
					})
				});
			});
			return series;
		}
let data={
				data:['1月','2月','3月','4月','5月','6月','7月','8月','9月','10月','11月','12月'],
		        series:[
					{name:"党委会",value:[0,0,1,1,2,0,4,0,0,0,0,0]},
	                {name:'董事会',value:[0,0,0,1,1,1,4,0,0,0,0,0]},
					{name:'总经理办公会',value:[0,1,0,1,0,0,2,0,0,0,0,0]}
				]
			},
            axisData=data.data,
            series=seriesFun(data),
			colorList: ['#6AC4FF', '#4B93C3', '#597789']
			let option = {
				grid: {
					top: '15%',
					left: '50',
					right: '30',
					bottom: '10%'
					// containLabel:true
				},
				xAxis: {
					type: 'category',
					// boundaryGap: false,
					axisLabel: {
						// rotate: 45,
						color: '#333333',
						fontSize: '14px',
						interval: 0
					},
					axisLine: {
						show: true,
						lineStyle: {
							color: '#EFEFEF'
						}
					},
					axisTick: {
						show: false
					},
					splitLine: {
						show: false
					},
					data: axisData
				},
				yAxis: {
					type: 'value',
					// minInterval: 1,
					nameTextStyle: {
						color: '333',
						padding: [0, 34, 0, 0],
						fontSize: 14
					},
					min: 0,
					axisLine: {
						show: false
					},
					axisLabel: {
						color: '#333',
						fontSize: '14px'
					},
					splitLine: {
						show: true // 是否展示分割线条
					}
				},
				legend: {
					show: true,
					left: 'center',
					top: 5,
					selectedMode: false,
					itemWidth: 12,
					itemHeight: 12,
					data: series.map((e, i) => {
						return {
							name: e.name,
							itemStyle: {
								color: colorList[i]
							}
						};
					})
				},
				emphasis: {
					disabled: true
				},
				tooltip: {
					formatter: res => {
						let str = '';
						data.series.forEach((e, i) => {
							str +=
								'<div style="display: flex;align-items: center">' +
								`<span style=\"display:inline-block;margin-right:4px;border-radius:10px;width:10px;height:10px;background-color:${this.colorList[i]}\"></span>` +
								e.name +
								`<span style=\" padding-left:10px\">${e.value[res.dataIndex]}</span>` +
								'</div>';
						});
						return str;
					}
				},
				series
			};

新的效果

可以看到,新的柱状图已经完全实现了需求,而且这个柱状图不仅适用于3个类型数据的场景,任意数量的也满足

总结

在这样的堆叠柱状图的需求场景下,我们把常规的series里写不同会议类型的数据的写法,改成了series里的先放最大的数值一组,再放较小的数值的一组,这样依次push进series。重点是要处理好每个数值原本的类型和它的颜色,本文中已在seriesFun函数中处理了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值