Echarts类目轴多级分组一种方法

2,621 阅读1分钟

实现效果

1688655521354.png

实现思路

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>