实现效果
实现思路
1、使用echarts的graphic属性添加分组图形元素。
2、使用echart的方法convertToPixel获得类目轴的坐标。
3、类目数据包含分组信息,进行解析和格式化,类似 ‘A-A1-A11’ 用-隔开实现三级分组。
4、datazoom事件触发时调用一下类目轴分组方法可以让分组随着区域缩放改变。
js完整代码
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<script src="https://fastly.jsdelivr.net/npm/echarts@5.4.2/dist/echarts.min.js"></script>
</head>
<body>
<div id="main" style="height: 400px"></div>
<script type="text/javascript">
const myChart = echarts.init(document.getElementById("main"));
const chartData = [
{ catetory: "A-A1-A11", value: Math.round(Math.random() * 100) },
{ catetory: "A-A1-A12", value: Math.round(Math.random() * 100) },
{ catetory: "A-A1-A13", value: Math.round(Math.random() * 100) },
{ catetory: "A-A2-A21", value: Math.round(Math.random() * 100) },
{ catetory: "A-A3-A31", value: Math.round(Math.random() * 100) },
{ catetory: "A-A3-A32", value: Math.round(Math.random() * 100) },
{ catetory: "B-B1-B11", value: Math.round(Math.random() * 100) },
{ catetory: "B-B1-B12", value: Math.round(Math.random() * 100) },
{ catetory: "B-B2-B21", value: Math.round(Math.random() * 100) },
{ catetory: "C-C1-C11", value: Math.round(Math.random() * 100) },
];
const catetoryData = chartData.map(({ catetory }) => catetory);
const serData = chartData.map(({ value }) => ({ value }));
const catetoryFontSize = 12;
// 指定图表的配置项和数据
const option = {
title: {
text: "类目轴多级分组",
left: "center",
},
grid: [
{
bottom: 70,
left: 100,
right: 40,
},
],
tooltip: {},
dataZoom: [
{
type: "slider",
show: true,
bottom: 10,
height: 10,
id: "categoryZoom",
},
],
graphic: [
{
id: "categoryGroup",
elements: [],
},
],
xAxis: {
data: catetoryData,
axisLabel: {
fontSize: catetoryFontSize,
margin: 4,
formatter: (value) => {
const valueArr = value.split("-");
return valueArr[valueArr.length - 1];
},
},
axisTick: {
length: catetoryFontSize + 4,
lineStyle: {
width: 2,
},
},
},
yAxis: {},
series: [
{
name: "销量",
type: "bar",
data: serData,
},
],
};
const groupCategoryAxis = () => {
const { dataZoom, graphic, grid } = myChart.getOption();
const categoryGroupIndex = graphic.findIndex(
(g) => g.id === "categoryGroup"
);
const categoryGroupGraphic = graphic[categoryGroupIndex] || {
id: "categoryGroup",
elements: [],
};
categoryGroupGraphic.elements.forEach((e) => {
if (e.type !== "group") {
e.$action = "remove";
}
});
if (categoryGroupIndex > -1) {
graphic.splice(categoryGroupIndex, 1);
}
let categoryArr = [...catetoryData];
const categoryZoom = dataZoom.find(({ id }) => id === "categoryZoom");
if (categoryZoom) {
const { startValue, endValue } = categoryZoom;
categoryArr = categoryArr.slice(startValue, endValue + 1);
}
const splitCategoryArr = categoryArr.map((category) =>
category.split("-")
);
const levelGroupArr = [];
for (let i = 0; i < splitCategoryArr[0].length - 1; i++) {
const groupArr = [];
let preGroup = {};
splitCategoryArr.forEach((splitCategory, j) => {
const category = categoryArr[j];
const value = splitCategory[i];
if (preGroup.value === value) {
preGroup.lte = category;
preGroup.count++;
} else {
groupArr.push({ value, count: 1, gte: category, lte: category });
preGroup = groupArr[groupArr.length - 1];
}
});
levelGroupArr.push(groupArr);
}
let textWidth = 0;
if (categoryArr.length > 1) {
textWidth =
myChart.convertToPixel({ xAxisIndex: 0 }, categoryArr[1]) -
myChart.convertToPixel({ xAxisIndex: 0 }, categoryArr[0]);
} else {
textWidth = myChart.getWidth() - grid[0].left - grid[0].right;
}
const textOffset = textWidth / 2;
const axisHeight = catetoryFontSize + 2;
const axisOffset =
grid[0].bottom - (levelGroupArr.length + 1) * axisHeight - 5;
levelGroupArr.forEach((levelGroup, i) => {
const bottom = i * axisHeight + axisOffset;
const group = {
type: "group",
silent: true,
bottom,
children: [],
};
levelGroup.forEach(({ value, gte, lte }) => {
let startX = myChart.convertToPixel({ xAxisIndex: 0 }, gte);
let endX = startX;
if (gte !== lte) {
endX = myChart.convertToPixel({ xAxisIndex: 0 }, lte);
}
startX -= textOffset;
endX += textOffset;
const shapes = [
{ x1: startX, y1: -2, x2: endX, y2: -2 },
{ x1: startX, y1: -2, x2: startX, y2: catetoryFontSize },
{ x1: endX, y1: -2, x2: endX, y2: catetoryFontSize },
];
shapes.forEach((shape) => {
group.children.push({
type: "line",
scale: [1, 1],
shape,
style: { lineWidth: 1, stroke: "#333" },
});
});
group.children.push({
type: "text",
style: {
x: (startX + endX) / 2,
text: value,
stroke: "#333",
font: `${catetoryFontSize}px`,
textAlign: "center",
width: endX - startX,
overflow: "truncate",
},
});
});
categoryGroupGraphic.elements.push(group);
});
graphic.push(categoryGroupGraphic);
myChart.setOption({ graphic });
};
myChart.on("datazoom", () => {
groupCategoryAxis();
});
myChart.setOption(option);
setTimeout(() => {
groupCategoryAxis();
}, 50);
</script>
</body>
</html>