Skip to content

Chart.js 速查表

简单灵活的 JavaScript 图表库。

01

入门

安装与第一个图表

Chart.js 在 canvas 元素上渲染。type 属性设置图表类型。data 包含 labels 和 datasets,后者携带数值和样式。

chartjs
<!-- 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' 导入并只注册你用到的部分。

chartjs
# 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 实例。

chartjs
// 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 覆盖。

chartjs
// 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' 报错来源。

chartjs
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 上创建第二个图表会导致内存泄漏并出现重复渲染。

chartjs
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.
02

柱状图

基础柱状图

柱状图把每个 label 映射为一根柱子,柱高等于数据值。backgroundColor 设置柱体填充,borderColor/borderWidth 设置轮廓。设置 y.beginAtZero 让柱子始终从零开始——否则 Chart.js 可能自动缩放坐标轴下限,在视觉上扭曲差异。

chartjs
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 比例尺变为数值轴。水平柱状图适合在垂直图上会重叠的长分类标签。

chartjs
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 数组。

chartjs
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 的柱子绘制在前一个之上,累加成总和。堆叠展示构成,但相比分组柱状图,上层序列在分类间更难比较。

chartjs
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 共同控制柱体粗细和间距。

chartjs
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 轴不应从零开始。水平柱也支持此用法。

chartjs
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 } } }
});
03

折线图

基础折线图

折线图按顺序连接数据点。borderColor 是线条颜色;backgroundColor 用于填充(启用 fill 时)。tension(0-1)控制曲线平滑度——0 为直线段,约 0.3-0.4 给出柔和的贝塞尔曲线。对股价等精确数据用 0。

chartjs
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 以便图例识别。

chartjs
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 是 [实线, 间隔] 模式,用于虚线/点线。

chartjs
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,使折线在填充下仍可见。

chartjs
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 设大些提供交互弹出效果。

chartjs
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(真实零值)——混淆两者是误导性图表的常见来源。

chartjs
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
04

饼图与环形图

基础饼图

饼图展示整体的各部分:每个值变成一个扇形,其角度与所占份额成正比。为每个切片提供一个 backgroundColor(数组),否则 Chart.js 会循环默认调色板。饼图最适合 3-6 个分类——更多会变得难以阅读。

chartjs
new Chart(ctx, {
  type: 'pie',
  data: {
    labels: ['Rent', 'Food', 'Fun', 'Save'],
    datasets: [{
      data: [1200, 600, 400, 800],
      backgroundColor: ['#FF6384', '#36A2EB', '#FFCE56', '#4BC0C0']
    }]
  }
});

基础环形图

环形图就是中间有洞的饼图。cutout 属性(百分比字符串或像素数)控制洞的大小——'50%' 是经典甜甜圈外观。中间的洞可通过插件放置汇总标签,环长在编码数值上略优于扇形角度。

chartjs
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 可出现在任一处。

chartjs
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 是数组(每切片一个),用于永久性地将特定扇形「炸开」以示强调。

chartjs
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 外观。中性灰色的「剩余」切片传达未完成的部分。

chartjs
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 持有求和所需的全部值。回调中的模板字符串在本速查表的代码字符串中必须转义反引号和 ${}。

chartjs
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}%)`;
        }
      }
    }
  }
}
05

雷达图

基础雷达图

雷达图把每个 label 绘制为多边形的一个顶点,连成闭合形状。fill: true 给内部着色。径向比例尺在 scales.r(而非 x/y)下配置。雷达图比较多变量轮廓,但轴太多或单位不可比时可能误导。

chartjs
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,使重叠的雷达形状仍可见。

chartjs
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 设为透明,使环数字不会遮挡网格线。

chartjs
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 让形状可比,并使用半透明填充,使两个轮廓在重叠处都可见。

chartjs
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 仅控制每个顶点处的分类文字。

chartjs
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
    }
  }
}
06

极坐标图

基础极坐标图

极坐标图把每个值绘制为一个扇区,扇区跨相同角度(各 360/N),用半径编码数值。与饼图(角度 = 值)不同,此处半径 = 值,因此所有切片角度等宽但长度不同。

chartjs
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 饼图

关键区别:饼图中每个切片的「角度」与其值成正比;极坐标图中每个切片的「半径」与其值成正比,而角度相等。当你希望每个分类都同等可见但按大小排序时,使用极坐标图。

chartjs
// 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 会按最大值自动缩放,可能夸大微小差异。

chartjs
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——扇区在中心相遇。

chartjs
datasets: [{
  data: [11, 16, 7, 14],
  backgroundColor: ['#FF6384', '#4BC0C0', '#FFCE56', '#36A2EB'],
  borderColor: 'white',
  borderWidth: 2,
  hoverBackgroundColor: '#333'
}]

极坐标悬停偏移

极坐标支持 hoverBackgroundColor 和 hoverBorderColor,但不像环形图那样支持 hoverOffset / offset,因为扇区共享径向比例尺。如果必须「弹出」切片,改用环形图。极坐标的交互性通过颜色和工具提示变化来实现。

chartjs
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
07

气泡图

基础气泡图

气泡图数据点是 {x, y, r} 对象,其中 x、y 是坐标,r 是以像素为单位的气泡「半径」。与散点图不同,气泡大小编码第三个变量。注意 r 是半径(非面积),因此值放大 2 倍在视觉上是 4 倍面积——考虑对大小数据做 sqrt 缩放。

chartjs
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,使值翻倍时可见面积也翻倍。选择一个除数,让最大的气泡不至于重叠太多邻居。这使图表在感知上诚实。

chartjs
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、大小、颜色)。

chartjs
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)有助于重叠气泡保持可读。

chartjs
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 在悬停时给半径增加额外像素,使目标气泡明显弹出。配合工具提示可给出清晰的交互聚焦。

chartjs
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)
}]
08

散点图

基础散点图

散点图数据点是 {x, y} 对象(不需要半径)。x 比例尺应为 type: 'linear'(不是 category),以便数值 x 映射到真实位置。散点图是展示两个连续变量相关性的标准图表。

chartjs
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)以保持交互响应。

chartjs
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* 属性接受数组以实现逐点样式——适合高亮特定观测。

chartjs
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 让它只作为一条线。

chartjs
// 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 值是真实数字(而非分类标签)但仍需连接线段时——例如不规则间距的时间序列——这是正确选择。

chartjs
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
  }]}
});
09

数据 — Datasets

Datasets 结构

data 持有 labels(所有 dataset 共享)和 datasets(序列数组)。每个 dataset 有 label(显示在图例/工具提示)、与 labels 对齐的 data 数组,以及样式属性。dataset 上设置的属性作用于每个点,除非被数组覆盖。

chartjs
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 等长以避免意外的间隙。

chartjs
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 时自动着色,即便如此,显式颜色在各版本间更可预测。

chartjs
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 序——数字越小越后绘制(在最上层)——在混合/重叠图中很有用。

chartjs
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 配置即可工作。

chartjs
new Chart(ctx, {
  type: 'bar',
  data: {
    datasets: [{
      data: [
        { name: 'Red', votes: 12 },
        { name: 'Blue', votes: 19 }
      ],
      parsing: { xAxisKey: 'name', yAxisKey: 'votes' }
    }]
  }
});

逐点样式数组

任何样式属性都可以是单个值(作用于所有点)或数组(每点一个)。这能高亮特定点——例如把大部分 backgroundColor 设为灰色、一个设为红色以吸引注意。数组长度应与 data 长度匹配;更短的数组会循环。

chartjs
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)
}]
10

选项 — 响应式与比例尺

Options 结构

options 是顶层配置对象。它按关注点分组:plugins(图例/标题/工具提示)、scales(x/y/r)、layout(内边距)、animation、interaction(悬停模式)和 events。几乎所有 Chart.js 行为都在这里调优,而非在单个 dataset 上。

chartjs
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(默认)时图表保持固定宽高比。

chartjs
<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 添加坐标轴标签。

chartjs
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 对象读取其选项。

chartjs
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 边缘之间添加空间——当标题、坐标轴标签或大型工具提示会被裁剪时很有用。单个数字对四边等量内边距;对象形式允许每边不同值。

chartjs
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 持有全局默认配置——在应用启动时设置一次,每个图表都会继承。这是品牌化(字体族、基础颜色、默认动画)的正确位置。单图表选项始终覆盖默认值,因此仍可自定义个别图表。

chartjs
// 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 } } });
11

坐标轴 — X 与 Y

线性轴

线性轴按比例映射数字。beginAtZero 强制轴从 0 开始(柱状图推荐)。min/max 设置硬限制;suggestedMin/suggestedMax 是软提示,Chart.js 可能扩展以适配刻度。ticks.callback 自定义标签文字(例如附加单位)。

chartjs
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 倾斜标签以在分类很多时防止重叠。

chartjs
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 自动选择单位。

chartjs
# 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 的幂标签(如科学计数法)。

chartjs
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 缩写或任何自定义文字,而无需改动底层数据。

chartjs
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。

chartjs
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 }
    }
  }
}
12

图例

图例基础

图例列出每个 dataset 的 label 及颜色色块。display:true 显示(默认),display:false 隐藏。position 接受 'top'、'bottom'、'left'、'right' 或 'center'(chartArea)。默认情况下点击图例项会切换该 dataset 的可见性——内置的过滤交互。

chartjs
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 限制图例区域,使很长的图例换行而不是把图表挤到一边。

chartjs
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)行为。

chartjs
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。

chartjs
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 图例不符合设计系统时这很常见。

chartjs
// 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);
});
13

工具提示

工具提示基础

工具提示在悬停时出现,显示 dataset 标签和值。默认 enabled:true。样式属性(backgroundColor、titleColor、bodyColor、padding、cornerRadius)控制外观。displayColors 切换每项旁的小色块——当多个 dataset 重叠时很有用。

chartjs
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)。在此格式化货币、附加单位或计算总和。返回的字符串(或字符串数组)成为工具提示正文。

chartjs
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 回调可得到完全自定义的工具提示内容。

chartjs
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 元素定位到这些坐标。

chartjs
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 在指针位于该列任何位置时触发工具提示,不只是在点上——通常是用户期望的行为。

chartjs
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 控制小指针箭头。

chartjs
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
  }
}
14

动画

动画时长

animation.duration 设置以毫秒为单位的总动画时长(默认 1000)。easing 控制加速曲线——'easeOutQuart'(默认)起步快并减速,感觉响应灵敏。对频繁更新的数据看板缩短时长(200-400ms)。

chartjs
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 而动画化其他属性。

chartjs
// 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' 俏皮但在数据图上可能显得花哨。让缓动与你应用整体的运动语言一致。

chartjs
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 次。

chartjs
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 产生「向上生长」效果。

chartjs
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) }
  }
}
15

事件与点击

onClick 处理器

options.onClick 在用户点击图表任意位置时触发。elements 参数是被点击图表元素(柱/点)的数组,每个含 .datasetIndex 和 .index。若 elements 为空表示点击了空白 canvas。这是让图表可交互(下钻、选择)的标准方式。

chartjs
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 之外查找元素时使用。

chartjs
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 或对处理函数去抖。

chartjs
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 同步。

chartjs
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。空数组禁用所有交互。

chartjs
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() 动画过渡。如果想要返回按钮,保留一个视图栈。

chartjs
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();
  }}
});
16

混合图表

柱+线混合图表

通过在单个 dataset 上设置 type 来混合图表类型——它为该序列覆盖图表级 type。柱+线组合是最常见的混合(如收入作柱、增长率作线)。当序列有不同单位或量级时使用第二个 y 轴(y1)。

chartjs
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 按数组顺序绘制,因此把背景序列(柱)放在前景序列(线)之前。

chartjs
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 以免与第二个重叠。给每个轴着色以匹配其序列。

chartjs
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 目标)中很常见。

chartjs
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)以避免双重网格线。

chartjs
// 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 } }
}}
17

插件

内置插件

Chart.js 内置三个插件:Title(canvas 上方的图表标题)、Legend(dataset 图例)、Tooltip(悬停弹出)。它们在 options.plugins 下按 id 配置,默认启用(Title 除外,需要 display:true 才显示文字)。

chartjs
// 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 状态变更。

chartjs
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(绘制在数据之下)。每个钩子接收图表实例。

chartjs
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 状态,使虚线样式不泄漏。

chartjs
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 小部件。

chartjs
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] 读取其配置。

chartjs
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
18

更新与销毁

chart.update()

修改 chart.data 或 chart.options 后,调用 chart.update() 重新渲染。默认从先前状态动画过渡。传 'none' 作为模式跳过动画(适合高频更新)。修改后更新比销毁重建图表便宜得多。

chartjs
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' 仅更新悬停元素以低成本重渲染悬停。默认(无参)正常动画。

chartjs
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 都调用默认动画更新会导致卡顿延迟。

chartjs
// 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 data

chart.destroy()

destroy() 拆除图表:移除事件监听器、取消动画、清空 canvas 并释放内存。当图表容器被移除时(React useEffect 清理、路由切换)始终调用 destroy。不销毁会泄漏内存,并在复用 canvas 时可能抛出 'Canvas is already in use' 错误。

chartjs
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 memory

chart.resize() 与 clear()

chart.resize() 重新计算图表尺寸——responsive:true 时窗口尺寸变化会自动触发,因此手动调用很少见。chart.clear() 清空 canvas 但不销毁图表(下次更新会重绘)。chart.resize(width, height) 强制特定像素尺寸,覆盖响应式尺寸。

chartjs
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);
19

响应式与设备像素比

响应式容器

对响应式图表,把 canvas 包在带明确宽高的定位容器中,并设置 responsive:true + maintainAspectRatio:false。canvas 填满容器并在窗口尺寸变化时重新渲染。不设 maintainAspectRatio:false 时图表保持固定宽高比,忽略容器高度。

chartjs
<!-- 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(正方形),因为圆形在宽矩形中看起来不对。

chartjs
// 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 才不会看起来像椭圆。

chartjs
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-like

chart.resize() 方法

responsive:true 时 Chart.js 自动监听窗口尺寸变化,但 CSS 过渡、侧栏折叠或标签切换带来的布局变化可能不触发窗口 resize。此时在容器尺寸稳定后(如在 setTimeout 或 transitionend 处理函数中)手动调用 chart.resize()。

chartjs
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 在视网膜上产生模糊输出但最大化渲染速度。

chartjs
// 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() 以防泄漏。这是复杂布局中最健壮的响应式模式。

chartjs
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();
}

这篇内容对您有帮助吗?

学习路径

从零开始学习

通过结构化课程从头学习这个语言。