Chart.js 雷达图 Scriptable 选项实战:用脚本化函数动态驱动样式与数据点渲染
【免费下载链接】Chart.jsSimple HTML5 Charts using the项目地址: https://gitcode.com/gh_mirrors/ch/Chart.js
本篇指南以 Chart.js 官方示例库中的雷达图脚本化选项示例(docs/samples/scriptable/radar.md)为核心,系统讲解 Scriptable Options 的完整用法:如何让折线颜色、点样式、透明度、点半径等属性随数据集索引、数据索引乃至具体数值动态变化。读完本文,你将掌握 Chart.js 脚本化选项的函数签名、context 上下文结构、选项解析层级,以及雷达图控制器在底层如何消费这些动态选项。
示例概述:一个纯脚本化驱动的雷达图
该示例在type: 'radar'的配置下,没有使用任何静态颜色或尺寸值,所有视觉属性均由脚本化函数在渲染时计算得出。它同时演示了脚本化选项在两类元素上的应用:
elements.line(雷达图的外圈折线):折线填充色backgroundColor与描边色borderColor;elements.point(雷达图顶点数据点):背景色、悬停背景色、半径、点样式、悬停半径。
示例还包含一个Randomize交互动作,可在运行时重新生成数据并触发重绘,便于观察脚本化函数随数据变化的行为。图例与 Tooltip 均被关闭(legend: false、tooltip: false),让画面焦点完全落在脚本化样式本身。
随机数据与多行标签
示例通过示例库专用工具 docs/scripts/utils.js 生成可复现的随机数据:
const DATA_COUNT = 7; Utils.srand(110); function generateData() { return Utils.numbers({ count: DATA_COUNT, min: 0, max: 100 }); } const data = { labels: [['Eating', 'Dinner'], ['Drinking', 'Water'], 'Sleeping', ['Designing', 'Graphics'], 'Coding', 'Cycling', 'Running'], datasets: [{ data: generateData() }] };两个值得注意的细节:
Utils.srand(110)固定随机种子。srand会重设utils.js内部的线性同余伪随机数生成器(_seed = (_seed * 9301 + 49297) % 233280),保证每次刷新页面渲染出的数据点完全一致,便于对照调试。- 标签支持字符串数组。雷达图 7 个轴点中,部分标签以
['Eating', 'Dinner']这样的两段数组形式给出,Chart.js 会自动将数组元素换行排版在径向刻度的pointLabels上。这是雷达图(径向线性刻度)特有的标签能力。
Scriptable 选项:函数即配置
在 docs/general/options.md 中,Scriptable Options 被定义为:
Scriptable options 也接受一个函数,该函数会针对每个底层数据值被调用一次,并接收唯一参数
context(上下文信息);同时传入一个 resolver 作为第二个参数,用于在同一上下文中解析其他选项的值。
也就是说,凡是标注为 "Scriptable" 的配置项,都可以把静态值替换为一个函数:
color: function(context) { const index = context.dataIndex; const value = context.dataset.data[index]; return value < 0 ? 'red' : // 负值画红色 index % 2 ? 'blue' : // 否则按索引奇偶交替蓝绿 'green'; }函数在每次元素更新/绘制解析选项时被调用,因此天然具备"响应数据变化"的能力。官方文档还特别提示:脚本化函数内应对context的type字段做校验,因为同一个函数可能在不同上下文('chart'、'dataset'、'data'、'scale'、'tick'、'pointLabel'、'tooltip')中被调用。
context 上下文结构
脚本化函数的context是分层继承的对象,子层级自动继承父层级全部字段:
chart层级:chart(图表实例)、type: 'chart';dataset层级:在 chart 基础上增加dataset、datasetIndex、index(同 datasetIndex)、active、mode、type: 'dataset';data层级:在 dataset 基础上增加dataIndex、parsed(已解析数据)、raw(原始数据)、element(当前元素,如 point/arc/bar)、type: 'data';- 此外还有
scale、tick、pointLabel(仅径向线性刻度使用)、tooltip等层级。
本示例的脚本化函数全部工作在'data'层级,因此可以放心使用ctx.datasetIndex、ctx.dataIndex与ctx.parsed.y。
逐项剖析示例中的脚本化函数
1. 按数据集索引取色:getLineColor
function getLineColor(ctx) { return Utils.color(ctx.datasetIndex); }Utils.color(index)从utils.js内置的 9 色COLORS调色板中取色(COLORS[index % COLORS.length]),多数据集时自动循环配色。该函数被同时赋给折线的backgroundColor、borderColor以及数据点的backgroundColor,保证同一条雷达图的线、点颜色一致。
2. 按数据索引交替点样式:alternatePointStyles
function alternatePointStyles(ctx) { const index = ctx.dataIndex; return index % 2 === 0 ? 'circle' : 'rect'; }利用ctx.dataIndex的奇偶性,让雷达图 7 个顶点在圆形circle与矩形rect之间交替出现,直接验证了脚本化选项对pointStyle的逐点控制能力。
3. 透明度控制:makeHalfAsOpaque与make20PercentOpaque
function makeHalfAsOpaque(ctx) { return Utils.transparentize(getLineColor(ctx)); } function make20PercentOpaque(ctx) { return Utils.transparentize(getLineColor(ctx), 0.8); }Utils.transparentize(value, opacity)的实现中,alpha = opacity === undefined ? 0.5 : 1 - opacity——不传第二参时生成 50% 透明度,传入0.8时生成1 - 0.8 = 0.2的 alpha(即 20% 不透明度)。示例用它们分别设置:
- 折线填充色
backgroundColor: make20PercentOpaque:半透明填充,隐约透出网格; - 点悬停背景色
hoverBackgroundColor: makeHalfAsOpaque:悬停时点变半透明。
值得注意的是,makeHalfAsOpaque内部先调用了同样以ctx为入参的getLineColor(ctx),体现了脚本化函数之间可以互相组合复用的编码风格。
4. 按数值分级点半径:adjustRadiusBasedOnData
function adjustRadiusBasedOnData(ctx) { const v = ctx.parsed.y; return v < 10 ? 5 : v < 25 ? 7 : v < 50 ? 9 : v < 75 ? 11 : 15; }这是本示例最具代表性的"数据驱动样式"写法:读取ctx.parsed.y(雷达图数据点经解析后的数值,内部格式为{x, y},参见 docs/charts/radar.md 的 Internal data format 一节),按数值区间返回 5/7/9/11/15 五档半径。数据点越大,点画得越大,让雷达图多了一层可视化信息维度。
元素配置的完整装配
options: { plugins: { legend: false, tooltip: false, }, elements: { line: { backgroundColor: make20PercentOpaque, borderColor: getLineColor, }, point: { backgroundColor: getLineColor, hoverBackgroundColor: makeHalfAsOpaque, radius: adjustRadiusBasedOnData, pointStyle: alternatePointStyles, hoverRadius: 15, } } }这里有两层结构值得说明:
elements命名空间:options.elements.line.*与options.elements.point.*分别作用于雷达图的两类核心元素——折线元素(datasetElementType: 'line')与数据点元素(dataElementType: 'point')。若某数据集未显式指定样式,这些值会按选项解析顺序回退到elements层,因此是配置"全部雷达图默认点/线样式"的推荐位置。- 悬停态的单独配置:
hoverRadius: 15固定为静态值,与脚本化的radius形成对比——脚本化选项与静态值可以混用,Chart.js 会按实际配置逐项解析。
雷达图相关配置项速查
结合 docs/charts/radar.md 的 Dataset Properties 表格,本示例涉及的可脚本化配置项如下:
| 配置项 | 所属 | 类型 | 默认值 | 说明 |
|---|---|---|---|---|
backgroundColor | line | Color(Scriptable) | 'rgba(0, 0, 0, 0.1)' | 折线填充色 |
borderColor | line | Color(Scriptable) | 'rgba(0, 0, 0, 0.1)' | 折线描边色 |
pointBackgroundColor | point | Color(Scriptable/Indexable) | 'rgba(0, 0, 0, 0.1)' | 点填充色 |
pointBorderWidth | point | number(Scriptable/Indexable) | 1 | 点边框宽度 |
pointHitRadius | point | number(Scriptable/Indexable) | 1 | 不可见命中区域像素半径 |
pointHoverBackgroundColor | point | Color(Scriptable/Indexable) | undefined | 悬停填充色 |
pointHoverRadius | point | number(Scriptable/Indexable) | 4 | 悬停半径 |
pointRadius | point | number(Scriptable/Indexable) | 3 | 点半径,为 0 时不渲染 |
pointRotation | point | number(Scriptable/Indexable) | 0 | 点旋转角度(度) |
pointStyle | point | pointStyle(Scriptable/Indexable) | 'circle' | 点形状,如'circle'/'rect'/'triangle'/'star' |
除脚本化外,这些点样式属性还支持Indexable(数组)写法:数组第 i 项对应第 i 个数据点,元素少于数据时循环复用;多数场景下脚本化函数表达力更强。
Randomize 动作:运行时验证脚本化逻辑
const actions = [ { name: 'Randomize', handler(chart) { chart.data.datasets.forEach(dataset => { dataset.data = generateData(); }); chart.update(); } }, ];actions数组是 chart-editor 示例框架约定的交互描述:handler接收图表实例,将每个数据集的data替换为新随机数组后调用chart.update()。由于脚本化选项在每次元素更新时都会重新求值,点击 Randomize 后,点的半径分布、交替样式与悬停透明度会立即随新数据刷新——这是验证"选项是否真正脚本化"的最直观手段。示例模块通过module.exports = {actions, config}导出,供示例渲染器加载。
源码视角:RadarController 如何消费脚本化选项
从源码结构看,脚本化选项的解析发生在控制器更新阶段。src/controllers/controller.radar.js 中RadarController的静态配置明确了雷达图的元素模型:
static defaults = { datasetElementType: 'line', dataElementType: 'point', indexAxis: 'r', showLine: true, elements: { line: { fill: 'start' } }, }; static overrides = { aspectRatio: 1, scales: { r: { type: 'radialLinear', } } };即:雷达图的"数据集元素"是折线、数据元素是点、径向轴索引为r,默认启用径向线性刻度且纵横比为 1。
在updateElements中,每个数据点都会调用this.resolveDataElementOptions(i, ...)——这正是脚本化选项的解析入口,随后通过径向刻度定位每个点的极坐标位置:
const pointPosition = scale.getPointPositionForValue(i, this.getParsed(i).r); const x = reset ? scale.xCenter : pointPosition.x; const y = reset ? scale.yCenter : pointPosition.y; const properties = { x, y, angle: pointPosition.angle, skip: isNaN(x) || isNaN(y), options };可以推断,resolveDataElementOptions(i, ...)在解析radius、pointStyle等脚本化属性时,会构造包含datasetIndex、dataIndex、parsed(含r值,经_parseObjectDataRadialScale归一为{x, y}内部格式)的context,再调用脚本化函数——这就是示例中ctx.parsed.y、ctx.dataIndex可用性的底层来源。折线部分则由resolveDatasetElementOptions(mode)解析backgroundColor、borderColor等,供线元素绘制填充与描边。
完整代码
将上述片段组装,得到完整可运行的示例配置:
const DATA_COUNT = 7; Utils.srand(110); const actions = [ { name: 'Randomize', handler(chart) { chart.data.datasets.forEach(dataset => { dataset.data = generateData(); }); chart.update(); } }, ]; function generateData() { return Utils.numbers({ count: DATA_COUNT, min: 0, max: 100 }); } const data = { labels: [['Eating', 'Dinner'], ['Drinking', 'Water'], 'Sleeping', ['Designing', 'Graphics'], 'Coding', 'Cycling', 'Running'], datasets: [{ data: generateData() }] }; function getLineColor(ctx) { return Utils.color(ctx.datasetIndex); } function alternatePointStyles(ctx) { const index = ctx.dataIndex; return index % 2 === 0 ? 'circle' : 'rect'; } function makeHalfAsOpaque(ctx) { return Utils.transparentize(getLineColor(ctx)); } function make20PercentOpaque(ctx) { return Utils.transparentize(getLineColor(ctx), 0.8); } function adjustRadiusBasedOnData(ctx) { const v = ctx.parsed.y; return v < 10 ? 5 : v < 25 ? 7 : v < 50 ? 9 : v < 75 ? 11 : 15; } const config = { type: 'radar', data: data, options: { plugins: { legend: false, tooltip: false, }, elements: { line: { backgroundColor: make20PercentOpaque, borderColor: getLineColor, }, point: { backgroundColor: getLineColor, hoverBackgroundColor: makeHalfAsOpaque, radius: adjustRadiusBasedOnData, pointStyle: alternatePointStyles, hoverRadius: 15, } } } }; module.exports = { actions, config, };延伸阅读
- Options 与 Scriptable Options 详解:选项解析优先级、context 完整字段定义、resolver 第二参数用法;
- Radar 雷达图图表指南:全部数据集属性表、Point Styling / Line Styling / Interactions 分类说明、
scales.r径向刻度配置; - Scriptable 示例系列:
line.md(折线版同款脚本化函数)、bar.md(按ctx.parsed.y分档配色)、pie.md与polar.md(按数值区间着色的colorize模式),可对照学习脚本化选项在不同图表类型上的复用与差异; - 示例工具 Utils 说明 与 docs/scripts/utils.js:
srand/numbers/color/transparentize等示例辅助函数的实现(注意:Utils 属示例专用工具,官方文档明确提示不应在生产环境依赖)。
【免费下载链接】Chart.jsSimple HTML5 Charts using the项目地址: https://gitcode.com/gh_mirrors/ch/Chart.js
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考