G6 连续色板(Continuous Palette)完全指南:基于数值字段的映射取色
【免费下载链接】G6♾ A Graph Visualization Framework in JavaScript.项目地址: https://gitcode.com/gh_mirrors/g6/G6
G6 的色板(Palette)机制用于为节点、边等元素自动分配颜色。其中连续色板(type: 'value')与离散色板(type: 'group')相对应,它不再按"分组"取色,而是根据元素的数值型字段(如吞吐量、温度、风险评分)在[0, 1]区间上归一化后映射为颜色,从而形成类似热力图的渐变视觉。读完本文,你将掌握连续色板的配置写法、color函数入参语义、内置色板的使用方式,以及它在 G6 渲染管线中的底层实现原理,可直接用于构建"数值越大颜色越深/越红"一类的数据可视化场景。
一、理解两种取色模式:分组(group)与数值(value)
G6 的色板配置(node.palette/edge.palette/combo.palette)支持两种核心取色方式,它们的区别在于决定颜色的依据是什么:
| 模式 | type值 | 取色依据 | 典型场景 |
|---|---|---|---|
| 分类取色 | 'group' | 按某个字段的值分组,每组分配一种颜色 | 按类别/部门/状态着色 |
| 连续取色 | 'value' | 按数值字段的大小线性映射颜色 | 热度图、风险等级、数值排名 |
离散模式(group)的标准配置如下(见 标准配置示例),它把category字段值相同的节点归为一组,再从tableau色板中按组序取色:
import { Graph } from '@antv/g6'; const graph = new Graph({ container: 'container', width: 200, height: 50, data: { nodes: new Array(6).fill(0).map((_, i) => ({ id: `node-${i}`, data: { category: ['A', 'B', 'C'][i % 3] } })), }, layout: { type: 'grid', cols: 6 }, node: { palette: { type: 'group', field: 'category', color: 'tableau', }, }, }); graph.render();而连续模式(value)则不关心"组",它只关注数值的大小关系,这正是本文的主角。
二、连续色板的标准配置(核心示例)
来自 continuous-palette.md 的官方示例,展示了连续色板最直接的用法——通过一个颜色函数把节点数值映射成颜色:
import { Graph } from '@antv/g6'; const graph = new Graph({ container: 'container', width: 200, height: 50, data: { nodes: new Array(6).fill(0).map((_, i) => ({ id: `node-${i}`, data: { value: (i + 1) * 20 } })), }, layout: { type: 'grid', cols: 6 }, node: { palette: { type: 'value', field: 'value', color: (value) => `rgb(${value * 255}, 0, 0)`, }, }, }); graph.render();示例中 6 个节点的data.value分别为20, 40, 60, 80, 100, 120,经过归一化后,color函数接收到的入参依次增大,最终输出的rgb红色通道也随之变强——节点的红度直观表达了数值高低。
各配置项含义如下:
| 配置项 | 类型 | 说明 |
|---|---|---|
type | 'value' | 声明为连续取色模式(按字段值映射) |
field | string \| ((datum) => string) | 取值字段名,指向datum.data上的数值属性,如'value';也支持传入函数 |
color | (ratio: number) => string | 颜色映射函数,接收归一化后的比例值,返回任意合法颜色字符串 |
invert | boolean | 是否倒序取色,默认false |
三、color函数接收的到底是什么?——归一化原理
这是连续色板最容易被误解的地方:color函数收到的不是原始字段值,而是归一化到[0, 1]区间的比例值。
在类型定义中,连续色板的签名被明确描述为(见 packages/g6/src/palettes/types.ts):
export type ContinuousPalette = (ratio: number) => string;归一化逻辑位于 packages/g6/src/utils/palette.ts 的assignColorByPalette函数中:
} else if (type === 'value') { const [min, max] = data.reduce( ([min, max], datum) => { const value = parseField(field, datum); if (typeof value !== 'number') throw new Error(format(`Palette field ${field} is not a number`)); return [Math.min(min, value), Math.max(max, value)]; }, [Infinity, -Infinity], ); const range = max - min; return assignColor( data.map((datum) => [datum.id, ((parseField(field, datum) as number) - min) / range]), ); }从源码可以提炼出连续取色的完整流程:
- 遍历所有元素,读取
field指定的字段值(字段可来自datum.data,即parseField对字符串字段名取datum.data[field]); - 求出全局最小值
min与最大值max,计算区间宽度range = max - min; - 对每个元素计算归一化比例
ratio = (value - min) / range,该值恒落在[0, 1]内; - 将
ratio作为入参调用color函数(连续色板),得到该元素的最终颜色。
也就是说,示例中value = 20的节点归一化比例接近0,而value = 120的节点接近1,因此color函数在两端分别生成接近黑色与接近鲜红(rgb(255,0,0))的颜色。
同时注意两点细节:
- 字段值必须是数字:源码在归一化前会校验类型,若字段值不是
number,会抛出Palette field ... is not a number错误; - 支持倒序:当
invert: true时,实际传给函数的是1 - ratio(见 packages/g6/src/utils/palette.ts),实现"数值越大颜色越浅"的反向映射。
四、连续色板同样支持内置色板与注册机制
color配置除了直接传函数,还可以传色板名称字符串或颜色数组。G6 内置了 5 套开箱即用的色板(见 packages/g6/src/palettes/index.ts):
| 内置色板名 | 颜色数 | 视觉特征 |
|---|---|---|
spectral | 11 | 冷到暖的彩虹渐变(青 → 黄 → 红) |
tableau | 10 | Tableau 经典分类色 |
oranges | 9 | 橙色系单色渐变 |
greens | 9 | 绿色系单色渐变 |
blues | 9 | 蓝色系单色渐变 |
这些色板在初始化时被注册进内置扩展注册表(见 packages/g6/src/registry/build-in.ts):
palette: { spectral, tableau, oranges, greens, blues, },当color传入字符串时,底层会通过getExtension('palette', colorPalette)从注册表解析出色板实体(见 packages/g6/src/utils/palette.ts)。这也意味着你可以在 packages/g6/src/registry/register.ts 中注册自定义色板,从而在任意图上复用。
简写形式:如果完全不写配置对象、只给色板名,比如默认配置示例(见 默认配置示例):
node: { palette: 'spectral', },此时parsePalette会把它解析成"按id分组的离散色板"(见 packages/g6/src/utils/palette.ts)。因此,字符串/数组形式本质上服务于group离散取色;要进行连续取色,请使用type: 'value'+ 函数形式的color。
五、类型定义速查:完整配置结构
色板配置的类型体系定义在 packages/g6/src/spec/element/palette.ts,摘录核心结构如下:
export type PaletteOptions = Palette | CategoricalPaletteOptions | ContinuousPaletteOptions; export type STDPaletteOptions = CategoricalPaletteOptions | ContinuousPaletteOptions; interface ContinuousPaletteOptions extends BasePaletteOptions { type?: 'value'; // 基于字段值取色 field?: string | ((datum: ElementDatum) => string); // 取值字段 } export interface BasePaletteOptions { color?: Palette; // 色板颜色:色板名 / 颜色数组 / 函数 invert?: boolean; // 倒序取色 }其中Palette(见 packages/g6/src/palettes/types.ts)的完整联合类型为:
export type Palette = string | BuiltInPalette | CategoricalPalette | ContinuousPalette; export type BuiltInPalette = 'spectral' | 'oranges' | 'greens' | 'blues'; export type CategoricalPalette = string[]; export type ContinuousPalette = (ratio: number) => string;注意类型定义中field的声明为string | ((datum) => string),但连续取色时字段值会被当作数字参与归一化——传入字符串形式的字段名时,必须确保datum.data上对应的值类型为数字。
六、色板在渲染管线中的工作流程
色板并不是在元素绘制时才临时算色,而是在渲染前集中计算并缓存。从 packages/g6/src/runtime/element.ts 的实现可以看出完整链路:
private computePaletteStyle() { const { options } = this.context; this.paletteStyle = {}; this.forEachElementData((elementType, elementData) => { const palette = Object.assign( {}, parsePalette(this.getTheme(elementType)?.palette), // 1. 主题级色板 parsePalette(options[elementType]?.palette), // 2. 元素级配置覆盖 ); if (palette?.field) { Object.assign(this.paletteStyle, assignColorByPalette(elementData, palette)); } }); }要点如下:
- 色板可以配置在主题(theme)上,作为全局默认;元素级配置(
node.palette等)通过Object.assign覆盖主题色板,遵循"元素级 > 主题级"的优先级; - 只有配置了
field才会触发取色——field是色板生效的开关,缺省时色板不参与颜色计算; - 计算结果按元素
id缓存在paletteStyle字典中,同组元素共享同一颜色; - 最终取色通过
getPaletteStyle应用到样式上(见 packages/g6/src/runtime/element.ts):节点/组合用fill,边用stroke,且该颜色与themeStyle、dataStyle、stateStyle等按序合并(见 packages/g6/src/runtime/element.ts),因此后续手动设置的样式、交互状态(hover/选中)仍可覆盖色板颜色。
七、实战建议与注意事项
- 连续色板首选函数式
color:由于value模式下传入的是归一化比例,用(ratio) => \rgb(...)`这类函数可以精确控制渐变端点;若必须使用内置色板名/数组,建议改用group` 模式搭配数值分箱(如按区间打标签),避免语义混乱。 - 善用内置单色渐变:
oranges/greens/blues本身就是按由浅到深排列的渐变序列,可作为热力渐变的灵感来源。 - 数值跨度悬殊时注意视觉区分度:归一化是线性的,若数据集中存在极端离群值,大部分节点会挤在
ratio ≈ 0附近,此时可考虑先对字段做对数/开方变换,再喂给色板。 invert: true实现反向映射:例如"数值越小越红"的风险图,只需一行配置即可翻转。field支持函数:当数值需要派生计算(如(d) => d.data.x * d.data.y)时,无需改写数据,直接在field中写函数即可。- 边也能用连续色板:色板同样适用于
edge.palette,且边元素会以stroke应用颜色,可用于表达边的权重/流量强度。 - 与分组配置对比:如果你的数据是类别而非连续值,请参考 标准配置示例 中的
group模式;想快速上手默认取色,可查看 默认配置示例,并参阅 入门教程中的色板章节 查看其在力导向图上的完整实战代码。
【免费下载链接】G6♾ A Graph Visualization Framework in JavaScript.项目地址: https://gitcode.com/gh_mirrors/g6/G6
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考