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

如何用DC.js实现带复选框的分组柱状图(系列可显隐)

没问题,我来一步步教你实现这个需求!我们会用DC.js配合Crossfilter完成带复选框控制的分组柱状图,实现每个年龄组系列的显示/隐藏功能,完全匹配你想要的效果~


步骤1:数据预处理

你提供的是宽表格式的数据集(每个对象包含多个年龄组字段和地区),但DC.js的分组柱状图更适合处理长表结构。先写个简单的转换函数把数据转成适合的格式:

// 原始宽表数据(示例)
const data = [
  {"under 5 years":12, "5 to 13 years":87, "14 to 17 years":25,"18 to 24 years":15,"25 to 44 years":25,"45 to 64 years":14,"place":"CA"},
  {"under 5 years":15, "5 to 13 years":92, "14 to 17 years":30,"18 to 24 years":18,"25 to 44 years":28,"45 to 64 years":16,"place":"NY"},
  {"under 5 years":9, "5 to 13 years":78, "14 to 17 years":22,"18 to 24 years":12,"25 to 44 years":22,"45 to 64 years":13,"place":"TX"}
  // 更多地区数据...
];

// 转换为长表:每条数据对应{地区, 年龄组, 数值}
const longData = data.flatMap(item => {
  const place = item.place;
  return Object.keys(item)
    .filter(key => key !== 'place')
    .map(key => ({
      place: place,
      ageGroup: key,
      value: item[key]
    }));
});
步骤2:初始化Crossfilter与数据维度

DC.js依赖Crossfilter做数据聚合,先创建实例并定义核心维度:

const cf = crossfilter(longData);

// 维度:按「地区」分组(对应柱状图的X轴)
const placeDimension = cf.dimension(d => d.place);

// 组:按「地区+年龄组」聚合,生成分组柱状图需要的嵌套数据结构
const ageGroupGroup = placeDimension.group().reduce(
  // 累加器:添加数据时更新对应年龄组的数值
  (p, v) => {
    p[v.ageGroup] = (p[v.ageGroup] || 0) + v.value;
    return p;
  },
  // 逆累加器:移除数据时更新数值(过滤场景用)
  (p, v) => {
    p[v.ageGroup] -= v.value;
    if(p[v.ageGroup] === 0) delete p[v.ageGroup];
    return p;
  },
  // 初始化空对象
  () => ({})
);
步骤3:创建分组柱状图基础配置

初始化DC.js的柱状图,设置基本样式和轴信息:

const barChart = dc.barChart('#bar-chart');

// 获取所有唯一的年龄组,用于后续复选框和系列配置
const allAgeGroups = [...new Set(longData.map(d => d.ageGroup))];

barChart
  .width(800)
  .height(400)
  .dimension(placeDimension)
  .group(ageGroupGroup, allAgeGroups[0]) // 默认显示第一个年龄组系列
  .x(d3.scaleBand())
  .xUnits(dc.units.ordinal) // X轴为分类类型
  .xAxisLabel('地区')
  .yAxisLabel('人口数量')
  .legend(dc.legend().x(700).y(20).itemHeight(15).gap(5)) // 添加右上角图例
  .renderHorizontalGridLines(true)
  .margins({top: 20, right: 80, bottom: 50, left: 60});

// 为所有年龄组添加堆叠系列
allAgeGroups.forEach((group, index) => {
  if(index > 0) { // 跳过已设置的第一个系列
    barChart.stack(ageGroupGroup, group, d => d.value[group] || 0);
  }
});
步骤4:添加复选框控件并绑定交互

先在HTML里添加复选框容器,再用JS生成控件并绑定显示/隐藏逻辑:

<!-- 复选框容器 -->
<div id="checkbox-container" style="margin: 10px 0; padding-left: 20px;"></div>
<!-- 柱状图容器 -->
<div id="bar-chart"></div>
const checkboxContainer = document.getElementById('checkbox-container');

// 为每个年龄组生成复选框
allAgeGroups.forEach(group => {
  const checkboxWrapper = document.createElement('div');
  checkboxWrapper.style.display = 'inline-block';
  checkboxWrapper.style.marginRight = '20px';

  const checkbox = document.createElement('input');
  checkbox.type = 'checkbox';
  checkbox.id = `checkbox-${group.replace(/\s+/g, '-')}`;
  checkbox.checked = true; // 默认全部勾选

  const label = document.createElement('label');
  label.htmlFor = checkbox.id;
  label.textContent = group;

  checkboxWrapper.appendChild(checkbox);
  checkboxWrapper.appendChild(label);
  checkboxContainer.appendChild(checkboxWrapper);

  // 绑定复选框变更事件
  checkbox.addEventListener('change', () => {
    // 获取当前勾选的年龄组
    const selectedGroups = allAgeGroups.filter(g => {
      const cb = document.getElementById(`checkbox-${g.replace(/\s+/g, '-')}`);
      return cb.checked;
    });

    // 重置图表的堆叠系列
    barChart._group = null;
    barChart._stackedGroups = [];

    // 重新添加选中的系列
    if(selectedGroups.length > 0) {
      barChart.group(ageGroupGroup, selectedGroups[0]);
      selectedGroups.slice(1).forEach(g => {
        barChart.stack(ageGroupGroup, g, d => d.value[g] || 0);
      });
    }

    // 重新渲染图表
    barChart.render();
  });
});
步骤5:渲染图表

最后调用DC.js的全局渲染方法,启动整个可视化:

dc.renderAll();

关键说明
  • 数据转换宽表转长表是核心前提,Crossfilter和DC.js对长格式数据的聚合支持更友好,能轻松实现分组堆叠效果。
  • 动态堆叠:通过barChart.stack()添加系列,复选框变更时重置并重新配置堆叠系列,实现显示/隐藏的交互。
  • 用户体验:默认全部勾选所有系列,保持初始视图完整;图例和复选框对应,用户能直观对应控件和图表元素。

内容的提问来源于stack exchange,提问作者Simhadri Akaash

火山引擎 最新活动