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

React+Chart.js Marimekko图表:堆叠柱宽度自定义问题求助

解决React + Chart.js Marimekko图表自定义柱宽与标签显示问题

核心问题分析

Chart.js默认条形图采用等宽类别轴,barPercentage和categoryPercentage是全局配置,无法为单个堆叠柱设置差异化宽度。要实现Marimekko图表的自定义宽度,需改用**线性轴(linear scale)**手动控制每个柱的位置与宽度,同时通过插件实现百分比与数值标签。

实现步骤与代码示例

1. 安装依赖

确保安装所需包:

npm install chart.js react-chartjs-2 chartjs-plugin-datalabels

2. 完整组件代码

import React from 'react';
import { Bar } from 'react-chartjs-2';
import { Chart as ChartJS, LinearScale, BarElement, Title, Tooltip, Legend, Filler } from 'chart.js';
import ChartDataLabels from 'chartjs-plugin-datalabels';

// 注册Chart.js所需模块
ChartJS.register(
  LinearScale,
  BarElement,
  Title,
  Tooltip,
  Legend,
  Filler,
  ChartDataLabels
);

const MarimekkoChart = () => {
  // 定义类别及对应宽度占比(总宽度为100%)
  const categories = [
    { name: '<7.5 Oz.', width: 40, start: 0 },
    { name: '7.5-12.5 Oz.', width: 20, start: 40 },
    { name: '12.5-17.5 Oz.', width: 10, start: 60 },
    { name: '17.5+ Oz.', width: 30, start: 70 },
  ];

  // 堆叠系列的原始数据(替换为你的真实数据)
  const seriesData = [
    { label: '产品A', values: [15, 8, 3, 12] },
    { label: '产品B', values: [20, 7, 2, 15] },
    { label: '产品C', values: [5, 5, 5, 3] },
  ];

  // 计算每个类别下的总数值(用于百分比计算)
  const categoryTotals = categories.map((_, idx) => 
    seriesData.reduce((sum, item) => sum + item.values[idx], 0)
  );

  // 转换数据适配线性轴格式
  const datasets = seriesData.map((series, seriesIdx) => ({
    label: series.label,
    data: categories.map((cat, catIdx) => ({
      x: cat.start + cat.width / 2, // 柱的中心x坐标
      y: series.values[catIdx],
    })),
    backgroundColor: [`rgba(255, 99, 132, 0.6)`, `rgba(54, 162, 235, 0.6)`, `rgba(255, 206, 86, 0.6)`][seriesIdx],
    barThickness: () => categories[catIdx]?.width || 0, // 绑定对应类别宽度
    datalabels: {
      // 堆叠内显示百分比
      formatter: (value, ctx) => `${Math.round((value.y / categoryTotals[ctx.dataIndex]) * 100)}%`,
      color: '#fff',
      anchor: 'center',
      align: 'center',
    },
  }));

  // 添加柱顶总数值的数据集(透明背景不影响堆叠)
  const totalDataset = {
    label: '总计',
    data: categories.map((cat, catIdx) => ({
      x: cat.start + cat.width / 2,
      y: categoryTotals[catIdx],
    })),
    backgroundColor: 'transparent',
    borderColor: 'transparent',
    barThickness: 0,
    datalabels: {
      formatter: (value) => value.y,
      color: '#222',
      anchor: 'end',
      align: 'top',
      offset: 8,
      font: { weight: 'bold' },
    },
  };

  datasets.push(totalDataset);

  const chartOptions = {
    indexAxis: 'y', // 水平条形图(垂直图改为'x')
    responsive: true,
    plugins: {
      legend: { position: 'top' },
      title: { display: true, text: 'Marimekko 图表' },
      tooltip: { mode: 'index', intersect: false },
    },
    scales: {
      x: {
        type: 'linear',
        min: 0,
        max: 100,
        ticks: {
          // 自定义x轴刻度标签,对应类别名称
          callback: (value) => {
            const matchCat = categories.find(cat => value >= cat.start && value <= cat.start + cat.width);
            return matchCat ? matchCat.name : '';
          },
          autoSkip: false,
          maxRotation: 0,
        },
        grid: { display: false },
      },
      y: {
        stacked: true, // 开启y轴堆叠
        grid: { display: true },
      },
    },
  };

  return <Bar data={{ datasets }} options={chartOptions} />;
};

export default MarimekkoChart;

关键配置说明

  1. 自定义柱宽

    • 改用线性x轴,将总宽度设为100(对应百分比)
    • 每个类别定义start(起始位置)和width(宽度占比),柱的中心x坐标为start + width/2
    • 通过barThickness绑定对应类别的宽度值,实现差异化占比
  2. 标签显示

    • 堆叠内百分比:通过chartjs-plugin-datalabels的formatter计算当前值占类别总值的比例
    • 柱顶总数值:新增透明数据集,专门显示每个类别的总和,通过datalabels设置标签位置与样式
  3. 轴与堆叠设置

    • 开启y.stacked: true实现堆叠效果
    • 自定义x轴刻度标签,匹配每个类别的位置范围

内容的提问来源于stack exchange,提问作者alankritha mani

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.19 22:53:16