Chart.js 线性数值轴(Linear Scale)详解:stepSize、count、grace 与 beginAtZero 的完整配置实战
本篇技术指南聚焦 Chart.js 中用于绘制数值数据的线性坐标轴(type: 'linear'),系统讲解轴级与刻度级的全部配置选项,包括 stepSize、count、grace、beginAtZero 等线性轴专属参数的用法与默认值,并结合仓库源码剖析刻度生成的“nice numbers”算法、像素与数值的线性映射实现,读完后可独立完成数值轴的自定义量程、刻度密度控制与留白设置。
线性轴的定位与基本特性
线性轴用于在坐标轴上绘制数值型数据,可以放在 x 轴或 y 轴上;散点图(scatter)类型会自动为 x 轴配置一个线性轴。顾名思义,线性轴使用线性插值来确定一个数值在轴上的位置。
从源码 src/scales/scale.linear.js 可以确认这一特性,LinearScale 的类注册名为 id = 'linear',其核心方法就是一组线性映射:
getPixelForValue(value) {
return value === null ? NaN : this.getPixelForDecimal((value - this._startValue) / this._valueRange);
}
getValueForPixel(pixel) {
return this._startValue + this.getDecimalForPixel(pixel) * this._valueRange;
}
即:数据值先换算为 [0, 1] 的“小数位置”(getPixelForDecimal),再映射为像素坐标;反向的 getValueForPixel 则用 _startValue 与 _valueRange 做线性还原。这也解释了文档末尾的说明——内部数据格式为纯数字(Internally, the linear scale uses numeric data),解析阶段会把原始值强制转为 +raw(见基类 parse() 方法,src/scales/scale.linearbase.js)。
线性轴还内置了数值格式的默认刻度标签回调:
static defaults = {
ticks: {
callback: Ticks.formatters.numeric
}
};
对应的刻度文本由 getLabelForValue 生成,底层调用 Intl.NumberFormat(formatNumber),这正是 ticks.format 选项能够生效的原因。
轴级配置选项(Axis Options)
轴级配置的命名空间为 options.scales[scaleId],例如 options.scales.y。
线性轴专属选项
| 名称 | 类型 | 说明 |
|---|---|---|
beginAtZero |
boolean |
为 true 时,若量程中不含 0,则强制把 0 纳入量程 |
grace |
number | string |
在数据范围上下方额外增加的余量:以 % 结尾的字符串表示百分比,数字表示绝对数值。详见 grace |
beginAtZero 的实际处理逻辑在基类方法 handleTickRangeOptions() 中(src/scales/scale.linearbase.js):根据当前 min/max 的符号决定向哪一侧扩到 0——若数据全为负数则把 max 抬到 0,全为正数则把 min 压到 0;同时该方法还会处理 min === max 的退化情况(自动外扩约 5% 的范围以避免轴塌缩成一条线)。
所有笛卡尔轴(Cartesian)共享选项
| 名称 | 类型 | 默认值 | 说明 |
|---|---|---|---|
bounds |
string |
'ticks' |
决定量程如何由刻度决定。更多细节见 Scale Bounds |
clip |
boolean |
true |
为 true 时,按坐标轴尺寸(而非 chart area)裁剪数据集绘制 |
position |
string | object |
轴的位置。更多细节见 Axis Position | |
stack |
string |
堆叠组。position 与 stack 都相同的轴会被堆叠在一起 |
|
stackWeight |
number |
1 |
堆叠组内该轴所占空间分配的权重 |
axis |
string |
轴类型:'x' 或 'y'。未设置时从 ID 首字符推断(应为首字母 'x' 或 'y') |
|
offset |
boolean |
false |
为 true 时在轴两端各增加一段空白,轴整体缩放到 chart area 内;柱状图默认开启。源码中该偏移在 configure() 中按 (end - start) / (ticks.length - 1) / 2 计算(src/scales/scale.linearbase.js) |
title |
object |
轴标题配置。更多细节见 Scale Title Configuration |
所有坐标轴共享选项
| 名称 | 类型 | 默认值 | 说明 |
|---|---|---|---|
type |
string |
使用的轴类型;自定义轴可通过字符串键注册 | |
alignToPixels |
boolean |
false |
是否把像素值对齐到设备像素 |
backgroundColor |
Color | 轴区域背景色 | |
border |
object |
边框配置,见 Border Configuration | |
display |
boolean | string |
true |
控制轴可见性;display: 'auto' 时仅当至少有一个关联数据集可见时才显示 |
grid |
object |
网格线配置,见 Grid Line Configuration | |
min |
number |
用户自定义最小值,覆盖数据推断的最小值。见 Axis Range Settings | |
max |
number |
用户自定义最大值,覆盖数据推断的最大值 | |
reverse |
boolean |
false |
反转刻度方向 |
stacked |
boolean | string |
false |
是否堆叠数据 |
suggestedMax |
number |
计算数据最大值时的参考值(不强制截断量程)。见 Axis Range Settings | |
suggestedMin |
number |
计算数据最小值时的参考值 | |
ticks |
object |
刻度配置,见 Tick Configuration | |
weight |
number |
0 |
轴排序权重,数值越大的轴离 chart area 越远 |
刻度配置选项(Tick Configuration)
刻度级配置的命名空间为 options.scales[scaleId].ticks。
线性轴专属刻度选项
| 名称 | 类型 | Scriptable | 默认值 | 说明 |
|---|---|---|---|---|
count |
number |
Yes | undefined |
生成的刻度总数。指定后覆盖自动生成逻辑 |
format |
object |
Yes | 默认标签格式化器使用的 Intl.NumberFormat 选项 |
|
precision |
number |
Yes | 若定义且未指定 stepSize,步长会被四舍五入到该小数位数 |
|
stepSize |
number |
Yes | 用户指定的固定步长。更多细节见 Step Size |
笛卡尔轴共享刻度选项
| 名称 | 类型 | 默认值 | 说明 |
|---|---|---|---|
align |
string |
'center' |
沿轴方向的刻度对齐方式:'start'、'center'、'end' 或 'inner'(水平轴上首个刻度按 start、末个按 end 对齐) |
crossAlign |
string |
'near' |
垂直于轴方向的刻度对齐:'near'、'center'、'far',见 Tick Alignment |
sampleSize |
number |
ticks.length |
判断能容纳多少标签时采样的刻度数,值越小越快但精度可能下降 |
autoSkip |
boolean |
true |
自动计算可显示的标签数量并隐藏多余标签;标签最多旋转到 maxRotation 之后才开始跳显 |
autoSkipPadding |
number |
3 |
水平轴上启用 autoSkip 时的刻度间最小间距 |
includeBounds |
boolean |
true |
自定义的 min/max 即使不是“整齐”值,也要作为刻度显示 |
labelOffset |
number |
0 |
标签相对刻度中心点的偏移像素(x 轴为 x 方向,y 轴为 y 方向),注意边缘标签可能被画布裁掉 |
maxRotation |
number |
50 |
压缩标签时的最大旋转角度(仅水平轴) |
minRotation |
number |
0 |
最小旋转角度(仅水平轴) |
mirror |
boolean |
false |
把刻度标签翻到轴内侧显示(仅垂直轴) |
padding |
number |
0 |
刻度标签与轴之间的距离;垂直轴上是水平(X)方向,水平轴上是垂直(Y)方向 |
maxTicksLimit |
number |
11 |
最多显示的刻度与网格线数量 |
所有轴共享刻度选项
| 名称 | 类型 | Scriptable | 默认值 | 说明 |
|---|---|---|---|---|
backdropColor |
Color | Yes | 'rgba(255, 255, 255, 0.75)' |
标签背景色 |
backdropPadding |
Padding | 2 |
标签背景内边距 | |
callback |
function |
返回刻度值的显示文本,见 Creating Custom Tick Formats | ||
display |
boolean |
true |
是否显示刻度标签 | |
color |
Color | Yes | Chart.defaults.color |
刻度文字颜色 |
font |
Font |
Yes | Chart.defaults.font |
字体,见 Fonts |
major |
object |
{} |
主刻度(major ticks)样式配置,见 Major Tick Configuration | |
showLabelBackdrop |
boolean |
Yes | 径向轴为 true,其余为 false |
是否在刻度标签后绘制背景 |
textStrokeColor |
Color | Yes | `` | 文字描边颜色 |
textStrokeWidth |
number |
Yes | 0 |
文字描边宽度 |
z |
number |
0 |
刻度图层的 z-index;<= 0 绘制在数据集下方,> 0 绘制在上方 |
Step Size:固定步长控制刻度
设置 stepSize 后,刻度将按 stepSize 的整数倍枚举,每个增量一个刻度;不设置时则使用 nice numbers 算法自动标注。
文档示例:将 y 轴固定为 0, 0.5, 1, 1.5, 2, 2.5, 3, 3.5, 4, 4.5, 5:
let options = {
scales: {
y: {
max: 5,
min: 0,
ticks: {
stepSize: 0.5
}
}
}
};
从源码可以确认该行为的实现细节。LinearScaleBase 的 buildTicks() 把 stepSize、count、precision、min、max、bounds、includeBounds 等一并打包传给 generateTicks()(src/scales/scale.linearbase.js),该函数按以下优先级处理(见其头部注释,src/scales/scale.linearbase.js):
- 同时定义了
min、max、stepSize且(max - min) / step为整数:刻度生成为[min, min + step, ..., max],且受maxCount约束; - 定义了
min、max、count:spacing = (max - min) / count,刻度为[min, min + spacing, ..., max]; - 只定义了
count:spacing = (niceMax - niceMin) / count; - 以上皆无:使用 niceNum 算法计算最优步长。
另外两点值得注意:
precision的生效位置在generateTicks()内部:spacing = Math.ceil(spacing * 10^precision) / 10^precision(src/scales/scale.linearbase.js),且仅当未指定stepSize时才有意义(因为stepSize直接作为unit参与 niceNum 计算);- 若
stepSize过小导致刻度数超过 1000,getTickLimit()会打印警告并截断为 1000 个刻度(src/scales/scale.linearbase.js)。
Grace:给数据范围增加上下留白
grace 的取值规则:以 % 结尾的字符串按百分比处理,数字按绝对值处理。其效果是把该值加到最大数据值上、从最小数据值上减去,相当于数据比实际“更大”时延伸出量程。
文档示例:数据为 [100, -50],设置 grace: '5%',量程会在 100 与 -50 上下各外扩 5%(相对范围的一半计算,见下文源码)。
const labels = Utils.months({count: 7});
const data = {
labels: ['Positive', 'Negative'],
datasets: [{
data: [100, -50],
backgroundColor: 'rgb(255, 99, 132)'
}],
};
const config = {
type: 'bar',
data,
options: {
scales: {
y: {
type: 'linear',
grace: '5%'
}
},
plugins: {
legend: false
}
}
};
module.exports = {
actions: [],
config: config,
};
该功能的核心实现在 src/helpers/helpers.options.ts 的 _addGrace():
export function _addGrace(minmax: { min: number; max: number; }, grace: number | string, beginAtZero: boolean) {
const {min, max} = minmax;
const change = toDimension(grace, (max - min) / 2);
const keepZero = (value: number, add: number) => beginAtZero && value === 0 ? 0 : value + add;
return {
min: keepZero(min, -Math.abs(change)),
max: keepZero(max, change)
};
}
可以观察到两个实现细节:
- 百分比的基准是
(max - min) / 2,即量程的一半,toDimension()负责把'5%'换算为对应数值、把数字直接当作绝对值; - 当
beginAtZero: true且边界恰好是 0 时,keepZero()会把 0 固定住,grace 只向外扩展,不会把 0 挤出量程。
该调用发生在 Scale 的范围确定流程中(src/core/core.scale.js:this._range = _addGrace(this, grace, beginAtZero)),即 grace 调整的是“数据范围”,而最终的刻度仍会基于这个扩展后的范围由 nice numbers 算法生成。
刻度数量的上限控制:computeTickLimit
线性轴还内置了一套基于轴长的刻度密度控制。LinearScale.computeTickLimit()(src/scales/scale.linear.js)按轴向长度、刻度最小旋转角与刻度字体行高计算最多能放下的刻度数,最小间距按 40 像素约束;buildTicks() 再将其与 ticks.maxTicksLimit(默认 11)取小,且保证至少 2 个刻度。若配置了 stepSize,则刻度上限直接由 (max - min) / stepSize 推导,maxTicksLimit 只起上限作用。
内部数据格式小结
线性轴内部使用数值数据:原始值在 parse() 中被转换为 +raw,非有限数(NaN/Infinity)或空值返回 null;determineDataLimits() 在数据范围缺失时回退为 min: 0, max: 1(src/scales/scale.linear.js)。这一约定保证了无论数据形态如何,轴都能得到一个可用的数值区间,再交由上述范围、grace、stepSize 等配置进一步调整。
参考文件:线性轴文档、Cartesian 轴通用文档、轴级通用文档、src/scales/scale.linear.js、src/scales/scale.linearbase.js、src/helpers/helpers.options.ts。
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 StartedRust0623
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