首页
/ Chart.js Bar Chart 完全指南:数据集属性、柱宽计算、堆叠与横向柱状图

Chart.js Bar Chart 完全指南:数据集属性、柱宽计算、堆叠与横向柱状图

2026-09-03 19:47:47作者:农烁颖Land

本篇基于 Chart.js 仓库中的 docs/charts/bar.md 官方文档,完整讲解条形图(bar chart)的配置体系:数据集属性全表、柱宽与间距的五个控制参数、borderSkipped/borderRadius 等样式细节、堆叠与横向柱状图的实现方式,并结合 src/controllers/controller.bar.jssrc/elements/element.bar.js 的源码,说明这些配置项在像素级渲染中如何生效。读完本文,你可以独立完成复杂条形图配置,并能从源码层面解释柱宽、圆角、负值翻转等行为的来龙去脉。

基础用法:垂直条形图

条形图用垂直柱形展示数值,常用于趋势展示和多组数据的并排对比。官方文档给出的最小可运行示例如下(示例中的 Utils.months 是文档站提供的工具函数,实际项目中可以直接写字面量标签):

const labels = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul'];
const data = {
  labels: labels,
  datasets: [{
    label: 'My First Dataset',
    data: [65, 59, 80, 81, 56, 55, 40],
    backgroundColor: [
      'rgba(255, 99, 132, 0.2)',
      'rgba(255, 159, 64, 0.2)',
      'rgba(255, 205, 86, 0.2)',
      'rgba(75, 192, 192, 0.2)',
      'rgba(54, 162, 235, 0.2)',
      'rgba(153, 102, 255, 0.2)',
      'rgba(201, 203, 207, 0.2)'
    ],
    borderColor: [
      'rgb(255, 99, 132)',
      'rgb(255, 159, 64)',
      'rgb(255, 205, 86)',
      'rgb(75, 192, 192)',
      'rgb(54, 162, 235)',
      'rgb(153, 102, 255)',
      'rgb(201, 203, 207)'
    ],
    borderWidth: 1
  }]
};

const config = {
  type: 'bar',
  data: data,
  options: {
    scales: {
      y: {
        beginAtZero: true
      }
    }
  }
};

type: 'bar' 会在注册表中解析到 BarControllerstatic id = 'bar')。从源码的 static defaults 可以看到条形图内置的两组关键默认值:

static defaults = {
  datasetElementType: false,
  dataElementType: 'bar',
  categoryPercentage: 0.8,
  barPercentage: 0.9,
  grouped: true,
  animations: {
    numbers: {
      type: 'number',
      properties: ['x', 'y', 'base', 'width', 'height']
    }
  }
};

categoryPercentage 默认 0.8barPercentage 默认 0.9grouped 默认 true(见 controller.bar.js 第 265-279 行)。同时 static overrides 声明了条形图对坐标轴的特有默认值:索引轴使用 category 类型且 offset: truegrid.offset: true,值轴使用 linear 类型且 beginAtZero: truecontroller.bar.js 第 284-298 行)。

命名空间与选项解析

条形图的选项可以在四个层级上指定,解析优先级从低到高为:

  • options — 整个图表的选项;
  • options.elements.bar — 所有 bar 元素 的选项;
  • options.datasets.bar — 所有 bar 数据集的选项;
  • data.datasets[index] — 仅当前数据集的选项。

文档强调:数据集中只有 data 一项是必填的,其余展示属性(颜色、边框、圆角等)通常就定义在数据集命名空间里。所有值为 undefined 的项会按 选项解析规则 回退到上述更高层级的作用域。

数据集属性总表

条形图支持的数据集属性如下(Scriptable 表示可用函数按上下文求值,Indexable 表示可用数组为每个数据点单独赋值,定义见 scriptable optionsindexable options):

名称 类型 Scriptable Indexable 默认值
backgroundColor Color 'rgba(0, 0, 0, 0.1)'
base number 值轴基值
barPercentage number - - 0.9
barThickness number | string - - (未设置)
borderColor Color 'rgba(0, 0, 0, 0.1)'
borderSkipped string | boolean 'start'
borderWidth number | object 0
borderRadius number | object 0
categoryPercentage number - - 0.8
clip number | object | false - - (未设置)
data object | object[] | number[] | string[] - - 必填
grouped boolean - - true
hoverBackgroundColor Color
hoverBorderColor Color
hoverBorderWidth number 1
hoverBorderRadius number 0
indexAxis string - - 'x'
inflateAmount number | 'auto' 'auto'
maxBarThickness number - -
minBarLength number - -
label string - - ''
order number - - 0
pointStyle pointStyle - 'circle'
skipNull boolean - -
stack string - - 'bar'
xAxisID string - - 第一个 x 轴
yAxisID string - - 第一个 y 轴

文档给出的一个典型数据集配置示例:

data: {
    datasets: [{
        barPercentage: 0.5,
        barThickness: 6,
        maxBarThickness: 8,
        minBarLength: 2,
        data: [10, 20, 30, 40, 50, 60, 70]
    }]
};

注意示例中 barThickness: 6maxBarThickness: 8 同时出现时,前者是“精确厚度”,后者是“上限”——从 element.bar.js 与 controller.bar.js 的实现 看,maxBarThickness 通过 Math.min(maxBarThickness, ...) 参与最终宽度计算。

General 通用属性

名称 说明
base 柱子在值轴上的基值(数据单位)。未设置时回退到值轴的基值(base value)。
clip 相对 chartArea 的裁剪方式。正值表示允许向外溢出多少像素,负值表示向内裁剪多少像素,0 表示恰好裁剪在 chartArea 边界。也可按边单独配置:clip: {left: 5, top: false, right: -2, bottom: 0}
grouped 是否按索引轴分组。true 时同一索引值的所有数据集柱子并排放在该索引值两侧居中;false 时每根柱子精确落在自身的索引轴位置上。
indexAxis 数据集的基准轴:'x' 为垂直柱,'y' 为横向柱。
label 数据集标签,显示在图例与 tooltip 中。
order 数据集的绘制顺序,同时影响堆叠、tooltip 与图例的顺序,参见 drawing order
skipNull true 时,null/undefined 值不参与确定柱宽的间距计算。
stack 数据集所属的分组 ID(堆叠图中每个分组构成一个独立的 stack),参见下文 堆叠条形图
xAxisID 该数据集绑定的 x 轴 ID。
yAxisID 该数据集绑定的 y 轴 ID。

indexAxis 的解析逻辑在 core.config.js 中:datasetOptions.indexAxis || options.indexAxis || datasetDefaults.indexAxis || 'x',即数据集级 indexAxis 优先于图表级 options.indexAxisBarController 还提供 getFirstScaleIdForIndexAxis()controller.bar.js 第 493-497 行),按 chart.options.indexAxis 匹配第一个同轴 scale,用于多轴场景下的定位。

Styling 样式属性

名称 说明
backgroundColor 柱体填充色。
borderColor 柱体边框色。
borderSkipped 绘制柱体时跳过哪一边。
borderWidth 柱体边框宽度(像素)。
borderRadius 柱体圆角半径(像素)。
minBarLength 保证柱子的最小像素长度。
pointStyle 图例中点标记的样式,见 point styles

以上值若为 undefined,会回退到对应的 elements.bar.* 选项。BarElement 的默认值 也印证了这一点:borderSkipped: 'start'borderWidth: 0borderRadius: 0inflateAmount: 'auto'

borderSkipped

borderSkipped 用于跳过柱体基座(base)处的描边,或对接触基座的圆角不生效,避免出现“浮空”的边框线。官方建议:除非你在创建派生自 bar 的图表类型,否则无需修改。

注意:垂直图中负值柱子的 topbottom 是翻转的;横向图中 leftright 同理。这一行为对应 controller.bar.js 中的 borderProps():它根据 properties.basex/y 的比较得出 reverse,当 reverse 为真时将 top 映射为 'end'bottom 映射为 'start'

可选取值:

  • 'start' / 'end'
  • 'middle'(仅堆叠柱有效:跳过堆叠相邻柱子之间的边框)
  • 'bottom' / 'left' / 'top' / 'right'
  • false(不跳过任何边)
  • true(跳过所有边)

源码中 setBorderSkipped() 会把它展开为 {top, right, bottom, left} 四方向的布尔对象挂在每个 bar 元素的 borderSkipped 属性上;其中 'middle' 分支只在堆叠上下文中生效:栈顶柱跳过 top 边、栈底柱跳过 bottom 边,中间柱则跳过下方边并启用 enableBorderRadius,从而让整条 stack 的交界处不画边框。

borderWidth

数值形式作用于矩形除 borderSkipped 外的所有边;对象形式可分别指定 leftrighttopbottom,缺省或被跳过的边不绘制。

borderRadius

数值形式作用于矩形除接触 borderSkipped 边之外的四个角(topLeft、topRight、bottomLeft、bottomRight);对象形式可按角单独指定。若 top 边被跳过,topLefttopRight 的圆角也会一并跳过——这正对应 parseBorderRadius()skip.top || skip.left 之类逐角判断逻辑,且每个角的半径会被限制在不超过柱体最小边长(Math.min(maxW, maxH))以内。

堆叠图的圆角行为:当 borderRadius 为数值且图表为堆叠时,圆角只施加在 stack 边缘的柱子上,或浮空(floating)的柱子上;需要覆盖此行为时使用对象语法。实现上,updateElements() 会计算 enableBorderRadius: !stack || isFloatBar(parsed._custom) || (index === stack._top || index === stack._bottom)controller.bar.js 第 410 行)。

inflateAmount

inflateAmount 用于向外膨胀绘制柱子的矩形,典型用途是消除 barPercentage * categoryPercentage = 1 时相邻柱子之间出现的发丝级缝隙。默认值 'auto' 在多数场景可用。源码 setInflateAmount() 中,'auto' 会解析为:当柱宽比例(ratio)为 1 时取 0.33 像素,否则取 0。最终的膨胀绘制发生在 BarElement.draw():先绘制向外膨胀的外层矩形,再叠加填充色为 borderColor 的内层矩形(fill('evenodd') 产生边框效果)。

Interactions 交互属性

名称 说明
hoverBackgroundColor 悬停时的柱体填充色。
hoverBorderColor 悬停时的边框色。
hoverBorderWidth 悬停时的边框宽度(像素)。
hoverBorderRadius 悬停时的圆角半径(像素)。

同为 undefined 时回退到 elements.bar.*。柱体的命中检测由 BarElement 的 inRange/inXRange/inYRange 完成,基于 x/y/base/width/height 计算出的包围盒做区间判断。

barPercentage

柱子在其所属分类(category)可用宽度中所占的百分比(0-1)。取 1.0 时柱子占满整个分类宽度、彼此紧贴。详见下文 barPercentage vs categoryPercentage

categoryPercentage

每个分类(category)在其所属采样间隔(sample)宽度中所占的百分比(0-1)。

barThickness

  • 数值:以像素为单位强制指定每根柱子的宽度。此时 barPercentagecategoryPercentage 被忽略。
  • 'flex':基于前后采样点自动计算基础宽度,使柱子无重叠地占满可用宽度;随后再用 barPercentagecategoryPercentage 微调,比例都为 1 时柱子间无间隙。数据间隔不均匀时会产生宽度不同的柱子。
  • 未设置(默认):使用“防止重叠的最小间隔”计算基础宽度,再用两个百分比参数定宽,此模式下所有柱子等宽。

这三种模式在 controller.bar.js 中一一对应:computeFitCategoryTraits() 处理“未设置/数值”两种情况(数值时 size = thickness * stackCountratio = 1,直接忽略百分比参数,与文档描述一致);computeFlexCategoryTraits() 处理 'flex',取当前像素点与其前后邻居像素的中点距离作为可用宽度(首尾数据点会镜像扩展一倍)。入口在 _calculateBarIndexPixels()options.barThickness === 'flex' ? computeFlexCategoryTraits(...) : computeFitCategoryTraits(...)

maxBarThickness

保证柱子宽度不超过该像素值。源码中通过 Math.min(maxBarThickness, ...) 应用于两种分组/非分组路径(controller.bar.js 第 652、656 行)。

坐标轴(Scale)配置

条形图对关联 scale 的默认值做了两处特殊设置:

名称 类型 默认值 说明
offset boolean true 为 true 时,索引轴两端各增加额外空间,轴按比例缩放到 chartArea 内。
grid.offset boolean true 为 true 时,每个数据点的柱子落在两条网格线之间,网格线左移半个刻度间隔;为 false 时网格线正好穿过柱子中线。

示例:

options = {
    scales: {
        x: {
            grid: {
                offset: true
            }
        }
    }
};

Offset Grid Lines(偏移网格线)

grid.offsettrue 时,特定数据点的柱子落在网格线之间——网格线相对刻度位置平移半个刻度间隔;为 false 时网格线直接穿过柱子中线。对于条形图中的 category scale,该值默认为 true;其他 scale 类型或图表类型默认为 false。这与 BarController.overrides_index_: {offset: true, grid: {offset: true}} 的声明(controller.bar.js 第 284-292 行)互相印证。

全局默认值

如需对“之后创建的所有 bar 图表”应用统一配置,应修改 Chart.overrides.bar。注意:修改全局选项只影响其之后创建的图表,已存在的图表不会被改变。

barPercentage vs categoryPercentage

下图展示两个百分比参数与柱体宽度的关系(Sample 是相邻数据点之间的可用区间,Category 是其中的分类区域,Bar 是其中的柱体):

// categoryPercentage: 1.0
// barPercentage: 1.0
Bar:        | 1.0 | 1.0 |
Category:   |    1.0    |
Sample:     |===========|

// categoryPercentage: 1.0
// barPercentage: 0.5
Bar:          |.5|  |.5|
Category:  |      1.0     |
Sample:    |==============|

// categoryPercentage: 0.5
// barPercentage: 1.0
Bar:             |1.0||1.0|
Category:        |   .5   |
Sample:     |==================|

用一句话说:categoryPercentage 控制“分类区”占采样区比例(影响柱子组与组之间的空隙),barPercentage 控制“柱子”占分类区比例(影响同一分类内多根柱子之间的空隙)。

数据结构

所有受支持的数据结构(基本数组、数组对、对象数组等)都可用于条形图。其中 [start, end] 形式的浮空柱(floating bar)由 controller.bar.js 的 parseFloatBar() 专门解析:它计算 min/max,当 |min| > |max| 时交换 barStart/barEnd,使 barEnd 始终保存“离原点更远的一端”,从而让堆叠计算保持简单。tooltip 显示上,getLabelAndValue() 对浮空柱返回 [start, end] 区间文本。仓库中 docs/samples/bar/floating.md 提供了浮空柱的完整示例。

堆叠条形图 Stacked Bar Chart

将 x 轴与 y 轴的 stacked 都设为 true 即可把条形图切换为堆叠模式,用于展示一个数据系列由哪些更小部分构成:

const stackedBar = new Chart(ctx, {
    type: 'bar',
    data: data,
    options: {
        scales: {
            x: {
                stacked: true
            },
            y: {
                stacked: true
            }
        }
    }
});

数据集上的 stack 属性用于指定堆叠分组:每个不同 stack ID 构成一个独立的 stack,多组 stack 会并排显示。仓库中的示例:

从源码看,分组数量由 _getStacks() 依据各可见数据集的 meta.stack 与索引轴的 stacked 选项计算得出(stacked === false 时每个数据集各自成栈,否则按 stack ID 归并),再参与 _calculateBarIndexPixels()chunk = size / stackCount 的宽度均分。堆叠上下文中还有两个值得留意的行为:borderSkipped: 'middle' 的交界边框跳过,以及 minBarLength 生效后写入 parsed._stacks[vScale.axis]._visualValues 的可视化数值补偿(controller.bar.js 第 613-616 行),保证 tooltip 读数与视觉高度一致。

横向条形图 Horizontal Bar Chart

横向条形图是垂直图的镜像变体。只需把 indexAxis 设为 'y'(默认 'x',即垂直柱):

const config = {
  type: 'bar',
  data,
  options: {
    indexAxis: 'y',
  }
};

横向条形图的配置项与垂直条形图完全相同,只是原来施加在 x 轴上的选项,现在施加在 y 轴上(因为索引轴与值轴互换了角色)。BarController 中大量 horizontal 分支(例如 updateElements() 中 x/y/width/height 的互换,以及 borderProps() 中起点/终点边取 'left'/'right')体现了这一镜像逻辑。完整示例见 docs/samples/bar/horizontal.md,其中还演示了通过 options.elements.bar.borderWidth: 2 统一设置所有横条的边框宽度,以及把图例放到右侧(plugins.legend.position: 'right')等常见搭配。

内部数据格式

条形图解析后的内部格式为 {x, y, _custom},其中 _custom 是可选对象,仅浮空柱会生成,内容为:

{ start, end, barStart, barEnd, min, max }

startend 是输入值;barStart(靠近原点)、barEnd(远离原点)、minmax 则是对输入值的重新排序副本。该结构由 parseFloatBar() 写入 item._customisFloatBar() 通过检查 barStart/barEnd 是否存在来判断是否浮空柱,并被 updateRangeFromParsed()(保证坐标轴范围覆盖 min/max 两端)、_calculateBarValuePixels()(浮空柱跳过堆叠)等路径复用。

数据验证与像素精度

条形图的两类核心行为均有测试佐证:

  • 单元/功能测试:test/specs/controller.bar.tests.js(覆盖 borderSkippedminBarLength 等行为);
  • 像素级回归快照:test/fixtures/controller.bar/ 目录,其中 bar-thickness-* 系列(如 bar-thickness-flex.jsonbar-thickness-max.jsonbar-thickness-per-dataset-stacked.json)分别对应当前文的数值厚度、maxBarThickness、按数据集堆叠厚度等场景;stacking/floatBar/borderRadius/skipNull/ 子目录则覆盖堆叠、浮空柱、圆角与空值跳过。

此外,minBarLength 的完整实现(controller.bar.js 第 601-617 行)值得注意:它把短于最小长度的柱子“拉伸”到最小长度,并把柱子夹在值轴像素范围内;当柱子值恰好等于基值时还会把基线向两侧对半偏移,避免出现负长度。

小结

  • 条形图的核心调节旋钮只有五个:barPercentagecategoryPercentagebarThickness(含 'flex')、maxBarThicknessminBarLength;它们最终都汇入 ruler + computeFitCategoryTraits/computeFlexCategoryTraits 的像素计算;
  • 样式细节(borderSkippedborderWidthborderRadiusinflateAmount)都有明确的“跳过/膨胀”规则,负值柱与堆叠柱有额外的方向翻转和边缘特殊处理;
  • 堆叠靠轴级 stacked: true + 数据集级 stack 分组,横向图靠 indexAxis: 'y' 且轴选项随之镜像;
  • 浮空柱 [start, end] 数据会产生 _custom 内部结构,影响堆叠、tooltip 与坐标轴范围。
登录后查看全文
热门项目推荐
相关项目推荐