入门
安装与第一个图表
Chart.js 在 canvas 元素上渲染。type 属性设置图表类型。data 包含 labels 和 datasets, 后者携带数值和样式。
<!-- include Chart.js -->
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<canvas id="myChart" width="400" height="400"></canvas>
<script>
const ctx = document.getElementById('myChart').getContext('2d');
new Chart(ctx, {
type: 'bar',
data: {
labels: ['Red', 'Blue', 'Green'],
datasets: [{
label: 'Votes',
data: [12, 19, 3],
backgroundColor: ['red', 'blue', 'green']
}]
}
});
</script>npm 安装与 ES 模块导入
使用 'chart.js/auto' 是最简单的方案——它导入 Chart.js 以及所有控制器/元素并自动注册。生产环境要更小的打包体积,推荐 tree-shaking:从 'chart.js' 导入并只注册你用到的部分。
# install
npm install chart.js
// import the auto-bundled build (registers everything)
import Chart from 'chart.js/auto';
const ctx = document.getElementById('myChart');
const chart = new Chart(ctx, {
type: 'line',
data: { /* ... */ },
options: { responsive: true }
});Canvas 与 2D 上下文
Chart.js 既接受 <canvas> 元素,也接受其 2d 渲染上下文。当 responsive 为 true 时,canvas 会被缩放以填满容器,因此 width/height HTML 属性仅作降级使用。每个 canvas 只能承载一个 Chart 实例。
// Chart.js accepts a canvas element OR a 2D context
const canvas = document.getElementById('myChart');
const chart = new Chart(canvas, { type: 'bar', data: {...} });
// equivalent: pass the 2d context
const ctx = canvas.getContext('2d');
const chart2 = new Chart(ctx, { type: 'bar', data: {...} });
// one chart per canvas; the canvas size is controlled by
// responsive options, NOT the width/height attributes可用图表类型
Chart.js 内置八种图表类型。bar、line、scatter、bubble 使用笛卡尔坐标轴;pie、doughnut、polarArea 是圆形图表;radar 有自己的径向轴。顶层 'type' 字段为所有 dataset 选择控制器,除非某个 dataset 用自己的 type 覆盖。
// the 8 built-in chart types
const types = ['bar', 'line', 'pie', 'doughnut',
'radar', 'polarArea', 'bubble', 'scatter'];
// each type maps to a controller:
// bar/line -> Cartesian controllers
// pie/doughnut/polarArea -> circular controllers
// radar -> radar controller
// bubble/scatter -> cartesian with point parsing
new Chart(ctx, { type: 'polarArea', data: {...} });Tree-shaking 与注册
从 'chart.js'(而非 /auto)导入时不会自动注册任何东西,因此必须自行注册控制器、元素、比例尺和插件。这种 tree-shakeable 方式能显著减小打包体积。忘记注册组件是最常见的 'scale is not a registered scale' 报错来源。
import {
Chart,
BarController,
BarElement,
CategoryScale,
LinearScale,
Legend,
Title,
Tooltip,
} from 'chart.js';
// register ONLY what you use -> smaller bundle
Chart.register(
BarController, BarElement,
CategoryScale, LinearScale,
Legend, Title, Tooltip,
);
new Chart(ctx, {
type: 'bar',
data: { labels: ['A', 'B'], datasets: [{ data: [1, 2] }] },
});销毁与生命周期
每个 Chart 实例独占其 canvas 及附加的事件监听器。调用 destroy() 会把一切清理干净以便复用 canvas——这在 SPA 和 React/Vue effect 中必不可少。在仍活跃的 canvas 上创建第二个图表会导致内存泄漏并出现重复渲染。
const chart = new Chart(ctx, config);
// ...later, to re-render with new config:
chart.destroy(); // cleans up listeners and canvas
const fresh = new Chart(ctx, newConfig);
// never create two Chart instances on the same canvas;
// always destroy() the old one first.柱状图
基础柱状图
柱状图把每个 label 映射为一根柱子,柱高等于数据值。backgroundColor 设置柱体填充,borderColor/borderWidth 设置轮廓。设置 y.beginAtZero 让柱子始终从零开始——否则 Chart.js 可能自动缩放坐标轴下限,在视觉上扭曲差异。
new Chart(ctx, {
type: 'bar',
data: {
labels: ['Jan', 'Feb', 'Mar', 'Apr', 'May'],
datasets: [{
label: 'Revenue',
data: [12, 19, 7, 15, 22],
backgroundColor: 'rgba(54, 162, 235, 0.6)',
borderColor: 'rgba(54, 162, 235, 1)',
borderWidth: 1
}]
},
options: { scales: { y: { beginAtZero: true } } }
});水平柱状图(indexAxis)
Chart.js v3+ 通过在 'bar' 类型上设置 indexAxis: 'y' 创建水平柱状图——旧的 'horizontalBar' 类型已被移除。x 比例尺变为数值轴。水平柱状图适合在垂直图上会重叠的长分类标签。
new Chart(ctx, {
type: 'bar',
data: {
labels: ['Task A', 'Task B', 'Task C'],
datasets: [{ label: 'Hours', data: [5, 8, 3] }]
},
options: {
indexAxis: 'y', // horizontal bars (v3+ syntax)
scales: { x: { beginAtZero: true } }
}
});分组柱状图(多 dataset)
柱状图中的多个 dataset 默认并排放置(分组)。仅当你 省略 backgroundColor 时 Chart.js 才会自动分配颜色,因此为每个 dataset 显式设置颜色以获得可预测的输出。每个 dataset 共享 x 轴上同一个 labels 数组。
new Chart(ctx, {
type: 'bar',
data: {
labels: ['Q1', 'Q2', 'Q3', 'Q4'],
datasets: [
{ label: '2023', data: [20, 35, 30, 35],
backgroundColor: 'rgba(255,99,132,0.6)' },
{ label: '2024', data: [25, 32, 34, 40],
backgroundColor: 'rgba(54,162,235,0.6)' }
]
},
options: { scales: { y: { beginAtZero: true } } }
});堆叠柱状图
堆叠柱状图要求 x 和 y 两个比例尺都设 stacked: true。每个 dataset 的柱子绘制在前一个之上,累加成总和。堆叠展示构成,但相比分组柱状图,上层序列在分类间更难比较。
new Chart(ctx, {
type: 'bar',
data: {
labels: ['A', 'B', 'C'],
datasets: [
{ label: 'Men', data: [20, 35, 30], backgroundColor: '#36A2EB' },
{ label: 'Women', data: [25, 32, 34], backgroundColor: '#FF6384' }
]
},
options: {
scales: {
x: { stacked: true },
y: { stacked: true, beginAtZero: true }
}
}
});柱体样式
borderRadius 给柱角加圆角(单个数字作用于所有角;对象如 {topLeft:8} 可指定特定角)。borderSkipped 默认为 'start',会省略一条边——设为 false 可绘制全部四条边。barPercentage 与 categoryPercentage 共同控制柱体粗细和间距。
new Chart(ctx, {
type: 'bar',
data: {
labels: ['A', 'B', 'C'],
datasets: [{
data: [10, 20, 15],
backgroundColor: '#FF6384',
borderColor: '#fff',
borderWidth: 2,
borderRadius: 8, // rounded corners (px or %)
borderSkipped: false, // draw border on all sides
barPercentage: 0.8, // bar width within category
categoryPercentage: 0.7 // category width
}]
}
});浮动柱([min, max])
把 [min, max] 配对作为数据值会产生不从零开始的浮动柱——非常适合日温差低-高、开盘-收盘或置信区间等范围数据。范围柱的 y 轴不应从零开始。水平柱也支持此用法。
new Chart(ctx, {
type: 'bar',
data: {
labels: ['Mon', 'Tue', 'Wed'],
datasets: [{
label: 'Low-High temp',
// each value is [min, max] -> a floating bar
data: [[10, 18], [12, 20], [8, 15]],
backgroundColor: 'rgba(75,192,192,0.6)'
}]
},
options: { scales: { y: { beginAtZero: false } } }
});折线图
基础折线图
折线图按顺序连接数据点。borderColor 是线条颜色;backgroundColor 用于填充(启用 fill 时)。tension(0-1)控制曲线平滑度——0 为直线段,约 0.3-0.4 给出柔和的贝塞尔曲线。对股价等精确数据用 0。
new Chart(ctx, {
type: 'line',
data: {
labels: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri'],
datasets: [{
label: 'Visitors',
data: [30, 45, 28, 60, 52],
borderColor: 'rgba(75,192,192,1)',
backgroundColor: 'rgba(75,192,192,0.2)',
tension: 0.3
}]
},
options: { scales: { y: { beginAtZero: true } } }
});多条折线
折线图中每个 dataset 成为一条折线,从默认调色板自动分配颜色(可用 borderColor 覆盖)。折线按 dataset 顺序绘制,最后一个 dataset 渲染在最上层。给每个 dataset 传 label 以便图例识别。
new Chart(ctx, {
type: 'line',
data: {
labels: ['Jan', 'Feb', 'Mar', 'Apr'],
datasets: [
{ label: 'Plan', data: [10, 20, 25, 30], borderColor: 'red' },
{ label: 'Actual', data: [8, 22, 24, 33], borderColor: 'blue' }
]
},
options: {
scales: { y: { beginAtZero: true } },
plugins: { legend: { position: 'top' } }
}
});线条张力与插值
tension 添加贝塞尔平滑,但可能越过数据点(在最小值下方制造凹陷)。cubicInterpolationMode: 'monotone' 平滑且不越界——适合股价等单调数据。borderDash 是 [实线, 间隔] 模式,用于虚线/点线。
datasets: [{
data: [1, 5, 2, 6, 3],
tension: 0, // straight lines
// tension: 0.4, // smooth bezier
cubicInterpolationMode: 'monotone', // no overshoot
borderDash: [5, 5], // dashed line
borderWidth: 2,
borderColor: 'purple'
}]填充折线(fill)
fill 控制折线下方的区域。true/'origin' 向下填充到 x 轴。数字(1、-1、+1)向另一个 dataset(按索引)填充,适合带状图。负数向前一个 dataset 填充。设置带 alpha 的 backgroundColor,使折线在填充下仍可见。
datasets: [
{ label: 'A', data: [3,5,4,6], fill: true, backgroundColor: 'rgba(255,99,132,0.3)' },
{ label: 'B', data: [1,2,3,2], fill: 'origin', backgroundColor: 'rgba(54,162,235,0.3)' },
{ label: 'C', data: [2,4,3,5], fill: 1, backgroundColor: 'rgba(75,192,192,0.3)' }
]
// fill values: false | true/'origin' | 1/-1/+1 | '-1' | {target:...}数据点样式
point 属性接受单个值或数组(每个数据点一个)。pointStyle 支持多种形状('circle'、'rect'、'rectRot'、'triangle'、'star'、'cross'、'crossRot')。pointRadius: 0 可完全隐藏数据点(适合密集折线图),pointHoverRadius 设大些提供交互弹出效果。
datasets: [{
data: [4, 6, 5, 7],
showLine: true,
pointRadius: 5, // point size (0 hides points)
pointHoverRadius: 9,
pointBackgroundColor: 'white',
pointBorderColor: 'black',
pointBorderWidth: 2,
pointStyle: 'rectRot', // 'circle','rect','triangle','star','cross'...
// per-point arrays also work:
// pointRadius: [0, 0, 6, 0],
}]跨越间隙与缺失数据
null、undefined 和 NaN 表示缺失数据,默认会断开折线。spanGaps: true 用单段线段连接间隙两侧的点。区分 NaN(缺失)与 0(真实零值)——混淆两者是误导性图表的常见来源。
datasets: [{
data: [5, null, 7, NaN, 9, undefined, 11],
spanGaps: true, // connect across null/NaN gaps
// spanGaps: false -> the line breaks at missing points
}]
// also skip points entirely by setting them to NaN
// NaN is "no data"; 0 is a legitimate zero value饼图与环形图
基础饼图
饼图展示整体的各部分:每个值变成一个扇形,其角度与所占份额成正比。为每个切片提供一个 backgroundColor(数组),否则 Chart.js 会循环默认调色板。饼图最适合 3-6 个分类——更多会变得难以阅读。
new Chart(ctx, {
type: 'pie',
data: {
labels: ['Rent', 'Food', 'Fun', 'Save'],
datasets: [{
data: [1200, 600, 400, 800],
backgroundColor: ['#FF6384', '#36A2EB', '#FFCE56', '#4BC0C0']
}]
}
});基础环形图
环形图就是中间有洞的饼图。cutout 属性(百分比字符串或像素数)控制洞的大小——'50%' 是经典甜甜圈外观。中间的洞可通过插件放置汇总标签,环长在编码数值上略优于扇形角度。
new Chart(ctx, {
type: 'doughnut',
data: {
labels: ['A', 'B', 'C'],
datasets: [{
data: [40, 35, 25],
backgroundColor: ['#FF6384', '#36A2EB', '#FFCE56'],
cutout: '50%' // size of the inner hole
}]
}
});cutout、rotation 与 circumference
rotation 设置起始角度(度,默认 0 = 右;-90 = 上)。circumference 控制绘制多少圆——180 制造半环形,适合仪表盘式图表。v3+ 中这两者都位于 options 下(而非 dataset),尽管 cutout 可出现在任一处。
new Chart(ctx, {
type: 'doughnut',
data: { labels: ['A','B','C'], datasets: [{ data: [30,40,30], backgroundColor: ['#f00','#0f0','#00f'] }] },
options: {
cutout: '60%',
rotation: -90, // start at top (degrees)
circumference: 360 // full circle; 180 = half doughnut
}
});边框与偏移
borderColor 配合 borderWidth 在视觉上分隔扇形(白色边框给出干净的分段外观)。hoverOffset 在用户悬停时把切片向外推,提供清晰的交互反馈。offset 是数组(每切片一个),用于永久性地将特定扇形「炸开」以示强调。
datasets: [{
data: [30, 40, 30],
backgroundColor: ['#FF6384', '#36A2EB', '#FFCE56'],
borderColor: 'white',
borderWidth: 3,
hoverOffset: 16, // pop slice out on hover
offset: [0, 20, 0] // permanently offset 2nd slice
}]半环形(仪表盘)
半环形(rotation: -90, circumference: 180)读作仪表/进度计。配合中心文字插件显示百分比,并禁用图例以获得干净的 KPI 外观。中性灰色的「剩余」切片传达未完成的部分。
new Chart(ctx, {
type: 'doughnut',
data: {
labels: ['Done', 'Remaining'],
datasets: [{
data: [72, 28],
backgroundColor: ['#4BC0C0', '#E0E0E0']
}]
},
options: {
rotation: -90,
circumference: 180,
plugins: { legend: { display: false } }
}
});工具提示百分比回调
饼图/环形图工具提示默认显示原始值。使用 tooltip callbacks.label 计算并显示百分比——ctx.parsed 是切片值,ctx.dataset.data 持有求和所需的全部值。回调中的模板字符串在本速查表的代码字符串中必须转义反引号和 ${}。
options: {
plugins: {
tooltip: {
callbacks: {
label: (ctx) => {
const total = ctx.dataset.data.reduce((a, b) => a + b, 0);
const pct = (ctx.parsed / total * 100).toFixed(1);
return `${ctx.label}: ${ctx.parsed} (${pct}%)`;
}
}
}
}
}雷达图
基础雷达图
雷达图把每个 label 绘制为多边形的一个顶点,连成闭合形状。fill: true 给内部着色。径向比例尺在 scales.r(而非 x/y)下配置。雷达图比较多变量轮廓,但轴太多或单位不可比时 可能误导。
new Chart(ctx, {
type: 'radar',
data: {
labels: ['Speed', 'Power', 'Range', 'Comfort', 'Price'],
datasets: [{
label: 'Model X',
data: [8, 7, 6, 9, 5],
borderColor: 'rgba(255,99,132,1)',
backgroundColor: 'rgba(255,99,132,0.2)',
fill: true
}]
},
options: { scales: { r: { beginAtZero: true } } }
});雷达图样式
雷达 dataset 接受与折线图相同的样式:borderColor、fill、point 样式、tension。较小的 tension(0.1)让顶点略微圆滑;0 保持尖锐的尖角。使用半透明 backgroundColor,使重叠的雷达形状仍可见。
datasets: [{
data: [7, 8, 6, 9, 5],
borderColor: 'blue',
backgroundColor: 'rgba(0,0,255,0.2)',
borderWidth: 2,
pointRadius: 4,
pointBackgroundColor: 'blue',
fill: true,
tension: 0.1 // slight curve between vertices
}]雷达比例尺(r)设置
径向比例尺 r 控制环线和标签。min/max 固定所有轴的取值范围(比较 dataset 时必不可少)。ticks.stepSize 设置环间距;pointLabels 样式化周边的分类名。把 ticks.backdropColor 设为透明,使环数字不会遮挡网格线。
options: {
scales: {
r: {
min: 0,
max: 10,
ticks: { stepSize: 2, backdropColor: 'transparent' },
pointLabels: { font: { size: 13 }, color: '#333' },
grid: { color: '#ccc' },
angleLines: { color: '#ccc' }
}
}
}多 dataset 雷达图
多个 dataset 叠加为同心多边形,适合前后对比或竞品对比。始终在 r 上设置相同的 min/max 让形状可比,并使用半透明填充,使两个轮廓在重叠处都可见。
new Chart(ctx, {
type: 'radar',
data: {
labels: ['A', 'B', 'C', 'D', 'E'],
datasets: [
{ label: 'Before', data: [5,6,4,7,5], borderColor: 'red', backgroundColor: 'rgba(255,0,0,0.15)', fill: true },
{ label: 'After', data: [7,8,6,9,7], borderColor: 'blue', backgroundColor: 'rgba(0,0,255,0.15)', fill: true }
]
}
});雷达角度线与网格
angleLines 是从中心到每个顶点的辐条;grid 是同心多边形的环线。隐藏 ticks(display:false)会移除数字环标签以获得更干净的外观,同时保留环线本身。pointLabels 仅控制每个顶点处的分类文字。
options: {
scales: {
r: {
angleLines: { display: true, color: 'rgba(0,0,0,0.2)', lineWidth: 1 },
grid: { display: true, color: 'rgba(0,0,0,0.1)' },
pointLabels: { display: true, color: '#000', font: { weight: 'bold' } },
ticks: { display: false } // hide numeric ring labels for a cleaner look
}
}
}极坐标图
基础极坐标图
极坐标图把每个值绘制为一个扇区,扇区跨相同角度(各 360/N),用半径编码数值。与饼图(角度 = 值)不同,此处半径 = 值,因此所有切片角度等宽但长度不同。
new Chart(ctx, {
type: 'polarArea',
data: {
labels: ['North', 'South', 'East', 'West'],
datasets: [{
data: [11, 16, 7, 14],
backgroundColor: ['#FF6384', '#4BC0C0', '#FFCE56', '#36A2EB']
}]
},
options: { scales: { r: { beginAtZero: true } } }
});极坐标图 vs 饼图
关键区别:饼图中每个切片的「角度」与其值成正比;极坐标图中每个切片的「半径」与其值成正比,而角度相等。当你希望每个分类都同等可见但按大小排序时,使用极坐标图。
// PIE: angle = value, radius = constant
new Chart(ctx, { type: 'pie', data: { datasets: [{ data: [10, 20, 30] }] } });
// POLAR: angle = constant, radius = value
new Chart(ctx, { type: 'polarArea', data: { datasets: [{ data: [10, 20, 30] }] } });
// polar area is better when values differ widely AND
// you want every category to occupy an equal angular share极坐标比例尺(r)
极坐标图使用所有扇区共享的单一径向比例尺 r——因此所有值都依据相同的最大半径衡量。设置显式 max 让图表对刻度诚实;不设置时 Chart.js 会按最大值自动缩放,可能夸大微小差异。
options: {
scales: {
r: {
min: 0,
max: 20,
ticks: { stepSize: 5 },
grid: { color: 'rgba(0,0,0,0.15)' },
angleLines: { color: 'rgba(0,0,0,0.15)' }
}
}
}极坐标边框样式
极坐标扇区接受与饼图/环形图相同的边框/填充样式。白色边框清晰分隔相邻扇区。hoverBackgroundColor 在悬停时改变扇区填充以提供交互反馈。与环形图不同,极坐标没有 cutout——扇区在中心相遇。
datasets: [{
data: [11, 16, 7, 14],
backgroundColor: ['#FF6384', '#4BC0C0', '#FFCE56', '#36A2EB'],
borderColor: 'white',
borderWidth: 2,
hoverBackgroundColor: '#333'
}]极坐标悬停偏移
极坐标支持 hoverBackgroundColor 和 hoverBorderColor,但不像环形图那样支持 hoverOffset / offset,因为扇区共享径向比例尺。如果必须「弹出」切片,改用环形图。极坐标的交互性通过颜色和工具提示变化来实现。
datasets: [{
data: [11, 16, 7, 14],
backgroundColor: ['#FF6384', '#4BC0C0', '#FFCE56', '#36A2EB'],
hoverOffset: 12 // not supported on polarArea the same way as doughnut;
// use hoverBackgroundColor + tooltips for interactivity
}]
// for true "pop out" use 'doughnut' instead气泡图
基础气泡图
气泡图数据点是 {x, y, r} 对象,其中 x、y 是坐标,r 是以像素为单位的气泡「半径」。与散点图不同,气泡大小编码第三个变量。注意 r 是半径(非面积),因此值放大 2 倍在视觉上是 4 倍面积——考虑对大小数据做 sqrt 缩放。
new Chart(ctx, {
type: 'bubble',
data: {
datasets: [{
label: 'Products',
data: [
{ x: 10, y: 20, r: 15 },
{ x: 15, y: 10, r: 10 },
{ x: 7, y: 25, r: 8 }
],
backgroundColor: 'rgba(255,99,132,0.6)'
}]
},
options: { scales: { y: { beginAtZero: true } } }
});气泡大小映射
由于人眼感知的是气泡「面积」而非半径,把值经 Math.sqrt 映射后再赋给 r,使值翻倍时可见面积也翻倍。选择一个除数,让最大的气泡不至于重叠太多邻居。这使图表在感知上诚实。
const raw = [{x:1,y:2,pop:1000}, {x:2,y:3,pop:5000}, {x:3,y:1,pop:20000}];
// map a real-world value to a pixel radius with sqrt scaling
// so bubble AREA (not radius) is proportional to the value
const data = raw.map(d => ({
x: d.x,
y: d.y,
r: Math.sqrt(d.pop) / 10 // tune the divisor for your dataset
}));
new Chart(ctx, { type: 'bubble', data: { datasets: [{ data }] } });每个气泡的颜色
与其他 Chart.js dataset 一样,气泡样式属性接受数组以单独样式化每个气泡。用此法按分类给气泡着色,同时用大小编码另一个数值变量,得到四维图表(x、y、大小、颜色)。
datasets: [{
data: [
{ x: 10, y: 20, r: 15 },
{ x: 15, y: 10, r: 10 }
],
// arrays give per-point styling
backgroundColor: ['rgba(255,99,132,0.6)', 'rgba(54,162,235,0.6)'],
borderColor: ['rgba(255,99,132,1)', 'rgba(54,162,235,1)'],
borderWidth: 2
}]多个气泡 dataset
多个 dataset 渲染为不同颜色的气泡组,每组都出现在图例中。叠加组时保持气泡半径适中,以免气泡完全遮挡彼此。alpha 半透明填充(0.5-0.6)有助于重叠气泡保持可读。
new Chart(ctx, {
type: 'bubble',
data: {
datasets: [
{ label: 'Asia', data: [{x:1,y:2,r:10},{x:3,y:4,r:8}], backgroundColor: 'rgba(255,99,132,0.6)' },
{ label: 'Europe', data: [{x:2,y:5,r:12},{x:4,y:2,r:7}], backgroundColor: 'rgba(54,162,235,0.6)' }
]
}
});气泡悬停样式
悬停属性(hoverBackgroundColor、hoverBorderColor、hoverBorderWidth、hoverRadius)仅作用于指针下的气泡。hoverRadius 在悬停时给半径增加额外像素,使目标气泡明显弹出。配合工具提示可给出清晰的交互聚焦。
datasets: [{
data: [{x:1,y:2,r:10}],
backgroundColor: 'rgba(75,192,192,0.5)',
hoverBackgroundColor: 'rgba(75,192,192,0.9)',
hoverBorderColor: 'black',
hoverBorderWidth: 3,
hoverRadius: 2 // EXTRA radius added on hover (v3+ uses hoverRadius)
}]散点图
基础散点图
散点图数据点是 {x, y} 对象(不需要半径)。x 比例尺应为 type: 'linear'(不是 category),以便数值 x 映射到真实位置。散点图是展示两个连续变量相关性的标准图表。
new Chart(ctx, {
type: 'scatter',
data: {
datasets: [{
label: 'Observations',
data: [
{ x: 1.2, y: 2.3 },
{ x: 1.8, y: 3.1 },
{ x: 2.5, y: 4.0 },
{ x: 3.1, y: 4.8 }
],
backgroundColor: 'rgba(75,192,192,0.7)'
}]
},
options: { scales: { x: { type: 'linear', position: 'bottom' } } }
});大量点的散点图
对于上千个点,缩小 pointRadius(1-2px)并使用半透明 backgroundColor(alpha 0.2-0.4),使重叠点通过更深的区域揭示密度。对超大数据集禁用点的悬停(pointHitRadius: 0)以保持交互响应。
const n = 2000;
const data = Array.from({ length: n }, () => ({
x: Math.random() * 100,
y: Math.random() * 100
}));
new Chart(ctx, {
type: 'scatter',
data: { datasets: [{ data, pointRadius: 1.5, backgroundColor: 'rgba(0,0,0,0.3)' }] }
});散点样式
pointStyle 支持默认圆形之外的多种形状:'rect'、'rectRot'、'triangle'、'star'、'cross'、'crossRot'、'dash'。pointRotation 旋转非圆形形状。所有 point* 属性接受数组以实现逐点样式——适合高亮特定观测。
datasets: [{
data: [{x:1,y:2},{x:2,y:3}],
pointRadius: 8,
pointHoverRadius: 12,
pointStyle: 'triangle', // 'circle','rect','star','cross',...
pointBackgroundColor: 'red',
pointBorderColor: 'darkred',
pointBorderWidth: 2,
pointRotation: 45 // rotation in degrees (for shapes)
}]带趋势线的散点图
Chart.js 没有内置趋势线,因此计算回归(此处为最小二乘法)并添加一个含两个端点、pointRadius:0 的 'line' dataset。line dataset 的 type 会按 dataset 覆盖图表类型——这也是混合图表的工作方式。fill:false 让它只作为一条线。
// simple least-squares fit
const pts = [{x:1,y:2},{x:2,y:3.5},{x:3,y:4.2},{x:4,y:5.1}];
const n = pts.length;
const mX = pts.reduce((s,p)=>s+p.x,0)/n;
const mY = pts.reduce((s,p)=>s+p.y,0)/n;
const slope = pts.reduce((s,p)=>s+(p.x-mX)*(p.y-mY),0) / pts.reduce((s,p)=>s+(p.x-mX)**2,0);
const intercept = mY - slope*mX;
new Chart(ctx, {
type: 'scatter',
data: { datasets: [
{ data: pts, backgroundColor: 'blue' },
{ type: 'line', data: [{x:0,y:intercept},{x:5,y:slope*5+intercept}],
borderColor: 'red', pointRadius: 0, fill: false }
]}
});散点转折线(showLine)
散点 dataset 上设置 showLine: true 会用线连接各点,实际上把散点图变成使用数值 x 坐标的折线图。当 x 值是真实数字(而非分类标签)但仍需连接线段时——例如不规则间距的时间序列——这是正确选择。
new Chart(ctx, {
type: 'scatter',
data: { datasets: [{
data: [{x:1,y:2},{x:2,y:3},{x:3,y:5}],
showLine: true, // connect points with a line
borderColor: 'green',
backgroundColor: 'green',
pointRadius: 4
}]}
});数据 — Datasets
Datasets 结构
data 持有 labels(所有 dataset 共享)和 datasets(序列数组)。每个 dataset 有 label(显示在图例/工具提示)、与 labels 对齐的 data 数组,以及样式属性。dataset 上设置的属性作用于每个点,除非被数组覆盖。
data: {
labels: ['A', 'B', 'C'], // shared category labels
datasets: [
{
label: 'Series 1', // legend + tooltip label
data: [10, 20, 30], // values (one per label)
backgroundColor: 'rgba(0,0,255,0.5)',
borderColor: 'blue',
borderWidth: 1,
// ... type-specific styling
}
]
}labels 与 data 对齐
每个 dataset 的 data 数组按位置与共享的 labels 数组对齐——data[i] 是 labels[i] 的值。较短的 data 数组会在缺失的尾部产生 undefined 值,折线图会将其视为间隙。始终让 labels 与 data 等长以避免意外的间隙。
data: {
labels: ['Jan', 'Feb', 'Mar'],
datasets: [
{ data: [10, 20, 30] }, // Jan=10, Feb=20, Mar=30
{ data: [5, 15] } // Jan=5, Feb=15, Mar=undefined
]
}
// data[i] always corresponds to labels[i]; a shorter data array
// leaves the remaining positions as undefined (missing data)多个 Datasets
多个 dataset 渲染为平行序列(分组柱、多条折线、重叠雷达)。它们共享一个 labels 数组。为每个 dataset 设置不同颜色——Chart.js 仅在省略 backgroundColor 时自动着色,即便如此,显式颜色在各版本间更可预测。
data: {
labels: ['Q1','Q2','Q3','Q4'],
datasets: [
{ label: '2023', data: [20,35,30,35], backgroundColor: 'rgba(255,99,132,0.6)' },
{ label: '2024', data: [25,32,34,40], backgroundColor: 'rgba(54,162,235,0.6)' },
{ label: '2025', data: [28,38,36,45], backgroundColor: 'rgba(75,192,192,0.6)' }
]
}Dataset 级样式
dataset 级样式作用于该 dataset 的每个点。hover* 变体在悬停时激活。hidden:true 让 dataset 初始隐藏(图例切换可再次显示)。order 控制 z 序——数字越小越后绘制(在最上层)——在混合/重叠图中很有用。
datasets: [{
label: 'Revenue',
data: [10, 20, 30],
backgroundColor: 'rgba(54,162,235,0.6)',
borderColor: 'rgba(54,162,235,1)',
borderWidth: 2,
hoverBackgroundColor: 'rgba(54,162,235,0.9)',
hidden: false, // set true to hide from view + legend toggle
order: 0 // lower order draws on top
}]解析对象数据
当数据是对象数组时,通过 parsing: {xAxisKey, yAxisKey} 告诉 Chart.js 哪些字段持轴值。这避免了手动映射为 {x, y} 对。键默认为 'x' 和 'y',因此 {x, y} 对象无需 parsing 配置即可工作。
new Chart(ctx, {
type: 'bar',
data: {
datasets: [{
data: [
{ name: 'Red', votes: 12 },
{ name: 'Blue', votes: 19 }
],
parsing: { xAxisKey: 'name', yAxisKey: 'votes' }
}]
}
});逐点样式数组
任何样式属性都可以是单个值(作用于所有点)或数组(每点一个)。这能高亮特定点——例如把大部分 backgroundColor 设为灰色、一个设为红色以吸引注意。数组长度应与 data 长度匹配;更短的数组会循环。
datasets: [{
data: [10, 20, 30, 40],
// every styling property accepts an array (one value per point)
backgroundColor: ['red', 'green', 'blue', 'orange'],
borderColor: ['darkred', 'darkgreen', 'darkblue', 'darkorange'],
borderWidth: [1, 2, 3, 4],
pointRadius: [3, 6, 9, 12] // (line/scatter only)
}]选项 — 响应式与比例尺
Options 结构
options 是顶层配置对象。它按关注点分组:plugins(图例/标题/工具提示)、scales(x/y/r)、layout(内边距)、animation、interaction(悬停模式)和 events。几乎所有 Chart.js 行为都在这里调优,而非在单个 dataset 上。
new Chart(ctx, {
type: 'bar',
data: { /* ... */ },
options: {
responsive: true,
maintainAspectRatio: true,
plugins: { legend: {}, title: {}, tooltip: {} },
scales: { x: {}, y: {} },
layout: { padding: 10 },
animation: { duration: 800 },
interaction: { mode: 'nearest', intersect: false },
events: ['mousemove', 'click', 'touchstart']
}
});响应式与 maintainAspectRatio
responsive:true 让图表填满容器宽度并在尺寸变化时重新渲染。maintainAspectRatio:false 释放高度,使容器的 CSS 高度被尊重——同时设置两者并把 canvas 包在带尺寸的 <div> 中。maintainAspectRatio:true(默认)时图表保持固定宽高比。
<div style="width:100%; height:400px;">
<canvas id="c"></canvas>
</div>
<script>
new Chart(document.getElementById('c'), {
type: 'line',
data: { /* ... */ },
options: {
responsive: true, // resize with container
maintainAspectRatio: false // let the container control height
}
});
</script>比例尺概览
scales 配置每个坐标轴。id(x、y、r)是比例尺键;type 选择比例尺类。常见类型:'category'(文本标签)、'linear'(数字)、'logarithmic'、'time'(需要日期适配器)、'radialLinear'(雷达/极坐标)。title.display + text 添加坐标轴标签。
options: {
scales: {
x: { type: 'category', title: { display: true, text: 'Month' } },
y: { type: 'linear', title: { display: true, text: 'Value' },
beginAtZero: true },
r: { type: 'radialLinear' } // for radar/polarArea
}
}
// built-in scale types: category, linear, logarithmic, time,
// timeseries, radialLinear插件概览
plugins 分组了内置的 title、legend、tooltip 插件,以及任何已注册自定义插件按其 id 存储的选项。title 在 canvas 上方添加图表标题。legend 控制数据集图例。tooltip 控制悬停弹出。自定义插件从同一个 plugins 对象读取其选项。
options: {
plugins: {
title: { display: true, text: 'My Chart', font: { size: 18 } },
legend: { display: true, position: 'top' },
tooltip: { enabled: true, mode: 'index', intersect: false },
// a custom inline plugin:
myPlugin: { prop: 'value' }
}
}布局(内边距)
layout.padding 在 canvas 内部、图表区域与 canvas 边缘之间添加空间——当标题、坐标轴标签或大型工具提示会被裁剪时很有用。单个数字对四边等量内边距;对象形式允许每边不同值。
options: {
layout: {
padding: {
top: 10, right: 20, bottom: 10, left: 20
}
}
}
// or a single number for all four sides:
// layout: { padding: 15 }全局默认值(Chart.defaults)
Chart.defaults 持 有全局默认配置——在应用启动时设置一次,每个图表都会继承。这是品牌化(字体族、基础颜色、默认动画)的正确位置。单图表选项始终覆盖默认值,因此仍可自定义个别图表。
// set once, applies to every chart created afterwards
Chart.defaults.font.family = "'Segoe UI', sans-serif";
Chart.defaults.font.size = 13;
Chart.defaults.color = '#333';
Chart.defaults.borderColor = '#ddd';
Chart.defaults.plugins.legend.labels.color = '#333';
Chart.defaults.animation.duration = 600;
// per-chart override still wins:
new Chart(ctx, { options: { font: { size: 16 } } });坐标轴 — X 与 Y
线性轴
线性轴按比例映射数字。beginAtZero 强制轴从 0 开始(柱状图推荐)。min/max 设置硬限制;suggestedMin/suggestedMax 是软提示,Chart.js 可能扩展以适配刻度。ticks.callback 自定义标签文字(例如附加单位)。
options: {
scales: {
y: {
type: 'linear',
beginAtZero: true,
min: 0, max: 100, // hard limits
ticks: { stepSize: 10, callback: (v) => v + '%' }
}
}
}类目轴
类目轴在固定位置显示文本标签。它是 bar/line 图的默认 x 轴。ticks.autoSkip:false 强制显示每个标签(否则 Chart.js 会丢弃重叠的标签)。maxRotation 倾斜标签以在分类很多时防止重叠。
options: {
scales: {
x: {
type: 'category',
labels: ['Mon','Tue','Wed'], // optional; usually from data.labels
ticks: { autoSkip: false, maxRotation: 45, minRotation: 0 }
}
}
}时间轴
时间轴按时间比例绘制 Date 对象或 ISO 字符串,在没有数据处留有空隙。它需要日期适配器包(chartjs-adapter-date-fns、-luxon、-dayjs)。time.unit 固定刻度粒度('day'、'month'、'year');不设置时 Chart.js 自动选择单位。
# install a date adapter
npm install chart.js date-fns date-fns-tz
import 'chartjs-adapter-date-fns';
new Chart(ctx, {
type: 'line',
data: { datasets: [{ data: [
{x: new Date('2024-01-01'), y: 10},
{x: new Date('2024-02-01'), y: 20},
{x: new Date('2024-04-01'), y: 15}
]}]},
options: { scales: { x: { type: 'time', time: { unit: 'month' } } } }
});对数轴
对数轴按 10 的幂排列刻度,对于跨越多个数量级的数据(人口、价格、频率)必不可少。它不能显示 0 或负值。ticks.callback 让你紧凑地格式化 10 的幂标签(如科学计数法)。
new Chart(ctx, {
type: 'line',
data: { datasets: [{ data: [1, 10, 100, 1000, 10000] }] },
options: {
scales: {
y: { type: 'logarithmic',
ticks: { callback: (v) => v >= 1 ? v.toExponential(0) : v } }
}
}
});坐标轴标题与刻度回调
title.display + title.text 添加坐标轴标签(默认始终关闭——设 display:true)。ticks.callback 接收每个刻度值并返回显示字符串,让你把数字格式化为货币、百分比、k/M 缩写或任何自定义文字,而无需改动底层数据。
options: {
scales: {
y: {
title: { display: true, text: 'Revenue (USD)', color: '#333', font: { size: 14, weight: 'bold' } },
ticks: {
callback: function(value) {
if (value >= 1000) return (value/1000) + 'k';
return value;
}
}
}
}
}min/max 与 suggestedMin/Max
min/max 是硬限制:即使数据超出范围,轴也保持在其中(数据被裁剪)。suggestedMin/suggestedMax 是软提示:Chart.js 以它们为基线,但可能扩展以产生圆整的刻度值。想要漂亮的圆整数时优先用 suggested*;对百分比等固定刻度用硬 min/max。
options: {
scales: {
y: {
// hard limits — chart never goes outside this range
min: 0, max: 100,
// OR soft hints — Chart.js may expand to fit nice ticks:
// suggestedMin: 0, suggestedMax: 100,
ticks: { stepSize: 20 }
}
}
}图例
图例基础
图例列出每个 dataset 的 label 及颜色色块。display:true 显示(默认),display:false 隐藏。position 接受 'top'、'bottom'、'left'、'right' 或 'center'(chartArea)。默认情况下点击图例项会切换该 dataset 的可见性——内置的过滤交互。
new Chart(ctx, {
type: 'bar',
data: { labels: ['A','B'], datasets: [{ label: 'Sales', data: [10,20] }] },
options: {
plugins: {
legend: { display: true, position: 'top' }
}
}
});图例位置与对齐
position 设置图例所在边;align 控制沿该边的位置('start'/'center'/'end')。labels 配置色块(boxWidth/boxHeight)和文字样式。maxWidth 限制图例区域,使很长的图例换行而不是把图表挤到一边。
plugins: {
legend: {
position: 'bottom',
align: 'center', // 'start' | 'center' | 'end'
maxWidth: 300,
labels: {
boxWidth: 20,
boxHeight: 20,
padding: 15,
color: '#333',
font: { size: 12 }
}
}
}图例 onClick(自定义切换)
覆盖 legend.onClick 以自定义点击行为。 默认是切换 dataset 可见性。签名是 (event, legendItem, legend)。改变状态后调用 chart.update() 重新渲染。可用于隐藏前确认、与外部 UI 同步,或实现单选(radio)行为。
plugins: {
legend: {
onClick(e, item, legend) {
const chart = legend.chart;
const dataset = chart.data.datasets[item.datasetIndex];
// custom: toggle visibility AND log the action
dataset.hidden = !dataset.hidden;
console.log('Toggled', dataset.label, '->', dataset.hidden);
chart.update();
}
}
}图例标签回调
generateLabels 返回标签对象数组,完全控制图例中显示的内容。每个项的 text、色块颜色和 hidden 状态都可自定义——适合添加计数、单位,或从非 dataset 数据派生图例。hidden 标志和 datasetIndex 把点击链接回正确的 dataset。
plugins: {
legend: {
labels: {
generateLabels(chart) {
return chart.data.datasets.map((ds, i) => ({
text: `${ds.label} (${ds.data.length} pts)`,
fillStyle: ds.backgroundColor,
strokeStyle: ds.borderColor,
lineWidth: ds.borderWidth,
hidden: ds.hidden,
datasetIndex: i
}));
}
}
}
}隐藏图例与外部图例
要完全控制样式时,禁用内置图例(display:false)并渲染自己的 HTML 图例。遍历 chart.data.datasets,渲染按钮/色块,点击时切换 dataset.hidden + chart.update()。当默认 canvas 图例不符合设计系统时这很常见。
// hide the built-in legend entirely
options: { plugins: { legend: { display: false } } }
// build your own external legend HTML, then wire it to the chart:
const chart = new Chart(ctx, { /* ... */, options: { plugins: { legend: { display: false } } } });
chart.data.datasets.forEach((ds, i) => {
const btn = document.createElement('button');
btn.textContent = ds.label;
btn.onclick = () => { ds.hidden = !ds.hidden; chart.update(); };
document.getElementById('legend').appendChild(btn);
});工具提示
工具提示基础
工具提示在悬停时出现,显示 dataset 标签和值。默认 enabled:true。样式属性(backgroundColor、titleColor、bodyColor、padding、cornerRadius)控制外观。displayColors 切换每项旁的小色块——当多个 dataset 重叠时很有用。
options: {
plugins: {
tooltip: {
enabled: true, // show tooltips
backgroundColor: 'rgba(0,0,0,0.8)',
titleColor: 'white',
bodyColor: 'white',
borderColor: 'gray',
borderWidth: 1,
padding: 10,
cornerRadius: 4,
displayColors: true // show color boxes
}
}
}工具提示 label 回调
callbacks.label 返回每个工具提示行的文字。ctx.dataset.label 是序列名,ctx.parsed.y 持有值(饼图/雷达图用 ctx.parsed)。在此格式化货币、附加单位或计算总和。返回的字符串(或字符串数组)成为工具提示正文。
plugins: {
tooltip: {
callbacks: {
label: (ctx) => {
const label = ctx.dataset.label || '';
const value = ctx.parsed.y ?? ctx.parsed;
return `${label}: $${value}`;
}
}
}
}工具提示标题回调
callbacks.title 控制工具提示标题(默认:x 轴标签)。函数接收工具提示项数组(mode:'index' 分组多个 dataset 时有用)。返回空字符串 '' 完全抑制标题。结合 title 和 label 回调可得到完全自定义的工具提示内容。
plugins: {
tooltip: {
callbacks: {
title: (items) => {
// items[0].label is the x-axis label / category
return 'Period: ' + items[0].label;
},
label: (ctx) => ctx.dataset.label + ': ' + ctx.formattedValue
}
}
}外部 HTML 工具提示
external 让你把工具提示渲染为真实 HTML——用于 canvas 上不可能的丰富样式、图片或链接。设置 enabled:false 以免默认工具提示双重渲染。回调接收一个 context,其中 tooltip 对象持有 caretX/caretY(位置)和正文行。把 HTML 元素定位到这些坐标。
plugins: {
tooltip: {
enabled: false, // disable default canvas tooltip
external(context) {
const { tooltip } = context;
let el = document.getElementById('tt');
if (!el) { el = document.createElement('div'); el.id = 'tt'; document.body.appendChild(el); }
if (tooltip.opacity === 0) { el.style.opacity = 0; return; }
el.innerHTML = tooltip.body.map(b => b.lines.join('')).join('<br>');
el.style.position = 'fixed';
el.style.left = tooltip.caretX + 'px';
el.style.top = tooltip.caretY + 'px';
el.style.opacity = 1;
}
}
}工具提示模式(交互)
interaction.mode 控制工具提示目标。'point' 仅显示悬停点;'index' 显示同一 x 处的所有 dataset(适合分组柱/线);'nearest' 找最近单点。intersect:false 在指针位于该列任何位置时触发工具提示,不只是在点上——通常是用户期望的行为。
options: {
interaction: {
mode: 'index', // 'point' | 'nearest' | 'index' | 'x' | 'y' | 'dataset'
intersect: false // trigger even when not directly over a point
},
plugins: { tooltip: { mode: 'index', intersect: false } }
}工具提示样式与过滤
filter 跳过不满足条件的工具提示行(如隐藏零/空值)。callbacks.footer 在所有项之后添加汇总行——非常适合显示总计。bodyFont/titleFont 样式化文字;caretSize 和 caretPadding 控制小指针箭头。
plugins: {
tooltip: {
filter: (item) => item.parsed.y !== 0, // hide zero values
callbacks: { footer: (items) => 'Total: ' + items.reduce((s,i)=>s+i.parsed.y,0) },
bodyFont: { size: 13, weight: 'bold' },
titleFont: { size: 14 },
caretSize: 6,
caretPadding: 8
}
}动画
动画时长
animation.duration 设置以毫秒为单位的总动画时长(默认 1000)。easing 控制加速曲线——'easeOutQuart'(默认)起步快并减速,感觉响应灵敏。对频繁更新的数据看板缩短时长(200-400ms)。
options: {
animation: {
duration: 1000, // total duration in ms
easing: 'easeOutQuart'
}
}
// easing options: 'linear','easeInQuad','easeOutQuad','easeInOutQuad',
// 'easeInCubic','easeOutCubic','easeInOutCubic','easeOutBounce',...禁用动画
设置 animation:false 完全关闭所有动画——最适合性能关键的看板或频繁更新场景。animation:{duration:0} 保持动画系统活跃但瞬时(悬停仍工作)。复数 animations 对象让你禁用特定属性如 y 而动画化其他属性。
// disable ALL animations
options: { animation: false }
// disable only the initial draw, keep hover animations:
options: { animation: { duration: 0 } }
// per-property: animate everything except the y axis
options: {
animations: {
y: { duration: 0 }
}
}缓动函数
缓动改变动画随时间推进的方式,极大影响观感。'easeOutQuart'(默认)感觉专业且响应灵敏。'easeOutBounce' 或 'easeInOutBack' 俏皮但在数据图上可能显得花哨。让缓动与你应用整体的运动语言一致。
options: {
animation: {
duration: 1200,
easing: 'easeInOutBack' // slight overshoot for a playful feel
}
}
// common easings:
// 'linear' constant speed (mechanical)
// 'easeOutQuart' default; snappy start, smooth end
// 'easeInOutCubic' symmetric in/out
// 'easeOutBounce' bouncy landing
// 'easeInOutBack' overshoots at both ends动画 onComplete
onComplete 在动画完成时触发——适合链式动作,如启用导出按钮或触发后续渲染。onProgress 每个动画帧触发并带步数。避免在 onProgress 中做重活,因为它每秒运行 60 次。
options: {
animation: {
onComplete: function() {
console.log('Animation finished');
// e.g. enable a "download PNG" button now that the chart is stable
document.getElementById('downloadBtn').disabled = false;
},
onProgress: function(ctx) {
// fired each frame; ctx.currentStep / ctx.numSteps
}
}
}逐属性动画(animations)
复数 animations 对象独立配置各个属性。每项可指定 from/to(或解析上下文的函数)、duration、easing 和 loop。把 tension 从 1 动画到 0.3 产生「线条落定」效果;从坐标轴基线动画 y 产生「向上生长」效果。
options: {
animations: {
tension: {
duration: 1500,
easing: 'easeOutBounce',
from: 1, // start very curved
to: 0.3, // settle at a gentle curve
loop: false
},
colors: { duration: 800 },
y: { from: ctx => ctx.chart.scales.y.getPixelForValue(0) }
}
}事件与点击
onClick 处理器
options.onClick 在用户点击图表任意位置时触发。elements 参数是被点击图表元素(柱/点)的数组,每个含 .datasetIndex 和 .index。若 elements 为空表示点击了空白 canvas。这是让图表可交互(下钻、选择)的标准方式。
new Chart(ctx, {
type: 'bar',
data: { labels: ['A','B','C'], datasets: [{ data: [10,20,30] }] },
options: {
onClick(event, elements, chart) {
if (elements.length > 0) {
const el = elements[0];
const label = chart.data.labels[el.index];
const value = chart.data.datasets[el.datasetIndex].data[el.index];
alert(`Clicked ${label} = ${value}`);
}
}
}
});getElementsAtEventForMode
getElementsAtEventForMode 在原生鼠标事件下查找图表元素,模式选项与 interaction 相同('nearest'、'index'、'x' 等)。第 4 个参数 useFinalPosition 命中检测最终渲染位置时应为 true。需要在 options.onClick 之外查找元素时使用。
const chart = new Chart(ctx, { /* ... */ });
canvas.addEventListener('click', (e) => {
const points = chart.getElementsAtEventForMode(
e, 'nearest', { intersect: true }, true // (event, mode, options, useFinalPosition)
);
if (points.length) {
const p = points[0];
console.log('dataset', p.datasetIndex, 'index', p.index);
}
});onHover
options.onHover 在鼠标移动经过图表时触发。用于廉价的 UI 反馈,如改变光标(如所示)或高亮相关外部元素。避免在此做昂贵操作——它触发非常频繁。较重的逻辑用 onClick 或对处理函数去抖。
options: {
onHover(event, elements, chart) {
// change cursor to a pointer when hovering a bar
event.native.target.style.cursor =
elements.length ? 'pointer' : 'default';
}
}悬停与活动元素
hover 配置控制图表如何高亮指针下的元素。mode:'nearest' + intersect:true 高亮最近单点。animationDuration 平滑高亮过渡。chart.setActiveElements 以编程方式触发悬停状态——适合把图表高亮与外部 UI 同步。
options: {
hover: {
mode: 'nearest',
intersect: true,
animationDuration: 200 // smooth transition when hover target changes
}
}
// programmatically set the hovered element:
chart.setActiveElements([{ datasetIndex: 0, index: 2 }]);
chart.update();事件列表
events 限制图表监听哪些 DOM 事件。减少列表(如仅 'click')会禁用悬停效果以获得静态观感,并提升看板性能。对仅触摸的移动场景,包含 touchstart/touchmove。空数组禁用所有交互。
options: {
events: ['mousemove', 'mouseout', 'click', 'touchstart', 'touchmove']
// restrict to just click for a non-interactive "static" feel:
// events: ['click']
}
// supported events: mousemove, mouseout, click, touchstart, touchmove,
// touchend, pointer events (Chart.js v3.4+)下钻示例
常见模式:点击柱子把 dataset 切换到更详细的视图(下钻)。捕获被点击的索引,替换 chart.data.labels 和 chart.data.datasets[...].data,然后调用 chart.update() 动画过渡。如果想要返回按钮,保留一个视图栈。
const monthly = { labels: ['Jan','Feb','Mar'], data: [100,120,90] };
const daily = { labels: Array.from({length:28},(_,i)=>'D'+(i+1)),
data: Array.from({length:28},()=>Math.random()*50) };
const chart = new Chart(ctx, {
type: 'bar',
data: { labels: monthly.labels, datasets: [{ data: monthly.data }] },
options: { onClick(evt, els) {
if (!els.length) return;
const idx = els[0].index;
chart.data.labels = daily.labels;
chart.data.datasets[0].data = daily.data;
chart.update();
}}
});混合图表
柱+线混合图表
通过在单个 dataset 上设置 type 来混合图表类型——它为该序列覆盖图表级 type。柱+线组合是最常见的混合(如收入作柱、增长率作线)。当序列有不同单位或量级时使用第二个 y 轴(y1)。
new Chart(ctx, {
type: 'bar', // default type for all datasets
data: {
labels: ['Jan','Feb','Mar','Apr'],
datasets: [
{ type: 'bar', label: 'Revenue', data: [20,35,30,40], backgroundColor: '#36A2EB', yAxisID: 'y' },
{ type: 'line', label: 'Growth', data: [5,8,6,12], borderColor: '#FF6384', yAxisID: 'y1', fill: false }
]
},
options: { scales: { y: { position: 'left' }, y1: { position: 'right', grid: { drawOnChartArea: false } } } }
});Dataset type 覆盖
任何 dataset 都可覆盖图表级 type,让你在一个图中组合柱、线和散点。没有显式 type 的 dataset 继承图表的 type。注意 z 序:dataset 按数组顺序绘制,因此把背景序列(柱)放在前景序列(线)之前。
new Chart(ctx, {
type: 'line', // chart-level default
data: {
labels: ['A','B','C'],
datasets: [
{ data: [1,2,3] }, // inherits 'line'
{ type: 'bar', data: [3,2,1] }, // becomes a bar
{ type: 'scatter', data: [{x:0,y:2},{x:2,y:3}], pointRadius: 5 } // scatter points
]
}
});多个 Y 轴(y、y1、y2)
用自定义 id(y、y1、y2)定义多个 y 轴,并通过 yAxisID 分配 dataset。grid.drawOnChartArea:false 防止第二个轴在第一个上绘制网格线。position:'right' 堆叠在右侧;给第三个轴加 offset:true 以免与第二个重叠。给每个轴着色以匹配其序列。
options: {
scales: {
y: { type: 'linear', position: 'left', title: { display: true, text: 'Price ($)' } },
y1: { type: 'linear', position: 'right', title: { display: true, text: 'Volume' }, grid: { drawOnChartArea: false } },
y2: { type: 'linear', position: 'right', offset: true, title: { display: true, text: '%' } }
}
}
// assign each dataset: yAxisID: 'y' | 'y1' | 'y2'不同形状的混合数据
混合图可把值序列(柱)与参考线(常数目标、趋势)组合。在线 dataset 上设 pointRadius:0 和 fill:false,使其渲染为无标记、无填充的纯线。此模式在绩效看板(实际 vs 目标)中很常见。
new Chart(ctx, {
type: 'bar',
data: {
labels: ['Mon','Tue','Wed','Thu','Fri'],
datasets: [
{ type:'bar', label:'Tasks', data:[8,6,7,9,5], backgroundColor:'rgba(54,162,235,0.6)' },
{ type:'line', label:'Target',data:[7,7,7,7,7], borderColor:'red', borderWidth:2, pointRadius:0, fill:false },
{ type:'line', label:'Trend', data:[6,6.5,7,8,8.5], borderColor:'green', borderDash:[5,5], pointRadius:0, fill:false }
]
}
});混合样式技巧
在混合图中,设置 dataset.order 使线绘制在柱之上(order 越小越在上)。给每个轴的刻度和标题着色以匹配其序列,让读者知道哪个刻度属于哪条线。禁用第二个轴的网格(drawOnChartArea:false)以避免双重网格线。
// 1. assign order so lines draw on top of bars
datasets: [
{ type:'bar', data:[...], order: 2 },
{ type:'line', data:[...], order: 1 } // lower order draws later (on top)
]
// 2. give each axis a matching color
options: { scales: {
y: { ticks: { color: '#36A2EB' }, title: { text: 'Revenue', color: '#36A2EB' } },
y1: { ticks: { color: '#FF6384' }, title: { text: 'Growth', color: '#FF6384' }, position: 'right', grid: { drawOnChartArea: false } }
}}插件
内置插件
Chart.js 内置三个插件:Title(canvas 上方的图表标题)、Legend(dataset 图例)、Tooltip(悬停弹出)。它们在 options.plugins 下按 id 配置,默认启用(Title 除外,需要 display:true 才显示文字)。
// three built-in plugins (always available):
// Legend -> options.plugins.legend
// Title -> options.plugins.title
// Tooltip -> options.plugins.tooltip
new Chart(ctx, {
type: 'line',
data: { /* ... */ },
options: {
plugins: {
title: { display: true, text: 'Sales 2024', font: { size: 18 } },
legend: { position: 'bottom' },
tooltip:{ mode: 'index', intersect: false }
}
}
});自定义内联插件
通过 plugins 选项给单个图表传插件对象数组。每个插件有 id 和钩子函数(beforeInit、afterInit、beforeDraw、afterDraw、beforeUpdate 等)。内联插件仅作用于该图表实例。ctx.save()/restore() 包裹你做的任何 canvas 状态变更。
new Chart(ctx, {
type: 'line',
data: { /* ... */ },
options: { /* ... */ },
plugins: [{
id: 'centerText',
afterDraw(chart) {
const { ctx, chartArea } = chart;
ctx.save();
ctx.font = 'bold 24px Arial';
ctx.fillStyle = 'black';
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.fillText('Hello', (chartArea.left+chartArea.right)/2,
(chartArea.top+chartArea.bottom)/2);
ctx.restore();
}
}]
});插件钩子(生命周期)
插件钩子在图表生命周期的每个阶段运行:init、update、render、draw、resize、destroy。before* 钩子可通过返回 false 取消动作。自定义绘制最常用的是 afterDraw(绘制在图表之上)或 beforeDraw(绘制在数据之下)。每个钩子接收图表实例。
const plugin = {
id: 'myHook',
beforeInit(chart) { console.log('before init'); },
afterInit(chart) { console.log('chart ready'); },
beforeUpdate(chart){ console.log('about to update'); },
beforeDraw(chart) { console.log('before draw'); },
afterDraw(chart) { console.log('after draw'); },
beforeRender(chart){ console.log('before first render'); },
resize(chart, size){ console.log('resized', size); },
destroy(chart) { console.log('destroyed'); }
};插件:在索引处画垂直线
一个常见的自定义插件在工具提示位置画垂直引导线(类似默认十字线)。afterDraw 读取活动工具提示元素的 x 坐标,并从图表区域顶部到底部描一条虚线。用 save()/restore() 包裹 canvas 状态,使虚线样式不泄漏。
const verticalLine = {
id: 'verticalLine',
afterDraw(chart) {
if (chart.tooltip?._active?.length) {
const x = chart.tooltip._active[0].element.x;
const { top, bottom } = chart.chartArea;
const ctx = chart.ctx;
ctx.save();
ctx.beginPath();
ctx.moveTo(x, top);
ctx.lineTo(x, bottom);
ctx.lineWidth = 1;
ctx.strokeStyle = 'gray';
ctx.setLineDash([4, 4]);
ctx.stroke();
ctx.restore();
}
}
};插件:环形图中心文字
绘制居中汇总文字是环形图插件的经典用例。afterDatasetsDraw 在扇形之后运行,使文字位于上层。计算总计(或任何汇总)并渲染到 chartArea 的几何中心。这把环形图变成信息丰富的 KPI 小部件。
const centerText = {
id: 'centerText',
afterDatasetsDraw(chart) {
const { ctx, data } = chart;
const total = data.datasets[0].data.reduce((a,b)=>a+b, 0);
ctx.save();
ctx.font = 'bold 28px Arial';
ctx.fillStyle = '#333';
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
const { left, right, top, bottom } = chart.chartArea;
ctx.fillText(total, (left+right)/2, (top+bottom)/2);
ctx.restore();
}
};
new Chart(ctx, { type:'doughnut', data:{...}, plugins:[centerText] });全局注册插件
Chart.register(plugin) 让插件应用于之后创建的每个图表——适合水印、品牌或分析等应用级插件。一次性图表用内联 plugins 数组代替。Chart.unregister 移除它。已注册插件从 options.plugins[id] 读取其配置。
import { Chart } from 'chart.js';
const myPlugin = { id: 'watermark', afterDraw(chart) { /* ... */ } };
// register once -> applies to EVERY chart
Chart.register(myPlugin);
// or unregister later
Chart.unregister(myPlugin);
// per-chart-only: skip Chart.register and pass via options.plugins instead更新与销毁
chart.update()
修改 chart.data 或 chart.options 后,调用 chart.update() 重新渲染。默认从先前状态动画过渡。传 'none' 作为模式跳过动画(适合高频更新)。修改后更新比销毁重建图表便宜得多。
const chart = new Chart(ctx, config);
// mutate the data/config, then update:
chart.data.labels.push('Jun');
chart.data.datasets[0].data.push(28);
chart.options.scales.y.max = 50;
chart.update(); // animate to the new state
// chart.update('none'); // skip animation更新模式
update(mode) 接受模式字符串控 制行为。'none' 禁用动画进行静默刷新。'reset' 倒回动画前的值再重新动画(适合重放入场)。'active' 仅更新悬停元素以低成本重渲染悬停。默认(无参)正常动画。
chart.update(); // default: animates the change
chart.update('none'); // no animation
chart.update('reset'); // reset to pre-animation state, then animate
chart.update('resize'); // re-compute layout (call after container resize)
chart.update('show'); // show currently-hidden dataset animations
chart.update('hide'); // hide dataset animations
chart.update('active'); // update only the active (hovered) elements更新数据与标签
可以整体替换 data/labels 或原地修改(push/shift)。对实时流图表(如 60 秒窗口),追加新值并移除最旧值,然后 update('none') 进行平滑无动画刷新——每 tick 都调用默认动画更新会导致卡顿延迟。
// replace the entire data array
chart.data.datasets[0].data = [5, 10, 15, 20];
chart.data.labels = ['A','B','C','D'];
chart.update();
// or append streaming data (and trim the oldest):
const arr = chart.data.datasets[0].data;
arr.push(newValue);
if (arr.length > 60) arr.shift();
chart.update('none'); // instant update for live datachart.destroy()
destroy() 拆除图表:移除事件监听器、取消动画、清空 canvas 并释放内存。当图表容器被移除时(React useEffect 清理、路由切换)始终调用 destroy。不销毁会泄漏内存,并在复用 canvas 时可能抛出 'Canvas is already in use' 错误。
const chart = new Chart(ctx, config);
// when done (route change, component unmount, etc.):
chart.destroy();
// after destroy the canvas is free; a new Chart can reuse it:
const fresh = new Chart(ctx, newConfig);
// destroy also removes event listeners and frees memorychart.resize() 与 clear()
chart.resize() 重新计算图表尺寸——responsive:true 时窗口尺寸变化会自动触发,因此手动调用很少见。chart.clear() 清空 canvas 但不销毁图表(下次更新会重绘)。chart.resize(width, height) 强制特定像素尺寸,覆盖响应式尺寸。
const chart = new Chart(ctx, config);
// manually trigger a resize (rarely needed; responsive:true auto-resizes)
chart.resize();
// clear the canvas (chart stays alive, just blank until next render):
chart.clear();
chart.draw(); // re-draw without recomputing layout
// resize the canvas to explicit dimensions:
chart.resize(800, 400);响应式与设备像素比
响应式容器
对响应式图表,把 canvas 包在带明确宽高的定位容器中,并设置 responsive:true + maintainAspectRatio:false。canvas 填满容器并在窗口尺寸变化时重新渲染。不设 maintainAspectRatio:false 时图表保持固定宽高比,忽略容器高度。
<!-- container controls the size -->
<div style="position: relative; width: 100%; height: 400px;">
<canvas id="c"></canvas>
</div>
<script>
new Chart(document.getElementById('c'), {
type: 'line',
data: { /* ... */ },
options: { responsive: true, maintainAspectRatio: false }
});
</script>maintainAspectRatio
maintainAspectRatio:true(默认)使 canvas 保持由 aspectRatio 设置的固定宽高比(默认 2 = 2:1)。maintainAspectRatio:false 把高度释放给容器。饼图/环形图默认 aspectRatio:1(正方形),因为圆形在宽矩形中看起来不对。
// default: chart keeps a 2:1 (width:height) aspect ratio
options: { responsive: true, maintainAspectRatio: true, aspectRatio: 2 }
// let the container control height:
options: { responsive: true, maintainAspectRatio: false }
// square charts:
options: { responsive: true, maintainAspectRatio: true, aspectRatio: 1 }aspectRatio
aspectRatio = 宽 / 高。maintainAspectRatio:true 时 canvas 尺寸遵循此比例。宽看板磁贴用更高比例(3-4),高面板用更低比例(1-1.5)。饼图/环形图默认为 1,因为圆形需要正方形 canvas 才不会看起来像椭圆。
new Chart(ctx, {
type: 'bar',
data: { /* ... */ },
options: {
responsive: true,
maintainAspectRatio: true,
aspectRatio: 3 // width:height = 3:1 (wide, short chart)
}
});
// common ratios:
// 2 -> default for bar/line
// 1 -> pie/doughnut (square)
// 3 -> wide dashboards
// 16/9 -> video-likechart.resize() 方法
responsive:true 时 Chart.js 自动监听窗口尺寸变化,但 CSS 过渡、侧栏折叠或标签切换带来的布局变化可能不触发窗口 resize。此时在容器尺寸稳定后(如在 setTimeout 或 transitionend 处理函数中)手动调用 chart.resize()。
const chart = new Chart(ctx, { /* responsive: true */ });
// manually trigger a re-layout (e.g. after a sidebar collapses)
window.dispatchEvent(new Event('resize'));
// or:
chart.resize();
// explicit size:
chart.resize(600, 300);devicePixelRatio(清晰渲染)
devicePixelRatio 缩放 canvas 后备存储,使线条在高 DPI/视网膜屏上保持清晰(默认使用 window.devicePixelRatio,通常为 2 或 3)。限制为 2 保持文字清晰同时限制 3 倍手机上的像素工作量。设为 1 在视网膜上产生模糊输出但最大化渲染速度。
// let Chart.js use the screen's full pixel density (default):
new Chart(ctx, { options: { devicePixelRatio: window.devicePixelRatio } });
// cap DPR for performance on retina displays (fewer pixels to render):
options: { devicePixelRatio: 2 }
// ignore DPR (renders at 1x — blurry on retina but fastest):
options: { devicePixelRatio: 1 }ResizeObserver 模式
在图表容器上的 ResizeObserver 能捕获窗口 resize 错过的、来自 CSS flexbox/grid、可折叠侧栏和标签切换的尺寸变化。在清理中断开观察者并调用 chart.destroy() 以防泄漏。这是复杂布局中最健壮的响应式模式。
const canvas = document.getElementById('c');
const chart = new Chart(canvas, config);
// observe the CONTAINER, not the canvas:
const ro = new ResizeObserver(() => chart.resize());
ro.observe(canvas.parentElement);
// cleanup on destroy:
function teardown() {
ro.disconnect();
chart.destroy();
}相关 Chart.js 代码片段
Copy-paste ready code for common tasks.
Bar Chart
Vertical bar chart with custom colors and rounded corners.
Line Chart
Smooth line chart with fill, tension, and hover styling.
Pie Chart
Pie chart with per-slice colors and legend positioning.
Doughnut Chart
Doughnut chart with cutout control and centered title.
Radar Chart
Multi-series radar chart for comparing entities across dimensions.
Responsive Chart
Chart that fills its container with maintainAspectRatio disabled.
Options Configuration
Title, legend, axis formatting, animations, and live updates.
Tooltips
Custom-styled tooltips with title, label, and footer callbacks.
这篇内容对您有帮助吗?