You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Chart.js环形图如何实现分组内展示子分组及图例联动

Chart.js 分组联动环形图实现方案

核心实现逻辑是绕开默认的单数据项图例生成规则,自定义图例仅保留3个顶层分组,同时改写图例点击事件,实现点击一个分组批量控制组内12个子项的显隐;同组子项用同色系做视觉区分,保证用户能直观识别子项归属。

实现步骤

1. 准备容器与依赖

页面中添加canvas容器作为图表挂载点:

<canvas id="groupDoughnut" width="600" height="500"></canvas>

引入Chart.js依赖,本地安装或者CDN引入都可以:

# 本地npm安装
npm install chart.js

2. 定义分层数据结构

把3个顶层分组和36个子项做关联映射,每个子项绑定所属分组ID,同组子项配置同色系不同透明度/深浅的填充色,视觉上自动归组:

// 顶层分组配置,仅这3项会显示在图例区
const topGroups = [
  { id: 'g1', name: '分类1', baseColor: 'rgb(59, 130, 246)' },
  { id: 'g2', name: '分类2', baseColor: 'rgb(239, 68, 68)' },
  { id: 'g3', name: '分类3', baseColor: 'rgb(16, 185, 129)' }
]

// 组装36个子项数据,每个分组12条,可直接替换为实际业务数据
const chartItems = []
topGroups.forEach(group => {
  for (let i = 0; i < 12; i++) {
    // 同组子项透明度从0.25到1渐变,既区分不同子项,又能识别同组归属
    const alpha = 0.25 + (i / 12) * 0.75
    chartItems.push({
      groupId: group.id,
      label: `${group.name}-子项${i+1}`,
      value: Math.round(Math.random() * 80 + 20), // 模拟数值,替换为实际业务值即可
      bgColor: group.baseColor.replace('rgb', 'rgba').replace(')', `, ${alpha})`)
    })
  }
})

3. 初始化图表,自定义图例交互

核心是重写legend插件的generateLabels方法控制图例仅显示顶层分组,重写onClick方法实现分组批量显隐:

const ctx = document.getElementById('groupDoughnut').getContext('2d')
new Chart(ctx, {
  type: 'doughnut',
  data: {
    labels: chartItems.map(i => i.label),
    datasets: [{
      data: chartItems.map(i => i.value),
      backgroundColor: chartItems.map(i => i.bgColor),
      borderWidth: 1,
      borderColor: '#ffffff',
    }]
  },
  options: {
    responsive: true,
    // 可调整环形厚度,数值越大环越细
    cutout: '60%',
    plugins: {
      legend: {
        position: 'top',
        // 自定义图例项,仅生成3个顶层分组
        generateLabels: (chart) => {
          return topGroups.map(group => {
            // 找出当前分组下所有子项的索引
            const itemIndexes = chartItems
              .map((item, idx) => item.groupId === group.id ? idx : -1)
              .filter(idx => idx !== -1)
            // 判断当前分组是否全部隐藏,控制图例选中态
            const isHidden = itemIndexes.every(idx => !chart.getDataVisibility(idx))
            return {
              text: group.name,
              fillStyle: group.baseColor,
              strokeStyle: group.baseColor,
              hidden: isHidden,
              itemIndexes // 存下子项索引供点击事件调用
            }
          })
        },
        // 重写点击逻辑,批量切换组内子项显隐
        onClick: (e, legendItem, legend) => {
          const chart = legend.chart
          const toggleToShow = legendItem.hidden
          legendItem.itemIndexes.forEach(idx => {
            toggleToShow ? chart.show(idx) : chart.hide(idx)
          })
          chart.update()
        }
      },
      tooltip: {
        // 提示框正常展示子项名称、数值、占比
        callbacks: {
          label: (ctx) => {
            const total = ctx.dataset.data.reduce((sum, v) => sum + v, 0)
            const percent = ((ctx.parsed / total) * 100).toFixed(1)
            return `${ctx.label}: ${ctx.parsed}(占比${percent}%)`
          }
        }
      }
    }
  }
})

自定义调整说明

  • 同组子项配色如果不想用透明度区分,可替换为同色系不同明度的纯色值,视觉归类效果一致
  • 如果需要默认隐藏某个分组,初始化完成后遍历对应分组的子项索引调用chart.hide(idx)后执行chart.update()即可
  • 图例位置、环形厚度、边框样式都可以通过Chart.js原生配置项修改,不会影响分组联动逻辑

内容的提问来源于stack exchange,提问作者Pernic Ryuusei Huy

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.26 22:36:23