You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Highcharts增强型分组层级X轴标签:功能名称与实现咨询

Highcharts 实现多层级分组X轴标签

嘿,你说的这个功能在Highcharts里有专门的叫法,一般叫多层级轴(Multi-level Axes)或者分组轴标签(Grouped Axis Labels),官方是完全支持的,估计之前是关键词没找对才没搜到~

我给你拆解下实现思路和具体代码:

核心实现逻辑

Highcharts本身支持通过嵌套类别配合自定义渲染来实现三层标签结构,主要分两步:

  1. 把X轴的类别设置成嵌套数组,包含「月份→日期分段→具体日期」的层级
  2. 利用Highcharts的渲染API(chart.renderer)在轴的上方绘制上层和中层的分组标签,同时控制底层只显示具体日期

完整代码示例

Highcharts.chart('container', {
    chart: {
        type: 'column',
        marginTop: 80 // 预留空间给上层标签,避免和标题/图表内容重叠
    },
    xAxis: {
        categories: [
            // 5月1-10日
            ['2024年5月', '1-10日', '5/1'],
            ['2024年5月', '1-10日', '5/2'],
            ['2024年5月', '1-10日', '5/3'],
            ['2024年5月', '1-10日', '5/4'],
            ['2024年5月', '1-10日', '5/5'],
            ['2024年5月', '1-10日', '5/6'],
            ['2024年5月', '1-10日', '5/7'],
            ['2024年5月', '1-10日', '5/8'],
            ['2024年5月', '1-10日', '5/9'],
            ['2024年5月', '1-10日', '5/10'],
            // 5月11-20日
            ['2024年5月', '11-20日', '5/11'],
            ['2024年5月', '11-20日', '5/12'],
            ['2024年5月', '11-20日', '5/13'],
            ['2024年5月', '11-20日', '5/14'],
            ['2024年5月', '11-20日', '5/15'],
            ['2024年5月', '11-20日', '5/16'],
            ['2024年5月', '11-20日', '5/17'],
            ['2024年5月', '11-20日', '5/18'],
            ['2024年5月', '11-20日', '5/19'],
            ['2024年5月', '11-20日', '5/20'],
            // 5月21-31日
            ['2024年5月', '21-31日', '5/21'],
            ['2024年5月', '21-31日', '5/22'],
            ['2024年5月', '21-31日', '5/23'],
            ['2024年5月', '21-31日', '5/24'],
            ['2024年5月', '21-31日', '5/25'],
            ['2024年5月', '21-31日', '5/26'],
            ['2024年5月', '21-31日', '5/27'],
            ['2024年5月', '21-31日', '5/28'],
            ['2024年5月', '21-31日', '5/29'],
            ['2024年5月', '21-31日', '5/30'],
            ['2024年5月', '21-31日', '5/31'],
            // 6月1-10日
            ['2024年6月', '1-10日', '6/1'],
            ['2024年6月', '1-10日', '6/2']
        ],
        labels: {
            useHTML: true,
            // 只显示最底层的具体日期
            formatter: function() {
                return this.value[2];
            }
        },
        // 图表加载完成后绘制上层分组标签
        events: {
            load: function() {
                var chart = this.chart,
                    xAxis = this,
                    categories = xAxis.categories,
                    tickPositions = xAxis.tickPositions;
                
                let currentMonth = '',
                    currentSegment = '',
                    monthStartIndex = 0,
                    segmentStartIndex = 0;

                // 遍历所有类别,找到每个分组的起始和结束位置
                categories.forEach((cat, index) => {
                    // 处理月份分组
                    if (cat[0] !== currentMonth) {
                        // 如果不是第一个分组,先绘制上一个月份的标签
                        if (currentMonth) {
                            drawGroupLabel(
                                currentMonth,
                                xAxis.toPixels(tickPositions[monthStartIndex]),
                                xAxis.toPixels(tickPositions[index - 1]),
                                xAxis.top - 40,
                                chart
                            );
                        }
                        currentMonth = cat[0];
                        monthStartIndex = index;
                    }

                    // 处理日期分段分组
                    if (cat[1] !== currentSegment) {
                        if (currentSegment) {
                            drawGroupLabel(
                                currentSegment,
                                xAxis.toPixels(tickPositions[segmentStartIndex]),
                                xAxis.toPixels(tickPositions[index - 1]),
                                xAxis.top - 15,
                                chart
                            );
                        }
                        currentSegment = cat[1];
                        segmentStartIndex = index;
                    }

                    // 处理最后一个分组
                    if (index === categories.length - 1) {
                        drawGroupLabel(currentMonth, xAxis.toPixels(tickPositions[monthStartIndex]), xAxis.toPixels(tickPositions[index]), xAxis.top - 40, chart);
                        drawGroupLabel(currentSegment, xAxis.toPixels(tickPositions[segmentStartIndex]), xAxis.toPixels(tickPositions[index]), xAxis.top - 15, chart);
                    }
                });
            }
        }
    },
    series: [{
        name: '业务数据',
        data: [
            12,19,15,21,18,25,22,17,14,20,
            23,16,19,24,21,18,26,23,19,15,
            22,27,24,20,16,23,28,25,21,17,24,
            20,18
        ]
    }]
});

// 封装绘制分组标签的函数,方便自定义样式
function drawGroupLabel(text, startX, endX, y, chart) {
    // 计算分组的中心位置,让标签居中显示
    const centerX = (startX + endX) / 2;
    // 绘制标签文本
    chart.renderer.text(text, centerX, y)
        .attr({
            align: 'center',
            fontSize: '14px',
            fontWeight: 'bold',
            fill: '#333',
            zIndex: 10
        })
        .add();
    // 可选:绘制分组分隔线,增强层级感
    chart.renderer.path(['M', endX + 2, y + 15, 'L', endX + 2, chart.plotTop + chart.plotHeight])
        .attr({
            stroke: '#ccc',
            strokeWidth: 1,
            zIndex: 5
        })
        .add();
}

额外说明

  • 你可以通过调整marginTop来预留足够的标签空间,避免和图表标题重叠
  • 封装的drawGroupLabel函数可以自定义标签的字体大小、颜色、位置,也可以选择是否添加分隔线
  • 官方文档里搜索「Grouped Categories」或者「Multi-level Axes」就能找到官方示例,你可以基于这些示例进一步调整细节

内容的提问来源于stack exchange,提问作者Cyrus Downey

火山引擎 最新活动