Chart.js Bar Chart 完全指南:数据集属性、柱宽计算、堆叠与横向柱状图
本篇基于 Chart.js 仓库中的 docs/charts/bar.md 官方文档,完整讲解条形图(bar chart)的配置体系:数据集属性全表、柱宽与间距的五个控制参数、borderSkipped/borderRadius 等样式细节、堆叠与横向柱状图的实现方式,并结合 src/controllers/controller.bar.js 与 src/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' 会在注册表中解析到 BarController(static 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.8、barPercentage 默认 0.9、grouped 默认 true(见 controller.bar.js 第 265-279 行)。同时 static overrides 声明了条形图对坐标轴的特有默认值:索引轴使用 category 类型且 offset: true、grid.offset: true,值轴使用 linear 类型且 beginAtZero: true(controller.bar.js 第 284-298 行)。
命名空间与选项解析
条形图的选项可以在四个层级上指定,解析优先级从低到高为:
options— 整个图表的选项;options.elements.bar— 所有 bar 元素 的选项;options.datasets.bar— 所有 bar 数据集的选项;data.datasets[index]— 仅当前数据集的选项。
文档强调:数据集中只有 data 一项是必填的,其余展示属性(颜色、边框、圆角等)通常就定义在数据集命名空间里。所有值为 undefined 的项会按 选项解析规则 回退到上述更高层级的作用域。
数据集属性总表
条形图支持的数据集属性如下(Scriptable 表示可用函数按上下文求值,Indexable 表示可用数组为每个数据点单独赋值,定义见 scriptable options 与 indexable 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: 6 与 maxBarThickness: 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.indexAxis。BarController 还提供 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: 0、borderRadius: 0、inflateAmount: 'auto'。
borderSkipped
borderSkipped 用于跳过柱体基座(base)处的描边,或对接触基座的圆角不生效,避免出现“浮空”的边框线。官方建议:除非你在创建派生自 bar 的图表类型,否则无需修改。
注意:垂直图中负值柱子的 top 与 bottom 是翻转的;横向图中 left 与 right 同理。这一行为对应 controller.bar.js 中的 borderProps():它根据 properties.base 与 x/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 外的所有边;对象形式可分别指定 left、right、top、bottom,缺省或被跳过的边不绘制。
borderRadius
数值形式作用于矩形除接触 borderSkipped 边之外的四个角(topLeft、topRight、bottomLeft、bottomRight);对象形式可按角单独指定。若 top 边被跳过,topLeft 与 topRight 的圆角也会一并跳过——这正对应 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
- 数值:以像素为单位强制指定每根柱子的宽度。此时
barPercentage与categoryPercentage被忽略。 'flex':基于前后采样点自动计算基础宽度,使柱子无重叠地占满可用宽度;随后再用barPercentage与categoryPercentage微调,比例都为 1 时柱子间无间隙。数据间隔不均匀时会产生宽度不同的柱子。- 未设置(默认):使用“防止重叠的最小间隔”计算基础宽度,再用两个百分比参数定宽,此模式下所有柱子等宽。
这三种模式在 controller.bar.js 中一一对应:computeFitCategoryTraits() 处理“未设置/数值”两种情况(数值时 size = thickness * stackCount、ratio = 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.offset 为 true 时,特定数据点的柱子落在网格线之间——网格线相对刻度位置平移半个刻度间隔;为 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 会并排显示。仓库中的示例:
- docs/samples/bar/stacked.md — 基础堆叠;
- docs/samples/bar/stacked-groups.md — 按
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 }
start 与 end 是输入值;barStart(靠近原点)、barEnd(远离原点)、min、max 则是对输入值的重新排序副本。该结构由 parseFloatBar() 写入 item._custom,isFloatBar() 通过检查 barStart/barEnd 是否存在来判断是否浮空柱,并被 updateRangeFromParsed()(保证坐标轴范围覆盖 min/max 两端)、_calculateBarValuePixels()(浮空柱跳过堆叠)等路径复用。
数据验证与像素精度
条形图的两类核心行为均有测试佐证:
- 单元/功能测试:test/specs/controller.bar.tests.js(覆盖
borderSkipped、minBarLength等行为); - 像素级回归快照:test/fixtures/controller.bar/ 目录,其中
bar-thickness-*系列(如bar-thickness-flex.json、bar-thickness-max.json、bar-thickness-per-dataset-stacked.json)分别对应当前文的数值厚度、maxBarThickness、按数据集堆叠厚度等场景;stacking/、floatBar/、borderRadius/、skipNull/子目录则覆盖堆叠、浮空柱、圆角与空值跳过。
此外,minBarLength 的完整实现(controller.bar.js 第 601-617 行)值得注意:它把短于最小长度的柱子“拉伸”到最小长度,并把柱子夹在值轴像素范围内;当柱子值恰好等于基值时还会把基线向两侧对半偏移,避免出现负长度。
小结
- 条形图的核心调节旋钮只有五个:
barPercentage、categoryPercentage、barThickness(含'flex')、maxBarThickness、minBarLength;它们最终都汇入ruler+computeFitCategoryTraits/computeFlexCategoryTraits的像素计算; - 样式细节(
borderSkipped、borderWidth、borderRadius、inflateAmount)都有明确的“跳过/膨胀”规则,负值柱与堆叠柱有额外的方向翻转和边缘特殊处理; - 堆叠靠轴级
stacked: true+ 数据集级stack分组,横向图靠indexAxis: 'y'且轴选项随之镜像; - 浮空柱
[start, end]数据会产生_custom内部结构,影响堆叠、tooltip 与坐标轴范围。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0627
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00