需求
展示一个柱状图,x轴为日期,y轴为百分比。要求每个y轴要用两个柱堆叠占满,且柱的宽度可跨多个横坐标。例:(1月1日-6月1日) 展示一个柱;(6月1日-9月1日) 展示一个柱,如下图
实现
实现思路:x轴y轴展示都容易,y轴堆叠占满可以通过渲染两个柱堆叠来实现,跨横坐标的功能需要使用 自定义系列 去硬写柱的宽和高实现
-
x轴
xAxis: { type: 'category', // 是否留白 图形两边和边界之间否留空 boundaryGap: true, // x轴的刻度线和标签是否对齐 axisTick: { alignWithLabel: true }, data: [ '01月01日', '02月01日', '03月01日', '04月01日', '05月01日', '06月01日', '07月01日', '08月01日', '09月01日', '10月01日', '11月01日', '12月01日' ] },
-
y轴
yAxis: { // 最大100% max: 100, // 最小0% min: 0, // y轴分隔成多少份, splitNumber: 10, // y轴标签加单位 axisLabel: { formatter: '{value} %' } },
-
series
series: [ // 下面的柱 { // 自定义类型 type: 'custom', // 自定义的图形渲染逻辑 // params renderItem: function (params, api) { // 通过值获取图形在图表里的像素坐标 var start = api.coord([api.value(0), api.value(2)]); // 通过值获取图形在图表里的宽和高 var size = api.size([api.value(0), api.value(2)]); // 因为x轴是均匀的12份,所以一份的宽度就是 All/12 // params.coordSys.width 整个图型的宽度 var width = params.coordSys.width / 12; // 输出 return { type: 'rect', shape: { // x轴上 柱的起始坐标 x: start[0], // y轴上 柱的结束坐标 y: start[1], // 柱的宽度 width: width * api.value(1), // 柱的高度 height: size[1] // 生成一个矩形需要知道xy的最大最小来确定范围 // xMax = x + width; xMin = x // yMax = y; yMin = y - height }, style: api.style() }; }, // 标题展示的位置 label: { show: true, position: 'inside' }, // 定义data里的维度名称 dimensions: ['起始日期', '结束日期', 'name'], // 定义维度对应什么 encode: { // x轴使用data里的第一个维度数据 x: 0, // y轴使用data里的第三个维度数据 y: 2, // 图例使用data里的第三个维度数据 tooltip: 2, // 名称使用data里的第四个维度数据 itemName: 3 }, data: [ // '01月01日' x轴的值 // 5 当前图形跨越x轴坐标的个数,用于计算柱的宽度 // 30 y轴的值 // 'a' 名称 ['01月01日', 5, 30, 'a'], ['06月01日', 3, 10, 'b'], ['09月01日', 3, 37, 'c'] ].map((item, index) => { return { value: item, itemStyle: { color: ['#66CCFF', '#ff6699', '#ccff99'][index] } }; }); }, // 上面的柱 { type: 'custom', renderItem: function (params, api) { // 和上面的区别是 yMax现在是最大也就是100了 yMin = yMax- Height var start = api.coord([api.value(0), 100]); var size = api.size([api.value(0), 100 - api.value(2)]); var width = params.coordSys.width / 12; return { type: 'rect', shape: { x: start[0], y: start[1], width: width * api.value(1), height: size[1] }, style: api.style() }; }, label: { show: true, position: 'inside', color: '#000' }, dimensions: ['起始日期', '结束日期', 'name'], encode: { x: 0, y: 2, tooltip: [0, 1, 2], itemName: 3 }, data: [ ['01月01日', 5, 30, 'a'], ['06月01日', 3, 10, 'b'], ['09月01日', 3, 37, 'c'] ].map((item, index) => return { value: item, itemStyle: { color: ['#66CCFF', '#ff6699', '#ccff99'][index] } }; }); } ]
-
完整代码
其它的部分比较简单就不完善了
const data1 = [
['01月01日', 5, 30, 'a'],
['06月01日', 3, 10, 'b'],
['09月01日', 3, 37, 'c']
].map((item, index) => {
return {
value: item,
itemStyle: {
color: ['#66CCFF', '#ff6699', '#ccff99', '#bce9a6', '#8da2e4'][index]
}
};
});
const data2 = [
['01月01日', 5, 30, 'A'],
['06月01日', 3, 10, 'B'],
['09月01日', 3, 37, 'C']
].map((item, index) => {
return {
value: item,
itemStyle: {
color: ['#a4e0f7', '#ff9b9b', '#ffe68f', '#bce9a6', '#8da2e4'][index]
}
};
});
option = {
title: {
text: '百分比',
left: 'center'
},
tooltip: {},
xAxis: {
type: 'category',
boundaryGap: true,
axisTick: {
alignWithLabel: true
},
data: [
'01月01日',
'02月01日',
'03月01日',
'04月01日',
'05月01日',
'06月01日',
'07月01日',
'08月01日',
'09月01日',
'10月01日',
'11月01日',
'12月01日'
]
},
yAxis: {
max: 100,
min: 0,
splitNumber: 20,
axisLabel: {
formatter: '{value} %'
}
},
series: [
{
type: 'custom',
renderItem: function (params, api) {
var start = api.coord([api.value(0), api.value(2)]);
var size = api.size([api.value(0), api.value(2)]);
var width = params.coordSys.width / 12;
return {
type: 'rect',
shape: {
x: start[0],
y: start[1],
width: width * api.value(1),
height: size[1]
},
style: api.style()
};
},
label: {
show: true,
position: 'inside'
},
dimensions: ['起始日期', '结束日期', 'name'],
encode: {
x: 0,
y: 2,
tooltip: 2,
itemName: 3
},
data: data1
},
{
type: 'custom',
renderItem: function (params, api) {
var start = api.coord([api.value(0), 100]);
var size = api.size([api.value(0), 100 - api.value(2)]);
var width = params.coordSys.width / 12;
return {
type: 'rect',
shape: {
x: start[0],
y: start[1],
width: width * api.value(1),
height: size[1]
},
style: api.style()
};
},
label: {
show: true,
position: 'inside',
color: '#000'
},
dimensions: ['起始日期', '结束日期', 'name'],
encode: {
x: 0,
y: 2,
tooltip: [0, 1, 2],
itemName: 3
},
data: data2
}
]
};
参考:
补充20230605
1. x轴的日期不为规则的如何显示(如【01-02,02-15,04-20】)
将x轴的data设置为365天[01-01,01-02 ··· 12-31],series的data设置为二维数组[[01-02,100],[02-15,140],[04-20,300]],这样就能找到对应的点
注:x轴的标签会重叠,因为300多项太多了,需要进行过滤:xAxis.axisLabel.interval = (index, value) => xData.includes(value),这样x轴就会只展示有数据的标签
2. x轴标签还是重叠了怎么办
几种办法
1. xAxis.axisLabel.interval:设置标签之间的间隔或者通过回调去设置
1. xAxis.axisLabel.rotate:可以设置标签的倾斜度数
2. xAxis.axisLabel.hideOverlap:true:5.2.0以上的版本可以通过设置这个属性去隐藏重叠的标签
3. 类目轴怎么设置两侧留白
添加额外数据:比如数据的范围是01/01~12/31,那么就可以往x轴的data就前后各加30天
有的人会问,01-01这种为什么不格式化成数字去设置两边的留白呢?
01-01 这种字符x轴对应的是类目轴(category),就是data的x值是多少,x轴就展示什么标签
0101 会变成数值轴(value),x轴标签会显示值附近的整数(值是0102,x轴的标签就是100、200)