立体堆叠柱图实现的实现
实现效果示例:
1、技术原理
1.1、技术分析
- 平面上的立体柱图可以看作是由4个平行四边形拼接而成的图形,分别是:
- 1、顶部菱形
- 2和3、左右矩形,他们两个色值相同但亮度不同,从而产生立体阴影效果
- 4、底部菱形,用于产生柱体中间的填角
-
分析了立体柱图的组成后,接下来就是如何使用echart提供的配置能力分别渲染出对应的图形,其中:
-
组成2和3的两个矩形可以看作使用了两种背景色(线性渐变)的普通柱图,因此只需要使用echarts提供的Color对象定义普通柱图的颜色填充即可实现,即
series.itemStyle.color
。 -
1和4的菱形可以通过echarts提供的象形柱图(pictorialBar)的diamond菱形实现,底部的菱形是为了产生折角效果。
-
-
另外,如果是堆叠柱图,为了使上下柱连接处也呈现立体折角效果,还需要在连接处也添加一个菱形,该菱形的颜色与上柱颜色相同。
1.2、pictorialBar象形柱图
象形柱图是echarts提供的一种2d图表,它以标准的矩形柱图为容器,允许我们通过配置symbol的方式在容器中实现个性化的图形效果,类比起来相当于图标按钮或图片按钮的实现思路。象形柱图的主要配置项如下:
-
symbol:图形类型,echarts提供了多种symbol类型标记,包括内置的
cicle圆
,rect矩形
,diamond菱形
,arrow箭头
等,还支持自定义的图片url和svg图形。 -
symbolSize:图形尺寸,值为 [w, h] 分别表示宽和高,可以是具体值,也可以是百分百值。当为百分百值是基于基准柱尺寸(的宽和高)计算,基准柱就是原始的矩形柱图。
-
symbolPosition:基于基准柱的图形对齐定位,可取值start(默认),end,center,与flex的justfy-content效果类似,基准柱相当于flex容器。
-
symbolOffset:图形相对与基准定位的偏移量,值为 [left, top] ,可以是具体值,也可以是(相对自身尺寸的)百分比。
-
symbolRotate:图形绕自身旋转角度
-
symbolRepeat:图形是否可以重复,如果为true可以产生类似rate评分的效果
-
symbolRepeatDirection:重复方向
-
symbolClip:是否剪切图形,如果为true,则根据基准值symbolBoundingData进行比例裁剪显示。echarts的示例人体含水比例即采用了本属性。
1.3、Color对象
Color是echarts提供的通用调色板对象,echarts实例可以配置全局color数组作为图表的默认颜色集,用于诸如饼图等多色场景。同时Color也用在各种可以独立设置的颜色的场合,如series的itemStyle中。
color支持RGBA的纯色和渐变/纹理填充两种格式。
线性渐变配置说明
-
type:linear
-
x,y,x2,y2:取值范围 0 - 1,表示颜色在图形容器中的百分比,用于表示渐变方向,也就是css linear-gradient的第一个参数,其中x表示左,x2表示右,y表示上,y2表示下。例如:
-
x:0,y:0,x2:0,y2:1:表示从上到下
-
x:0,y:1,x2:0,y2:0:表示从下到上
-
x:0,y:0,x2:1,y2:0:从左到右
-
x:1,y:0,x2:0,y2:0:从右到左
-
-
colorStops:颜色段点,也就是css linear-gradient后面的参数组
2、实现过程
以下代码演示包含两段柱的立体堆叠柱图的实现。
-
1、计算堆叠柱图总高度,目的是为了获得顶部菱形的位置
-
2、声明普通堆叠柱图的颜色,要一半深色一半浅色,从而模拟柱子的两条边
-
3、处理当上半柱或者下半柱高度为0的特殊情况下顶部和底部菱形的展示形态,当要堆叠的柱子超过两个时,情况会更加复杂一些,本示例不涉及。
-
4、声明图形的series集合,整个立体堆叠柱图由两个普通堆叠柱图和3个象形柱图(菱形)构成,要注意各图的z-index值(z)
完整示例代码如下:
import * as echarts from 'echarts';
const ups = [80, 200, 90, 140, 70, 80, 0];
const downs = [120, 300, 150, 300, 270, 0, 100];
const xData = ['1月', '2月', '3月', '4月', '5月', '6月', '7月'];
// 声明柱图基准宽度
const baseWidth = 40;
// 1、计算柱图总高度
const sum = ups.map((item, index) => item + downs[index]);
// 2、声明主体柱图的同色值不同亮度的线性渐变,由于要配合菱形,
// 双色渐变的两色各占一半
const color = [
{
type: "linear",
x: 0,
x2: 1,
y: 0,
y2: 0,
colorStops: [
{
offset: 0,
color: "#54a2d3", // 青蓝
},
{
offset: 0.5,
color: "#54a2d3",
},
{
offset: 0.5,
color: "#7ed1e3", // 青绿
},
{
offset: 1,
color: "#7ed1e3",
},
],
},
{
type: "linear",
x: 0,
x2: 1,
y: 0,
y2: 0,
colorStops: [
{
offset: 0,
color: "#a3a418", // 深棕
},
{
offset: 0.5,
color: "#a3a418",
},
{
offset: 0.5,
color: "#cdbf38", // 浅棕
},
{
offset: 1,
color: "#cdbf38",
},
],
},
];
const bottomDiamond = [];
const midDiamond = [...downs];
const topDiamond = [];
// 3、计算顶部菱形和底部菱形颜色及位置
for (let i = 0; i < sum.length; i++) {
// 总长度等于0,则什么上下菱形都不显示
if (sum[i] <= 0) {
bottomDiamond.push(0);
topDiamond.push({
value: 1,
itemStyle: {
borderColor: '#rgba(0,0,0,0)',
borderWidth: 2,
color: '#rgba(0,0,0,0)'
}
});
} else {
// ↓计算底部折角
// 如果柱子只有上半段,则底部菱形颜色要与上段颜色相同
if (sum[i] === ups[i]) {
bottomDiamond.push({
value: 1, // 底部菱形高度为1
itemStyle: {
normal: {
color: color[1] // 专门设置颜色
}
}
})
} else {
// 否则底部菱形颜色与下段颜色相同
bottomDiamond.push(1);
}
// ↓计算顶部菱形
// 如果柱子存在上半段,则顶部菱形颜色与上段相近
if (ups[i] > 0) {
topDiamond.push({
value: sum[i], // 顶部菱形所属标准柱的高度为堆叠柱图总高度
itemStyle: {
normal: {
borderColor: "#e9d86c", // 黄棕
borderWidth: 2,
color: "#e9d86c",
}
}
});
} else {
// 如果柱子只有下半段,则顶部菱形颜色与下段相近
topDiamond.push({
value: sum[i],
itemStyle: {
normal: {
borderColor: "#89e3ec", // 浅绿
borderWidth: 2,
color: "#89e3ec",
}
}
});
// 去掉中间菱形
midDiamond[i] = '';
}
}
}
const dom = document.getElementById('chart');
const chart = echarts.init(dom);
const option = {
xAxis: {
type: 'category',
data: xData
},
yAxis: {
type: 'value'
},
legend: {
data: [
{
name: '已处理',
itemStyle: {
color: '#a3a418'
}
},
{
name: '未处理',
itemStyle: {
color: '#54a2d3'
}
}
],
right: 20,
selectedMode: false // 关闭图例可点击功能,防止菱形被晾出来
},
series: [
// 基础下段
{
z: 1,
name: '已处理',
type: 'bar',
barWidth: baseWidth,
stack: 'sum',
itemStyle: {
color: color[0]
},
data: downs
},
// 基础上段
{
z: 2,
name: '未处理',
type: 'bar',
barWidth: baseWidth,
stack: 'sum',
itemStyle: {
color: color[1]
},
data: ups
},
// 底部菱形,用于产生底座处的折线效果
{
z: 3,
name: 'all',
type: 'pictorialBar',
data: bottomDiamond,
symbol: 'diamond',
symbolOffset: ['0%', '50%'], // 下移50%,产生底部填角
symbolSize: [baseWidth, 10], // 上下菱形的尺寸保持一致
itemStyle: {
color: color[0] // 颜色与下段柱保持一致
},
tooltip: {
show: false
}
},
// 中间连接处菱形,用于产生上下段连接处的折线效果
{
z: 4,
name: 'all',
type: 'pictorialBar',
data: midDiamond,
symbol: 'diamond',
symbolPosition: 'end',
symbolOffset: ['0%', '-50%'],
symbolSize: [baseWidth, 10],
itemStyle: {
normal: {
color: color[1]
}
}
},
// 顶部菱形
{
z: 5,
name: 'all',
type: 'pictorialBar',
data: topDiamond,
symbol: 'diamond',
symbolPosition: 'end',
symbolOffset: ['0%', '-65%'],
symbolSize: [baseWidth - 4, (10 * (baseWidth - 4)) / baseWidth],
tooltip: {
show: false,
}
}
]
};
chart.setOption(option);