Skill: lxfu1/g2-chart (version 1.0.1)

--- name: antv-g2-chart description: Generate G2 v5 chart code. Use when user asks for G2 charts, bar charts, line charts, pie charts, scatter plots, area charts, or any data visualization with G2 library. --- # G2 v5 Chart Code Generator You are an expert in AntV G2 v5 charting library. Generate accurate, runnable code following G2 v5 best practices. --- ## 1. Core Constraints / 核心约束 (MUST follow) 1. **`container` is mandatory**: `new Chart({ container: 'container', ... })` 2. **Use Spec Mode ONLY**: `chart.options({ type: 'interval', data, encode: {...} })`（V4 链式 API 见 Forbidden Patterns） 3. **`chart.options()` 只能调用一次**：多次调用会完整覆盖前一次配置，只有最后一次生效。多 mark 叠加必须用 `type: 'view'` + `children` 数组，而不是多次调用 `chart.options()` 4. **`encode` object**: `encode: { x, y }`（禁止 V4 的 `.position('x*y')`） 5. **`transform` must be array**: `transform: [{ type: 'stackY' }]` 6. **`labels` is plural**: Use `labels: [{ text: 'field' }]` not `label: {}` 7. **`coordinate` 规则**： - 坐标系类型直接写：`coordinate: { type: 'theta' }`、`coordinate: { type: 'polar' }` - transpose 是**变换**不是坐标系类型，必须写在 `transform` 数组里：`coordinate: { transform: [{ type: 'transpose' }] }` - ❌ 禁止：`coordinate: { type: 'transpose' }` 8. **范围编码**（甘特图、candlestick 等）：`encode: { y: 'start', y1: 'end' }`，禁止 `y: ['start', 'end']` 9. **样式原则**：用户描述中提到的样式（radius、fillOpacity、color、fontSize 等）必须完整保留；用户未提及的装饰性样式（`shadowBlur`、`shadowColor`、`shadowOffsetX/Y` 等）不要自行添加 10. **`animate` 规则**：用户未明确要求动画时不要添加 `animate` 配置（G2 自带默认动画），只有用户明确描述动画需求时才添加 11. **`scale.color.palette` 只能用合法值**：palette 通过 d3-scale-chromatic 查找，非法名称会抛 `Unknown palette` 错误。**不要推断或创造不存在的名称**（如 `'blueOrange'`、`'redGreen'`、`'hot'`、`'jet'`、`'coolwarm'` 等均非法）。合法的常用值：顺序色阶 `'blues'|'greens'|'reds'|'ylOrRd'|'viridis'|'plasma'|'turbo'`；发散色阶 `'rdBu'|'rdYlGn'|'spectral'`；不确定时用 `range: ['#startColor', '#endColor']` 自定义替代 12. **禁止在用户代码中使用 `d3.*`**：G2 内部使用 d3，但 `d3` 对象不会暴露到用户代码作用域，调用 `d3.sum()` 等会抛 `ReferenceError: d3 is not defined`。如需聚合，优先使用 G2 内置选项（如 `sortX` 的 `reducer: 'sum'`），不得不自定义时用原生 JS：`d3.sum(arr, d=>d.v)` → `arr.reduce((s,d)=>s+d.v,0)`；`d3.max(arr, d=>d.v)` → `Math.max(...arr.map(d=>d.v))` 13. **用户未指定配色时，禁止使用白色或近白色作为图形填充色**：`style: { fill: '#fff' }`、`style: { fill: 'white' }`、`style: { fill: '#ffffff' }` 等在白色背景下会让图形完全不可见。未指定配色时应依赖 G2 的 `encode.color` 自动分配主题色，或使用有明确视觉区分度的颜色（如 `'#5B8FF9'`）。以下是合法例外：label 文字 `fill: '#fff'`（深色背景内标签）、分隔线 `stroke: '#fff'`（堆叠/pack/treemap 的分隔白线） 14. **`padding` 只接受 `number | 'auto'`，禁止数组形式**：`padding: [40, 30, 40, 50]` 在 G2 v5 中无效（会被忽略或报错）。四边统一用 `padding: 40`；分方向控制用 `paddingTop` / `paddingRight` / `paddingBottom` / `paddingLeft` 单独设置；默认 `'auto'` 已自动为坐标轴/图例预留空间，大多数情况无需手动配置。**禁止设置 `padding: 0`**——会关闭自动计算，导致坐标轴/图例被截断；只需调整某一方向时单独设置对应方向即可 15. **`autoFit: true` 时禁止同时设置 `width`**：`autoFit` 会完全忽略 `width`，同时出现时 `width` 无效。`autoFit: true` 时只设 `height`；需要固定宽高时去掉 `autoFit` 改用 `width` + `height` 16. **用户未指定容器时**： `container` 默认为 `'container'`，不要通过 `document.createElement('div')` 进行创建，代码末尾必须有 `chart.render();` ### 1.1 Forbidden Patterns / 禁止使用的写法 **禁止使用 V4 语法**，必须使用 V5 Spec 模式： ```javascript // ❌ 禁止：V4 createView const view = chart.createView(); view.options({...}); // ❌ 禁止：V4 链式 API 调用 chart.interval() .data([...]) .encode('x', 'genre') .encode('y', 'sold') .style({ radius: 4 }); // ❌ 禁止：V4 链式 encode chart.line().encode('x', 'date').encode('y', 'value'); // ❌ 禁止：V4 source chart.source(data); // ❌ 禁止：V4 position chart.interval().position('genre*sold'); // ✅ 正确：V5 Spec 模式 chart.options({ type: 'interval', data: [...], encode: { x: 'genre', y: 'sold' }, style: { radius: 4 }, }); ``` **原因**：V5 使用 Spec 模式，结构清晰，易于序列化、动态生成和调试。 #### `createView` 的正确 V5 替代方案 `chart.createView()` 在 V4 中用于"多视图共享容器但数据各异"，V5 中对应两种场景： **场景 A：同一坐标系内叠加多个 mark（最常见）** → 用 `type: 'view'` + `children` 数组，`children` 中不能再嵌套 `view` 或者 `children` ： ```javascript // ✅ 多 mark 叠加（折线 + 散点） chart.options({ type: 'view', data, children: [ { type: 'line', encode: { x: 'date', y: 'value' } }, { type: 'point', encode: { x: 'date', y: 'value' } }, ], }); ``` **场景 B：多个独立坐标系并排/叠加（如人口金字塔、butterfly 图）** → 用 `type: 'spaceLayer'` + `children`（各子 view 有独立数据和坐标系）： ```javascript // ✅ 人口金字塔：左右两侧独立视图叠加，共享 Y 轴 chart.options({ type: 'spaceLayer', children: [ { type: 'interval', data: leftData, // 左侧数据（负值或翻转） coordinate: { transform: [{ type: 'transpose' }, { type: 'reflectX' }] }, encode: { x: 'age', y: 'male' }, axis: { y: { position: 'right' } }, }, { type: 'interval', data: rightData, // 右侧数据 coordinate: { transform: [{ type: 'transpose' }] }, encode: { x: 'age', y: 'female' }, axis: { y: false }, }, ], }); // ✅ 更简单方案：单一视图 + 负值技巧（数据可在一个数组里） chart.options({ type: 'interval', data: combinedData, // 合并数据，用负值区分方向 coordinate: { transform: [{ type: 'transpose' }] }, encode: { x: 'age', y: (d) => d.sex === 'male' ? -d.population : d.population, color: 'sex', }, axis: { y: { labelFormatter: (d) => Math.abs(d) }, // 显示绝对值 }, }); ``` **选择原则**： - 两侧数据结构相同、只是方向相反 → **优先用负值技巧**（单 `interval`，代码最简洁） - 两侧需要完全独立的坐标系/比例尺 → 用 `spaceLayer` ### 1.2 禁止使用的幻觉 Mark 类型 / Hallucinated Mark Types 以下类型来自其他图表库（如 ECharts、Vega），**G2 中不存在**，使用将导致运行时报错： | ❌ 错误写法 | ✅ 正确替换 | |------------|-----------| | `type: 'ruleX'` | `type: 'lineX'`（垂直参考线） | | `type: 'ruleY'` | `type: 'lineY'`（水平参考线） | | `type: 'regionX'` | `type: 'rangeX'`（X 轴区间高亮） | | `type: 'regionY'` | `type: 'rangeY'`（Y 轴区间高亮） | | `type: 'venn'` | `type: 'path'` + `data.transform: [{ type: 'venn' }]` | **G2 合法 mark 类型完整列表**（不得凭空创造其他 type）： - 基础：`interval`、`line`、`area`、`point`、`rect`、`cell`、`text`、`image`、`path`、`polygon`、`shape` - 连接：`link`、`connector`、`vector` - 参考线/区域：`lineX`、`lineY`、`rangeX`、`rangeY`；`range`（极少用，仅在 x/y 均需限定二维矩形时使用，且数据的 x/y 字段必须是 `[start,end]` 数组） - 统计：`box`、`boxplot`、`density`、`heatmap`、`beeswarm` - 层次/关系：`treemap`、`pack`、`partition`、`tree`、`sankey`、`chord`、`forceGraph` - 特殊：`wordCloud`、`gauge`、`liquid` - 需引入扩展包：`sunburst`（需 `@antv/g2-extension-plot`，见 [旭日图](references/marks/g2-mark-sunburst.md)） --- ## 2. Common Mistakes / 常见错误 ### ⚠️ 最高频错误：禁止多次调用 `chart.options()` `chart.options()` 是**全量替换**，不是合并。多次调用时**只有最后一次生效**，前面的配置全部丢失。**每个图表只能调用一次 `chart.options()`。** ```javascript // ❌ Wrong: 多次调用 chart.options() —— 每次完整替换前一次，只有最后一次生效 chart.options({ type: 'interval', data, encode: { x: 'x', y: 'y' } }); // ❌ 被覆盖，不渲染 chart.options({ type: 'line', data, encode: { x: 'x', y: 'y' } }); // ❌ 被覆盖，不渲染 chart.options({ type: 'text', data, encode: { x: 'x', y: 'y', text: 'label' } }); // 只有这个生效 // ✅ Correct: 多 mark 叠加必须用 type: 'view' + children，一次 chart.options() 搞定 chart.options({ type: 'view', data, children: [ { type: 'interval', encode: { x: 'x', y: 'y' } }, { type: 'line', encode: { x: 'x', y: 'y' } }, { type: 'text', encode: { x: 'x', y: 'y', text: 'label' } }, ], }); // ✅ 子 mark 需要不同数据时，在 children 里单独指定 data chart.options({ type: 'view', data: mainData, children: [ { type: 'interval', encode: { x: 'x', y: 'y' } }, { type: 'text', data: labelData, encode: { x: 'x', text: 'label' } }, ], }); ``` 多 mark 组合规则： - 只能使用 `children`，禁止 `marks`、`layers` 等属性 - `children` 不能嵌套（`children` 内不能再有 `type: 'view'` + `children`） - 复杂多坐标系组合用 `spaceLayer`/`spaceFlex` ```javascript // ❌ Wrong: 使用 marks/layers（禁止） chart.options({ type: 'view', data, marks: [...] }); // ❌ chart.options({ type: 'view', data, Layers: [...] }); // ❌ // ❌ Wrong: children 嵌套（禁止） chart.options({ type: 'view', children: [{ type: 'view', children: [...] }] }); // ❌ // ✅ Correct: 复杂多坐标系组合用 spaceLayer chart.options({ type: 'spaceLayer', children: [ { type: 'view', children: [...] }, { type: 'line', encode: { x: 'x', y: 'y' } }, ], }); ``` ### 其他常见错误 ```javascript // ❌ Wrong: padding 数组形式（CSS 简写），G2 v5 不支持，会被忽略 const chart = new Chart({ container: 'container', padding: [40, 30, 40, 50] }); // ❌ // ✅ Correct: 四边统一 const chart = new Chart({ container: 'container', padding: 40 }); // ✅ Correct: 分方向控制 const chart = new Chart({ container: 'container', paddingTop: 40, paddingLeft: 60 }); // ❌ Wrong: missing container const chart = new Chart({ width: 640, height: 480 }); // ✅ Correct: container required const chart = new Chart({ container: 'container', width: 640, height: 480 }); // ❌ Wrong: transform as object chart.options({ transform: { type: 'stackY' } }); // ✅ Correct: transform as array chart.options({ transform: [{ type: 'stackY' }] }); // ❌ Wrong: label (singular) chart.options({ label: { text: 'value' } }); // ✅ Correct: labels (plural) chart.options({ labels: [{ text: 'value' }] }); // ❌ Wrong: unnecessary scale type specification chart.options({ scale: { x: { type: 'linear' }, y: { type: 'linear' } } }); // ✅ Correct: let G2 infer scale type automatically chart.options({ scale: { y: { domain: [0, 100] } } }); ``` <!-- CONSTRAINTS:END --> --- ## 3. Basic Structure / 基础结构 ```javascript import { Chart } from '@antv/g2'; const chart = new Chart({ container: 'container', width: 640, height: 480 }); chart.options({ type: 'interval', // Mark type data: [...], // Data array encode: { x: 'field', y: 'field', color: 'field' }, transform: [], // Data transforms scale: {}, // Scale config coordinate: {}, // Coordinate system style: {}, // Style config labels: [], // Data labels tooltip: {}, // Tooltip config axis: {}, // Axis config legend: {}, // Legend config }); chart.render(); ``` --- ## 4. Core / 核心概念 核心概念是 G2 的基础，理解这些概念是正确使用 G2 的前提。 ### 4.1 Chart 初始化 Chart 是 G2 的核心类，所有图表都从 Chart 实例开始。 ```javascript import { Chart } from '@antv/g2'; const chart = new Chart({ container: 'container', // 必填：DOM 容器 ID 或元素 width: 640, // 可选：宽度 height: 480, // 可选：高度 autoFit: true, // 可选：自适应容器大小 padding: 'auto', // 可选：内边距 theme: 'light', // 可选：主题 }); ``` > **详细文档**: [Chart 初始化](references/core/g2-core-chart-init.md) ### 4.2 encode 通道系统 encode 将数据字段映射到视觉通道，是 G2 的核心概念。 | 通道 | 用途 | 示例 | |------|------|------| | `x` | X 轴位置 | `encode: { x: 'month' }` | | `y` | Y 轴位置 | `encode: { y: 'value' }` | | `color` | 颜色 | `encode: { color: 'category' }` | | `size` | 大小 | `encode: { size: 'population' }` | | `shape` | 形状 | `encode: { shape: 'type' }` | > **详细文档**: [encode 通道系统](references/core/g2-core-encode-channel.md) ### 4.3 视图组合 (view + children) 使用 `view` 类型配合 `children` 数组组合多个 mark。 ```javascript chart.options({ type: 'view', data, children: [ { type: 'line', encode: { x: 'date', y: 'value' } }, { type: 'point', encode: { x: 'date', y: 'value' } }, ], }); ``` > **详细文档**: [视图组合](references/core/g2-core-view-composition.md) --- ## 5. Concepts / 概念指南 概念指南帮助选择正确的图表类型和配置方案。 ### 5.1 图表类型选择 / Chart Selection 根据数据特征和可视化目标选择合适的图表类型： | 数据关系 | 推荐图表 | Mark | |---------|---------|------| | 比较 | 柱状图、条形图 | `interval` | | 趋势 | 折线图、面积图 | `line`、`area` | | 占比 | 饼图、环形图 | `interval` + `theta` | | 分布 | 直方图、箱线图 | `rect`、`boxplot` | | 相关 | 散点图、气泡图 | `point` | | 层级 | 矩形树图、分区图、旭日图 | `treemap`、`partition`、`sunburst`（需扩展包） | > **详细文档**: [图表类型选择指南](references/concepts/g2-concept-chart-selection.md) ### 5.2 视觉通道 / Visual Channels 视觉通道是数据到视觉属性的映射方式： | 通道类型 | 适合数据 | 感知精度 | |---------|---------|---------| | 位置 | 连续/离散 | 最高 | | 长度 | 连续 | 高 | | 颜色（色相） | 离散 | 中 | | 颜色（亮度） | 连续 | 中 | | 大小 | 连续 | 中低 | | 形状 | 离散 | 低 | > **详细文档**: [视觉通道](references/concepts/g2-concept-visual-channels.md) ### 5.3 配色理论 / Color Theory 选择合适的配色方案提升图表可读性： | 场景 | 推荐方案 | 示例 | |------|---------|------| | 分类数据 | 离散色板 | `category10`、`category20` | | 连续数据 | 顺序色板 | `Blues`、`RdYlBu` | | 正负对比 | 发散色板 | `RdYlGn` | > **详细文档**: [配色理论](references/concepts/g2-concept-color-theory.md) --- ## 6. Marks / 图表类型 Marks 是 G2 的核心可视化元素，决定了数据的视觉表现形式。每种 Mark 适合特定的数据类型和可视化场景。 ### 6.1 柱状图系列 / Interval 柱状图用于比较分类数据的大小，是最常用的图表类型。基础柱状图使用 `interval` mark，堆叠柱状图需要添加 `stackY` transform，分组柱状图使用 `dodgeX` transform。 | 类型 | Mark | 关键配置 | |------|------|----------| | 基础柱状图 | `interval` | - | | 堆叠柱状图 | `interval` | `transform: [{ type: 'stackY' }]` | | 分组柱状图 | `interval` | `transform: [{ type: 'dodgeX' }]` | | 百分比柱状图 | `interval` | `transform: [{ type: 'normalizeY' }]` | | 水平柱状图 | `interval` | `coordinate: { transform: [{ type: 'transpose' }] }` | > **详细文档**: [基础柱状图](references/marks/g2-mark-interval-basic.md) | [堆叠柱状图](references/marks/g2-mark-interval-stacked.md) | [分组柱状图](references/marks/g2-mark-interval-grouped.md) | [百分比柱状图](references/marks/g2-mark-interval-normalized.md) ### 6.2 折线图系列 / Line 折线图用于展示数据随时间或有序类别的变化趋势。支持单线、多线对比，以及不同插值方式。 | 类型 | Mark | 关键配置 | |------|------|----------| | 基础折线图 | `line` | - | | 多系列折线 | `line` | `encode: { color: 'category' }` | | 平滑曲线 | `line` | `encode: { shape: 'smooth' }` | | 阶梯线 | `line` | `encode: { shape: 'step' }` | > **详细文档**: [基础折线图](references/marks/g2-mark-line-basic.md) | [多系列折线](references/marks/g2-mark-line-multi.md) | [LineX/LineY](references/marks/g2-mark-linex-liney.md) ### 6.3 面积图系列 / Area 面积图在折线图基础上填充区域，强调数量随时间的变化程度。堆叠面积图用于展示各部分对整体的贡献。 | 类型 | Mark | 关键配置 | |------|------|----------| | 基础面积图 | `area` | - | | 堆叠面积图 | `area` | `transform: [{ type: 'stackY' }]` | > **详细文档**: [基础面积图](references/marks/g2-mark-area-basic.md) | [堆叠面积图](references/marks/g2-mark-area-stacked.md) ### 6.4 饼图/环形图 / Arc (Pie/Donut) 饼图用于展示各部分占整体的比例关系。使用 `theta` 坐标系配合 `interval` mark 实现。 | 类型 | Mark | 关键配置 | |------|------|----------| | 饼图 | `interval` | `coordinate: { type: 'theta' }` + `stackY` | | 环形图 | `interval` | `coordinate: { type: 'theta', innerRadius: 0.6 }` | > **详细文档**: [饼图](references/marks/g2-mark-arc-pie.md) | [环形图](references/marks/g2-mark-arc-donut.md) ### 6.5 散点图/气泡图 / Point 散点图用于展示两个数值变量的关系，气泡图通过点的大小展示第三个维度。 | 类型 | Mark | 关键配置 | |------|------|----------| | 散点图 | `point` | `encode: { x, y }` | | 气泡图 | `point` | `encode: { x, y, size }` | > **详细文档**: [散点图](references/marks/g2-mark-point-scatter.md) | [气泡图](references/marks/g2-mark-point-bubble.md) ### 6.6 直方图 / Histogram 直方图用于展示连续数值数据的分布情况，使用 `rect` 标记配合 `binX` 转换实现。与柱状图不同，直方图的柱子之间无间隔，表示数据连续。 | 类型 | Mark | 关键配置 | |------|------|----------| | 基础直方图 | `rect` | `transform: [{ type: 'binX', y: 'count' }]` | | 多分布对比 | `rect` | `groupBy` 分组 | > **详细文档**: [直方图](references/marks/g2-mark-histogram.md) ### 6.7 玫瑰图/玉珏图 / Polar Charts 极坐标系下的图表，通过半径或弧长表示数值大小，视觉上更加美观。 | 类型 | Mark | 关键配置 | |------|------|----------| | 玫瑰图 | `interval` | `coordinate: { type: 'polar' }` | | 玉珏图 | `interval` | `coordinate: { type: 'radial' }` | > **详细文档**: [玫瑰图](references/marks/g2-mark-rose.md) | [玉珏图](references/marks/g2-mark-radial-bar.md) ### 6.8 统计分布图 / Distribution 展示数据分布特征的图表，适用于统计分析和探索性数据分析。 | 类型 | Mark | 用途 | |------|------|------| | 箱线图 | `boxplot` | 数据分布统计 | | 箱型图(Box) | `box` | 手动指定五数概括的箱线图 | | 密度图 | `density` | 核密度估计曲线 | | 小提琴图 | `density` + `boxplot` | 密度分布 + 统计信息 | | 多边形 | `polygon` | 自定义多边形区域 | > **详细文档**: [箱线图](references/marks/g2-mark-boxplot.md) | [箱型图(Box)](references/marks/g2-mark-box-boxplot.md) | [密度图](references/marks/g2-mark-density.md) | [小提琴图](references/marks/g2-mark-violin.md) | [多边形](references/marks/g2-mark-polygon.md) ### 6.9 关系图 / Relation 展示数据之间关系的图表，适用于网络分析和集合关系展示。 | 类型 | Mark | 用途 | |------|------|------| | 桑基图 | `sankey` | 流向/转移关系 | | 和弦图 | `chord` | 矩阵流向关系 | | 韦恩图 | `path` + venn数据变换 | 集合交集关系（venn 是 data transform，不是 mark type） | | 弧长连接图 | `line` + `point` | 节点链接关系 | > **详细文档**: [桑基图](references/marks/g2-mark-sankey.md) | [和弦图](references/marks/g2-mark-chord.md) | [韦恩图](references/marks/g2-mark-venn.md) | [弧长连接图](references/marks/g2-mark-arc-diagram.md) ### 6.10 项目管理图 / Project 适用于项目管理和进度跟踪的图表。 | 类型 | Mark | 用途 | |------|------|------| | 甘特图 | `interval` | 任务时间安排 | | 子弹图 | `interval` + `point` | KPI 指标展示 | > **详细文档**: [甘特图](references/marks/g2-mark-gantt.md) | [子弹图](references/marks/g2-mark-bullet.md) ### 6.11 金融图表 / Finance 适用于金融数据分析的专业图表。 | 类型 | Mark | 用途 | |------|------|------| | K线图 | `link` + `interval` | 股票四价数据 | > **详细文档**: [K线图](references/marks/g2-mark-k-chart.md) ### 6.12 多维数据图 / Multivariate 展示多维数据关系的图表。 | 类型 | Mark | 用途 | |------|------|------| | 平行坐标系 | `line` | 多维数据关系分析 | | 雷达图 | `line` | 多维数据对比 | > **详细文档**: [平行坐标系](references/marks/g2-mark-parallel.md) | [雷达图](references/marks/g2-mark-radar.md) ### 6.13 对比图 / Comparison 适用于数据对比的图表。 | 类型 | Mark | 用途 | |------|------|------| | 双向柱状图 | `interval` | 正负数据对比 | > **详细文档**: [双向柱状图](references/marks/g2-mark-bi-directional-bar.md) ### 6.14 基础标记 / Basic Marks 基础标记是 G2 的底层构建块，可独立使用或组合构建复杂图表。 | 类型 | Mark | 用途 | |------|------|------| | 矩形 | `rect` | 矩形区域，直方图/热力图基础 | | 文本 | `text` | 文本标注和标签 | | 图片 | `image` | 图片标记，数据点用图片表示 | | 路径 | `path` | 自定义路径绘制 | | 链接 | `link` | 两点之间的连线 | | 连接器 | `connector` | 数据点之间的连接线 | | 形状 | `shape` | 自定义形状绘制 | | 向量 | `vector` | 向量/箭头标记，风场图等 | > **详细文档**: [rect](references/marks/g2-mark-rect.md) | [text](references/marks/g2-mark-text.md) | [image](references/marks/g2-mark-image.md) | [path](references/marks/g2-mark-path.md) | [link](references/marks/g2-mark-link.md) | [connector](references/marks/g2-mark-connector.md) | [shape](references/marks/g2-mark-shape.md) | [vector](references/marks/g2-mark-vector.md) ### 6.15 范围标记 / Range 范围标记用于展示数据的区间范围。 | 类型 | Mark | 用途 | |------|------|------| | 时间段/区间高亮（X 方向） | `rangeX` | X 轴区间，`encode: { x: 'start', x1: 'end' }` | | 数值范围高亮（Y 方向） | `rangeY` | Y 轴区间，`encode: { y: 'min', y1: 'max' }` | | 二维矩形区域 | `range` | x/y 字段为 `[start,end]` 数组，`encode: { x:'x', y:'y' }`，极少使用 | > **详细文档**: [range/rangeY](references/marks/g2-mark-range-rangey.md) | [rangeX](references/marks/g2-mark-rangex.md) ### 6.16 分布与打包图 / Distribution & Pack | 类型 | Mark | 用途 | |------|------|------| | 蜂群图 | `point` + `pack` | 数据点紧密排列展示分布 | | 打包图 | `pack` | 层级数据的圆形打包 | > **详细文档**: [蜂群图](references/marks/g2-mark-beeswarm.md) | [打包图](references/marks/g2-mark-pack.md) ### 6.17 层次结构图 / Hierarchy 展示层级数据的图表，通过面积或半径表示数值占比。 | 类型 | Mark | 用途 | |------|------|------| | 矩形树图 | `treemap` | 层级数据占比 | | 旭日图 | `sunburst`⚠️ | 多层级同心圆展示（需引入 @antv/g2-extension-plot） | | 分区图 | `partition` | 层级数据分区展示 | | 树图 | `tree` | 树形层级结构 | > **详细文档**: [矩形树图](references/marks/g2-mark-treemap.md) | [旭日图](references/marks/g2-mark-sunburst.md) | [分区图](references/marks/g2-mark-partition.md) | [树图](references/marks/g2-mark-tree.md) ### 6.18 其他图表 / Others | 类型 | Mark | 用途 | |------|------|------| | 热力图 | `cell` | 二维矩阵数据可视化 | | 密度热力图 | `heatmap` | 连续密度热力图 | | 仪表盘 | `gauge` | 指标进度展示 | | 词云 | `wordCloud` | 文本频率可视化 | | 水波图 | `liquid` | 百分比进度 | > **详细文档**: [热力图](references/marks/g2-mark-cell-heatmap.md) | [密度热力图](references/marks/g2-mark-heatmap.md) | [仪表盘](references/marks/g2-mark-gauge.md) | [词云](references/marks/g2-mark-wordcloud.md) | [水波图](references/marks/g2-mark-liquid.md) --- ## 7. Data / 数据变换 数据变换在数据加载阶段执行，配置在 `data.transform` 中，影响所有使用该数据的标记。 ### 7.1 Data Transform 类型（配置在 `data.transform`） | 变换 | 类型 | 用途 | 示例场景 | |------|------|------|---------| | **fold** | `fold` | 宽表转长表 | 多列数据转多系列 | | **filter** | `filter` | 条件过滤数据 | 过滤无效数据 | | **sort** | `sort` | 使用回调函数排序 | 自定义排序逻辑 | | **sortBy** | `sortBy` | 按字段排序 | 按字段值排序 | | **map** | `map` | 数据映射转换 | 添加计算字段 | | **join** | `join` | 合并数据表 | 关联外部数据 | | **pick** | `pick` | 选择指定字段 | 精简字段 | | **rename** | `rename` | 重命名字段 | 字段重命名 | | **slice** | `slice` | 截取数据范围 | 分页/截取 | | **ema** | `ema` | 指数移动平均 | 时间序列平滑 | | **kde** | `kde` | 核密度估计 | 密度图/小提琴图 | | **log** | `log` | 打印数据到控制台 | 调试 | | **custom** | `custom` | 自定义数据处理 | 复杂转换 | ### 7.2 数据格式与模式 | 类型 | 用途 | |------|------| | 表格数据格式 | G2 接受的标准表格数据格式说明 | | 数据变换模式 | Data Transform 和 Mark Transform 的组合使用模式 | > **详细文档**: [filter](references/data/g2-data-filter.md) | [sort](references/data/g2-data-sort.md) | [sortBy](references/data/g2-data-sortby.md) | [fold](references/data/g2-data-fold.md) | [slice](references/data/g2-data-slice.md) | [ema](references/data/g2-data-ema.md) | [kde](references/data/g2-data-kde.md) | [log](references/data/g2-data-log.md) | [fetch](references/data/g2-data-fetch.md) | [数据变换模式](references/data/g2-data-transform-patterns.md) ### 7.3 常见错误：Data Transform 放错位置 ```javascript // ❌ 错误：fold 是数据变换，不能放在 mark transform chart.options({ type: 'interval', data: wideData, transform: [{ type: 'fold', fields: ['a', 'b'] }], // ❌ 错误！ }); // ✅ 正确：fold 放在 data.transform chart.options({ type: 'interval', data: { type: 'inline', value: wideData, transform: [{ type: 'fold', fields: ['a', 'b'] }], // ✅ 正确 }, transform: [{ type: 'stackY' }], // mark transform }); ``` ### 7.4 组合示例：宽表数据 + 堆叠图 ```javascript // 宽表数据：每个月有多个类型的数据列 const wideData = [ { year: '2000', '类型 A': 21, '类型 B': 16, '类型 C': 8 }, { year: '2001', '类型 A': 25, '类型 B': 16, '类型 C': 8 }, // ... ]; chart.options({ type: 'interval', data: { type: 'inline', value: wideData, transform: [ // ✅ Data Transform：宽表转长表 { type: 'fold', fields: ['类型 A', '类型 B', '类型 C'], key: 'type', value: 'value' }, ], }, encode: { x: 'year', y: 'value', color: 'type' }, transform: [ // ✅ Mark Transform：堆叠 { type: 'stackY' }, ], coordinate: { type: 'polar' }, // 极坐标系 }); ``` --- ## 8. Transforms / 标记变换 标记变换在绑定视觉通道时执行，配置在 mark 的 `transform` 数组中，用于数据聚合、防重叠等。 **配置位置**：`transform` 数组，与 `data`、`encode` 同级，**不是**在 `data.transform` 中。 ```javascript chart.options({ type: 'interval', data, encode: { x: 'category', y: 'value', color: 'type' }, transform: [ // ✅ Mark Transform：与 data/encode 同级 { type: 'stackY' }, { type: 'sortX', by: 'y' }, ], }); ``` ### 8.1 防重叠变换 / Anti-overlap | 变换 | 类型 | 用途 | |------|------|------| | 堆叠 | `stackY` | 数据堆叠，用于堆叠图 | | 分组 | `dodgeX` | 数据分组，用于分组图 | | 抖动 | `jitter` | 散点抖动避免重叠 | | X轴抖动 | `jitterX` | X 方向抖动 | | Y轴抖动 | `jitterY` | Y 方向抖动 | | 打包 | `pack` | 紧密排列数据点 | > **详细文档**: [stackY](references/transforms/g2-transform-stacky.md) | [dodgeX](references/transforms/g2-transform-dodgex.md) | [jitter](references/transforms/g2-transform-jitter.md) | [jitterX](references/transforms/g2-transform-jitterx.md) | [jitterY](references/transforms/g2-transform-jittery.md) | [pack](references/transforms/g2-transform-pack.md) ### 8.2 聚合变换 / Aggregation | 变换 | 类型 | 用途 | |------|------|------| | 通用分组 | `group` | 通用分组聚合 | | 分组聚合 | `groupX` / `groupY` | 按维度分组并聚合 | | 分组颜色 | `groupColor` | 按颜色分组聚合 | | 分箱 | `bin` | 二维分箱 | | X轴分箱 | `binX` | X 轴方向分箱 | | 采样 | `sample` | 数据采样 | > **详细文档**: [group](references/transforms/g2-transform-group.md) | [groupX](references/transforms/g2-transform-groupx.md) | [groupY](references/transforms/g2-transform-groupy.md) | [groupColor](references/transforms/g2-transform-groupcolor.md) | [bin](references/transforms/g2-transform-bin.md) | [binX](references/transforms/g2-transform-binx.md) | [sample](references/transforms/g2-transform-sample.md) ### 8.3 排序变换 / Sorting | 变换 | 类型 | 用途 | |------|------|------| | X轴排序 | `sortX` | 按 X 通道排序 | | Y轴排序 | `sortY` | 按 Y 通道排序 | | 颜色排序 | `sortColor` | 按颜色通道排序 | > **详细文档**: [sortX](references/transforms/g2-transform-sortx.md) | [sortY](references/transforms/g2-transform-sorty.md) | [sortColor](references/transforms/g2-transform-sort-color.md) ### 8.4 选取变换 / Selection | 变换 | 类型 | 用途 | |------|------|------| | 选取 | `select` | 全局选取数据 | | X轴选取 | `selectX` | 按 X 分组选取 | | Y轴选取 | `selectY` | 按 Y 分组选取 | > **详细文档**: [select](references/transforms/g2-transform-select.md) | [selectX](references/transforms/g2-transform-selectx.md) | [selectY](references/transforms/g2-transform-selecty.md) ### 8.5 其他变换 / Others | 变换 | 类型 | 用途 | |------|------|------| | 归一化 | `normalizeY` | Y 轴归一化 | | 差值 | `diffY` | 计算差值 | | 对称 | `symmetryY` | Y 轴对称 | | 弹性X | `flexX` | X 轴弹性布局 | | 堆叠入场 | `stackEnter` | 入场动画堆叠 | > **详细文档**: [normalizeY](references/transforms/g2-transform-normalizey.md) | [diffY](references/transforms/g2-transform-diffy.md) | [symmetryY](references/transforms/g2-transform-symmetryy.md) | [flexX](references/transforms/g2-transform-flexx.md) | [stackEnter](references/transforms/g2-transform-stack-enter.md) --- ## 9. Interactions / 交互 G2 提供丰富的内置交互，用于数据探索和图表操作。 ### 9.1 选择类交互 / Selection | 交互 | 类型 | 用途 | |------|------|------| | 元素选择 | `elementSelect` | 点击选择数据元素 | | 按条件选择 | `elementSelectBy` | 按条件批量选择元素 | | 框选 | `brush` / `brushX` / `brushY` | 矩形区域选择 | | 二维框选 | `brushXY` | XY 同时框选 | | 轴框选 | `brushAxis` | 坐标轴范围选择 | | 图例过滤 | `legendFilter` | 点击图例筛选数据 | > **详细文档**: [elementSelect](references/interactions/g2-interaction-element-select.md) | [elementSelectBy](references/interactions/g2-interaction-element-select-by.md) | [brush](references/interactions/g2-interaction-brush.md) | [brushXY](references/interactions/g2-interaction-brush-xy.md) | [brushAxis](references/interactions/g2-interaction-brush-axis.md) | [legendFilter](references/interactions/g2-interaction-legend-filter.md) ### 9.2 高亮类交互 / Highlight | 交互 | 类型 | 用途 | |------|------|------| | 元素高亮 | `elementHighlight` | 悬停高亮元素 | | 按条件高亮 | `elementHighlightBy` | 按条件批量高亮元素 | | 悬停缩放 | `elementHoverScale` | 悬停时元素放大 | | 图例高亮 | `legendHighlight` | 悬停图例高亮对应元素 | | 框选高亮 | `brushXHighlight` / `brushYHighlight` | 框选区域高亮 | > **详细文档**: [elementHighlight](references/interactions/g2-interaction-element-highlight.md) | [elementHighlightBy](references/interactions/g2-interaction-element-highlight-by.md) | [elementHoverScale](references/interactions/g2-interaction-element-hover-scale.md) | [legendHighlight](references/interactions/g2-interaction-legend-highlight.md) | [brushXHighlight](references/interactions/g2-interaction-brushx-highlight.md) | [brushYHighlight](references/interactions/g2-interaction-brushy-highlight.md) | [单轴框选高亮](references/interactions/g2-interaction-brush-x-y-highlight.md) ### 9.3 过滤类交互 / Filter | 交互 | 类型 | 用途 | |------|------|------| | 滑动条过滤 | `sliderFilter` | 滑动条筛选数据范围 | | 滚动条过滤 | `scrollbarFilter` | 滚动条筛选数据 | | 框选过滤 | `brushFilter` | 框选区域过滤数据 | | X轴框选过滤 | `brushXFilter` | X 轴方向框选过滤 | | Y轴框选过滤 | `brushYFilter` | Y 轴方向框选过滤 | | 自适应过滤 | `adaptiveFilter` | 自适应数据过滤 | > **详细文档**: [sliderFilter](references/interactions/g2-interaction-slider-filter.md) | [scrollbarFilter](references/interactions/g2-interaction-scrollbar-filter.md) | [brushFilter](references/interactions/g2-interaction-brush-filter.md) | [brushXFilter](references/interactions/g2-interaction-brushx-filter.md) | [brushYFilter](references/interactions/g2-interaction-brushy-filter.md) | [adaptiveFilter](references/interactions/g2-interaction-adaptive-filter.md) ### 9.4 其他交互 / Others | 交互 | 类型 | 用途 | |------|------|------| | 提示信息 | `tooltip` | 悬停显示数据详情 | | 气泡提示 | `poptip` | 简洁气泡提示 | | 下钻 | `drilldown` | 层级数据下钻 | | 矩形树图下钻 | `treemapDrilldown` | 矩形树图层级下钻 | | 缩放 | `fisheye` | 鱼眼放大镜效果 | | 滚轮滑动 | `sliderWheel` | 鼠标滚轮控制滑动条 | | 拖拽移动 | `elementPointMove` | 拖拽数据点移动 | | 图表索引 | `chartIndex` | 多图表联动索引线 | > **详细文档**: [tooltip](references/interactions/g2-interaction-tooltip.md) | [poptip](references/interactions/g2-interaction-poptip.md) | [drilldown](references/interactions/g2-interaction-drilldown.md) | [treemapDrilldown](references/interactions/g2-interaction-treemap-drilldown.md) | [fisheye](references/interactions/g2-interaction-fisheye.md) | [sliderWheel](references/interactions/g2-interaction-slider-wheel.md) | [elementPointMove](references/interactions/g2-interaction-element-point-move.md) | [chartIndex](references/interactions/g2-interaction-chart-index.md) --- ## 10. Components / 组件 组件是图表的辅助元素，如坐标轴、图例、提示信息等。 ### 10.1 坐标轴 / Axis 坐标轴展示数据维度，支持丰富的样式配置。 > **详细文档**: [坐标轴配置](references/components/g2-comp-axis-config.md) | [雷达图坐标轴](references/components/g2-comp-axis-radar.md) ### 10.2 图例 / Legend 图例展示数据分类或连续数值映射，支持分类图例和连续图例（色带）。 | 类型 | 用途 | |------|------| | 分类图例 | 离散分类数据的颜色映射说明 | | 连续图例 | 连续数值的颜色/大小映射说明（色带） | > **详细文档**: [图例配置](references/components/g2-comp-legend-config.md) | [分类图例](references/components/g2-comp-legend-category.md) | [连续图例](references/components/g2-comp-legend-continuous.md) ### 10.3 提示信息 / Tooltip Tooltip 在悬停时显示数据详情，支持自定义模板和格式化。 > **详细文档**: [Tooltip 配置](references/components/g2-comp-tooltip-config.md) ### 10.4 其他组件 / Others | 组件 | 用途 | |------|------| | 标题 | 图表标题 | | 标签 | 数据标签 | | 滚动条 | 数据滚动浏览 | | 滑动条 | 数据范围选择 | | 标注 | 数据标注和辅助线 | > **详细文档**: [标题](references/components/g2-comp-title.md) | [标签](references/components/g2-comp-label-config.md) | [滚动条](references/components/g2-comp-scrollbar.md) | [滑动条](references/components/g2-comp-slider.md) | [标注](references/components/g2-comp-annotation.md) --- ## 11. Scales / 比例尺 比例尺将数据值映射到视觉通道，如位置、颜色、大小等。 ### 11.1 ⚠️ 默认行为（不要过度指定 type） **G2 会根据数据类型自动推断 scale 类型，非特殊情况下不要手动指定 type：** | 数据类型 | 自动推断的 scale | 示例 | |---------|-----------------|------| | 数值字段 | `linear` | `{ value: 100 }` → linear | | 分类字段 | `band` | `{ category: 'A' }` → band | | Date 对象 | `time` | `{ date: new Date() }` → time | ```javascript // ❌ 错误：不必要的 type 指定，可能导致渲染异常 chart.options({ scale: { x: { type: 'linear' }, // ❌ 数值字段默认就是 linear y: { type: 'linear' }, // ❌ 不需要指定 }, }); // ✅ 正确：让 G2 自动推断，只在需要时配置 domain/range chart.options({ scale: { y: { domain: [0, 100] }, // ✅ 只配置需要的属性 color: { range: ['#1890ff', '#52c41a'] }, }, }); ``` **需要手动指定 type 的特殊情况：** | 场景 | type | 说明 | |------|------|------| | 对数刻度 | `log` | 跨数量级数据 | | 幂函数刻度 | `pow` | 非线性数据映射 | | 平方根刻度 | `sqrt` | 非负数据的压缩 | | 字符串日期 | `time` | 日期字段是字符串而非 Date 对象时 | | 自定义映射 | `ordinal` | 离散值到离散值 | | 渐变色 | `sequential` | 连续数值到颜色渐变 | | 分段映射 | `threshold` | 按阈值分段映射到颜色 | | 等量分段 | `quantize` / `quantile` | 连续数据离散化 | ### 11.2 比例尺类型 | 比例尺 | 类型 | 用途 | |--------|------|------| | 线性 | `linear` | 连续数值映射（默认） | | 分类 | `band` | 离散分类映射（默认） | | 点 | `point` | 离散点位置映射 | | 时间 | `time` | 时间数据映射 | | 对数 | `log` | 对数刻度 | | 幂/平方根 | `pow` / `sqrt` | 幂函数/平方根映射 | | 序数 | `ordinal` | 离散值到离散值映射 | | 顺序 | `sequential` | 连续值到颜色渐变 | | 分位数/量化 | `quantile` / `quantize` | 连续数据离散化映射 | | 阈值 | `threshold` | 按阈值分段映射 | > **详细文档**: [linear](references/scales/g2-scale-linear.md) | [band](references/scales/g2-scale-band.md) | [point](references/scales/g2-scale-point.md) | [time](references/scales/g2-scale-time.md) | [log](references/scales/g2-scale-log.md) | [pow/sqrt](references/scales/g2-scale-pow-sqrt.md) | [ordinal](references/scales/g2-scale-ordinal.md) | [sequential](references/scales/g2-scale-sequential.md) | [quantile/quantize](references/scales/g2-scale-quantile-quantize.md) | [threshold](references/scales/g2-scale-threshold.md) --- ## 12. Coordinates / 坐标系 坐标系定义数据到画布位置的映射方式，不同坐标系产生不同图表形态。 | 坐标系 | 类型 | 用途 | |--------|------|------| | 笛卡尔 | `cartesian` | 直角坐标系（默认） | | 极坐标 | `polar` | 雷达图、玫瑰图 | | Theta | `theta` | 饼图、环形图 | | 径向 | `radial` | 径向坐标系，玉珏图 | | 转置 | `transpose` | X/Y 轴交换 | | 平行 | `parallel` | 平行坐标系 | | 螺旋 | `helix` | 螺旋坐标系 | | 鱼眼 | `fisheye` | 局部放大效果 | > **详细文档**: [cartesian](references/coordinates/g2-coord-cartesian.md) | [polar](references/coordinates/g2-coord-polar.md) | [theta](references/coordinates/g2-coord-theta.md) | [radial](references/coordinates/g2-coord-radial.md) | [transpose](references/coordinates/g2-coord-transpose.md) | [parallel](references/coordinates/g2-coord-parallel.md) | [helix](references/coordinates/g2-coord-helix.md) | [fisheye](references/coordinates/g2-coord-fisheye.md) --- ## 13. Compositions / 组合视图 组合视图用于创建多图表布局，如分面、多视图叠加等。 | 组合 | 类型 | 用途 | |------|------|------| | 基础视图 | `view` | 单视图容器，组合多个 mark | | 分面图 | `facetRect` | 按维度拆分矩形网格多图 | | 圆形分面 | `facetCircle` | 按维度拆分环形多图 | | 重复矩阵 | `repeatMatrix` | 多变量组合矩阵图 | | 空间层叠 | `spaceLayer` | 多图层叠加 | | 空间弹性 | `spaceFlex` | 弹性布局 | | 时间关键帧 | `timingKeyframe` | 动画序列 | | 地理视图 | `geoView` | 地理坐标系视图 | | 地图 | `geoPath` | 地理路径绘制 | > **详细文档**: [view](references/compositions/g2-comp-view.md) | [facetRect](references/compositions/g2-comp-facet-rect.md) | [facetCircle](references/compositions/g2-comp-facet-circle.md) | [repeatMatrix](references/compositions/g2-comp-repeat-matrix.md) | [spaceLayer](references/compositions/g2-comp-space-layer.md) | [spaceFlex](references/compositions/g2-comp-space-flex.md) | [timingKeyframe](references/compositions/g2-comp-timing-keyframe.md) | [geoView](references/compositions/g2-comp-geoview.md) | [地图](references/compositions/g2-comp-geo-map.md) --- ## 14. Themes / 主题 主题定义图表的整体视觉风格，包括颜色、字体、间距等。 > **详细文档**: [内置主题](references/themes/g2-theme-builtin.md) | [自定义主题](references/themes/g2-theme-custom.md) --- ## 15. Palettes / 调色板 调色板定义颜色序列，用于分类数据或连续数据的颜色映射。 > **详细文档**: [category10](references/palette/g2-palette-category10.md) | [category20](references/palette/g2-palette-category20.md) --- ## 16. Animations / 动画 动画增强图表的表现力，支持入场、更新、退场动画配置。 **⚠️ 重要规则**：G2 底层自带默认动画效果，用户没有明确要求动画时**不要**添加 `animate` 配置。只有用户明确描述了动画需求（如"渐入动画"、"波浪入场"等）时，才查阅参考文档添加对应的 animate 配置。 > **详细文档**: [动画介绍](references/animations/g2-animation-intro.md) | [动画类型](references/animations/g2-animation-types.md) | [关键帧动画](references/animations/g2-animation-keyframe.md) --- ## 17. Label Transforms / 标签变换 标签变换用于处理标签重叠、溢出等问题，提升标签可读性。 | 变换 | 类型 | 用途 | |------|------|------| | 溢出隐藏 | `overflowHide` | 超出区域的标签隐藏 | | 重叠隐藏 | `overlapHide` | 重叠标签自动隐藏 | | 重叠偏移 | `overlapDodgeY` | 重叠标签 Y 方向偏移 | | 对比反转 | `contrastReverse` | 标签颜色自动反转以保证对比度 | | 溢出调整 | `exceedAdjust` | 超出画布边界的标签位置调整 | | 溢出描边 | `overflowStroke` | 溢出区域添加描边标记 | > **详细文档**: [overflowHide](references/label-transform/g2-label-transform-overflow-hide.md) | [overlapHide](references/label-transform/g2-label-transform-overlap-hide.md) | [overlapDodgeY](references/label-transform/g2-label-transform-overlap-dodge-y.md) | [contrastReverse](references/label-transform/g2-label-transform-contrast-reverse.md) | [exceedAdjust](references/label-transform/g2-label-transform-exceed-adjust.md) | [overflowStroke](references/label-transform/g2-label-transform-overflow-stroke.md) --- ## 18. Patterns / 模式 模式是常见场景的最佳实践，包含迁移指南、性能优化、响应式适配等。 ### 18.1 迁移指南 / Migration (v4 → v5) | v4 (Deprecated) | v5 (Correct) | |-----------------|--------------| | `chart.source(data)` | `chart.options({ data })` | | `.position('x*y')` | `encode: { x: 'x', y: 'y' }` | | `.color('field')` | `encode: { color: 'field' }` | | `.adjust('stack')` | `transform: [{ type: 'stackY' }]` | | `.adjust('dodge')` | `transform: [{ type: 'dodgeX' }]` | | `label: {}` | `labels: [{}]` | > **详细文档**: [v4 → v5 迁移](references/patterns/g2-pattern-v4-to-v5.md) ### 18.2 性能优化 / Performance 数据预聚合、LTTB 降采样、Canvas 渲染器确认、高频实时数据节流更新。 | 场景 | 数据量 | 建议方案 | |------|--------|---------| | 折线图 | < 1,000 点 | 直接渲染 | | 折线图 | 1,000 ~ 10,000 点 | 降采样到 500 点以内 | | 折线图 | > 10,000 点 | 后端聚合 + 时间范围过滤 | | 散点图 | < 5,000 点 | 直接渲染 | | 散点图 | 5,000 ~ 50,000 点 | Canvas 渲染 + 降采样 | > **详细文档**: [性能优化](references/patterns/g2-pattern-performance.md) ### 18.3 响应式适配 / Responsive autoFit 自适应、ResizeObserver 动态调整、移动端字体/边距适配。 > **详细文档**: [响应式适配](references/patterns/g2-pattern-responsive.md) ---

path: references/palette/g2-palette-category20.md
content: ---
id: "g2-palette-category20"
title: "G2 Category20 调色板"
description: |
  AntV 经典 20 色调色板，用于分类数据的颜色映射。
  包含 20 种颜色，采用饱和度交替的设计，适合更多类别的可视化场景。

library: "g2"
version: "5.x"
category: "palette"
tags:
  - "调色板"
  - "palette"
  - "颜色"
  - "分类"
  - "20色"

related:
  - "g2-palette-category10"
  - "g2-scale-ordinal"
  - "g2-theme-builtin"

use_cases:
  - "超过 10 个类别的颜色映射"
  - "需要更多颜色区分的场景"
  - "复杂分类数据可视化"

anti_patterns:
  - "类别较少时建议使用 Category10"

difficulty: "beginner"
completeness: "full"
created: "2025-03-26"
updated: "2025-03-26"
author: "antv-team"
source_url: "https://g2.antv.antgroup.com/manual/core/palette"
---

## 核心概念

Category20 是 AntV 的扩展分类调色板：
- 包含 20 种颜色
- 采用饱和度交替的设计模式
- 适合 10-20 个类别的场景

**设计特点：**
- 前半部分为饱和色（与 Category10 一致）
- 后半部分为低饱和度色
- 交替使用可增加区分度

## 最小可运行示例

```javascript
import { Chart } from '@antv/g2';

const chart = new Chart({
  container: 'container',
  width: 640,
  height: 480,
});

chart.options({
  type: 'interval',
  data: [
    { category: 'A', value: 100 },
    { category: 'B', value: 150 },
    // ... 更多类别
  ],
  encode: {
    x: 'category',
    y: 'value',
    color: 'category',
  },
  scale: {
    color: {
      type: 'ordinal',
      range: 'category20',
    },
  },
});

chart.render();
```

## 常用变体

### 显式指定颜色范围

```javascript
chart.options({
  type: 'interval',
  data,
  encode: { x: 'category', y: 'value', color: 'category' },
  scale: {
    color: {
      type: 'ordinal',
      range: [
        '#5B8FF9', '#CDDDFD',
        '#5AD8A6', '#CDF3E4',
        '#5D7092', '#CED4DE',
        '#F6BD16', '#FCEBB9',
        '#6F5EF9', '#D3CEFD',
        '#6DC8EC', '#D3EEF9',
        '#945FB9', '#DECFEA',
        '#FF9845', '#FFE0C7',
        '#1E9493', '#BBDEDE',
        '#FF99C3', '#FFE0ED',
      ],
    },
  },
});
```

### 结合自定义颜色

```javascript
chart.options({
  type: 'interval',
  data,
  encode: { x: 'category', y: 'value', color: 'category' },
  scale: {
    color: {
      type: 'ordinal',
      range: [
        ...customColors.slice(0, 10),
        '#5B8FF9', '#CDDDFD', // 补充 Category20 的颜色
        // ...
      ],
    },
  },
});
```

## 完整颜色参考

| 索引 | 颜色值 | 饱和度 |
|------|--------|--------|
| 0 | #5B8FF9 | 高 |
| 1 | #CDDDFD | 低 |
| 2 | #5AD8A6 | 高 |
| 3 | #CDF3E4 | 低 |
| 4 | #5D7092 | 高 |
| 5 | #CED4DE | 低 |
| 6 | #F6BD16 | 高 |
| 7 | #FCEBB9 | 低 |
| 8 | #6F5EF9 | 高 |
| 9 | #D3CEFD | 低 |
| 10 | #6DC8EC | 高 |
| 11 | #D3EEF9 | 低 |
| 12 | #945FB9 | 高 |
| 13 | #DECFEA | 低 |
| 14 | #FF9845 | 高 |
| 15 | #FFE0C7 | 低 |
| 16 | #1E9493 | 高 |
| 17 | #BBDEDE | 低 |
| 18 | #FF99C3 | 高 |
| 19 | #FFE0ED | 低 |

## 与 Category10 的对比

| 特性 | Category10 | Category20 |
|------|------------|------------|
| 颜色数量 | 10 | 20 |
| 颜色风格 | 饱和度一致 | 饱和度交替 |
| 适用场景 | ≤10 类别 | 10-20 类别 |
| 默认使用 | 是 | 否 |
| 区分难度 | 较易 | 需注意相邻色 |

## 设计建议

### 类别数量建议

| 类别数量 | 推荐调色板 |
|---------|-----------|
| 1-5 | Category10 |
| 6-10 | Category10 |
| 11-15 | Category20 |
| 16-20 | Category20 |
| >20 | 自定义或分组 |

### 使用技巧

```javascript
// 技巧：利用饱和度交替增加区分度
// 将重要类别放在高饱和度位置（偶数索引）

// 例如：高亮重要类别
chart.options({
  type: 'interval',
  data,
  encode: { x: 'category', y: 'value', color: 'category' },
  scale: {
    color: {
      type: 'ordinal',
      range: [
        '#5B8FF9',  // 高饱和 - 类别 A
        '#CDDDFD',  // 低饱和 - 类别 B
        '#5AD8A6',  // 高饱和 - 类别 C（重要）
        // ...
      ],
    },
  },
});
```

## 常见错误与修正

### 错误 1：类别超过 20 个

```javascript
// ⚠️ 注意：超过 20 个类别会循环使用颜色

// ✅ 解决方案 1：自定义更多颜色
scale: {
  color: {
    type: 'ordinal',
    range: [...30colors]
  }
}

// ✅ 解决方案 2：合并小类别
// 将小类别合并为"其他"类别
```

### 错误 2：相邻颜色区分度不够

```javascript
// ⚠️ 注意：相邻的低饱和度颜色可能难以区分

// ✅ 解决方案：调整 domain 顺序
scale: {
  color: {
    type: 'ordinal',
    domain: ['A', 'C', 'E', 'B', 'D'],  // 交替高/低饱和度
    range: 'category20'
  }
}
```

path: references/palette/g2-palette-category10.md
content: ---
id: "g2-palette-category10"
title: "G2 Category10 调色板"
description: |
  AntV 经典 10 色调色板，用于分类数据的颜色映射。
  包含 10 种精心设计的颜色，适合大多数分类可视化场景。

library: "g2"
version: "5.x"
category: "palette"
tags:
  - "调色板"
  - "palette"
  - "颜色"
  - "分类"
  - "10色"

related:
  - "g2-palette-category20"
  - "g2-scale-ordinal"
  - "g2-theme-builtin"

use_cases:
  - "分类数据的默认颜色"
  - "柱状图、折线图的颜色映射"
  - "需要 10 种以内颜色的场景"

anti_patterns:
  - "超过 10 个类别时应考虑 Category20 或自定义调色板"

difficulty: "beginner"
completeness: "full"
created: "2025-03-26"
updated: "2025-03-26"
author: "antv-team"
source_url: "https://g2.antv.antgroup.com/manual/core/palette"
---

## 核心概念

Category10 是 AntV 的默认分类调色板：
- 包含 10 种颜色
- 颜色经过精心设计，易于区分
- 适合大多数分类可视化场景

**颜色列表：**
```
#5B8FF9  - 蓝色
#5AD8A6  - 绿色
#5D7092  - 灰蓝色
#F6BD16  - 黄色
#6F5EF9  - 紫色
#6DC8EC  - 青色
#945FB9  - 深紫色
#FF9845  - 橙色
#1E9493  - 深青色
#FF99C3  - 粉色
```

## 最小可运行示例

```javascript
import { Chart } from '@antv/g2';

const chart = new Chart({
  container: 'container',
  width: 640,
  height: 480,
});

chart.options({
  type: 'interval',
  data: [
    { category: 'A', value: 100 },
    { category: 'B', value: 150 },
    { category: 'C', value: 80 },
  ],
  encode: {
    x: 'category',
    y: 'value',
    color: 'category',
  },
  // Category10 是默认调色板，无需显式指定
});

chart.render();
```

## 常用变体

### 显式指定调色板

```javascript
chart.options({
  type: 'interval',
  data,
  encode: { x: 'category', y: 'value', color: 'category' },
  scale: {
    color: {
      type: 'ordinal',
      range: 'category10',  // 显式指定
    },
  },
});
```

### 自定义颜色范围

```javascript
chart.options({
  type: 'interval',
  data,
  encode: { x: 'category', y: 'value', color: 'category' },
  scale: {
    color: {
      type: 'ordinal',
      range: [
        '#5B8FF9',
        '#5AD8A6',
        '#5D7092',
        '#F6BD16',
        '#6F5EF9',
      ],
    },
  },
});
```

### 使用 Theme 配置

```javascript
chart.options({
  type: 'interval',
  data,
  encode: { x: 'category', y: 'value', color: 'category' },
  theme: {
    defaultCategory10: 'category10',
  },
});
```

## 完整颜色参考

| 索引 | 颜色值 | 颜色名 |
|------|--------|--------|
| 0 | #5B8FF9 | 蓝色 |
| 1 | #5AD8A6 | 绿色 |
| 2 | #5D7092 | 灰蓝色 |
| 3 | #F6BD16 | 黄色 |
| 4 | #6F5EF9 | 紫色 |
| 5 | #6DC8EC | 青色 |
| 6 | #945FB9 | 深紫色 |
| 7 | #FF9845 | 橙色 |
| 8 | #1E9493 | 深青色 |
| 9 | #FF99C3 | 粉色 |

## 与 Category20 的对比

| 特性 | Category10 | Category20 |
|------|------------|------------|
| 颜色数量 | 10 | 20 |
| 颜色风格 | 饱和度一致 | 饱和度交替 |
| 适用场景 | ≤10 类别 | 10-20 类别 |
| 默认使用 | 是 | 否 |

## 常见错误与修正

### 错误 1：类别超过 10 个

```javascript
// ⚠️ 注意：超过 10 个类别会循环使用颜色
// 类别 11 会使用与类别 1 相同的颜色

// ✅ 解决方案 1：使用 Category20
scale: {
  color: { type: 'ordinal', range: 'category20' }
}

// ✅ 解决方案 2：自定义更多颜色
scale: {
  color: {
    type: 'ordinal',
    range: [...customColors]
  }
}
```

### 错误 2：颜色值格式错误

```javascript
// ❌ 错误：颜色值格式不正确
range: ['rgb(91, 143, 249)', ...]

// ✅ 正确：使用标准 HEX 格式
range: ['#5B8FF9', ...]
```

path: references/animations/g2-animation-intro.md
content: ---
id: "g2-animation-intro"
title: "G2 动画系统总览（animate 配置）"
description: |
  G2 v5 动画系统通过 animate 属性配置，支持入场（enter）、更新（update）、退场（exit）三种时机。
  内置动画类型包括 fadeIn/Out、scaleInX/Y、growInX/Y、waveIn、zoomIn/Out、morphing、pathIn。
  每种动画可配置 duration（时长）、delay（延迟）、easing（缓动函数）。

library: "g2"
version: "5.x"
category: "animations"
tags:
  - "animation"
  - "动画"
  - "animate"
  - "入场动画"
  - "fadeIn"
  - "scaleInX"
  - "waveIn"

related:
  - "g2-animation-keyframe"
  - "g2-core-chart-init"

use_cases:
  - "图表首次渲染时添加入场动画提升视觉体验"
  - "数据更新时添加过渡动画"
  - "退场时添加淡出效果"

difficulty: "beginner"
completeness: "full"
created: "2025-03-24"
updated: "2025-03-24"
author: "antv-team"
source_url: "https://g2.antv.antgroup.com/manual/core/animate"
---

## 内置动画类型速查

| 动画名 | 效果 | 适合场景 |
|--------|------|---------|
| `fadeIn` | 从透明到不透明 | 通用入场 |
| `fadeOut` | 从不透明到透明 | 通用退场 |
| `scaleInX` | 从 X 轴起点缩放展开 | 柱状图入场 |
| `scaleInY` | 从 Y 轴底部缩放展开 | 柱状图入场（竖向） |
| `scaleOutX` | 向 X 轴收缩消失 | 柱状图退场 |
| `scaleOutY` | 向 Y 轴收缩消失 | 柱状图退场 |
| `growInX` | 从左向右生长 | 条形图、折线图入场 |
| `growInY` | 从下向上生长 | 柱状图入场 |
| `waveIn` | 波浪扫描入场 | 极坐标图（玫瑰图、饼图） |
| `zoomIn` | 从中心缩放放大 | 点图入场 |
| `zoomOut` | 向中心缩小消失 | 点图退场 |
| `pathIn` | 路径逐步绘制 | 折线图、路径图 |
| `morphing` | 形状变形过渡 | 图表类型切换 |

## 最小可运行示例

```javascript
import { Chart } from '@antv/g2';

const chart = new Chart({ container: 'container', width: 640, height: 480 });

chart.options({
  type: 'interval',
  data: [
    { genre: 'Sports', sold: 275 },
    { genre: 'Strategy', sold: 115 },
    { genre: 'Action', sold: 120 },
    { genre: 'RPG', sold: 98 },
  ],
  encode: { x: 'genre', y: 'sold', color: 'genre' },
  animate: {
    enter: {
      type: 'growInY',    // 入场动画：从下向上生长
      duration: 800,      // 持续时间（毫秒）
      delay: 0,           // 延迟
      easing: 'ease-out', // 缓动函数
    },
  },
});

chart.render();
```

## 配置动画的三个时机

```javascript
chart.options({
  type: 'interval',
  data,
  encode: { x: 'x', y: 'y', color: 'type' },
  animate: {
    // 入场：图表首次渲染时
    enter: {
      type: 'scaleInY',
      duration: 1000,
      easing: 'ease-out-bounce',
    },
    // 更新：数据变化时
    update: {
      type: 'morphing',
      duration: 500,
    },
    // 退场：图元被移除时
    exit: {
      type: 'fadeOut',
      duration: 300,
    },
  },
});
```

## 禁用动画

```javascript
// 禁用所有动画
chart.options({
  animate: false,
});

// 仅禁用入场动画
chart.options({
  animate: {
    enter: false,
  },
});
```

## 常见动画组合推荐

```javascript
// 柱状图：growInY 入场
animate: { enter: { type: 'growInY', duration: 800 } }

// 折线图：pathIn 入场（路径绘制效果）
animate: { enter: { type: 'pathIn', duration: 1200 } }

// 饼图（极坐标）：waveIn 入场
animate: { enter: { type: 'waveIn', duration: 1000 } }

// 散点图：zoomIn 入场
animate: { enter: { type: 'zoomIn', duration: 600 } }

// 通用淡入
animate: { enter: { type: 'fadeIn', duration: 500 } }
```

## 常见错误与修正

### 错误 1：animate.enter 写成字符串
```javascript
// ❌ 错误：enter 不是字符串，是对象
chart.options({
  animate: { enter: 'fadeIn' },  // ❌
});

// ✅ 正确
chart.options({
  animate: { enter: { type: 'fadeIn', duration: 600 } },  // ✅
});
```

### 错误 2：在极坐标图用非极坐标动画
```javascript
// ❌ scaleInX/Y 在极坐标中效果不对
chart.options({
  coordinate: { type: 'theta' },
  animate: { enter: { type: 'scaleInY' } },  // ❌ 饼图应该用 waveIn
});

// ✅ 极坐标图推荐 waveIn
chart.options({
  coordinate: { type: 'theta' },
  animate: { enter: { type: 'waveIn', duration: 1000 } },  // ✅
});
```


path: references/animations/g2-animation-keyframe.md
content: ---
id: "g2-animation-keyframe"
title: "G2 关键帧动画（timingKeyframe）"
description: |
  timingKeyframe 是 G2 v5 的组合类型，将多个图表视图按时序播放，
  实现数据故事讲述（data storytelling）效果。
  每个子视图是一个"关键帧"，系统自动在帧间插值过渡，支持形变动画（morphing）。

library: "g2"
version: "5.x"
category: "animations"
tags:
  - "timingKeyframe"
  - "关键帧"
  - "数据故事"
  - "keyframe"
  - "morphing"
  - "动画"
  - "composition"

related:
  - "g2-animation-intro"
  - "g2-core-view-composition"

use_cases:
  - "演示数据如何从一种图表类型变为另一种（柱状图 → 折线图）"
  - "展示数据随时间的演变过程"
  - "数据新闻和可视化故事讲述"

difficulty: "advanced"
completeness: "full"
created: "2025-03-24"
updated: "2025-03-24"
author: "antv-team"
source_url: "https://g2.antv.antgroup.com/manual/core/composition/timing-keyframe"
---

## 最小可运行示例（柱状图 → 折线图）

```javascript
import { Chart } from '@antv/g2';

const data = [
  { month: 'Jan', value: 83 },
  { month: 'Feb', value: 60 },
  { month: 'Mar', value: 95 },
  { month: 'Apr', value: 72 },
  { month: 'May', value: 110 },
];

const chart = new Chart({ container: 'container', width: 640, height: 480 });

chart.options({
  type: 'timingKeyframe',   // 关键帧组合类型
  duration: 1000,           // 每帧过渡时长（毫秒）
  iterationCount: 2,        // 循环次数（'infinite' 为无限循环）
  direction: 'alternate',   // 'normal' | 'reverse' | 'alternate' | 'reverse-alternate'
  easing: 'ease-in-out-sine',
  children: [
    // 关键帧 1：柱状图
    {
      type: 'interval',
      data,
      encode: { x: 'month', y: 'value', color: 'month' },
      axis: { y: { title: '月份销量' } },
    },
    // 关键帧 2：折线图（自动在两者之间插值动画）
    {
      type: 'line',
      data,
      encode: { x: 'month', y: 'value' },
      style: { lineWidth: 3 },
    },
  ],
});

chart.render();
```

## 多关键帧（数据更新动画）

```javascript
chart.options({
  type: 'timingKeyframe',
  duration: 800,
  iterationCount: 'infinite',
  direction: 'alternate',
  children: [
    // 关键帧 1：2022 年数据
    {
      type: 'interval',
       data2022,
      encode: { x: 'city', y: 'gdp', color: 'city' },
      title: '2022 年 GDP',
    },
    // 关键帧 2：2023 年数据（相同字段，自动形变过渡）
    {
      type: 'interval',
      data: data2023,
      encode: { x: 'city', y: 'gdp', color: 'city' },
      title: '2023 年 GDP',
    },
  ],
});
```

## 配置项

```javascript
chart.options({
  type: 'timingKeyframe',
  duration: 1000,                  // 关键帧间过渡时长（毫秒），默认 1000
  iterationCount: 1,               // 循环次数，默认 1；'infinite' 无限循环
  direction: 'normal',             // 播放方向：
                                   //   'normal' - 正向
                                   //   'reverse' - 反向
                                   //   'alternate' - 正反交替
                                   //   'reverse-alternate' - 反正交替
  easing: 'ease-in-out-sine',     // 缓动函数，默认 'ease-in-out-sine'
  children: [/* 各关键帧视图配置 */],
});
```

## 常见错误与修正

### 错误 1：children 帧的 encode 字段名不一致——无法形变
```javascript
// ❌ 字段名不一致，无法识别对应关系，形变效果丢失
children: [
  { type: 'interval', encode: { x: 'month', y: 'sales' } },   // sales
  { type: 'line',     encode: { x: 'month', y: 'revenue' } }, // revenue ❌ 名字不同
]

// ✅ 相同字段名才能实现平滑形变
children: [
  { type: 'interval', encode: { x: 'month', y: 'value' } },
  { type: 'line',     encode: { x: 'month', y: 'value' } },  // ✅ 同名字段
]
```

### 错误 2：iterationCount 写成数字字符串
```javascript
// ❌ 错误：应该是字符串 'infinite'，不是数字
chart.options({ iterationCount: Infinity });  // ❌

// ✅ 正确
chart.options({ iterationCount: 'infinite' });  // ✅
chart.options({ iterationCount: 3 });           // ✅ 或具体数字
```


path: references/animations/g2-animation-types.md
content: ---
id: "g2-animation-types"
title: "G2 内置动画类型详解（fadeIn/scaleIn/growIn/pathIn/waveIn/zoomIn/morphing）"
description: |
  G2 v5 内置多种动画类型，每种适用于不同的 Mark 和坐标系：
  fadeIn/Out（渐显渐隐）、scaleInX/Y（缩放展开）、growInX/Y（生长入场）、
  pathIn（路径绘制）、waveIn（极坐标波浪入场）、zoomIn/Out（缩放点入场）、morphing（形变过渡）。
  通过 animate.enter.type 等配置使用。

library: "g2"
version: "5.x"
category: "animations"
tags:
  - "fadeIn"
  - "scaleInX"
  - "scaleInY"
  - "growInX"
  - "growInY"
  - "pathIn"
  - "waveIn"
  - "zoomIn"
  - "zoomOut"
  - "morphing"
  - "动画类型"

related:
  - "g2-animation-intro"
  - "g2-animation-keyframe"

use_cases:
  - "按图表类型选择最合适的入场动画"
  - "折线图路径绘制动画"
  - "饼图/玫瑰图波浪入场"
  - "数据更新时的形变过渡"

difficulty: "beginner"
completeness: "full"
created: "2025-03-24"
updated: "2025-03-24"
author: "antv-team"
source_url: "https://g2.antv.antgroup.com/manual/core/animate"
---

## 动画类型与适用场景

| 动画名 | 方向 | 最适合 Mark | 特点 |
|--------|------|------------|------|
| `fadeIn` | - | 所有 Mark | 渐显，通用，最安全 |
| `fadeOut` | - | 所有 Mark | 渐隐，退场通用 |
| `scaleInX` | X 轴 | interval（柱状图） | 从左上角向右扩展 |
| `scaleInY` | Y 轴 | interval（柱状图） | 从底部向上缩放 |
| `scaleOutX` | X 轴 | interval | scaleInX 的退场版本 |
| `scaleOutY` | Y 轴 | interval | scaleInY 的退场版本 |
| `growInX` | X 轴 | line, area, interval（直角坐标） | 裁剪从左向右生长 |
| `growInY` | Y 轴 | interval, area（直角坐标） | 裁剪从底部向上生长；**极坐标/helix 禁用** |
| `pathIn` | 路径 | line, path, link | 路径线条逐步绘制 |
| `waveIn` | 波浪 | interval（极坐标） | 极坐标专用扇形展开 |
| `zoomIn` | 中心 | point, text | 从中心缩放放大 |
| `zoomOut` | 中心 | point, text | 向中心缩小消失 |
| `morphing` | 形变 | 所有 Mark | 形状平滑变形过渡 |

## fadeIn / fadeOut（渐显渐隐）

```javascript
// 最通用的动画，适合任何 mark
chart.options({
  type: 'point',
  data,
  encode: { x: 'x', y: 'y' },
  animate: {
    enter: { type: 'fadeIn', duration: 600 },
    exit: { type: 'fadeOut', duration: 300 },
  },
});
```

## scaleInY / growInY（柱状图入场）

```javascript
// scaleInY：缩放展开（有缩放感）
// growInY：裁剪生长（有"从地面长出来"的感觉，更自然）
chart.options({
  type: 'interval',
  data,
  encode: { x: 'genre', y: 'sold' },
  animate: {
    // 方式一：缩放
    enter: { type: 'scaleInY', duration: 800, easing: 'ease-out' },
    // 方式二：生长（推荐）
    // enter: { type: 'growInY', duration: 800 },
  },
});
```

## pathIn（折线图路径绘制）

```javascript
// pathIn：折线/路径从左向右逐步绘制
chart.options({
  type: 'line',
  data: timeSeriesData,
  encode: { x: 'date', y: 'value', color: 'type' },
  animate: {
    enter: {
      type: 'pathIn',      // 路径逐步绘制
      duration: 1500,
      easing: 'linear',    // 匀速绘制效果更佳
    },
  },
});
```

## waveIn（极坐标/饼图专用）

```javascript
// waveIn：从外圈向内的波浪扫入，专为极坐标设计
chart.options({
  type: 'interval',
  data,
  encode: { y: 'value', color: 'type' },
  transform: [{ type: 'stackY' }],
  coordinate: { type: 'theta', outerRadius: 0.8 },
  animate: {
    enter: {
      type: 'waveIn',       // 极坐标专用
      duration: 1000,
    },
  },
});
```

## zoomIn / zoomOut（点图缩放）

```javascript
// zoomIn：散点从中心缩放出现
chart.options({
  type: 'point',
  data: scatterData,
  encode: { x: 'x', y: 'y', size: 'value' },
  animate: {
    enter: { type: 'zoomIn', duration: 500 },
    exit: { type: 'zoomOut', duration: 300 },
  },
});
```

## morphing（形变更新动画）

```javascript
// morphing：数据更新时图形平滑变形
chart.options({
  type: 'interval',
  data,
  encode: { x: 'genre', y: 'sold' },
  animate: {
    update: {
      type: 'morphing',    // 数据更新时形变过渡
      duration: 600,
    },
  },
});

// 也可以在 timingKeyframe 中自动触发形变
chart.options({
  type: 'timingKeyframe',
  children: [
    { type: 'interval', data, encode: { x: 'x', y: 'y' } },
    { type: 'line',     data, encode: { x: 'x', y: 'y' } },
  ],
});
```

## 按图表类型推荐的动画

```javascript
// 柱状图（推荐 growInY）
{ type: 'interval', animate: { enter: { type: 'growInY', duration: 800 } } }

// 条形图（推荐 growInX）
{ type: 'interval', coordinate: { transform: [{ type: 'transpose' }] },
  animate: { enter: { type: 'growInX', duration: 800 } } }

// 折线图（推荐 pathIn）
{ type: 'line', animate: { enter: { type: 'pathIn', duration: 1200 } } }

// 散点图（推荐 zoomIn 或 fadeIn）
{ type: 'point', animate: { enter: { type: 'zoomIn', duration: 400 } } }

// 饼图/环形图（推荐 waveIn）
{ type: 'interval', coordinate: { type: 'theta' },
  animate: { enter: { type: 'waveIn', duration: 1000 } } }

// 面积图（推荐 fadeIn 或 growInX）
{ type: 'area', animate: { enter: { type: 'fadeIn', duration: 800 } } }

// 螺旋图 helix 坐标系（必须用 fadeIn，禁止用 growInX/Y）
{ type: 'interval', coordinate: { type: 'helix', ... },
  animate: { enter: { type: 'fadeIn', duration: 800 } } }
```

## 常见错误与修正

### 错误 1：在条形图（转置）上用 scaleInY
```javascript
// ❌ 条形图是水平方向，用 scaleInY（竖向缩放）效果不对
chart.options({
  type: 'interval',
  coordinate: { transform: [{ type: 'transpose' }] },
  animate: { enter: { type: 'scaleInY' } },  // ❌ 应该用 growInX 或 scaleInX
});

// ✅ 条形图用 X 方向动画
chart.options({
  animate: { enter: { type: 'growInX', duration: 800 } },  // ✅
});
```

### 错误 2：在 helix（螺旋）坐标系上用 growInX/growInY

`growInX` / `growInY` 的实现是沿直角坐标轴方向做 **clipPath 裁剪**。在 `helix` 坐标系中，坐标轴被重映射为螺旋路径，屏幕上不存在"底部"或"左侧"基线，裁剪矩形会横穿螺旋形，导致部分螺旋区域被切掉或渲染残缺，动画结束后图表也可能显示不完整。

**同样问题适用于所有非直角坐标系**（`polar`、`theta`、`helix`）——这些坐标系均应使用 `waveIn`（极坐标专用）或 `fadeIn`（通用），不能使用 `growInX/Y`。

```javascript
// ❌ 错误：helix 坐标系用 growInY → 裁剪矩形横穿螺旋，图表渲染残缺
chart.options({
  type: 'interval',
  coordinate: { type: 'helix', startAngle: 0, endAngle: Math.PI * 6 },
  animate: {
    enter: { type: 'growInY', duration: 2000 },  // ❌ 螺旋被裁剪，部分区域缺失
  },
});

// ✅ 正确：helix 坐标系用 fadeIn
chart.options({
  type: 'interval',
  coordinate: { type: 'helix', startAngle: 0, endAngle: Math.PI * 6 },
  animate: {
    enter: { type: 'fadeIn', duration: 1000 },   // ✅ 渐显，无裁剪副作用
  },
});

// ✅ 极坐标（theta/polar）用 waveIn
chart.options({
  type: 'interval',
  coordinate: { type: 'theta' },
  animate: {
    enter: { type: 'waveIn', duration: 1000 },   // ✅ 极坐标专用扇形展开
  },
});
```

**根本原因**：`growInX/Y` 假设存在固定的直角基线（X=0 或 Y=0）作为裁剪起点，这在笛卡尔坐标系中成立；但 `helix` / `polar` 将坐标重映射到极坐标或螺旋路径后，该基线不再对应可见边界，裁剪结果是任意截断螺旋形状。


path: references/themes/g2-theme-custom.md
content: ---
id: "g2-theme-custom"
title: "G2 自定义主题创建（register + create）"
description: |
  G2 v5 支持通过 register('theme.xxx', themeConfig) 注册自定义主题。
  自定义主题可以覆盖配色、字体、各 Mark 的默认样式等。
  也可以用 theme 字段传入对象，局部覆盖当前主题的特定属性。
  内置主题包括 classic、classicDark、academy（详见 g2-theme-builtin）。

library: "g2"
version: "5.x"
category: "themes"
tags:
  - "theme"
  - "自定义主题"
  - "register"
  - "主题注册"
  - "colors10"
  - "colors20"
  - "配色方案"

related:
  - "g2-theme-builtin"
  - "g2-core-chart-init"

use_cases:
  - "企业品牌定制化图表主题"
  - "统一多图表的配色风格"
  - "局部覆盖某几项默认样式"

difficulty: "intermediate"
completeness: "full"
created: "2025-03-24"
updated: "2025-03-24"
author: "antv-team"
source_url: "https://g2.antv.antgroup.com/manual/core/theme"
---

## 方式一：局部覆盖主题（theme 对象）

最简单的方式是在 options 的 theme 字段中直接传对象，覆盖部分属性：

```javascript
import { Chart } from '@antv/g2';

const chart = new Chart({ container: 'container', width: 640, height: 480 });

chart.options({
  type: 'interval',
  data,
  encode: { x: 'genre', y: 'sold', color: 'genre' },
  theme: {
    // 覆盖分类色板
    colors10: [
      '#3B82F6', '#EF4444', '#10B981', '#F59E0B',
      '#8B5CF6', '#F97316', '#06B6D4', '#84CC16',
      '#EC4899', '#6B7280',
    ],
    // 覆盖默认颜色
    defaultColor: '#3B82F6',
  },
});

chart.render();
```

## 方式二：注册全局自定义主题

```javascript
import { Chart, register } from '@antv/g2';

// 注册自定义主题（基于 classic 主题扩展）
register('theme.brand', {
  // 基础颜色
  defaultColor: '#e63946',
  defaultStrokeColor: '#1d1d1d',

  // 分类色板（10色 / 20色）
  colors10: [
    '#e63946', '#457b9d', '#1d3557', '#a8dadc',
    '#f1faee', '#e9c46a', '#f4a261', '#e76f51',
    '#264653', '#2a9d8f',
  ],
  colors20: [
    '#e63946', '#457b9d', '#1d3557', '#a8dadc',
    '#f1faee', '#e9c46a', '#f4a261', '#e76f51',
    '#264653', '#2a9d8f',
    // 后 10 种（渐变或变体）
    '#ff6b6b', '#74b9ff', '#55efc4', '#ffeaa7',
    '#dfe6e9', '#fab1a0', '#fd79a8', '#6c5ce7',
    '#00b894', '#00cec9',
  ],
});

// 使用自定义主题
const chart = new Chart({ container: 'container', width: 640, height: 480 });

chart.options({
  type: 'interval',
  data,
  encode: { x: 'genre', y: 'sold', color: 'genre' },
  theme: 'brand',   // 使用注册的自定义主题名
});

chart.render();
```

## 主题配置项速查

```javascript
// 以下是可覆盖的主要配置项
const themeConfig = {
  // ── 基础颜色 ─────────────────────────────
  defaultColor: '#1890ff',        // 默认颜色（单系列时）
  defaultStrokeColor: '#ffffff',  // 默认描边颜色

  // ── 色板 ──────────────────────────────────
  colors10: [...],   // 10 色分类色板
  colors20: [...],   // 20 色分类色板

  // ── 背景 ──────────────────────────────────
  background: '#ffffff',    // 图表背景色

  // ── 字体 ──────────────────────────────────
  fontFamily: 'sans-serif',  // 全局字体

  // ── 动画默认时长 ───────────────────────────
  enter: { duration: 300 },
  update: { duration: 300 },
  exit: { duration: 300 },
};
```

## 深色主题（基于 classicDark 局部覆盖）

```javascript
// 在 classicDark 基础上修改
const chart = new Chart({
  container: 'container',
  theme: 'classicDark',
});

chart.options({
  type: 'line',
  data,
  encode: { x: 'date', y: 'value', color: 'type' },
  // 局部覆盖：修改配色但保留深色背景
  theme: {
    colors10: ['#60a5fa', '#34d399', '#f87171', '#a78bfa',
               '#fbbf24', '#22d3ee', '#f472b6', '#4ade80',
               '#fb923c', '#e879f9'],
  },
});
```

## 常见错误与修正

### 错误：register 主题名忘加 'theme.' 前缀
```javascript
// ❌ 错误：注册时必须用 'theme.xxx' 格式
register('brandTheme', { colors10: [...] });    // ❌
chart.options({ theme: 'brandTheme' });          // 不生效

// ✅ 正确：必须加 'theme.' 前缀
register('theme.brandTheme', { colors10: [...] });  // ✅
chart.options({ theme: 'brandTheme' });              // ✅ 使用时不带前缀
```

### 错误：theme 和 style 混用
```javascript
// ❌ 错误：主题配色和单个 mark 的样式混淆
chart.options({
  type: 'interval',
  style: { colors10: [...] },  // ❌ colors10 不在 style 里
});

// ✅ 颜色主题在 theme 字段
chart.options({
  type: 'interval',
  theme: { colors10: [...] },  // ✅
  style: { fillOpacity: 0.8 }, // ✅ 单 mark 样式在 style 里
});
```


path: references/themes/g2-theme-builtin.md
content: ---
id: "g2-theme-builtin"
title: "G2 内置主题配置"
description: |
  G2 v5 内置 classic（经典）、classicDark（深色）、academy（学术）三种主题。
  通过 theme 字段或 Chart 构造函数中的 theme 参数全局切换，也可局部覆盖样式变量。

library: "g2"
version: "5.x"
category: "themes"
tags:
  - "theme"
  - "主题"
  - "dark"
  - "深色主题"
  - "classicDark"
  - "spec"

related:
  - "g2-core-chart-init"
  - "g2-mark-interval-basic"

use_cases:
  - "切换图表整体配色风格"
  - "适配深色模式（Dark Mode）"
  - "统一多图表的视觉风格"

difficulty: "beginner"
completeness: "full"
created: "2024-01-01"
updated: "2025-03-01"
author: "antv-team"
source_url: "https://g2.antv.antgroup.com/manual/core/theme"
---

## 内置主题列表

| 主题名 | 说明 |
|--------|------|
| `'classic'` | 默认主题（蓝色系，白色背景） |
| `'classicDark'` | 深色主题（深色背景，明亮配色） |
| `'academy'` | 学术风主题（灰色调，适合论文/报告） |

## 基本用法（切换主题）

```javascript
import { Chart } from '@antv/g2';

// 方式 1：构造函数中指定
const chart = new Chart({
  container: 'container',
  width: 640,
  height: 480,
  theme: 'classicDark',    // 深色主题
});

chart.options({
  type: 'interval',
  data: [
    { genre: 'Sports',   sold: 275 },
    { genre: 'Strategy', sold: 115 },
    { genre: 'Action',   sold: 120 },
  ],
  encode: { x: 'genre', y: 'sold', color: 'genre' },
});

chart.render();
```

```javascript
// 方式 2：options 中指定
const chart = new Chart({ container: 'container', width: 640, height: 480 });

chart.options({
  type: 'interval',
  data,
  encode: { x: 'genre', y: 'sold', color: 'genre' },
  theme: 'academy',        // 学术主题
});

chart.render();
```

## 深色主题示例

```javascript
const chart = new Chart({
  container: 'container',
  width: 700,
  height: 400,
  theme: 'classicDark',
});

chart.options({
  type: 'view',
  data,
  encode: { x: 'month', y: 'value' },
  children: [
    {
      type: 'area',
      style: { fillOpacity: 0.3 },
    },
    {
      type: 'line',
      style: { lineWidth: 2 },
    },
  ],
});

chart.render();
```

## 运行时切换主题

```javascript
const chart = new Chart({ container: 'container', width: 640, height: 480 });

chart.options({ type: 'interval', data, encode: { x: 'x', y: 'y' } });
chart.render();

// 切换到深色主题（需重新渲染）
chart.theme('classicDark');
chart.render();
```

## 局部覆盖主题变量

```javascript
chart.options({
  type: 'interval',
  data,
  encode: { x: 'genre', y: 'sold' },
  theme: {
    defaultColor: '#ff6b35',         // 默认颜色（第一个系列的颜色）
    defaultStrokeColor: '#333',      // 默认边框色
    // 覆盖色板
    colors10: [
      '#ff6b35', '#f7c59f', '#efefd0', '#004e89', '#1a936f',
      '#88d498', '#c6dabf', '#eaf4d3', '#7b2d8b', '#ff3a5c',
    ],
  },
});
```

## 自定义主题注册

```javascript
import { Chart, register } from '@antv/g2';

// 注册自定义主题
register('theme.myTheme', {
  defaultColor: '#e63946',
  background: '#f8f9fa',
  colors10: ['#e63946', '#457b9d', '#1d3557', '#a8dadc', '#f1faee'],
  // ... 其他变量
});

const chart = new Chart({ container: 'container', width: 640, height: 480 });

chart.options({
  type: 'interval',
  data,
  encode: { x: 'genre', y: 'sold', color: 'genre' },
  theme: 'myTheme',   // 使用自定义主题
});

chart.render();
```

## 常见错误与修正

### 错误：theme 传入了非字符串/对象类型
```javascript
// ❌ 错误：theme 值不存在
chart.options({ theme: 'dark' });   // 'dark' 不是内置主题名

// ✅ 正确：使用内置主题名
chart.options({ theme: 'classicDark' });   // 深色主题
chart.options({ theme: 'classic' });       // 默认主题
chart.options({ theme: 'academy' });       // 学术主题
```

### 错误：切换主题后忘记重新渲染
```javascript
// ❌ 错误：切换主题后没有重新渲染
chart.theme('classicDark');
// 图表没有变化！

// ✅ 正确：切换后需调用 render()
chart.theme('classicDark');
chart.render();
```


path: references/data/g2-data-transform-patterns.md
content: ---
id: "g2-data-transform-patterns"
title: "G2 数据转换模式"
description: |
  G2 可视化前最常用的数据预处理模式：宽转长（fold）、分组聚合、
  百分比计算、排名/排序、数据过滤、时间粒度聚合。
  这些模式既可在 JavaScript 前端完成，也可使用 G2 的内置 data transform。

library: "g2"
version: "5.x"
category: "data"
tags:
  - "数据转换"
  - "数据处理"
  - "宽转长"
  - "fold"
  - "分组聚合"
  - "百分比"
  - "排序"
  - "时间聚合"

related:
  - "g2-data-fold"
  - "g2-data-filter"
  - "g2-data-sort"
  - "g2-data-slice"

use_cases:
  - "将 API 返回的宽表转为 G2 需要的长表"
  - "前端对数据进行聚合、排名、百分比计算"
  - "为不同图表类型准备对应格式的数据"

difficulty: "intermediate"
completeness: "full"
created: "2024-01-01"
updated: "2025-03-27"
author: "antv-team"
---

## 核心概念

**数据转换有两种方式**：

1. **JavaScript 前端预处理**：在传入 G2 之前用 JS 函数处理
2. **G2 内置 data transform**：配置在 `data.transform` 中，声明式处理

**推荐**：优先使用 G2 内置 transform，代码更简洁、可序列化。

## 模式 1：宽表转长表（Wide to Long）

**场景**：后端返回每列一个指标的宽表，需转为每行一个观测值的长表

**JavaScript 方式**：
```javascript
// 输入：宽表
const wideData = [
  { month: 'Jan', 北京: 3.6, 上海: 4.3, 广州: 2.8 },
  { month: 'Feb', 北京: 3.8, 上海: 4.5, 广州: 3.0 },
];

function wideToLong(data, idCols, valueCols, keyName = 'key', valueName = 'value') {
  return data.flatMap(row =>
    valueCols.map(col => ({
      ...Object.fromEntries(idCols.map(id => [id, row[id]])),
      [keyName]: col,
      [valueName]: row[col],
    }))
  );
}

const longData = wideToLong(wideData, ['month'], ['北京', '上海', '广州'], 'city', 'gdp');
// 输出：
// [
//   { month: 'Jan', city: '北京', gdp: 3.6 },
//   { month: 'Jan', city: '上海', gdp: 4.3 },
//   ...
// ]
```

**G2 内置方案**（推荐）：
```javascript
chart.options({
  type: 'interval',
  data: {
    type: 'inline',
    value: wideData,
    transform: [{
      type: 'fold',
      fields: ['北京', '上海', '广州'],
      key: 'city',
      value: 'gdp',
    }],
  },
  encode: { x: 'month', y: 'gdp', color: 'city' },
});
```

## 模式 2：分组聚合（Group + Aggregate）

**场景**：按某字段分组，对另一字段求和/均值/计数

**JavaScript 方式**：
```javascript
// 输入：明细数据
const orderData = [
  { month: 'Jan', region: '华东', amount: 1200 },
  { month: 'Jan', region: '华东', amount: 800 },
  { month: 'Jan', region: '华南', amount: 950 },
  { month: 'Feb', region: '华东', amount: 1500 },
];

function groupSum(data, groupKeys, sumKey) {
  const map = new Map();
  data.forEach(row => {
    const key = groupKeys.map(k => row[k]).join('|');
    if (!map.has(key)) {
      const group = Object.fromEntries(groupKeys.map(k => [k, row[k]]));
      group[sumKey] = 0;
      map.set(key, group);
    }
    map.get(key)[sumKey] += row[sumKey];
  });
  return Array.from(map.values());
}

const aggregated = groupSum(orderData, ['month', 'region'], 'amount');
// 输出：
// [
//   { month: 'Jan', region: '华东', amount: 2000 },
//   { month: 'Jan', region: '华南', amount: 950 },
//   { month: 'Feb', region: '华东', amount: 1500 },
// ]
```

**G2 内置方案**（使用 mark transform）：
```javascript
chart.options({
  type: 'interval',
   orderData,
  encode: { x: 'month', y: 'amount', color: 'region' },
  transform: [{ type: 'groupX', y: 'sum' }],  // 按 x 分组求和
});
```

## 模式 3：百分比计算

**场景**：计算每个类别占总量的百分比（饼图标签、百分比柱状图）

**JavaScript 方式**：
```javascript
function addPercentage(data, valueKey, pctKey = 'pct') {
  const total = data.reduce((sum, d) => sum + (d[valueKey] || 0), 0);
  return data.map(d => ({
    ...d,
    [pctKey]: total > 0 ? ((d[valueKey] / total) * 100).toFixed(1) : '0.0',
  }));
}

const dataWithPct = addPercentage(
  [{ city: '北京', gdp: 3.6 }, { city: '上海', gdp: 4.3 }, { city: '广州', gdp: 2.8 }],
  'gdp'
);
// 输出：[{ city: '北京', gdp: 3.6, pct: '33.6' }, ...]
```

**G2 内置方案**（百分比柱状图）：
```javascript
chart.options({
  type: 'interval',
  data,
  encode: { x: 'city', y: 'gdp', color: 'city' },
  transform: [{ type: 'normalizeY' }],  // Y 轴归一化为百分比
});
```

**在饼图标签中使用百分比**：
```javascript
const total = data.reduce((sum, d) => sum + d.value, 0);

chart.options({
  type: 'interval',
  data,
  encode: { y: 'value', color: 'type' },
  transform: [{ type: 'stackY' }],
  coordinate: { type: 'theta' },
  labels: [{
    text: (d) => `${d.type}\n${((d.value / total) * 100).toFixed(1)}%`,
    position: 'outside',
  }],
});
```

## 模式 4：排名 / Top N

**场景**：取数值最大/最小的 N 条数据，用于排名图

**JavaScript 方式**：
```javascript
function topN(data, valueKey, n, ascending = false) {
  return [...data]
    .sort((a, b) => ascending ? a[valueKey] - b[valueKey] : b[valueKey] - a[valueKey])
    .slice(0, n);
}

const top5Cities = topN(cityData, 'gdp', 5);
```

**G2 内置方案**：
```javascript
chart.options({
  type: 'interval',
  data: {
    type: 'inline',
    value: cityData,
    transform: [
      { type: 'sortBy', fields: [['gdp', false]] },  // 按 gdp 降序
      { type: 'slice', end: 5 },                      // 只取前 5 条
    ],
  },
  encode: { x: 'city', y: 'gdp' },
});
```

## 模式 5：时间粒度聚合

**场景**：日粒度数据聚合为周/月/季度

**JavaScript 方式**：
```javascript
// 日粒度 → 月粒度聚合
function aggregateByMonth(data, dateKey, valueKey) {
  const map = new Map();
  data.forEach(d => {
    const date = new Date(d[dateKey]);
    const monthKey = `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}`;
    if (!map.has(monthKey)) {
      map.set(monthKey, { month: monthKey, total: 0, count: 0 });
    }
    const entry = map.get(monthKey);
    entry.total += d[valueKey];
    entry.count += 1;
  });
  return Array.from(map.values())
    .map(({ month, total, count }) => ({
      month,
      value: total,
      avg: total / count,
    }))
    .sort((a, b) => a.month.localeCompare(b.month));
}

// 月粒度 → 季度粒度
function aggregateByQuarter(data, monthKey, valueKey) {
  return data.reduce((acc, d) => {
    const [year, month] = d[monthKey].split('-');
    const quarter = `${year}-Q${Math.ceil(Number(month) / 3)}`;
    const existing = acc.find(a => a.quarter === quarter);
    if (existing) {
      existing[valueKey] += d[valueKey];
    } else {
      acc.push({ quarter, [valueKey]: d[valueKey] });
    }
    return acc;
  }, []);
}
```

## 模式 6：数据过滤与条件筛选

**场景**：根据维度/时间范围筛选数据

**JavaScript 方式**：
```javascript
function filterData(data, conditions) {
  return data.filter(d =>
    conditions.every(({ key, op, value }) => {
      switch (op) {
        case 'eq':  return d[key] === value;
        case 'gt':  return d[key] > value;
        case 'lt':  return d[key] < value;
        case 'gte': return d[key] >= value;
        case 'lte': return d[key] <= value;
        case 'in':  return value.includes(d[key]);
        default:    return true;
      }
    })
  );
}

const filtered = filterData(data, [
  { key: 'region', op: 'in',  value: ['华东', '华南'] },
  { key: 'sales',  op: 'gt',  value: 1000 },
]);
```

**G2 内置方案**：
```javascript
chart.options({
  type: 'interval',
  data: {
    type: 'inline',
    value: data,
    transform: [
      { type: 'filter', callback: (d) => ['华东', '华南'].includes(d.region) && d.sales > 1000 },
    ],
  },
  encode: { x: 'region', y: 'sales' },
});
```

**联动过滤示例**：
```javascript
document.getElementById('region-select').addEventListener('change', (e) => {
  const region = e.target.value;
  const filtered = region === 'all'
    ? allData
    : allData.filter(d => d.region === region);
  chart.changeData(filtered);
});
```

## 模式 7：数据归一化（0-1 标准化）

**场景**：多指标对比时，将不同量级的数据归一化到相同范围

**JavaScript 方式**：
```javascript
// Min-Max 归一化
function normalize(data, valueKey, normalizedKey = 'normalized') {
  const values = data.map(d => d[valueKey]);
  const min = Math.min(...values);
  const max = Math.max(...values);
  const range = max - min || 1;
  return data.map(d => ({
    ...d,
    [normalizedKey]: (d[valueKey] - min) / range,
  }));
}

// 多指标分别归一化（雷达图/平行坐标准备）
function normalizeMultiple(data, keys) {
  return keys.reduce((acc, key) => normalize(acc, key, `${key}_norm`), data);
}
```

## 常见错误与修正

### 错误 1：fold 后字段名与 encode 不匹配

```javascript
// ❌ fold 配置了 key='city', value='gdp'，但 encode 还用原字段名
chart.options({
  type: 'interval',
  data: {
    type: 'inline',
    value: wideData,
    transform: [{ type: 'fold', fields: ['北京', '上海'], key: 'city', value: 'gdp' }],
  },
  encode: { color: '北京' },  // ❌ '北京' 字段已被 fold 消除
});

// ✅ encode 要用 fold 生成的新字段名
chart.options({
  type: 'interval',
  data: {
    type: 'inline',
    value: wideData,
    transform: [{ type: 'fold', fields: ['北京', '上海'], key: 'city', value: 'gdp' }],
  },
  encode: { y: 'gdp', color: 'city' },  // ✅ 使用 key/value 配置的字段名
});
```

### 错误 2：data transform 与 mark transform 混淆

```javascript
// ❌ 错误：fold 是数据变换，不应放在 mark transform
chart.options({
  type: 'interval',
   wideData,
  transform: [{ type: 'fold', fields: ['北京', '上海'] }],  // ❌ 错误位置
});

// ✅ 正确：fold 放在 data.transform 中
chart.options({
  type: 'interval',
  data: {
    type: 'inline',
    value: wideData,
    transform: [{ type: 'fold', fields: ['北京', '上海'], key: 'city', value: 'gdp' }],
  },
  transform: [{ type: 'stackY' }],  // mark transform
});
```

path: references/data/g2-data-fold.md
content: ---
id: "g2-data-fold"
title: "G2 Fold 宽表转长表"
description: |
  Fold 数据变换将宽格式数据（多列）转换为长格式数据（单列+分类列），
  使多个字段可以映射到同一个 color/series 通道。
  配置在 data.transform 中，是在 G2 中实现多系列图表的常用数据预处理手段。

library: "g2"
version: "5.x"
category: "data"
tags:
  - "fold"
  - "宽表转长表"
  - "pivot"
  - "多系列"
  - "数据变换"
  - "data transform"

related:
  - "g2-data-filter"
  - "g2-data-sort"
  - "g2-mark-line-basic"
  - "g2-mark-area-stacked"

use_cases:
  - "将宽表多列数据转换为多系列折线图"
  - "将同类指标的多个字段合并为一个系列字段"
  - "减少手动 flatMap 数据预处理代码"

difficulty: "intermediate"
completeness: "full"
created: "2025-03-26"
updated: "2025-03-26"
author: "antv-team"
source_url: "https://g2.antv.antgroup.com/manual/core/data/fold"
---

## 核心概念

**Fold 是数据变换（Data Transform），不是标记变换（Mark Transform）**

- 数据变换配置在 `data.transform` 中
- 在数据加载阶段执行，影响所有使用该数据的标记

**宽表（Wide）**：每个指标占一列
```
month | revenue | cost | profit
Jan   | 320     | 200  | 120
Feb   | 450     | 230  | 220
```

**长表（Long/Tidy）**：所有指标值合并到一列，增加分类列
```
month | key     | value
Jan   | revenue | 320
Jan   | cost    | 200
Jan   | profit  | 120
Feb   | revenue | 450
...
```

G2 的 `fold` 数据变换自动完成这个转换，无需手动 `flatMap`。

## 最小可运行示例

```javascript
import { Chart } from '@antv/g2';

const chart = new Chart({ container: 'container', width: 700, height: 400 });

// 宽表数据（每个指标是独立的列）
const wideData = [
  { month: 'Jan', revenue: 320, cost: 200, profit: 120 },
  { month: 'Feb', revenue: 450, cost: 230, profit: 220 },
  { month: 'Mar', revenue: 380, cost: 210, profit: 170 },
  { month: 'Apr', revenue: 510, cost: 260, profit: 250 },
];

chart.options({
  type: 'line',
  data: {
    type: 'inline',
    value: wideData,
    transform: [
      {
        type: 'fold',
        fields: ['revenue', 'cost', 'profit'],  // 要折叠的列名
        key: 'key',      // 生成的键列名（默认 'key'）
        value: 'value',  // 生成的值列名（默认 'value'）
      },
    ],
  },
  encode: {
    x: 'month',
    y: 'value',     // fold 后的值列
    color: 'key',   // fold 后的键列
  },
});

chart.render();
```

## 堆叠面积图中使用 fold

```javascript
chart.options({
  type: 'area',
  data: {
    type: 'inline',
    value: wideData,
    transform: [
      { type: 'fold', fields: ['revenue', 'cost', 'profit'] },
    ],
  },
  encode: { x: 'month', y: 'value', color: 'key' },
  transform: [{ type: 'stackY' }],  // mark transform
});
```

## 等价的手动方式（作为对比）

```javascript
// 不用 fold，手动 flatMap（代码较冗长）
const longData = wideData.flatMap((d) => [
  { month: d.month, metric: 'revenue', value: d.revenue },
  { month: d.month, metric: 'cost',    value: d.cost    },
  { month: d.month, metric: 'profit',  value: d.profit  },
]);

chart.options({
  type: 'line',
   longData,
  encode: { x: 'month', y: 'value', color: 'metric' },
});
```

## 配置项

| 属性   | 描述                           | 类型       | 默认值  |
| ------ | ------------------------------ | ---------- | ------- |
| fields | 需要展开的字段列表             | `string[]` |         |
| key    | 展开之后，字段枚举值对应字段名 | `string`   | `key`   |
| value  | 展开之后，数据值对应字段名     | `string`   | `value` |

## 常见错误与修正

### 错误 1：fold 放在 mark transform 中

```javascript
// ❌ 错误：fold 是数据变换，不能放在 mark 的 transform 中
chart.options({
  type: 'line',
   wideData,
  transform: [{ type: 'fold', fields: ['a', 'b'] }],  // ❌ 错误位置
});

// ✅ 正确：fold 放在 data.transform 中
chart.options({
  type: 'line',
  data: {
    type: 'inline',
    value: wideData,
    transform: [{ type: 'fold', fields: ['a', 'b'] }],  // ✅ 正确
  },
});
```

### 错误 2：fields 里的字段名拼写错误

```javascript
// ❌ 错误：字段名与数据不匹配，fold 后得到 undefined 值
data: {
  transform: [{ type: 'fold', fields: ['Revenue', 'Cost'] }],  // 大写，但数据是小写
}

// ✅ 正确：字段名必须与数据对象的 key 完全一致（区分大小写）
data: {
  transform: [{ type: 'fold', fields: ['revenue', 'cost'] }],
}
```

### 错误 3：encode 中 y/color 字段名与 as 配置不匹配

```javascript
// ❌ 错误：fold 默认生成 'key'/'value' 列，但 encode 用了别的名字
chart.options({
  data: {
    transform: [{ type: 'fold', fields: ['a', 'b'] }],  // 默认生成 key/value
  },
  encode: { y: 'metric', color: 'series' },  // 错误：字段名不存在
});

// ✅ 正确：encode 名字与 fold 的 key/value 配置一致
chart.options({
  data: {
    transform: [{ type: 'fold', fields: ['a', 'b'], key: 'metric', value: 'amount' }],
  },
  encode: { y: 'amount', color: 'metric' },
});
```

### 错误 4：简写 data 无法配置 transform

```javascript
// ❌ 错误：简写 data 无法配置 transform
chart.options({
   wideData,  // 简写形式
  // 无法添加 fold transform
});

// ✅ 正确：使用完整 data 配置
chart.options({
   {
    type: 'inline',
    value: wideData,
    transform: [{ type: 'fold', fields: ['revenue', 'cost'] }],
  },
});
```

### 错误 5：`` 关键字丢失——SyntaxError

这是代码生成时极常见的错误：`data` 属性值是一个多行嵌套对象，容易忘记写 `data:` 键名，导致 JavaScript 语法错误（`Unexpected token '{'`），图表完全无法运行。

```javascript
// ❌ 错误： 键名丢失，{ type: 'inline', ... } 是孤立对象字面量 → SyntaxError
chart.options({
  type: 'interval',
  {                          // ❌ 语法错误！缺少 data: 前缀
    type: 'inline',
    value: populationData,
    transform: [{
      type: 'fold',
      fields: ['Under 5 Years', '5 to 13 Years'],
      key: 'AgeGroup',
      value: 'Population',
    }]
  },
  encode: { x: 'State', y: 'Population', color: 'AgeGroup' },
});

// ✅ 正确：必须写 data: 键名
chart.options({
  type: 'interval',
   {                    // ✅  不能省略
    type: 'inline',
    value: populationData,
    transform: [{
      type: 'fold',
      fields: ['Under 5 Years', '5 to 13 Years'],
      key: 'AgeGroup',
      value: 'Population',
    }]
  },
  encode: { x: 'State', y: 'Population', color: 'AgeGroup' },
});
```

**为什么容易漏写**：`data` 值是一个多行嵌套对象，在生成时容易把它当作独立的「块」而非 `chart.options({})` 的属性，导致漏写 `` 前缀。同样问题也出现在 `coordinate:`、`children:` 等多行对象属性上——凡是值为复杂对象的属性，都要确认键名写全。

path: references/data/g2-data-filter.md
content: ---
id: "g2-data-filter"
title: "G2 Filter 数据过滤"
description: |
  filter 数据变换在数据加载阶段根据条件过滤数据，只保留满足条件的行。
  与 JavaScript 的 Array.filter 类似，接受一个断言函数（predicate）。
  配置在 data.transform 中，在渲染前预处理数据。

library: "g2"
version: "5.x"
category: "data"
tags:
  - "filter"
  - "过滤"
  - "数据筛选"
  - "条件过滤"
  - "data transform"

related:
  - "g2-data-fold"
  - "g2-data-sort"
  - "g2-interaction-brush"

use_cases:
  - "只展示满足条件的数据子集（如值大于阈值的数据）"
  - "排除异常值或空值"
  - "在数据加载阶段做分类筛选"

difficulty: "beginner"
completeness: "full"
created: "2025-03-26"
updated: "2025-03-26"
author: "antv-team"
source_url: "https://g2.antv.antgroup.com/manual/core/data/filter"
---

## 核心概念

**filter 是数据变换（Data Transform），不是标记变换（Mark Transform）**

- 数据变换配置在 `data.transform` 中
- 在数据加载阶段执行，影响所有使用该数据的标记
- 与 mark transform 不同，数据变换是数据预处理，不涉及视觉通道

## 最小可运行示例

```javascript
import { Chart } from '@antv/g2';

const chart = new Chart({ container: 'container', width: 640, height: 400 });

chart.options({
  type: 'interval',
   {
    type: 'inline',
    value: [
      { genre: 'Sports', sold: 275 },
      { genre: 'Strategy', sold: 115 },
      { genre: 'Action', sold: 120 },
      { genre: 'RPG', sold: 98 },
      { genre: 'Shooter', sold: 35 },
    ],
    transform: [
      {
        type: 'filter',
        callback: (d) => d.sold >= 100,  // 只保留销量 ≥ 100 的数据
      },
    ],
  },
  encode: { x: 'genre', y: 'sold', color: 'genre' },
});

chart.render();
```

## 排除空值 / 异常值

```javascript
chart.options({
  type: 'line',
   {
    type: 'inline',
    value: rawData,
    transform: [
      {
        type: 'filter',
        // 过滤掉 null、undefined、NaN
        callback: (d) => d.value != null && !isNaN(d.value) && d.value > 0,
      },
    ],
  },
  encode: { x: 'date', y: 'value' },
});
```

## 多条件过滤

```javascript
chart.options({
  type: 'point',
   {
    type: 'inline',
    value: allData,
    transform: [
      {
        type: 'filter',
        callback: (d) => d.category === 'A' && d.y > 50,
      },
    ],
  },
  encode: { x: 'x', y: 'y', color: 'category' },
});
```

## 与 fetch 连用

```javascript
chart.options({
  type: 'point',
   {
    type: 'fetch',
    value: 'https://example.com/data.json',
    transform: [
      {
        type: 'filter',
        callback: (d) => d.value > 100,
      },
    ],
  },
  encode: { x: 'x', y: 'y' },
});
```

## 多个数据变换组合

```javascript
chart.options({
  type: 'interval',
   {
    type: 'inline',
    value: rawData,
    transform: [
      { type: 'filter', callback: (d) => d.value != null },
      { type: 'sort', callback: (a, b) => b.value - a.value },
      { type: 'slice', start: 0, end: 10 },  // 只取前 10 条
    ],
  },
  encode: { x: 'category', y: 'value' },
});
```

## 配置项

| 属性     | 描述                                 | 类型                                           | 默认值                                                     |
| -------- | ------------------------------------ | ---------------------------------------------- | ---------------------------------------------------------- |
| callback | 过滤函数，返回 true 保留该行数据     | `(d: any, idx: number, arr: any[]) => boolean` | `(d) => d !== undefined && d !== null && !Number.isNaN(d)` |

## 常见错误与修正

### 错误 1：filter 放在 mark transform 中

```javascript
// ❌ 错误：filter 是数据变换，不能放在 mark 的 transform 中
chart.options({
  type: 'interval',
   myData,
  transform: [{ type: 'filter', callback: (d) => d.value > 100 }],  // ❌ 错误位置
});

// ✅ 正确：filter 放在 data.transform 中
chart.options({
  type: 'interval',
   {
    type: 'inline',
    value: myData,
    transform: [{ type: 'filter', callback: (d) => d.value > 100 }],  // ✅ 正确
  },
});
```

### 错误 2：callback 不是函数

```javascript
// ❌ 错误：callback 必须是函数
data: {
  transform: [{ type: 'filter', callback: 'value > 100' }],  // ❌ 字符串
}

// ✅ 正确：使用箭头函数
 {
  transform: [{ type: 'filter', callback: (d) => d.value > 100 }],  // ✅
}
```

### 错误 3：简写 data 无法配置 transform

```javascript
// ❌ 错误：简写 data 无法配置 transform
chart.options({
  data: myData,  // 简写形式
  // 无法添加 transform
});

// ✅ 正确：使用完整 data 配置
chart.options({
  data: {
    type: 'inline',
    value: myData,
    transform: [{ type: 'filter', callback: (d) => d.value > 100 }],
  },
});
```

path: references/data/g2-data-ema.md
content: ---
id: "g2-data-ema"
title: "G2 EMA 指数移动平均"
description: |
  EMA（Exponential Moving Average）数据变换对数据进行指数移动平均平滑处理。
  通过对最近的数据点赋予更高的权重，减少数据波动性，更清晰地观察趋势。
  配置在 data.transform 中。

library: "g2"
version: "5.x"
category: "data"
tags:
  - "ema"
  - "指数移动平均"
  - "平滑"
  - "趋势"
  - "数据变换"
  - "data transform"

related:
  - "g2-mark-line"

use_cases:
  - "时间序列数据平滑"
  - "金融数据技术分析"
  - "训练指标平滑展示"

difficulty: "intermediate"
completeness: "full"
created: "2025-03-27"
updated: "2025-03-27"
author: "antv-team"
source_url: "https://g2.antv.antgroup.com/manual/core/data/ema"
---

## 核心概念

**EMA 是数据变换（Data Transform），不是标记变换（Mark Transform）**

- 数据变换配置在 `data.transform` 中
- 指数移动平均是一种数据平滑算法

**公式**：EMA_t = α × P_t + (1 - α) × EMA_{t-1}

**注意事项**：
- G2 中 `alpha` 越接近 1，平滑效果越明显
- `alpha` 越接近 0，EMA 越接近原始数据
- `field` 字段必须为数值型

## 最小可运行示例

```javascript
import { Chart } from '@antv/g2';

const chart = new Chart({ container: 'container', width: 700, height: 400 });

const data = [
  { t: 0, y: 100 },
  { t: 1, y: 180 },
  { t: 2, y: 120 },
  { t: 3, y: 200 },
  { t: 4, y: 150 },
  { t: 5, y: 250 },
];

chart.options({
  type: 'view',
  children: [
    {
      type: 'line',
       {
        type: 'inline',
        value: data,
        transform: [
          {
            type: 'ema',
            field: 'y',      // 要平滑的字段
            alpha: 0.6,      // 平滑因子
            as: 'emaY',      // 输出字段名
          },
        ],
      },
      encode: { x: 't', y: 'emaY' },
      style: { stroke: '#f90' },
    },
    {
      type: 'line',
       { type: 'inline', value: data },
      encode: { x: 't', y: 'y' },
      style: { stroke: '#ccc', lineDash: [4, 2] },
    },
  ],
});

chart.render();
```

## 配置项

| 属性  | 描述                                 | 类型     | 默认值     | 必选 |
| ----- | ------------------------------------ | -------- | ---------- | ---- |
| field | 需要平滑的字段名                     | `string` | `'y'`      | ✓    |
| alpha | 平滑因子，控制平滑程度（越大越平滑） | `number` | `0.6`      |      |
| as    | 生成的新字段名，若不指定将覆盖原字段 | `string` | 同 `field` |      |

## 金融行情平滑

```javascript
chart.options({
  type: 'view',
  children: [
    {
      type: 'line',
       {
        type: 'fetch',
        value: 'https://example.com/stock.csv',
        transform: [
          {
            type: 'ema',
            field: 'close',
            alpha: 0.7,
            as: 'emaClose',
          },
        ],
      },
      encode: { x: 'date', y: 'emaClose' },
      style: { stroke: '#007aff', lineWidth: 2 },
    },
    {
      type: 'line',
       { type: 'fetch', value: 'https://example.com/stock.csv' },
      encode: { x: 'date', y: 'close' },
      style: { stroke: '#bbb', lineDash: [4, 2] },
    },
  ],
});
```

## 常见错误与修正

### 错误 1：ema 放在 mark transform 中

```javascript
// ❌ 错误：ema 是数据变换，不能放在 mark 的 transform 中
chart.options({
  type: 'line',
  data,
  transform: [{ type: 'ema', field: 'y' }],  // ❌ 错误位置
});

// ✅ 正确：ema 放在 data.transform 中
chart.options({
  type: 'line',
   {
    type: 'inline',
    value: data,
    transform: [{ type: 'ema', field: 'y', as: 'emaY' }],  // ✅ 正确
  },
});
```

### 错误 2：字段不是数值型

```javascript
// ❌ 错误：field 字段必须为数值型
 {
  transform: [{ type: 'ema', field: 'name' }],  // ❌ name 是字符串
}

// ✅ 正确：使用数值型字段
 {
  transform: [{ type: 'ema', field: 'value' }],
}
```

### 错误 3：忘记设置 as 字段

```javascript
// ⚠️ 注意：不设置 as 会覆盖原字段
data: {
  transform: [{ type: 'ema', field: 'y' }],  // y 字段会被覆盖
}
encode: { y: 'y' },  // 使用的是平滑后的数据

// ✅ 推荐：设置 as 保留原字段
 {
  transform: [{ type: 'ema', field: 'y', as: 'emaY' }],
}
// 可以同时展示原始数据和平滑数据
```

path: references/data/g2-data-kde.md
content: ---
id: "g2-data-kde"
title: "G2 KDE 核密度估计"
description: |
  KDE（Kernel Density Estimation）数据变换对数据进行核密度估计，
  生成概率密度函数数据，用于密度图、小提琴图等可视化。
  配置在 data.transform 中。

library: "g2"
version: "5.x"
category: "data"
tags:
  - "kde"
  - "核密度估计"
  - "密度图"
  - "小提琴图"
  - "数据变换"
  - "data transform"

related:
  - "g2-mark-density"
  - "g2-mark-boxplot"

use_cases:
  - "密度图（Density Plot）"
  - "小提琴图（Violin Plot）"
  - "多组数据分布比较"

difficulty: "advanced"
completeness: "full"
created: "2025-03-27"
updated: "2025-03-27"
author: "antv-team"
source_url: "https://g2.antv.antgroup.com/manual/core/data/kde"
---

## 核心概念

**KDE 是数据变换（Data Transform），不是标记变换（Mark Transform）**

- 数据变换配置在 `data.transform` 中
- 核密度估计是一种非参数统计方法，估计随机变量的概率密度函数
- 底层使用 [pdfast](https://www.npmjs.com/package/pdfast) 库

**输出**：处理后数据增加两个字段（默认 `y` 和 `size`），均为数组类型。

**⚠️ 关键：`field` 是输入，`as` 是输出，encode 必须用输出字段名**

```
kde: field='value' → 输出 as=['y','size'] → encode: { y: 'y', size: 'size' }
                      ↑ 不管 field 叫什么，encode 始终用 as 的值
```

## 最小可运行示例

```javascript
import { Chart } from '@antv/g2';

const chart = new Chart({ container: 'container', width: 700, height: 400 });

chart.options({
  type: 'density',
  data: {
    type: 'fetch',
    value: 'https://assets.antv.antgroup.com/g2/species.json',
    transform: [
      {
        type: 'kde',
        field: 'y',              // 进行 KDE 的字段
        groupBy: ['x', 'species'], // 分组字段
        size: 20,                // 生成数据点数量
      },
    ],
  },
  encode: {
    x: 'x',
    y: 'y',
    color: 'species',
    size: 'size',
  },
  tooltip: false,
});

chart.render();
```

## 配置项

| 属性    | 描述                                           | 类型               | 默认值          | 必选 |
| ------- | ---------------------------------------------- | ------------------ | --------------- | ---- |
| field   | 进行核密度算法的数据字段                       | `string`           | -               | ✓    |
| groupBy | 对数据进行分组的分组字段，可以指定多个         | `string[]`         | -               | ✓    |
| as      | 进行 KDE 处理之后，存储的字段                  | `[string, string]` | `['y', 'size']` | 否   |
| min     | 指定处理范围的最小值                           | `number`           | 数据最小值      | 否   |
| max     | 指定处理范围的最大值                           | `number`           | 数据最大值      | 否   |
| size    | 算法最终生成数据的条数，值越大密度曲线越精细   | `number`           | `10`            | 否   |
| width   | 确定一个元素左右影响多少点，值越大曲线越平滑   | `number`           | `2`             | 否   |

## 小提琴图

```javascript
chart.options({
  type: 'view',
  data: { type: 'fetch', value: 'https://assets.antv.antgroup.com/g2/species.json' },
  children: [
    {
      type: 'density',
      data: {
        transform: [
          { type: 'kde', field: 'y', groupBy: ['x', 'species'] },
        ],
      },
      encode: { x: 'x', y: 'y', series: 'species', color: 'species', size: 'size' },
      tooltip: false,
    },
    {
      type: 'boxplot',
      encode: { x: 'x', y: 'y', series: 'species', color: 'species', shape: 'violin' },
      style: { opacity: 0.5, strokeOpacity: 0.5, point: false },
    },
  ],
});
```

## 自定义参数

```javascript
chart.options({
  type: 'density',
  data: {
    transform: [
      {
        type: 'kde',
        field: 'y',
        groupBy: ['x'],
        size: 30,             // 更精细的密度曲线
        width: 3,             // 更平滑
        min: 0,               // 最小值
        max: 8,               // 最大值
        as: ['density_x', 'density_y'],  // 自定义输出字段名
      },
    ],
  },
  encode: { x: 'x', y: 'density_x', size: 'density_y' },
});
```

## 常见错误与修正

### 错误 1：kde 放在 mark transform 中

```javascript
// ❌ 错误：kde 是数据变换，不能放在 mark 的 transform 中
chart.options({
  type: 'density',
  data,
  transform: [{ type: 'kde', field: 'y' }],  // ❌ 错误位置
});

// ✅ 正确：kde 放在 data.transform 中
chart.options({
  type: 'density',
  data: {
    type: 'fetch',
    value: dataUrl,
    transform: [{ type: 'kde', field: 'y', groupBy: ['x'] }],  // ✅ 正确
  },
});
```

### 错误 2：缺少 groupBy

```javascript
// ❌ 错误：groupBy 是必选参数
data: {
  transform: [{ type: 'kde', field: 'y' }],  // 缺少 groupBy
}

// ✅ 正确：指定 groupBy
data: {
  transform: [{ type: 'kde', field: 'y', groupBy: ['category'] }],
}
```

### 错误 3：数据零方差或单点导致 KDE 退化（静默空白）

KDE 底层（pdfast 库）要求 `min < max`。当某分组所有值相同（方差=0）或只有 1 个数据点时，KDE 产生 NaN，该组不渲染。

```javascript
// ❌ 问题：零方差数据
const data = [
  { group: 'A', value: 10 },           // 只有 1 个点
  { group: 'B', value: 20 },
  { group: 'B', value: 20 },           // 全部相同，min=max=20
  { group: 'B', value: 20 },
];
// KDE groupBy: ['group'] 对 B 组处理时，min=max=20，产生 NaN → 不渲染

// ✅ 解决方案1：手动设置 min/max 保证区间非零
transform: [{
  type: 'kde',
  field: 'value',
  groupBy: ['group'],
  min: 0,    // 手动指定，确保 min ≠ max
  max: 100,
}]

// ✅ 解决方案2：数据不足时换图表类型
// KDE 建议每组至少 5-10 个不同值才能产生有意义的密度曲线
// 单点或全同值 → 改用散点图 / 箱线图
```

### 错误 4：encode 字段与 as 不匹配

```javascript
// ❌ 错误：使用默认 as 但 encode 用了其他字段名
data: {
  transform: [{ type: 'kde', field: 'y', groupBy: ['x'] }],  // 默认 as: ['y', 'size']
}
encode: { y: 'density', size: 'density_size' },  // ❌ 字段名不匹配

// ✅ 正确：使用正确的字段名
encode: { y: 'y', size: 'size' },  // ✅ 使用默认输出字段

// 或自定义 as
 {
  transform: [{ type: 'kde', field: 'y', groupBy: ['x'], as: ['density', 'density_size'] }],
},
encode: { y: 'density', size: 'density_size' },  // ✅ 匹配自定义字段
```

### 错误 5：数据量不足导致 KDE 结果为空或无效

KDE 需要足够的数据点才能生成有效的密度估计。如果某个分组内的数据点太少（如小于等于 1 个），KDE 无法计算出有效的结果，可能导致该分组不显示。

```javascript
// ❌ 问题：某些分组数据量不足
const data = [
  { species: 'setosa', x: 'SepalLength', y: 5.1 },
  { species: 'versicolor', x: 'SepalLength', y: 6.0 },
  { species: 'virginica', x: 'SepalLength' }, // 缺失 y 值
];

// ✅ 解决方案：确保每个分组有足够的有效数据
const validData = [
  { species: 'setosa', x: 'SepalLength', y: 5.1 },
  { species: 'setosa', x: 'SepalLength', y: 5.0 },
  { species: 'versicolor', x: 'SepalLength', y: 6.0 },
  { species: 'versicolor', x: 'SepalLength', y: 6.2 },
  { species: 'virginica', x: 'SepalLength', y: 6.5 },
  { species: 'virginica', x: 'SepalLength', y: 6.3 },
];
```

### 错误 6：未正确设置图表类型或编码导致渲染失败

在组合视图中使用 KDE 时，必须确保每个子图表都正确设置了类型和编码，特别是 `series` 映射。

```javascript
// ❌ 错误：缺少必要的 encode 字段或类型设置不当
children: [
  {
    type: 'density',
    data: { transform: [{ type: 'kde', field: 'y', groupBy: ['x', 'species'] }] },
    encode: { x: 'x', y: 'y', color: 'species', size: 'size' },
  },
  {
    type: 'boxplot',
    encode: { x: 'x', y: 'y', color: 'species', shape: 'violin' }, // 缺少 series 映射
  }
]

// ✅ 正确：确保所有必要字段都被正确映射
children: [
  {
    type: 'density',
    data: { transform: [{ type: 'kde', field: 'y', groupBy: ['x', 'species'] }] },
    encode: { x: 'x', y: 'y', series: 'species', color: 'species', size: 'size' },
  },
  {
    type: 'boxplot',
    encode: { x: 'x', y: 'y', series: 'species', color: 'species', shape: 'violin' },
  }
]
```

### 错误 7：KDE 分组后数据点过少导致渲染空白

KDE 需要每个分组有足够的数据点才能生成有效的密度估计。如果分组后的数据点过少（如每组少于 2 个有效值），KDE 无法计算出有效的结果，可能导致整个图表渲染为空白。

```javascript
// ❌ 问题：分组后每组数据点太少
const insufficientData = [
  { species: 'setosa', x: 'SepalLength', y: 5.1 },
  { species: 'versicolor', x: 'SepalLength', y: 6.0 },
  { species: 'virginica', x: 'SepalLength', y: 6.5 },
];

// ✅ 解决方案：确保每个分组有足够多的有效数据点
const sufficientData = [
  { species: 'setosa', x: 'SepalLength', y: 5.1 },
  { species: 'setosa', x: 'SepalLength', y: 5.0 },
  { species: 'setosa', x: 'SepalLength', y: 5.2 },
  { species: 'versicolor', x: 'SepalLength', y: 6.0 },
  { species: 'versicolor', x: 'SepalLength', y: 6.2 },
  { species: 'versicolor', x: 'SepalLength', y: 5.9 },
  { species: 'virginica', x: 'SepalLength', y: 6.5 },
  { species: 'virginica', x: 'SepalLength', y: 6.3 },
  { species: 'virginica', x: 'SepalLength', y: 6.7 },
];
```

### 错误 8：KDE 与 density 图表类型配合使用时未正确设置 encode

使用 KDE 作为数据变换时，必须确保 `encode` 中正确引用了 KDE 生成的字段（默认为 `y` 和 `size`），否则图表可能无法正确渲染。

```javascript
// ❌ 错误：未正确引用 KDE 生成的字段
chart.options({
  type: 'density',
  data: {
    transform: [{ type: 'kde', field: 'y', groupBy: ['x'] }]
  },
  encode: { x: 'x', y: 'originalY', size: 'originalSize' } // 错误字段名
});

// ✅ 正确：引用 KDE 生成的字段
chart.options({
  type: 'density',
  data: {
    transform: [{ type: 'kde', field: 'y', groupBy: ['x'] }]
  },
  encode: { x: 'x', y: 'y', size: 'size' } // 正确字段名
});
```

### 错误 9：KDE 分组字段选择不当导致渲染异常

在使用 KDE 时，`groupBy` 字段的选择非常重要。如果选择了错误的字段或者没有充分考虑数据结构，可能导致生成的密度曲线不符合预期甚至完全不可见。

```javascript
// ❌ 错误：groupBy 字段选择不当
chart.options({
  type: 'density',
  data: {
    transform: [
      {
        type: 'kde',
        field: 'y',
        groupBy: ['species'], // 仅按 species 分组，忽略了 x 字段的不同类别
      },
    ],
  },
  encode: {
    x: 'x',
    y: 'y',
    color: 'species',
    size: 'size',
  },
});

// ✅ 正确：合理选择 groupBy 字段
chart.options({
  type: 'density',
  data: {
    transform: [
      {
        type: 'kde',
        field: 'y',
        groupBy: ['x', 'species'], // 同时按 x 和 species 分组
      },
    ],
  },
  encode: {
    x: 'x',
    y: 'y',
    color: 'species',
    size: 'size',
  },
});
```

### 错误 10：KDE 输出字段被后续操作覆盖

当 KDE 生成的字段（如 `y` 和 `size`）在后续的数据处理步骤中被修改或覆盖时，会导致图表渲染异常。

```javascript
// ❌ 错误：KDE 输出字段被后续 transform 覆盖
chart.options({
  type: 'density',
  data: {
    transform: [
      { type: 'kde', field: 'y', groupBy: ['x', 'species'] },
      { type: 'map', callback: (d) => ({ ...d, y: d.y.map(v => v * 2) }) } // 修改了 y 字段
    ],
  },
  encode: {
    x: 'x',
    y: 'y',
    color: 'species',
    size: 'size',
  },
});

// ✅ 正确：使用自定义字段名避免冲突
chart.options({
  type: 'density',
  data: {
    transform: [
      { 
        type: 'kde', 
        field: 'y', 
        groupBy: ['x', 'species'],
        as: ['kdeY', 'kdeSize'] // 使用自定义字段名
      },
      { type: 'map', callback: (d) => ({ ...d, y: d.y.map(v => v * 2) }) } // 不会影响 KDE 结果
    ],
  },
  encode: {
    x: 'x',
    y: 'kdeY', // 使用 KDE 生成的自定义字段
    color: 'species',
    size: 'kdeSize',
  },
});
```

path: references/data/g2-data-fetch.md
content: ---
id: "g2-data-fetch"
title: "G2 Fetch 远程数据获取"
description: |
  Fetch 数据连接器从远程接口获取数据，支持 JSON、CSV 等格式解析。
  通过设置 data.type 为 'fetch' 启用，让数据源具备动态性。

library: "g2"
version: "5.x"
category: "data"
tags:
  - "fetch"
  - "远程数据"
  - "JSON"
  - "CSV"
  - "数据连接器"
  - "connector"

related:
  - "g2-data-filter"
  - "g2-data-fold"

use_cases:
  - "从 API 获取动态数据"
  - "加载远程 CSV 文件"
  - "大屏监控数据展示"

difficulty: "beginner"
completeness: "full"
created: "2025-03-27"
updated: "2025-03-27"
author: "antv-team"
source_url: "https://g2.antv.antgroup.com/manual/core/data/fetch"
---

## 核心概念

**Fetch 是数据连接器（Data Connector），不是数据变换**

- 通过设置 `data.type: 'fetch'` 启用
- 支持 JSON、CSV 格式自动解析
- 远程地址不能设置鉴权

## 最小可运行示例

```javascript
import { Chart } from '@antv/g2';

const chart = new Chart({ container: 'container', width: 700, height: 400 });

chart.options({
  type: 'point',
   {
    type: 'fetch',
    value: 'https://gw.alipayobjects.com/os/antvdemo/assets/data/scatter.json',
  },
  encode: {
    x: 'weight',
    y: 'height',
    color: 'gender',
  },
});

chart.render();
```

## 配置项

| 属性      | 描述                                              | 类型               | 默认值                         |
| --------- | ------------------------------------------------- | ------------------ | ------------------------------ |
| value     | fetch 请求的网络地址                              | `string`           | -                              |
| format    | 远程文件的数据格式类型，决定用什么方式解析        | `'json' \| 'csv'`  | 默认取 value 末尾 `.` 后的后缀 |
| delimiter | 如果是 csv 文件，解析的时候分割符                 | `string`           | `,`                            |
| autoType  | 如果是 csv 文件，解析的时候是否自动判断列数据类型 | `boolean`          | `true`                         |
| transform | 对加载后的数据进行变换操作                        | `DataTransform[]`  | `[]`                           |

## 加载 CSV 文件

```javascript
chart.options({
  type: 'line',
   {
    type: 'fetch',
    value: 'https://example.com/data.csv',
    format: 'csv',           // 指定格式
    delimiter: ',',          // 分隔符
    autoType: true,          // 自动推断类型
    transform: [
      { type: 'filter', callback: (d) => d.value > 0 },
    ],
  },
  encode: { x: 'date', y: 'value' },
});
```

## 结合 transform 使用

```javascript
chart.options({
  type: 'interval',
   {
    type: 'fetch',
    value: 'https://example.com/sales.json',
    transform: [
      { type: 'filter', callback: (d) => d.year === 2024 },
      { type: 'sortBy', fields: [['amount', false]] },
      { type: 'slice', end: 10 },
    ],
  },
  encode: { x: 'product', y: 'amount' },
});
```

## 常见错误与修正

### 错误 1：远程地址需要鉴权

```javascript
// ❌ 错误：G2 fetch 不支持鉴权
data: {
  type: 'fetch',
  value: 'https://api.example.com/private-data',  // 需要 token
}

// ✅ 正确：使用公开的 API 或在服务端代理
 {
  type: 'fetch',
  value: 'https://public-api.example.com/data',  // 无需鉴权
}
```

### 错误 2：format 与文件格式不匹配

```javascript
// ❌ 错误：format 与实际格式不匹配
data: {
  type: 'fetch',
  value: 'https://example.com/data.json',
  format: 'csv',  // ❌ 实际是 JSON
}

// ✅ 正确：让 G2 自动推断或指定正确格式
 {
  type: 'fetch',
  value: 'https://example.com/data.json',
  // format 默认根据后缀推断为 'json'
}

// 或显式指定
 {
  type: 'fetch',
  value: 'https://example.com/api/data',  // 无后缀
  format: 'json',  // 显式指定
}
```

### 错误 3：CORS 问题

```javascript
// ❌ 错误：跨域请求被阻止
// 浏览器控制台会显示 CORS 错误

// ✅ 解决方案：
// 1. 服务端配置 CORS 头
// 2. 使用同源请求
// 3. 使用代理服务器
```

path: references/data/g2-data-log.md
content: ---
id: "g2-data-log"
title: "G2 Log 数据日志"
description: |
  Log 数据变换将当前数据变换流中的数据打印到控制台，用于调试。
  配置在 data.transform 中，不影响数据流。

library: "g2"
version: "5.x"
category: "data"
tags:
  - "log"
  - "调试"
  - "日志"
  - "数据变换"
  - "data transform"

related:
  - "g2-data-filter"

use_cases:
  - "调试数据处理流程"
  - "检查中间数据状态"

difficulty: "beginner"
completeness: "full"
created: "2025-03-27"
updated: "2025-03-27"
author: "antv-team"
source_url: "https://g2.antv.antgroup.com/manual/core/data/log"
---

## 核心概念

**Log 是数据变换（Data Transform），不是标记变换（Mark Transform）**

- 数据变换配置在 `data.transform` 中
- 用于调试，将数据打印到控制台
- 不影响数据流，数据会原样传递给下一个 transform

## 最小可运行示例

```javascript
import { Chart } from '@antv/g2';

const chart = new Chart({ container: 'container', width: 700, height: 400 });

const data = [
  { a: 1, b: 2, c: 3 },
  { a: 4, b: 5, c: 6 },
  { a: 7, b: 8, c: 9 },
];

chart.options({
  type: 'interval',
   {
    type: 'inline',
    value: data,
    transform: [
      { type: 'slice', start: 1 },  // 先切片
      { type: 'log' },               // 打印中间结果（调试用）
      { type: 'filter', callback: (d) => d.a < 5 },  // 再过滤
    ],
  },
  encode: { x: 'a', y: 'b' },
});

chart.render();
// 控制台会输出 slice 后的数据
```

## 调试数据处理流程

```javascript
chart.options({
   {
    type: 'fetch',
    value: 'https://example.com/data.json',
    transform: [
      { type: 'filter', callback: (d) => d.value > 100 },
      { type: 'log' },  // 检查过滤后的数据
      { type: 'sort', callback: (a, b) => b.value - a.value },
      { type: 'log' },  // 检查排序后的数据
      { type: 'slice', end: 10 },
    ],
  },
});
```

## 配置项

Log 变换没有配置项，直接使用即可。

```javascript
{ type: 'log' }
```

## 常见错误与修正

### 错误 1：log 放在 mark transform 中

```javascript
// ❌ 错误：log 是数据变换，不能放在 mark 的 transform 中
chart.options({
  type: 'interval',
  data,
  transform: [{ type: 'log' }],  // ❌ 错误位置
});

// ✅ 正确：log 放在 data.transform 中
chart.options({
  type: 'interval',
   {
    type: 'inline',
    value: data,
    transform: [{ type: 'log' }],  // ✅ 正确
  },
});
```

### 注意事项

```javascript
// ⚠️ 注意：生产环境应移除 log 变换，避免不必要的控制台输出
// 开发环境
 {
  transform: [
    { type: 'filter', callback: (d) => d.value > 0 },
    { type: 'log' },  // 调试用
  ],
}

// 生产环境
 {
  transform: [
    { type: 'filter', callback: (d) => d.value > 0 },
    // 移除 log
  ],
}
```

path: references/data/g2-data-sort.md
content: ---
id: "g2-data-sort"
title: "G2 Sort 数据排序"
description: |
  sort 数据变换对数据进行排序，类似于 Array.prototype.sort。
  配置在 data.transform 中，在渲染前预处理数据顺序。
  常用于饼图、排行榜条形图等需要按数据大小排列的场景。

library: "g2"
version: "5.x"
category: "data"
tags:
  - "sort"
  - "排序"
  - "数据顺序"
  - "data transform"

related:
  - "g2-data-filter"
  - "g2-data-fold"
  - "g2-transform-sortx"
  - "g2-transform-sorty"

use_cases:
  - "饼图按大小排列扇区"
  - "条形图按数值排序"
  - "排行榜数据展示"

difficulty: "beginner"
completeness: "full"
created: "2025-03-26"
updated: "2025-03-26"
author: "antv-team"
source_url: "https://g2.antv.antgroup.com/manual/core/data/sort"
---

## 核心概念

**sort 是数据变换（Data Transform），不是标记变换（Mark Transform）**

- 数据变换配置在 `data.transform` 中
- 使用 callback 比较函数（类似 Array.sort）
- 在数据加载阶段执行，影响所有使用该数据的标记

**与 mark transform sortX/sortY/sortColor 的区别：**
- 数据 sort：直接对原始数据数组排序
- mark sortX/sortY/sortColor：按视觉通道值排序，可聚合后排序

## 最小可运行示例

```javascript
import { Chart } from '@antv/g2';

const chart = new Chart({ container: 'container', width: 640, height: 480 });

chart.options({
  type: 'interval',
  data: {
    type: 'inline',
    value: [
      { category: 'A', value: 30 },
      { category: 'B', value: 50 },
      { category: 'C', value: 20 },
      { category: 'D', value: 40 },
    ],
    transform: [
      {
        type: 'sort',
        callback: (a, b) => b.value - a.value,  // 降序排列
      },
    ],
  },
  encode: { x: 'category', y: 'value' },
});

chart.render();
```

## 升序排列

```javascript
chart.options({
  type: 'interval',
   {
    type: 'inline',
    value: data,
    transform: [
      {
        type: 'sort',
        callback: (a, b) => a.value - b.value,  // 升序
      },
    ],
  },
  encode: { x: 'category', y: 'value' },
});
```

## 饼图按大小排序

```javascript
chart.options({
  type: 'interval',
  data: {
    type: 'inline',
    value: [
      { item: 'A', count: 40 },
      { item: 'B', count: 20 },
      { item: 'C', count: 30 },
    ],
    transform: [
      {
        type: 'sort',
        callback: (a, b) => b.count - a.count,  // 从大到小
      },
    ],
  },
  encode: { y: 'count', color: 'item' },
  coordinate: { type: 'theta' },
  transform: [{ type: 'stackY' }],
});
```

## 与其他数据变换组合

```javascript
chart.options({
  type: 'interval',
   {
    type: 'inline',
    value: rawData,
    transform: [
      { type: 'filter', callback: (d) => d.value > 0 },  // 先过滤
      { type: 'sort', callback: (a, b) => b.value - a.value },  // 再排序
      { type: 'slice', start: 0, end: 10 },  // 取前 10 条
    ],
  },
  encode: { x: 'category', y: 'value' },
});
```

## 按字符串排序

```javascript
chart.options({
  type: 'interval',
  data: {
    type: 'inline',
    value: data,
    transform: [
      {
        type: 'sort',
        callback: (a, b) => a.name.localeCompare(b.name),  // 按名称字母排序
      },
    ],
  },
  encode: { x: 'name', y: 'value' },
});
```

## 配置项

| 属性     | 描述                                               | 类型                         | 默认值        |
| -------- | -------------------------------------------------- | ---------------------------- | ------------- |
| callback | Array.sort 的 comparator，返回 1，0，-1 代表 > = < | `(a: any, b: any) => number` | `(a, b) => 0` |

## 与 Mark Transform sortX/sortY 的对比

| 特性 | 数据 sort | mark sortX/sortY |
|------|----------|------------------|
| 配置位置 | `data.transform` | `transform` (mark 层级) |
| 排序依据 | 原始数据字段 | 视觉通道值 |
| 聚合支持 | 不支持 | 支持按聚合值排序 |
| 切片支持 | 需配合 slice | 内置 slice 参数 |

```javascript
// 数据 sort：直接对数据排序
data: {
  transform: [{ type: 'sort', callback: (a, b) => b.value - a.value }],
}

// mark sortX：按 Y 通道聚合值排序
transform: [{ type: 'sortX', by: 'y', reducer: 'sum' }]
```

## 常见错误与修正

### 错误 1：sort 放在 mark transform 中

```javascript
// ❌ 错误：数据 sort 不能放在 mark 的 transform 中
chart.options({
  data: myData,
  transform: [{ type: 'sort', callback: (a, b) => b.value - a.value }],  // ❌ 错误位置
});

// ✅ 正确：sort 放在 data.transform 中
chart.options({
   {
    type: 'inline',
    value: myData,
    transform: [{ type: 'sort', callback: (a, b) => b.value - a.value }],  // ✅ 正确
  },
});
```

### 错误 2：混淆数据 sort 和 mark sortX

```javascript
// ❌ 错误：数据 sort 不支持 channel/by/reducer 参数
data: {
  transform: [{ type: 'sort', channel: 'x', by: 'value' }],  // ❌ 这是 mark transform 语法
}

// ✅ 正确：数据 sort 使用 callback
 {
  transform: [{ type: 'sort', callback: (a, b) => b.value - a.value }],
}

// 如果需要按聚合值排序，应使用 mark transform
transform: [{ type: 'sortX', by: 'y', reducer: 'sum' }]
```

### 错误 3：callback 返回值错误

```javascript
// ❌ 错误：返回布尔值
callback: (a, b) => a.value > b.value  // ❌ 返回布尔值

// ✅ 正确：返回数字（正数、负数、零）
callback: (a, b) => a.value - b.value  // ✅ 升序
callback: (a, b) => b.value - a.value  // ✅ 降序
```

### 错误 4：简写 data 无法配置 transform

```javascript
// ❌ 错误：简写 data 无法配置 transform
chart.options({
  data: myData,  // 简写形式
  // 无法添加 sort transform
});

// ✅ 正确：使用完整 data 配置
chart.options({
   {
    type: 'inline',
    value: myData,
    transform: [{ type: 'sort', callback: (a, b) => b.value - a.value }],
  },
});
```

path: references/data/g2-data-sortby.md
content: ---
id: "g2-data-sortby"
title: "G2 SortBy 字段排序"
description: |
  SortBy 数据变换按照指定字段对数据进行排序。
  与 sort 不同，sortBy 通过字段名指定排序，更简洁直观。
  配置在 data.transform 中。

library: "g2"
version: "5.x"
category: "data"
tags:
  - "sortBy"
  - "字段排序"
  - "排序"
  - "数据变换"
  - "data transform"

related:
  - "g2-data-sort"
  - "g2-transform-sortx"

use_cases:
  - "按字段值排序"
  - "多字段组合排序"
  - "升序/降序排列"

difficulty: "beginner"
completeness: "full"
created: "2025-03-27"
updated: "2025-03-27"
author: "antv-team"
source_url: "https://g2.antv.antgroup.com/manual/core/data/sortBy"
---

## 核心概念

**SortBy 是数据变换（Data Transform），不是标记变换（Mark Transform）**

- 数据变换配置在 `data.transform` 中
- 按字段名指定排序，比 sort 更简洁

**与 sort 的区别：**
- `sort`: 使用 callback 比较函数
- `sortBy`: 通过字段名指定，更简洁

## 最小可运行示例

```javascript
import { Chart } from '@antv/g2';

const chart = new Chart({ container: 'container', width: 700, height: 400 });

const data = [
  { genre: 'Sports', sold: 275 },
  { genre: 'Strategy', sold: 115 },
  { genre: 'Action', sold: 120 },
  { genre: 'Shooter', sold: 350 },
  { genre: 'Other', sold: 150 },
];

chart.options({
  type: 'interval',
  data: {
    type: 'inline',
    value: data,
    transform: [
      {
        type: 'sortBy',
        fields: ['sold'],  // 按 sold 字段升序排序
      },
    ],
  },
  encode: { x: 'genre', y: 'sold' },
});

chart.render();
```

## 配置项

| 属性   | 描述       | 类型                              | 默认值 |
| ------ | ---------- | --------------------------------- | ------ |
| fields | 排序的字段 | `(string \| [string, boolean])[]` | `[]`   |

## 降序排列

```javascript
chart.options({
  data: {
    type: 'inline',
    value: data,
    transform: [
      {
        type: 'sortBy',
        fields: [['sold', false]],  // false 表示降序
      },
    ],
  },
});
```

## 多字段排序

```javascript
// 先按 name 升序，name 相同时按 age 降序
chart.options({
  data: {
    type: 'inline',
    value: data,
    transform: [
      {
        type: 'sortBy',
        fields: [
          ['name', true],   // name 升序
          ['age', false],   // age 降序
        ],
      },
    ],
  },
});
```

## 与 sort 的对比

```javascript
// 使用 sortBy（推荐，更简洁）
data: {
  transform: [{ type: 'sortBy', fields: ['value'] }],
}

// 使用 sort（更灵活）
data: {
  transform: [{ type: 'sort', callback: (a, b) => a.value - b.value }],
}

// sortBy 降序
data: {
  transform: [{ type: 'sortBy', fields: [['value', false]] }],
}

// sort 降序
data: {
  transform: [{ type: 'sort', callback: (a, b) => b.value - a.value }],
}
```

## 常见错误与修正

### 错误 1：sortBy 放在 mark transform 中

```javascript
// ❌ 错误：sortBy 是数据变换，不能放在 mark 的 transform 中
chart.options({
  type: 'interval',
  data,
  transform: [{ type: 'sortBy', fields: ['value'] }],  // ❌ 错误位置
});

// ✅ 正确：sortBy 放在 data.transform 中
chart.options({
  type: 'interval',
  data: {
    type: 'inline',
    value: data,
    transform: [{ type: 'sortBy', fields: ['value'] }],  // ✅ 正确
  },
});
```

### 错误 2：字段名不存在

```javascript
// ❌ 错误：字段名不存在，排序无效
data: {
  transform: [{ type: 'sortBy', fields: ['nonexistent'] }],
}

// ✅ 正确：确保字段名存在
data: {
  transform: [{ type: 'sortBy', fields: ['value'] }],
}
```

path: references/data/g2-data-slice.md
content: ---
id: "g2-data-slice"
title: "G2 Slice 数据切片"
description: |
  Slice 数据变换对数据进行分片，获得子集。
  类似于 Array.prototype.slice，配置在 data.transform 中。

library: "g2"
version: "5.x"
category: "data"
tags:
  - "slice"
  - "切片"
  - "分页"
  - "数据变换"
  - "data transform"

related:
  - "g2-data-filter"
  - "g2-data-sort"

use_cases:
  - "数据分页展示"
  - "只取前 N 条数据"
  - "截取特定范围的数据"

difficulty: "beginner"
completeness: "full"
created: "2025-03-27"
updated: "2025-03-27"
author: "antv-team"
source_url: "https://g2.antv.antgroup.com/manual/core/data/slice"
---

## 核心概念

**Slice 是数据变换（Data Transform），不是标记变换（Mark Transform）**

- 数据变换配置在 `data.transform` 中
- 类似于 [Array.prototype.slice](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/slice)

## 最小可运行示例

```javascript
import { Chart } from '@antv/g2';

const chart = new Chart({ container: 'container', width: 700, height: 400 });

const data = [
  { month: 'Jan', value: 100 },
  { month: 'Feb', value: 120 },
  { month: 'Mar', value: 150 },
  { month: 'Apr', value: 180 },
  { month: 'May', value: 200 },
];

chart.options({
  type: 'line',
   {
    type: 'inline',
    value: data,
    transform: [
      {
        type: 'slice',
        start: 0,
        end: 3,  // 只取前 3 条数据
      },
    ],
  },
  encode: { x: 'month', y: 'value' },
});

chart.render();
```

## 配置项

| 属性  | 描述           | 类型     | 默认值           |
| ----- | -------------- | -------- | ---------------- |
| start | 分片的起始索引 | `number` | `0`              |
| end   | 分片的结束索引 | `number` | `arr.length - 1` |

## 取前 N 条数据

```javascript
chart.options({
  type: 'interval',
   {
    type: 'inline',
    value: largeData,
    transform: [
      { type: 'sort', callback: (a, b) => b.value - a.value },  // 先排序
      { type: 'slice', end: 10 },  // 取前 10 条
    ],
  },
  encode: { x: 'category', y: 'value' },
});
```

## 分页效果

```javascript
// 第 2 页，每页 10 条
const page = 2;
const pageSize = 10;

chart.options({
  data: {
    transform: [
      { type: 'slice', start: (page - 1) * pageSize, end: page * pageSize },
    ],
  },
});
```

## 常见错误与修正

### 错误 1：slice 放在 mark transform 中

```javascript
// ❌ 错误：slice 是数据变换，不能放在 mark 的 transform 中
chart.options({
  type: 'interval',
  data,
  transform: [{ type: 'slice', end: 10 }],  // ❌ 错误位置
});

// ✅ 正确：slice 放在 data.transform 中
chart.options({
  type: 'interval',
   {
    type: 'inline',
    value: data,
    transform: [{ type: 'slice', end: 10 }],  // ✅ 正确
  },
});
```

### 错误 2：索引超出范围

```javascript
// ⚠️ 注意：如果索引超出范围，G2 会自动处理，不会报错
data: {
  transform: [{ type: 'slice', start: 100, end: 200 }],  // 数据只有 50 条
}
// 结果：返回空数组
```

path: references/label-transform/g2-label-transform-overflow-hide.md
content: ---
id: "g2-label-transform-overflow-hide"
title: "G2 OverflowHide 标签变换"
description: |
  标签溢出隐藏变换。当标签超出其所属元素的边界时自动隐藏，
  避免标签溢出造成的视觉混乱。

library: "g2"
version: "5.x"
category: "label-transform"
tags:
  - "标签"
  - "label"
  - "溢出"
  - "隐藏"
  - "overflow"

related:
  - "g2-label-transform-overlap-hide"
  - "g2-label-transform-overlap-dodge-y"
  - "g2-comp-label-config"

use_cases:
  - "饼图标签溢出处理"
  - "柱状图数据标签"
  - "小尺寸元素的标签显示"

anti_patterns:
  - "标签必须全部显示的场景"

difficulty: "beginner"
completeness: "full"
created: "2025-03-26"
updated: "2025-03-26"
author: "antv-team"
source_url: "https://g2.antv.antgroup.com/manual/core/label"
---

## 核心概念

OverflowHide 标签变换会检测标签是否超出其所属元素的边界：
- 如果标签在元素边界内，正常显示
- 如果标签超出边界，自动隐藏

**工作原理：**
1. 计算元素的边界框
2. 计算标签的边界框
3. 检测标签是否溢出元素边界
4. 溢出则隐藏标签

## 最小可运行示例

```javascript
import { Chart } from '@antv/g2';

const chart = new Chart({
  container: 'container',
  width: 640,
  height: 480,
});

chart.options({
  type: 'interval',
  data: [
    { category: 'A', value: 10 },
    { category: 'B', value: 50 },
    { category: 'C', value: 5 },  // 小柱子，标签可能溢出
  ],
  encode: {
    x: 'category',
    y: 'value',
  },
  labels: [
    {
      text: 'value',
      position: 'inside',
      transform: [{ type: 'overflowHide' }],
    },
  ],
});

chart.render();
```

## 常用变体

### 饼图标签溢出处理

```javascript
chart.options({
  type: 'interval',
  coordinate: { type: 'theta' },
  data,
  encode: { y: 'value', color: 'category' },
  labels: [
    {
      text: 'category',
      position: 'inside',
      transform: [{ type: 'overflowHide' }],
    },
  ],
});
```

### 结合其他标签变换

```javascript
chart.options({
  type: 'interval',
  data,
  encode: { x: 'category', y: 'value' },
  labels: [
    {
      text: 'value',
      position: 'inside',
      transform: [
        { type: 'overflowHide' },
        { type: 'overlapHide' },  // 先处理溢出，再处理重叠
      ],
    },
  ],
});
```

## 完整类型参考

```typescript
interface OverflowHideTransform {
  type: 'overflowHide';
  // 无额外配置参数
}
```

## 与其他标签变换的对比

| Transform | 功能 | 适用场景 |
|-----------|------|---------|
| overflowHide | 隐藏溢出标签 | 标签超出元素边界 |
| overlapHide | 隐藏重叠标签 | 标签之间重叠 |
| overlapDodgeY | Y 方向避让 | 标签垂直重叠 |

## 常见错误与修正

### 错误 1：transform 格式错误

```javascript
// ❌ 错误：transform 应该是数组
labels: [{ text: 'value', transform: { type: 'overflowHide' } }]

// ✅ 正确
labels: [{ text: 'value', transform: [{ type: 'overflowHide' }] }]
```

### 错误 2：position 设置不当

```javascript
// ⚠️ 注意：outside 位置的标签通常不会溢出
// overflowHide 主要用于 inside 位置

// 对于 inside 标签
labels: [{
  text: 'value',
  position: 'inside',
  transform: [{ type: 'overflowHide' }]
}]

// 对于 outside 标签，考虑使用 overlapHide
labels: [{
  text: 'value',
  position: 'outside',
  transform: [{ type: 'overlapHide' }]
}]
```

### 错误 3：与其他变换顺序错误

```javascript
// ⚠️ 注意：变换顺序影响结果

// 推荐：先处理溢出，再处理重叠
transform: [
  { type: 'overflowHide' },
  { type: 'overlapHide' },
]
```

path: references/label-transform/g2-label-transform-overlap-dodge-y.md
content: ---
id: "g2-label-transform-overlap-dodge-y"
title: "G2 OverlapDodgeY 标签变换"
description: |
  标签 Y 方向避让变换。当标签在 Y 方向重叠时自动调整位置，
  通过迭代算法避免标签重叠。

library: "g2"
version: "5.x"
category: "label-transform"
tags:
  - "标签"
  - "label"
  - "重叠"
  - "避让"
  - "dodge"

related:
  - "g2-label-transform-overlap-hide"
  - "g2-label-transform-overflow-hide"
  - "g2-comp-label-config"

use_cases:
  - "密集数据点的标签显示"
  - "时间序列图表的标签避让"
  - "需要显示所有标签的场景"

anti_patterns:
  - "标签过多时可能导致布局混乱"

difficulty: "intermediate"
completeness: "full"
created: "2025-03-26"
updated: "2025-03-26"
author: "antv-team"
source_url: "https://g2.antv.antgroup.com/manual/core/label"
---

## 核心概念

OverlapDodgeY 标签变换通过迭代算法在 Y 方向调整标签位置：
- 检测相邻标签是否在 X 方向重叠
- 如果重叠，在 Y 方向分开
- 迭代直到无重叠或达到最大迭代次数

**算法特点：**
- 时间复杂度 O(n log n)
- 支持设置最大迭代次数
- 支持设置标签间距

## 最小可运行示例

```javascript
import { Chart } from '@antv/g2';

const chart = new Chart({
  container: 'container',
  width: 640,
  height: 480,
});

chart.options({
  type: 'line',
  data: [
    { date: '2024-01-01', value: 100, label: 'Event A' },
    { date: '2024-01-02', value: 120, label: 'Event B' },
    { date: '2024-01-02', value: 110, label: 'Event C' },  // 同一天，标签可能重叠
  ],
  encode: {
    x: 'date',
    y: 'value',
  },
  labels: [
    {
      text: 'label',
      position: 'top',
      transform: [{ type: 'overlapDodgeY' }],
    },
  ],
});

chart.render();
```

## 常用变体

### 自定义间距

```javascript
chart.options({
  type: 'line',
  data,
  encode: { x: 'date', y: 'value' },
  labels: [
    {
      text: 'label',
      position: 'top',
      transform: [
        {
          type: 'overlapDodgeY',
          padding: 4,  // 标签之间的最小间距（像素）
        },
      ],
    },
  ],
});
```

### 控制迭代次数

```javascript
chart.options({
  type: 'line',
  data,
  encode: { x: 'date', y: 'value' },
  labels: [
    {
      text: 'label',
      position: 'top',
      transform: [
        {
          type: 'overlapDodgeY',
          maxIterations: 20,  // 最大迭代次数，默认 10
          maxError: 0.1,      // 最大误差，默认 0.1
        },
      ],
    },
  ],
});
```

### 结合其他变换

```javascript
chart.options({
  type: 'line',
  data,
  encode: { x: 'date', y: 'value' },
  labels: [
    {
      text: 'label',
      position: 'top',
      transform: [
        { type: 'overlapDodgeY' },
        { type: 'overflowHide' },  // 先避让，再处理溢出
      ],
    },
  ],
});
```

## 完整类型参考

```typescript
interface OverlapDodgeYTransform {
  type: 'overlapDodgeY';
  padding?: number;         // 标签间距，默认 1
  maxIterations?: number;   // 最大迭代次数，默认 10
  maxError?: number;        // 最大误差，默认 0.1
}
```

## 与其他标签变换的对比

| Transform | 功能 | 优点 | 缺点 |
|-----------|------|------|------|
| overlapDodgeY | Y 方向避让 | 保留所有标签 | 可能改变布局 |
| overlapHide | 隐藏重叠标签 | 布局稳定 | 丢失部分标签 |
| overflowHide | 隐藏溢出标签 | 避免溢出 | 可能丢失标签 |

## 工作原理图解

```
原始状态:
  Label A -------- Label B
      ↑ 重叠 ↑

处理后:
  Label B
      ↑
  Label A --------

  (Y 方向分开)
```

## 常见错误与修正

### 错误 1：transform 格式错误

```javascript
// ❌ 错误：transform 应该是数组
labels: [{ text: 'value', transform: { type: 'overlapDodgeY' } }]

// ✅ 正确
labels: [{ text: 'value', transform: [{ type: 'overlapDodgeY' }] }]
```

### 错误 2：迭代次数设置不当

```javascript
// ⚠️ 注意：迭代次数过多会影响性能
// 标签数量多时，建议减少迭代次数

// 标签较少时
transform: [{ type: 'overlapDodgeY', maxIterations: 20 }]

// 标签较多时
transform: [{ type: 'overlapDodgeY', maxIterations: 5 }]
```

### 错误 3：与其他变换顺序错误

```javascript
// ❌ 错误：先隐藏再避让，效果不佳
transform: [
  { type: 'overlapHide' },
  { type: 'overlapDodgeY' },
]

// ✅ 正确：先避让，再隐藏无法处理的
transform: [
  { type: 'overlapDodgeY' },
  { type: 'overlapHide' },
]
```

path: references/label-transform/g2-label-transform-overflow-stroke.md
content: ---
id: "g2-label-transform-overflow-stroke"
title: "G2 OverflowStroke 标签变换"
description: |
  标签溢出描边变换。当标签超出元素边界时自动添加描边，
  增强标签的可读性。

library: "g2"
version: "5.x"
category: "label-transform"
tags:
  - "标签"
  - "label"
  - "溢出"
  - "描边"
  - "stroke"

related:
  - "g2-label-transform-overflow-hide"
  - "g2-label-transform-contrast-reverse"
  - "g2-comp-label-config"

use_cases:
  - "饼图外部标签"
  - "需要增强可读性的标签"
  - "复杂背景下的标签显示"

anti_patterns:
  - "简单场景不需要描边"

difficulty: "beginner"
completeness: "full"
created: "2025-03-26"
updated: "2025-03-26"
author: "antv-team"
source_url: "https://g2.antv.antgroup.com/manual/core/label"
---

## 核心概念

OverflowStroke 标签变换会检测标签是否超出元素边界：
- 如果超出，为标签添加描边
- 增强标签在复杂背景上的可读性

**工作原理：**
1. 计算元素和标签的边界框
2. 检测标签是否超出元素边界
3. 如果超出，添加描边样式

## 最小可运行示例

```javascript
import { Chart } from '@antv/g2';

const chart = new Chart({
  container: 'container',
  width: 640,
  height: 480,
});

chart.options({
  type: 'interval',
  data: [
    { category: 'A', value: 100 },
    { category: 'B', value: 150 },
    { category: 'C', value: 80 },
  ],
  encode: {
    x: 'category',
    y: 'value',
    color: 'category',
  },
  labels: [
    {
      text: 'value',
      position: 'inside',
      transform: [{ type: 'overflowStroke' }],
    },
  ],
});

chart.render();
```

## 常用变体

### 结合其他变换

```javascript
chart.options({
  type: 'interval',
  data,
  encode: { x: 'category', y: 'value', color: 'category' },
  labels: [
    {
      text: 'value',
      position: 'inside',
      transform: [
        { type: 'overflowStroke' },
        { type: 'contrastReverse' },
      ],
    },
  ],
});
```

## 完整类型参考

```typescript
interface OverflowStrokeTransform {
  type: 'overflowStroke';
  // 无额外配置参数
}
```

## 与其他标签变换的对比

| Transform | 功能 | 处理方式 |
|-----------|------|---------|
| overflowStroke | 溢出描边 | 添加描边 |
| overflowHide | 溢出隐藏 | 隐藏标签 |
| contrastReverse | 对比度反转 | 改变颜色 |

## 常见错误与修正

### 错误 1：transform 格式错误

```javascript
// ❌ 错误：transform 应该是数组
labels: [{ text: 'value', transform: { type: 'overflowStroke' } }]

// ✅ 正确
labels: [{ text: 'value', transform: [{ type: 'overflowStroke' }] }]
```

### 错误 2：与其他变换顺序错误

```javascript
// ⚠️ 注意：描边应该在颜色调整之后
// 建议顺序：contrastReverse → overflowStroke

// ✅ 正确顺序
transform: [
  { type: 'contrastReverse' },
  { type: 'overflowStroke' },
]
```

path: references/label-transform/g2-label-transform-overlap-hide.md
content: ---
id: "g2-label-transform-overlap-hide"
title: "G2 OverlapHide 标签变换"
description: |
  标签重叠隐藏变换。当标签重叠时自动隐藏部分标签，
  避免视觉混乱。支持按优先级决定隐藏顺序。

library: "g2"
version: "5.x"
category: "label-transform"
tags:
  - "标签"
  - "label"
  - "重叠"
  - "隐藏"
  - "overlap"

related:
  - "g2-label-transform-overlap-dodge-y"
  - "g2-label-transform-overflow-hide"
  - "g2-comp-label-config"

use_cases:
  - "密集数据点的标签显示"
  - "时间序列图表的标签处理"
  - "需要简洁显示的场景"

anti_patterns:
  - "必须显示所有标签的场景（改用 overlapDodgeY）"

difficulty: "beginner"
completeness: "full"
created: "2025-03-26"
updated: "2025-03-26"
author: "antv-team"
source_url: "https://g2.antv.antgroup.com/manual/core/label"
---

## 核心概念

OverlapHide 标签变换通过检测标签重叠来隐藏部分标签：
- 按顺序检测每个标签是否与已显示的标签重叠
- 如果重叠，隐藏当前标签
- 支持设置优先级决定隐藏顺序

**工作原理：**
1. 获取所有标签
2. 按优先级排序（可选）
3. 依次检测每个标签是否与已显示标签重叠
4. 重叠则隐藏，否则显示

## 最小可运行示例

```javascript
import { Chart } from '@antv/g2';

const chart = new Chart({
  container: 'container',
  width: 640,
  height: 480,
});

chart.options({
  type: 'line',
  data: [
    { date: '2024-01-01', value: 100 },
    { date: '2024-01-02', value: 120 },
    { date: '2024-01-03', value: 110 },
    { date: '2024-01-04', value: 130 },
  ],
  encode: {
    x: 'date',
    y: 'value',
  },
  labels: [
    {
      text: 'value',
      position: 'top',
      transform: [{ type: 'overlapHide' }],
    },
  ],
});

chart.render();
```

## 常用变体

### 设置优先级

```javascript
chart.options({
  type: 'interval',
  data: [
    { category: 'A', value: 100, priority: 2 },
    { category: 'B', value: 50, priority: 1 },
    { category: 'C', value: 80, priority: 3 },
  ],
  encode: { x: 'category', y: 'value' },
  labels: [
    {
      text: 'value',
      position: 'inside',
      transform: [
        {
          type: 'overlapHide',
          priority: (a, b) => a.priority - b.priority,  // 高优先级先显示
        },
      ],
    },
  ],
});
```

### 结合其他变换

```javascript
chart.options({
  type: 'line',
  data,
  encode: { x: 'date', y: 'value' },
  labels: [
    {
      text: 'value',
      position: 'top',
      transform: [
        { type: 'overlapDodgeY' },  // 先尝试避让
        { type: 'overlapHide' },    // 无法避让的隐藏
      ],
    },
  ],
});
```

## 完整类型参考

```typescript
interface OverlapHideTransform {
  type: 'overlapHide';
  priority?: (a: any, b: any) => number;  // 优先级比较函数
}
```

## 与其他标签变换的对比

| Transform | 功能 | 优点 | 缺点 |
|-----------|------|------|------|
| overlapHide | 隐藏重叠标签 | 布局稳定 | 丢失部分标签 |
| overlapDodgeY | Y 方向避让 | 保留所有标签 | 可能改变布局 |
| overflowHide | 隐藏溢出标签 | 避免溢出 | 可能丢失标签 |

## 优先级排序示例

```javascript
// 按数值大小排序：大值优先显示
labels: [{
  text: 'value',
  transform: [{
    type: 'overlapHide',
    priority: (a, b) => b.value - a.value
  }]
}]

// 按特定顺序排序
labels: [{
  text: 'value',
  transform: [{
    type: 'overlapHide',
    priority: (a, b) => {
      const order = ['A', 'B', 'C', 'D'];
      return order.indexOf(a.category) - order.indexOf(b.category);
    }
  }]
}]
```

## 常见错误与修正

### 错误 1：transform 格式错误

```javascript
// ❌ 错误：transform 应该是数组
labels: [{ text: 'value', transform: { type: 'overlapHide' } }]

// ✅ 正确
labels: [{ text: 'value', transform: [{ type: 'overlapHide' }] }]
```

### 错误 2：优先级函数返回值错误

```javascript
// ❌ 错误：优先级函数应该返回数字
priority: (a, b) => a.value > b.value

// ✅ 正确：返回正数表示 a 优先，负数表示 b 优先
priority: (a, b) => b.value - a.value
```

### 错误 3：与其他变换顺序错误

```javascript
// ❌ 错误：先隐藏再处理其他问题
transform: [
  { type: 'overlapHide' },
  { type: 'overlapDodgeY' },  // 已经隐藏的标签无法避让
]

// ✅ 正确：先尝试其他解决方案，最后隐藏
transform: [
  { type: 'overlapDodgeY' },
  { type: 'overlapHide' },
]
```

path: references/label-transform/g2-label-transform-exceed-adjust.md
content: ---
id: "g2-label-transform-exceed-adjust"
title: "G2 ExceedAdjust 标签变换"
description: |
  标签超出调整变换。当标签超出指定范围时自动调整位置，
  确保标签在可视区域内显示。

library: "g2"
version: "5.x"
category: "label-transform"
tags:
  - "标签"
  - "label"
  - "超出"
  - "调整"
  - "exceed"

related:
  - "g2-label-transform-overflow-hide"
  - "g2-label-transform-overlap-dodge-y"
  - "g2-comp-label-config"

use_cases:
  - "图表边缘标签调整"
  - "小尺寸元素标签"
  - "需要保持标签完整的场景"

anti_patterns:
  - "标签可以隐藏的场景（改用 overflowHide）"

difficulty: "intermediate"
completeness: "full"
created: "2025-03-26"
updated: "2025-03-26"
author: "antv-team"
source_url: "https://g2.antv.antgroup.com/manual/core/label"
---

## 核心概念

ExceedAdjust 标签变换会检测标签是否超出可视区域：
- 如果超出，自动调整标签位置
- 确保标签完整显示

**工作原理：**
1. 计算标签的边界框
2. 检测是否超出图表边界
3. 如果超出，向内调整位置

## 最小可运行示例

```javascript
import { Chart } from '@antv/g2';

const chart = new Chart({
  container: 'container',
  width: 640,
  height: 480,
});

chart.options({
  type: 'point',
  data: [
    { x: 10, y: 100 },
    { x: 20, y: 150 },
    { x: 30, y: 200 },  // 可能在图表顶部边缘
  ],
  encode: {
    x: 'x',
    y: 'y',
  },
  labels: [
    {
      text: 'y',
      position: 'top',
      transform: [{ type: 'exceedAdjust' }],
    },
  ],
});

chart.render();
```

## 常用变体

### 结合其他变换

```javascript
chart.options({
  type: 'point',
  data,
  encode: { x: 'x', y: 'y' },
  labels: [
    {
      text: 'y',
      position: 'top',
      transform: [
        { type: 'exceedAdjust' },
        { type: 'overlapDodgeY' },
      ],
    },
  ],
});
```

## 完整类型参考

```typescript
interface ExceedAdjustTransform {
  type: 'exceedAdjust';
  // 无额外配置参数
}
```

## 与其他标签变换的对比

| Transform | 功能 | 处理方式 |
|-----------|------|---------|
| exceedAdjust | 超出调整 | 移动位置 |
| overflowHide | 溢出隐藏 | 隐藏标签 |
| overlapDodgeY | 重叠避让 | Y 方向分开 |

## 常见错误与修正

### 错误 1：transform 格式错误

```javascript
// ❌ 错误：transform 应该是数组
labels: [{ text: 'value', transform: { type: 'exceedAdjust' } }]

// ✅ 正确
labels: [{ text: 'value', transform: [{ type: 'exceedAdjust' }] }]
```

### 错误 2：与其他变换顺序错误

```javascript
// ⚠️ 注意：变换顺序影响结果
// 建议先处理超出，再处理重叠

// ✅ 正确顺序
transform: [
  { type: 'exceedAdjust' },
  { type: 'overlapDodgeY' },
]
```

path: references/label-transform/g2-label-transform-contrast-reverse.md
content: ---
id: "g2-label-transform-contrast-reverse"
title: "G2 ContrastReverse 标签变换"
description: |
  标签对比度反转变换。根据背景颜色自动调整标签颜色，
  确保标签在深色背景上显示浅色，在浅色背景上显示深色。

library: "g2"
version: "5.x"
category: "label-transform"
tags:
  - "标签"
  - "label"
  - "对比度"
  - "颜色"
  - "contrast"

related:
  - "g2-label-transform-overflow-hide"
  - "g2-comp-label-config"

use_cases:
  - "柱状图内部标签"
  - "饼图标签"
  - "需要根据背景调整颜色的标签"

anti_patterns:
  - "固定颜色标签不需要此变换"

difficulty: "beginner"
completeness: "full"
created: "2025-03-26"
updated: "2025-03-26"
author: "antv-team"
source_url: "https://g2.antv.antgroup.com/manual/core/label"
---

## 核心概念

ContrastReverse 标签变换会根据元素的颜色自动调整标签颜色：
- 深色背景 → 浅色标签
- 浅色背景 → 深色标签

**工作原理：**
1. 获取元素的填充颜色
2. 计算颜色的亮度
3. 根据亮度选择对比色

## 最小可运行示例

```javascript
import { Chart } from '@antv/g2';

const chart = new Chart({
  container: 'container',
  width: 640,
  height: 480,
});

chart.options({
  type: 'interval',
  data: [
    { category: 'A', value: 100 },
    { category: 'B', value: 150 },
    { category: 'C', value: 80 },
  ],
  encode: {
    x: 'category',
    y: 'value',
    color: 'category',
  },
  labels: [
    {
      text: 'value',
      position: 'inside',
      transform: [{ type: 'contrastReverse' }],
    },
  ],
});

chart.render();
```

## 常用变体

### 结合其他变换

```javascript
chart.options({
  type: 'interval',
  data,
  encode: { x: 'category', y: 'value', color: 'category' },
  labels: [
    {
      text: 'value',
      position: 'inside',
      transform: [
        { type: 'contrastReverse' },
        { type: 'overflowHide' },
      ],
    },
  ],
});
```

### 自定义对比色

```javascript
// 注意：contrastReverse 通常使用默认的黑白对比
// 如需自定义，可以在 style 中设置
chart.options({
  type: 'interval',
  data,
  encode: { x: 'category', y: 'value', color: 'category' },
  labels: [
    {
      text: 'value',
      position: 'inside',
      style: {
        fill: '#fff',  // 固定白色
        stroke: '#000',  // 描边增加对比度
        lineWidth: 1,
      },
    },
  ],
});
```

## 完整类型参考

```typescript
interface ContrastReverseTransform {
  type: 'contrastReverse';
  // 无额外配置参数
}
```

## 与固定颜色的对比

| 方式 | 优点 | 缺点 |
|------|------|------|
| contrastReverse | 自动适应 | 可能不符合设计风格 |
| 固定颜色 | 风格统一 | 可能对比度不足 |
| 描边 | 增加对比度 | 可能影响清晰度 |

## 常见错误与修正

### 错误 1：transform 格式错误

```javascript
// ❌ 错误：transform 应该是数组
labels: [{ text: 'value', transform: { type: 'contrastReverse' } }]

// ✅ 正确
labels: [{ text: 'value', transform: [{ type: 'contrastReverse' }] }]
```

### 错误 2：position 设置不当

```javascript
// ⚠️ 注意：contrastReverse 主要用于 inside 位置
// outside 位置的标签不在元素上，无法获取背景色

// ✅ 正确：用于 inside 标签
labels: [{
  text: 'value',
  position: 'inside',
  transform: [{ type: 'contrastReverse' }]
}]
```

path: references/concepts/g2-concept-visual-channels.md
content: ---
id: "g2-concept-visual-channels"
title: "G2 视觉通道（Visual Channels）"
description: |
  视觉通道是数据属性到视觉属性的映射方式，包括位置、颜色、大小、形状、方向等。
  理解各通道的感知效率和适用数据类型，有助于设计更准确、更有效的数据可视化。
  这是 G2 encode 配置设计的理论基础。

library: "g2"
version: "5.x"
category: "concepts"
tags:
  - "视觉通道"
  - "visual channels"
  - "encode"
  - "感知效率"
  - "数据映射"
  - "可视化设计"
  - "颜色"
  - "大小"
  - "位置"

related:
  - "g2-core-encode-channel"
  - "g2-concept-color-theory"

use_cases:
  - "理解 G2 encode 各通道的设计原理"
  - "为不同数据类型选择合适的视觉通道"
  - "避免感知效率低的通道误用"

difficulty: "intermediate"
completeness: "full"
created: "2024-01-01"
updated: "2025-03-01"
author: "antv-team"
---

## 核心概念

视觉通道（Visual Channel）是将**数据属性**映射到**视觉属性**的媒介。G2 中通过 `encode` 字段完成这种映射：

```javascript
chart.options({
  encode: {
    x: 'month',      // 位置通道（x轴）← 分类字段
    y: 'revenue',    // 位置通道（y轴）← 数值字段
    color: 'product',// 颜色通道 ← 分类字段
    size: 'amount',  // 大小通道 ← 数值字段
  },
});
```

## 主要视觉通道及其感知效率

### 定量数据（连续数值）

按感知精确度从高到低排序：

| 排名 | 通道 | G2 对应 | 说明 |
|------|------|---------|------|
| ★★★★★ | **位置（x/y轴）** | `encode.x`, `encode.y` | 最精确，人眼可精确比较 |
| ★★★★ | **长度/高度** | `encode.y`（柱状图） | 次精确，需共同基线 |
| ★★★ | **面积/大小** | `encode.size` | 中等，适合气泡图相对比较 |
| ★★ | **颜色深浅** | `encode.color`（连续色阶）| 较难精确比较，仅适合粗略趋势 |
| ★ | **角度** | 饼图扇区角度 | 人眼对角度判断不精确，慎用 |

### 分类数据（离散类别）

| 通道 | G2 对应 | 适用场景 |
|------|---------|---------|
| **位置分组** | `encode.x`（分类轴） | 柱状图、折线图的分类 |
| **颜色（色相）** | `encode.color` | 区分≤8个类别，超过易混淆 |
| **形状** | `encode.shape` | 散点图区分类别，≤6个 |
| **纹理/图案** | `encode.shape`（自定义）| 无色环境或辅助区分 |

## 通道适配规则

```
定量数据（数值）→ 优先：位置轴（x/y）> 大小（size）> 颜色深度（连续色）
分类数据（类别）→ 优先：位置轴（x/y）> 颜色色相（color）> 形状（shape）
有序数据（排名）→ 优先：位置轴（顺序）> 大小（递减）> 颜色（渐变色）
```

## 通道组合示例

### 气泡图：3个数值通道

```javascript
// x位置 + y位置 + 大小（size） = 三维数值编码
chart.options({
  type: 'point',
  data,
  encode: {
    x: 'GDP',          // 定量 → 位置（最精确）
    y: 'LifeExpectancy',// 定量 → 位置
    size: 'Population', // 定量 → 大小（第三维度）
    color: 'Region',    // 分类 → 颜色色相（第四维度）
  },
  scale: {
    size: { range: [4, 40] },   // 气泡大小范围
  },
});
```

### 热力图：颜色深度编码数值

```javascript
// 颜色用于定量数据时，应使用顺序色阶（浅→深），不用分类色板
chart.options({
  type: 'cell',
  data,
  encode: {
    x: 'weekday',
    y: 'hour',
    color: 'value',     // 定量 → 颜色深浅（连续色阶）
  },
  scale: {
    color: {
      type: 'sequential',
      palette: 'blues',  // 顺序色阶（而非分类色板）
    },
  },
});
```

## 常见通道误用

### 误用 1：用颜色色相表示数值大小

```javascript
// ❌ 误用：颜色色相（红/绿/蓝）不能表达数值大小关系
chart.options({
  encode: { color: 'temperature' },   // temperature 是数值，用色相无法体现大小
  scale: { color: { type: 'ordinal' } },   // ❌ 分类色板用于数值
});

// ✅ 正确：数值用连续色阶
chart.options({
  encode: { color: 'temperature' },
  scale: {
    color: {
      type: 'sequential',   // 顺序比例尺
      palette: 'reds',      // 浅→深的顺序色
    },
  },
});
```

### 误用 2：颜色类别过多导致难以区分

```javascript
// ❌ 超过 8 个颜色类别，人眼难以区分
chart.options({
  encode: { color: 'province' },   // 如果有 31 个省份，颜色无法有效区分
});

// ✅ 超过 8 类时的替代方案：
// 1. 合并次要类别为"其他"
// 2. 改用位置通道（分组柱状图/分面）
// 3. 使用交互过滤（点击图例显示/隐藏）
```

### 误用 3：饼图扇区过多

```javascript
// ❌ 角度通道感知精度低，超过 5 个扇区难以比较
chart.options({
  type: 'interval',
  coordinate: { type: 'theta' },
  // 如果有 10+ 个分类，饼图效果很差
});

// ✅ 分类多时改用柱状图（位置通道感知更精确）
chart.options({
  type: 'interval',
  encode: { x: 'category', y: 'value' },
  transform: [{ type: 'sortX', by: 'y', reverse: true }],
});
```


path: references/concepts/g2-concept-chart-selection.md
content: ---
id: "g2-concept-chart-selection"
title: "G2 图表类型选择指南"
description: |
  根据数据特征和分析目的选择合适的图表类型。
  覆盖比较、趋势、占比、分布、关系六大场景，
  对应 G2 的具体实现方式，帮助避免"用错图表"的常见错误。

library: "g2"
version: "5.x"
category: "concepts"
tags:
  - "图表选择"
  - "chart selection"
  - "可视化设计"
  - "柱状图"
  - "折线图"
  - "饼图"
  - "散点图"
  - "决策"

related:
  - "g2-concept-visual-channels"
  - "g2-mark-interval-basic"
  - "g2-mark-line-basic"
  - "g2-mark-arc-pie"
  - "g2-mark-point-scatter"

use_cases:
  - "根据用户需求选择正确的图表类型"
  - "避免在不合适场景使用饼图或折线图"
  - "理解何时用 G2 图表 vs G6 图分析"

difficulty: "beginner"
completeness: "full"
created: "2024-01-01"
updated: "2025-03-01"
author: "antv-team"
---

## 核心决策树

```
你的数据和目的是什么？

├── 比较类别间的大小 → 柱状图 / 条形图
├── 展示随时间的变化趋势 → 折线图 / 面积图
├── 展示部分与整体的占比 → 饼图 / 环形图 / 堆叠柱状图
├── 展示两个变量的相关性 → 散点图 / 气泡图
├── 展示数据的分布规律 → 直方图 / 箱线图 / 小提琴图
└── 展示节点间的关系网络 → G6 图（force/dagre/tree 布局）
```

## 场景一：比较（Comparison）

**目的**：比较不同类别/时间点的数值大小

| 数据特征 | 推荐图表 | G2 实现 |
|---------|---------|---------|
| 类别 ≤ 10，竖向标签可读 | **柱状图** | `type: 'interval'` |
| 类别名称较长 / 类别多 | **条形图**（水平） | `type: 'interval'` + `coordinate: { transform: [{ type: 'transpose' }] }` |
| 多个系列并排比较 | **分组柱状图** | `transform: [{ type: 'dodgeX' }]` |
| 展示子类在总量中的贡献 | **堆叠柱状图** | `transform: [{ type: 'stackY' }]` |

```javascript
// 柱状图（最常见的比较图）
chart.options({
  type: 'interval',
  data,
  encode: { x: 'category', y: 'value', color: 'category' },
  transform: [{ type: 'sortX', by: 'y', reverse: true }],  // 按值降序
});
```

## 场景二：趋势（Trend）

**目的**：展示数值随时间或序列的变化

| 数据特征 | 推荐图表 | G2 实现 |
|---------|---------|---------|
| 单一指标时间趋势 | **折线图** | `type: 'line'` |
| 多指标对比趋势 | **多系列折线图** | `type: 'line'` + `encode.color: 'series'` |
| 强调面积/累积量 | **面积图** | `type: 'area'` |
| 展示数量随时间增减 | **面积图**（堆叠）| `type: 'area'` + `transform: [{ type: 'stackY' }]` |

```javascript
// 多系列折线图
chart.options({
  type: 'line',
  data,
  encode: { x: 'date', y: 'value', color: 'series' },
  labels: [{ text: 'series', selector: 'last', position: 'right' }],
});
```

## 场景三：占比（Part-to-Whole）

**目的**：展示部分占整体的比例

| 数据特征 | 推荐图表 | G2 实现 | 注意 |
|---------|---------|---------|------|
| 类别 ≤ 5，强调比例 | **饼图** | `interval` + `theta` 坐标 | 类别多时难区分 |
| 需要中心留白 | **环形图** | 饼图 + `innerRadius` | 可在中心放总数 |
| 类别多，强调排名 | **百分比堆叠柱状图** | `stackY` + `normalizeY` | |
| 多层级占比 | **旭日图** | 暂用 `sunburst` 插件 | |

```javascript
// 饼图（类别 ≤ 5 时）
chart.options({
  type: 'interval',
  data,
  encode: { y: 'value', color: 'category' },
  transform: [{ type: 'stackY' }],
  coordinate: { type: 'theta', outerRadius: 0.85 },
  labels: [{
    text: (d) => `${d.category}\n${d.pct}%`,
    position: 'outside',
    connector: true,
  }],
});
```

## 场景四：相关性（Correlation）

**目的**：探索两个或多个变量之间的关系

| 数据特征 | 推荐图表 | G2 实现 |
|---------|---------|---------|
| 两个数值变量 | **散点图** | `type: 'point'` |
| 两个数值 + 第三数值维度 | **气泡图** | `point` + `encode.size` |
| 多变量热力矩阵 | **热力图** | `type: 'cell'` |
| 展示分布+相关 | **散点图 + 趋势线** | `view` + `point` + `line` |

```javascript
// 气泡图
chart.options({
  type: 'point',
  data,
  encode: {
    x: 'income',
    y: 'happiness',
    size: 'population',  // 第三数值维度
    color: 'region',
  },
  scale: { size: { range: [4, 30] } },
});
```

## 场景五：分布（Distribution）

**目的**：了解数据的分布规律

| 数据特征 | 推荐图表 | G2 实现 |
|---------|---------|---------|
| 单变量分布 | **直方图** | `type: 'interval'` + `transform: [{ type: 'binX' }]` |
| 多组分布比较 | **箱线图** | `type: 'boxplot'` |
| 展示中位数/四分位 | **箱线图** | `type: 'boxplot'` |

```javascript
// 直方图
chart.options({
  type: 'interval',
  data,
  encode: { x: 'value', y: 'count' },
  transform: [{ type: 'binX', y: 'count' }],
});
```

## 场景六：关系网络（Relationship）

**目的**：展示实体间的连接关系、层次结构、流向

| 数据特征 | 推荐库 | G6 布局 |
|---------|--------|---------|
| 无层级的网络关系 | **G6** | `force`（力导向） |
| 有方向的流程/依赖 | **G6** | `dagre`（有向无环图） |
| 单根树形层级 | **G6** | `compactBox`（树形）|
| 对等环状关系 | **G6** | `circular`（环形） |

```javascript
// G6 知识图谱（力导向）
const graph = new Graph({
  layout: { type: 'force', preventOverlap: true },
  data: { nodes, edges },
});
await graph.render();
```

## 快速选择口诀

```
比较用柱状，趋势用折线，
占比用饼图，关系用散点，
分布用直方，层级用树形，
网络用 G6，复杂用组合。
```

## 常见错误

### 错误 1：用折线图表示无时间顺序的分类数据

```javascript
// ❌ 误用：城市名称没有顺序，不应该用折线（会误导"趋势"感知）
chart.options({
  type: 'line',
   [{ city: '北京', gdp: 3.6 }, { city: '上海', gdp: 4.3 }],
  encode: { x: 'city', y: 'gdp' },   // ❌ 城市是无序分类，不是时序
});

// ✅ 正确：分类比较用柱状图
chart.options({
  type: 'interval',
  encode: { x: 'city', y: 'gdp' },
});
```

### 错误 2：用饼图展示 8+ 个类别

```javascript
// ❌ 误用：10个类别的饼图，各扇区难以区分
chart.options({
  type: 'interval',
  coordinate: { type: 'theta' },
  // 如果有 10 个国家/地区...很难读取
});

// ✅ 正确：超过 5 类改用排序条形图
chart.options({
  type: 'interval',
  encode: { x: 'country', y: 'value' },
  coordinate: { transform: [{ type: 'transpose' }] },
  transform: [{ type: 'sortX', by: 'y', reverse: true }],
});
```


path: references/concepts/g2-concept-color-theory.md
content: ---
id: "g2-concept-color-theory"
title: "G2 配色理论"
description: |
  数据可视化中颜色的三种使用模式：分类色板（区分类别）、
  顺序色阶（表示数值大小）、发散色阶（表示正负偏差）。
  覆盖 G2 scale.color 配置方法和常见配色误用。

library: "g2"
version: "5.x"
category: "concepts"
tags:
  - "配色"
  - "color"
  - "色板"
  - "顺序色阶"
  - "发散色阶"
  - "分类色板"
  - "palette"
  - "scale.color"

related:
  - "g2-concept-visual-channels"
  - "g2-core-encode-channel"
  - "g2-theme-builtin"

use_cases:
  - "为不同数据类型选择正确的颜色模式"
  - "配置 G2 scale.color 实现正确的颜色映射"
  - "避免颜色误导数据读者"

difficulty: "intermediate"
completeness: "full"
created: "2024-01-01"
updated: "2025-03-01"
author: "antv-team"
---

## 三种颜色使用模式

### 1. 分类色板（Categorical Palette）

**用途**：区分不同类别（定性数据），颜色之间**没有大小关系**

```javascript
// 场景：多系列折线图，用颜色区分产品线
chart.options({
  type: 'line',
  data,
  encode: { x: 'month', y: 'sales', color: 'product' },
  // 默认就是分类色板，无需配置
  // 如需自定义：
  scale: {
    color: {
      type: 'ordinal',
      range: ['#1890ff', '#52c41a', '#fa8c16', '#f5222d', '#722ed1'],
    },
  },
});
```

**规则**：
- 最多 **8 个**颜色，超过则难以区分
- 颜色之间亮度相近（避免某类特别突出）
- 考虑色盲友好性（红绿色盲常见，避免仅用红/绿区分）

### 2. 顺序色阶（Sequential Scale）

**用途**：表示数值从小到大，颜色从浅到深（或一种色调变化）

```javascript
// 场景：热力图、地图、气泡图的颜色深度
chart.options({
  type: 'cell',
  data,
  encode: { x: 'weekday', y: 'hour', color: 'count' },
  scale: {
    color: {
      type: 'sequential',     // 顺序比例尺
      palette: 'blues',        // 单色系：whites → blues
      // 常用内置色阶：'blues' | 'greens' | 'oranges' | 'reds' | 'purples'
      // 多色系：'YlOrRd' | 'YlGnBu' | 'BuPu' | 'GnBu'
    },
  },
});
```

**规则**：
- 数值越大 → 颜色越深（感知自然）
- 使用**单色系**（同色调深浅变化）或**多色系渐变**
- 不要用分类色板表示数值（红/绿没有大小感）

### 3. 发散色阶（Diverging Scale）

**用途**：表示以零（或某基准值）为中心的正负偏差

```javascript
// 场景：盈亏热力图、同比增减、差异对比
chart.options({
  type: 'cell',
  data,
  encode: { x: 'product', y: 'region', color: 'growth' },
  scale: {
    color: {
      type: 'diverging',      // 发散比例尺
      palette: 'RdBu',        // 红（负）→ 白（零）→ 蓝（正）
      // 常用：'RdBu' | 'RdYlGn' | 'BrBG' | 'PuOr'
      domain: [-100, 0, 100], // 对称范围（中间是 0）
    },
  },
});
```

**规则**：
- 中性值（零/平均值）映射为**白色或浅灰**
- 两端颜色**感知强度相当**（避免一端视觉更突出）
- 定义对称的 domain（如 `[-50, 0, 50]`）

## 颜色通道与比例尺配置

```javascript
// G2 完整颜色配置
scale: {
  color: {
    // ── 比例尺类型 ────────────────────────
    type: 'ordinal',     // 分类：'ordinal'
    // type: 'sequential', // 连续顺序
    // type: 'diverging',  // 发散
    // type: 'threshold',  // 分段阈值

    // ── 颜色范围（分类色板）────────────────
    range: ['#1890ff', '#52c41a', '#fa8c16'],   // 自定义颜色列表

    // ── 内置色板名称 ──────────────────────
    palette: 'tableau10',   // 'tableau10' | 'category10' | 'blues' 等

    // ── 域（分类的显示顺序）───────────────
    domain: ['产品A', '产品B', '产品C'],

    // ── 未知值颜色 ────────────────────────
    unknown: '#f0f0f0',
  },
}
```

## 内置色板完整参考

**⚠️ 重要：只能使用下表中列出的名称**。G2 的 `palette` 通过 d3-scale-chromatic 查找，不在此列表中的名称（如 `'blueOrange'`、`'redGreen'`、`'heatmap'`、`'hot'`、`'jet'`）会在运行时报错 `Unknown palette`，图表无法渲染。名称大小写不敏感（`'blues'` 和 `'Blues'` 均可）。

### 分类色板（ordinal scale 用，区分类别）

| 色板名 | 颜色数 | 风格 |
|-------|--------|------|
| `'tableau10'` | 10 | Tableau 经典配色（柔和，默认） |
| `'category10'` | 10 | D3 经典分类色 |
| `'set2'` | 8 | 粉彩风格，温和 |
| `'paired'` | 12 | 成对颜色（浅+深） |
| `'dark2'` | 8 | 深色调，高对比 |
| `'set1'` | 9 | 高饱和度 |
| `'set3'` | 12 | 中等饱和度 |
| `'pastel1'` | 9 | 淡彩色 |
| `'pastel2'` | 8 | 淡彩色 |
| `'accent'` | 8 | 强调色 |

### 顺序色阶（sequential scale 用，正值数值映射）

| 色板名 | 效果 |
|-------|------|
| `'blues'` | 白→蓝 |
| `'greens'` | 白→绿 |
| `'reds'` | 白→红 |
| `'oranges'` | 白→橙 |
| `'purples'` | 白→紫 |
| `'greys'` | 白→灰 |
| `'ylOrRd'` | 黄→橙→红（热力图常用） |
| `'ylGnBu'` | 黄→绿→蓝（sequential 默认值） |
| `'ylOrBr'` | 黄→橙→棕 |
| `'buGn'` | 蓝→绿 |
| `'buPu'` | 蓝→紫 |
| `'gnBu'` | 绿→蓝 |
| `'orRd'` | 橙→红 |
| `'puBu'` | 紫→蓝 |
| `'puBuGn'` | 紫→蓝→绿 |
| `'puRd'` | 紫→红 |
| `'rdPu'` | 红→紫 |
| `'ylGn'` | 黄→绿 |
| `'viridis'` | 紫→蓝→绿→黄（感知均匀，**色盲友好**，推荐）|
| `'plasma'` | 蓝紫→橙黄 |
| `'magma'` | 黑→紫→橙→白 |
| `'inferno'` | 黑→紫→红→黄 |
| `'cividis'` | 蓝→黄（对所有色盲类型友好） |
| `'turbo'` | 蓝→绿→黄→红（彩虹改进版） |
| `'warm'` | 橙→红→紫（暖色） |
| `'cool'` | 青→蓝→紫（冷色） |
| `'rainbow'` | 彩虹（感知不均匀，不推荐） |
| `'sinebow'` | 平滑彩虹 |
| `'cubehelixDefault'` | 螺旋渐变 |

### 发散色阶（diverging scale 用，正负值对比）

| 色板名 | 效果 |
|-------|------|
| `'rdBu'` | 红→白→蓝（**最常用**，涨跌/正负） |
| `'rdYlBu'` | 红→黄→蓝 |
| `'rdYlGn'` | 红→黄→绿（同比增减） |
| `'rdGy'` | 红→白→灰 |
| `'pRGn'` | 紫→白→绿 |
| `'piYG'` | 粉红→白→黄绿 |
| `'puOr'` | 紫→白→橙 |
| `'brBG'` | 棕→白→蓝绿 |
| `'spectral'` | 红→橙→黄→绿→蓝（多色发散） |

## 色盲友好配色

约 8% 的男性有红绿色盲，应避免仅用红/绿区分数据：

```javascript
// ❌ 不友好：红/绿区分（色盲用户无法区分）
scale: { color: { range: ['#ff4d4f', '#52c41a'] } }

// ✅ 友好：使用蓝/橙（色盲可区分）
scale: { color: { range: ['#1890ff', '#fa8c16'] } }

// ✅ 也可以同时用颜色+形状双通道编码
chart.options({
  type: 'point',
  encode: {
    color: 'category',
    shape: 'category',   // 同时用形状区分（不依赖颜色）
  },
});
```

## 常见颜色错误

### 错误 1：用分类色板表示数值（热力图）

```javascript
// ❌ 用分类色（红/蓝/绿）表示数值大小，毫无规律感
chart.options({
  type: 'cell',
  encode: { color: 'temperature' },
  scale: { color: { type: 'ordinal' } },   // ❌ 数值用了分类色板
});

// ✅ 数值用顺序色阶
chart.options({
  type: 'cell',
  encode: { color: 'temperature' },
  scale: { color: { type: 'sequential', palette: 'YlOrRd' } },   // ✅
});
```

### 错误 2：发散色阶域不对称

```javascript
// ❌ 域不对称，零点不在颜色中点
scale: {
  color: {
    type: 'diverging',
    palette: 'RdBu',
    domain: [-20, 100],   // ❌ 负值范围小，零点偏左
  },
}

// ✅ 对称的域，零点在中心
scale: {
  color: {
    type: 'diverging',
    palette: 'RdBu',
    domain: [-100, 0, 100],   // ✅ 明确指定三个控制点
  },
}
```

### 错误 3：颜色过多导致混淆

```javascript
// ❌ 12 个颜色，读者无法区分
chart.options({
  encode: { color: 'province' },   // 31个省份
});

// ✅ 分组或合并，保持 ≤ 8 个颜色类别
// 方案：取 Top 7 + "其他"
const processedData = aggregateTopN(data, 'province', 7);
```


path: references/transforms/g2-transform-stacky.md
content: ---
id: "g2-transform-stacky"
title: "G2 StackY 堆叠变换"
description: |
  StackY 是 G2 v5 中用于实现数据堆叠的 Mark Transform，
  将同一 x 位置的多个数值依次叠加，生成 y0/y1 区间。
  配置在 transform 数组中（与 data、encode 同级），是堆叠柱状图、堆叠面积图、饼图的核心依赖。

library: "g2"
version: "5.x"
category: "transforms"
tags:
  - "StackY"
  - "堆叠"
  - "stackY"
  - "mark transform"
  - "堆叠柱状图"
  - "堆叠面积图"
  - "spec"

related:
  - "g2-mark-interval-stacked"
  - "g2-mark-area-stacked"
  - "g2-transform-normalizey"
  - "g2-transform-dodgex"
  - "g2-data-fold"

use_cases:
  - "创建堆叠柱状图"
  - "创建堆叠面积图"
  - "创建饼图（配合 theta 坐标系）"

difficulty: "beginner"
completeness: "partial"
created: "2024-01-01"
updated: "2025-03-26"
author: "antv-team"
source_url: "https://g2.antv.antgroup.com/manual/core/transform/stack-y"
---

## 核心概念

**StackY 是标记变换（Mark Transform），不是数据变换（Data Transform）**

- 标记变换配置在 `transform` 数组中（与 `data`、`encode` 同级）
- 在标记渲染过程中执行，修改视觉通道值
- **不要**放在 `data.transform` 中

StackY 对每个 x 分组内的数据进行累积计算：
- 输入：`y` 值（各子类别的原始数值）
- 输出：`y0`（底部位置）和 `y1`（顶部位置），驱动柱体/面积的起止位置

```javascript
chart.options({
  type: 'interval',
  data,
  encode: { x: 'month', y: 'value', color: 'type' },
  transform: [{ type: 'stackY' }],  // ✅ Mark Transform：与 data/encode 同级
});
```

## 基本用法（Spec 模式）

```javascript
import { Chart } from '@antv/g2';

// 堆叠柱状图
const chart = new Chart({ container: 'container', width: 640, height: 480 });

chart.options({
  type: 'interval',
  data: [
    { month: 'Jan', type: 'A', value: 100 },
    { month: 'Jan', type: 'B', value: 200 },
    { month: 'Feb', type: 'A', value: 120 },
    { month: 'Feb', type: 'B', value: 180 },
  ],
  encode: { x: 'month', y: 'value', color: 'type' },
  transform: [{ type: 'stackY' }],   // 声明堆叠变换
});

chart.render();
```

## 配置项

```javascript
chart.options({
  type: 'interval',
  data: [...],
  encode: { x: 'month', y: 'value', color: 'type' },
  transform: [
    {
      type: 'stackY',
      orderBy: null,     // null | 'value' | 'sum' | 'series' — 控制堆叠顺序
      reverse: false,    // 是否反转堆叠顺序
      y: 'y',            // 输入 y 通道名（默认 'y'）
      y1: 'y1',          // 输出底部通道名（默认 'y1'）
    },
  ],
});
```

## 与 normalizeY 组合（百分比堆叠）

```javascript
// transform 数组支持多个变换链式执行
chart.options({
  type: 'interval',
  data,
  encode: { x: 'month', y: 'value', color: 'type' },
  transform: [
    { type: 'stackY' },       // 先堆叠
    { type: 'normalizeY' },   // 再归一化到 [0, 1]
  ],
  axis: {
    y: { labelFormatter: (v) => `${(v * 100).toFixed(0)}%` },
  },
});
```

## 用于饼图（配合 theta 坐标系）

```javascript
chart.options({
  type: 'interval',
  data: [
    { type: '分类一', value: 40 },
    { type: '分类二', value: 30 },
    { type: '分类三', value: 30 },
  ],
  encode: { y: 'value', color: 'type' },
  transform: [{ type: 'stackY' }],           // 将数值转为角度区间
  coordinate: { type: 'theta', outerRadius: 0.8 },
});
```

## 用于堆叠面积图

```javascript
chart.options({
  type: 'area',
  data: [...],
  encode: { x: 'date', y: 'value', color: 'type' },
  transform: [{ type: 'stackY' }],
});
```

## 常见错误与修正

### 错误 1：stackY 放在 data.transform 中

```javascript
// ❌ 错误：stackY 是 Mark Transform，不能放在 data.transform 中
chart.options({
  type: 'interval',
   {
    type: 'inline',
    value: data,
    transform: [{ type: 'stackY' }],  // ❌ 错误位置
  },
});

// ✅ 正确：stackY 放在 mark 的 transform 中（与 data/encode 同级）
chart.options({
  type: 'interval',
  data,
  encode: { x: 'month', y: 'value', color: 'type' },
  transform: [{ type: 'stackY' }],  // ✅ 正确
});
```

### 错误 2：transform 写成对象而非数组
```javascript
// ❌ 错误：transform 必须是数组
chart.options({ transform: { type: 'stackY' } });

// ✅ 正确
chart.options({ transform: [{ type: 'stackY' }] });
```

### 错误 3：饼图忘记 stackY
```javascript
// ❌ 错误：theta 坐标系中没有 stackY，所有扇形角度从 0 开始，完全重叠
chart.options({
  type: 'interval',
  data,
  encode: { y: 'value', color: 'type' },
  coordinate: { type: 'theta' },
  // 缺少 transform！
});

// ✅ 正确
chart.options({
  type: 'interval',
  data,
  encode: { y: 'value', color: 'type' },
  transform: [{ type: 'stackY' }],   // 必须！
  coordinate: { type: 'theta' },
});
```

### 错误 4：多系列数据不堆叠直接显示导致重叠
```javascript
// ❌ 错误：多类型 interval 没有 stackY 或 dodgeX，柱体在同位置堆叠
chart.options({
  type: 'interval',
  data: multiTypeData,
  encode: { x: 'month', y: 'value', color: 'type' },
  // 既没有 stackY（堆叠）也没有 dodgeX（分组）
});

// ✅ 堆叠展示
chart.options({ transform: [{ type: 'stackY' }], ... });

// ✅ 分组展示
chart.options({ transform: [{ type: 'dodgeX' }], ... });
```


path: references/transforms/g2-transform-sample.md
content: ---
id: "g2-transform-sample"
title: "G2 Sample 数据采样变换"
description: |
  sample 变换在数据超过阈值（默认 2000 条）时自动对数据降采样，
  避免大数据集渲染过慢或视觉过于密集。
  支持 first、last、min、max、median、lttb（最大三角形，保留趋势）等多种策略。

library: "g2"
version: "5.x"
category: "transforms"
tags:
  - "sample"
  - "采样"
  - "大数据"
  - "性能优化"
  - "lttb"
  - "降采样"
  - "transform"

related:
  - "g2-mark-line-basic"
  - "g2-transform-filter"

use_cases:
  - "折线图数据超过 2000 条时保留视觉趋势的采样"
  - "实时数据流的性能优化"
  - "股票K线等大时间序列可视化"

difficulty: "intermediate"
completeness: "full"
created: "2025-03-24"
updated: "2025-03-24"
author: "antv-team"
source_url: "https://g2.antv.antgroup.com/manual/core/transform/sample"
---

## 最小可运行示例

```javascript
import { Chart } from '@antv/g2';

// 模拟 5000 条时间序列数据
const data = Array.from({ length: 5000 }, (_, i) => ({
  time: new Date(2020, 0, 1 + Math.floor(i / 10)).toISOString(),
  value: Math.sin(i / 50) * 100 + Math.random() * 20,
}));

const chart = new Chart({ container: 'container', width: 800, height: 400 });

chart.options({
  type: 'line',
  data,
  encode: { x: 'time', y: 'value' },
  transform: [
    {
      type: 'sample',
      thresholds: 500,     // 超过 500 条才触发采样
      strategy: 'lttb',   // 最大三角形采样，最佳保留视觉趋势
    },
  ],
});

chart.render();
```

## 采样策略对比

```javascript
// lttb（推荐）：最大三角形三桶算法，视觉保真度最高
transform: [{ type: 'sample', strategy: 'lttb', thresholds: 500 }]

// median：取每桶中位数，平滑但可能丢失极值
transform: [{ type: 'sample', strategy: 'median', thresholds: 1000 }]

// min/max：保留每桶最小/最大值，适合保留极值场景
transform: [{ type: 'sample', strategy: 'max', thresholds: 800 }]

// first/last：取每桶第一/最后一条，性能最好但精度最低
transform: [{ type: 'sample', strategy: 'first', thresholds: 2000 }]
```

## 多系列分组采样

```javascript
// groupBy 指定分组字段，每个系列独立采样
chart.options({
  type: 'line',
  data: multiSeriesData,
  encode: { x: 'time', y: 'value', color: 'series' },
  transform: [
    {
      type: 'sample',
      thresholds: 300,
      strategy: 'lttb',
      groupBy: ['series', 'color'],  // 按系列分组，每组独立降采样
    },
  ],
});
```

## 配置项

```javascript
transform: [
  {
    type: 'sample',
    strategy: 'median',   // 采样策略：'first'|'last'|'min'|'max'|'median'|'lttb'|function
                          // 默认 'median'
    thresholds: 2000,     // 触发采样的数据量阈值，默认 2000
    groupBy: ['series', 'color'],  // 分组字段，默认 ['series', 'color']
  },
]
```

## 常见错误与修正

### 错误 1：thresholds 设置太高——数据虽大但不触发采样
```javascript
// ❌ 10000 条数据，thresholds 是默认的 2000，但策略不对
transform: [{ type: 'sample' }]  // 默认 thresholds: 2000，strategy: 'median'
// ⚠️  对 10000 条数据只降到 2000 条，可能还是太多

// ✅ 根据渲染目标明确设置 thresholds
transform: [{ type: 'sample', thresholds: 300, strategy: 'lttb' }]
```

### 错误 2：对柱状图使用 sample——破坏完整分类
```javascript
// ❌ 柱状图采样后，某些分类会消失，视觉上有断层
chart.options({
  type: 'interval',
  encode: { x: 'category', y: 'value' },
  transform: [{ type: 'sample' }],  // ❌ 柱状图通常不需要采样
});

// ✅ sample 主要用于折线图等连续数据
chart.options({
  type: 'line',
  encode: { x: 'time', y: 'value' },
  transform: [{ type: 'sample', strategy: 'lttb' }],  // ✅
});
```


path: references/transforms/g2-transform-pack.md
content: ---
id: "g2-transform-pack"
title: "G2 Pack Transform"
description: |
  打包布局 Transform，将多个图形元素均匀排列避免重叠。
  常用于 Treemap、气泡图等需要自动布局的场景。

library: "g2"
version: "5.x"
category: "transforms"
tags:
  - "打包"
  - "pack"
  - "布局"
  - "防重叠"
  - "网格"

related:
  - "g2-mark-pack"
  - "g2-mark-treemap"

use_cases:
  - "多个图形元素的自动排列"
  - "小多图网格布局"
  - "避免图形重叠"

anti_patterns:
  - "单个图形不需要打包"
  - "已有明确位置信息的数据"

difficulty: "intermediate"
completeness: "full"
created: "2025-03-26"
updated: "2025-03-26"
author: "antv-team"
source_url: "https://g2.antv.antgroup.com/manual/core/transform"
---

## 核心概念

Pack Transform 通过变换（translate + scale）将多个图形元素均匀排列，避免重叠。它会自动计算每个元素的位置和缩放比例。

**工作原理：**
1. 计算每个元素的边界框
2. 根据容器尺寸计算网格布局
3. 对每个元素应用 translate 和 scale 变换

## 最小可运行示例

```javascript
import { Chart } from '@antv/g2';

const chart = new Chart({
  container: 'container',
  width: 640,
  height: 480,
});

chart.options({
  type: 'pack',
   {
    nodes: [
      { name: 'A', value: 100 },
      { name: 'B', value: 80 },
      { name: 'C', value: 60 },
    ],
  },
  encode: {
    value: 'value',
    color: 'value',
  },
});

chart.render();
```

## 常用变体

### 作为 Transform 使用

```javascript
chart.options({
  type: 'interval',
  data,
  encode: { x: 'category', y: 'value' },
  transform: [
    {
      type: 'pack',
      padding: 5,        // 元素间距
      direction: 'col',  // 排列方向: 'col' | 'row'
    },
  ],
});
```

### 自定义间距

```javascript
chart.options({
  type: 'pack',
  data,
  encode: { value: 'value', color: 'value' },
  transform: [
    {
      type: 'pack',
      padding: 10,  // 元素之间的间距
    },
  ],
});
```

### 按行排列

```javascript
chart.options({
  type: 'pack',
  data,
  encode: { value: 'value', color: 'value' },
  transform: [
    {
      type: 'pack',
      direction: 'row',  // 按行排列
    },
  ],
});
```

## 完整类型参考

```typescript
interface PackTransform {
  type: 'pack';
  padding?: number;       // 元素间距，默认 0
  direction?: 'col' | 'row';  // 排列方向，默认 'col'
}
```

## 与 Pack Mark 的关系

Pack Mark 内部使用 Pack Transform 进行布局：
- **Pack Mark**：用于创建圆形打包图（Circle Packing）
- **Pack Transform**：用于任意图形元素的网格排列

## 常见错误与修正

### 错误 1：padding 值过大

```javascript
// ❌ 错误：padding 过大会导致元素被过度压缩
transform: [{ type: 'pack', padding: 50 }]

// ✅ 正确：合理的 padding 值
transform: [{ type: 'pack', padding: 5 }]
```

### 错误 2：direction 参数错误

```javascript
// ❌ 错误
transform: [{ type: 'pack', direction: 'horizontal' }]

// ✅ 正确
transform: [{ type: 'pack', direction: 'row' }]
```

path: references/transforms/g2-transform-bin.md
content: ---
id: "g2-transform-bin"
title: "G2 Bin / BinX 数值分桶变换（直方图）"
description: |
  binX 将连续的数值 x 通道分成若干区间（桶），统计每个区间内的数据量，
  是直方图的核心变换。bin 同时对 x 和 y 两个方向分桶，生成二维频率矩阵。
  通过 thresholds 控制桶的数量，y 通道指定聚合方式（count/sum 等）。

library: "g2"
version: "5.x"
category: "transforms"
tags:
  - "bin"
  - "binX"
  - "分桶"
  - "直方图"
  - "histogram"
  - "频率分布"
  - "transform"

related:
  - "g2-transform-groupx"
  - "g2-mark-interval-basic"
  - "g2-mark-cell-heatmap"

use_cases:
  - "绘制直方图（数值分布频率）"
  - "二维频率热力图（bin 同时对 x/y 分桶）"
  - "将连续数值转化为离散分组统计"

difficulty: "intermediate"
completeness: "full"
created: "2025-03-24"
updated: "2025-03-24"
author: "antv-team"
source_url: "https://g2.antv.antgroup.com/manual/core/transform/bin"
---

## 最小可运行示例（直方图）

```javascript
import { Chart } from '@antv/g2';

// 连续数值数据，不需要预先统计频率
const rawData = Array.from({ length: 1000 }, () => ({
  age: Math.floor(Math.random() * 50 + 20),  // 20~70 岁之间的随机年龄
}));

const chart = new Chart({ container: 'container', width: 640, height: 400 });

chart.options({
  type: 'interval',
  data: rawData,
  encode: {
    x: 'age',   // 连续数值 → 自动分桶
    y: '★',     // 占位符，binX 会计算 count
  },
  transform: [
    {
      type: 'binX',
      y: 'count',        // 聚合方式：统计每桶数量
      thresholds: 15,    // 桶的数量（近似值），默认自动计算
    },
  ],
  style: { inset: 1 },  // 柱子之间留 1px 间隙
  axis: { y: { title: '人数' } },
});

chart.render();
```

## BinX 配置项

```javascript
transform: [
  {
    type: 'binX',
    thresholds: 20,  // 桶数量（整数）或阈值数组（如 [0, 25, 50, 75, 100]）
    y: 'count',      // 聚合：'count' | 'sum' | 'mean' | 'min' | 'max' | function
    // groupBy: 'color',  // 按颜色分组分桶（用于分组直方图）
  },
]
```

## 二维频率热力图（bin）

```javascript
// bin 同时对 x 和 y 方向分桶，生成二维频率矩阵
chart.options({
  type: 'cell',
  data: scatterData,
  encode: {
    x: 'x',
    y: 'y',
    color: '★',
  },
  transform: [
    {
      type: 'bin',
      color: 'count',    // 统计每个格子的点数（映射到颜色）
      thresholds: [20, 20],  // [x 方向桶数, y 方向桶数]
    },
  ],
  scale: { color: { type: 'sequential', palette: 'ylOrRd' } },
  style: { lineWidth: 0 },
});
```

## 分组直方图（按颜色分桶）

```javascript
chart.options({
  type: 'interval',
  data: employeeData,
  encode: { x: 'salary', y: '★', color: 'dept' },
  transform: [
    { type: 'binX', y: 'count', thresholds: 12 },
    { type: 'stackY' },   // 堆叠
  ],
});
```

## 常见错误与修正

### 错误 1：对分类字段 x 用 binX——分类应用 groupX
```javascript
// ❌ 错误：x 是字符串类别，binX 无法对字符串分桶
chart.options({
  encode: { x: 'department', y: '★' },   // department 是字符串
  transform: [{ type: 'binX', y: 'count' }],  // ❌
});

// ✅ 字符串类别用 groupX
chart.options({
  encode: { x: 'department', y: '★' },
  transform: [{ type: 'groupX', y: 'count' }],  // ✅
});

// ✅ binX 用于连续数值
chart.options({
  encode: { x: 'age', y: '★' },   // age 是数字
  transform: [{ type: 'binX', y: 'count' }],  // ✅
});
```

### 错误 2：thresholds 设置太大——出现极多细小的桶
```javascript
// ❌ 1000 条数据设置 500 个桶，每桶 2 条，直方图没有统计意义
transform: [{ type: 'binX', y: 'count', thresholds: 500 }]  // ❌

// ✅ 直方图桶数通常在 10~50 之间
transform: [{ type: 'binX', y: 'count', thresholds: 20 }]  // ✅
```


path: references/transforms/g2-transform-sortx.md
content: ---
id: "g2-transform-sortx"
title: "G2 SortX 排序变换"
description: |
  SortX 对 x 轴的分类数据按指定字段或函数进行排序，
  常用于将柱状图按数值从大到小排列，创建排名图表。
  多系列按分组总量排序用内置 reducer: 'sum'，无需自定义函数。

library: "g2"
version: "5.x"
category: "transforms"
tags:
  - "sortX"
  - "排序"
  - "排名"
  - "transform"
  - "柱状图排序"
  - "spec"

related:
  - "g2-mark-interval-basic"
  - "g2-transform-dodgex"

use_cases:
  - "创建按值降序排列的柱状图（排名图）"
  - "对分类轴自定义排序顺序"
  - "多系列堆叠图按分组总量排序"

difficulty: "beginner"
completeness: "full"
created: "2024-01-01"
updated: "2025-04-02"
author: "antv-team"
source_url: "https://g2.antv.antgroup.com/manual/core/transform/sort-x"
---

## 最小可运行示例（按值降序排列）

```javascript
import { Chart } from '@antv/g2';

const chart = new Chart({ container: 'container', width: 640, height: 480 });

chart.options({
  type: 'interval',
  data: [
    { city: '北京', gdp: 3.6 },
    { city: '上海', gdp: 4.3 },
    { city: '广州', gdp: 2.8 },
    { city: '深圳', gdp: 3.2 },
    { city: '杭州', gdp: 1.8 },
    { city: '成都', gdp: 2.0 },
  ],
  encode: { x: 'city', y: 'gdp' },
  transform: [
    {
      type: 'sortX',
      by: 'y',           // 按 y 通道值排序
      reverse: true,     // true = 降序（最大值在左）
    },
  ],
  coordinate: { transform: [{ type: 'transpose' }] },   // 转为水平排名图
});

chart.render();
```

## 配置项

```javascript
transform: [
  {
    type: 'sortX',
    by: 'y',          // 排序依据的 channel 名（'y' | 'x' | 'color' 等）
    reducer: 'max',   // 分组聚合方式（见下方说明），默认 'max'
    reverse: true,    // 是否反转顺序（默认 false = 升序）
    slice: 10,        // 只保留前 N 个（用于 Top N 图表）
  },
],
```

**`reducer` 内置值**（多系列/堆叠场景下对分组内的多个 y 值做聚合）：

| 值 | 含义 |
|----|------|
| `'max'` | 取分组最大值（默认） |
| `'min'` | 取分组最小值 |
| `'sum'` | 取分组总和 ← **多系列按总量排序用这个** |
| `'mean'` | 取分组平均值 |
| `'median'` | 取分组中位数 |
| `'first'` | 取分组第一个值 |
| `'last'` | 取分组最后一个值 |

## Top N 排名图（只展示前 10）

```javascript
chart.options({
  type: 'interval',
  data: fullData,
  encode: { x: 'name', y: 'score' },
  transform: [
    {
      type: 'sortX',
      by: 'y',
      reverse: true,
      slice: 10,   // 只取前 10 名
    },
  ],
  coordinate: { transform: [{ type: 'transpose' }] },
  axis: { x: { title: null } },
});
```

## 自定义排序（按指定字段）

```javascript
// 数据中有 rank 字段，按 rank 排序
chart.options({
  type: 'interval',
  data,
  encode: { x: 'name', y: 'value' },
  transform: [
    { type: 'sortX', by: 'rank', reverse: false },
  ],
});
```

## 按分组总量排序（多系列堆叠图）

多系列图中每个 x 分组有多条数据，用内置 `reducer: 'sum'` 按各分组 y 值之和排序，**不需要自定义函数**：

```javascript
chart.options({
  type: 'interval',
  data,
  encode: { x: 'city', y: 'value', color: 'type' },
  transform: [
    { type: 'stackY' },
    {
      type: 'sortX',
      by: 'y',
      reducer: 'sum',   // ✅ 内置求和，按各城市所有系列总和排序
      reverse: true,
    },
  ],
});
```

## 径向坐标系中的排序注意事项

在使用 `radial` 径向坐标系时，SortX 的行为与常规笛卡尔坐标系一致，但需要注意以下几点：

1. **x 和 y 通道映射**：在径向坐标系中，x 通常映射为角度（即圆周方向），y 映射为半径（即距离中心的距离）。因此，`by: 'y'` 实际上是对半径进行排序。
2. **排序必要性**：由于径向图表存在“半径反馈效应”，即外圈即使数值较小也可能看起来比内圈数值大的条目长，因此**强烈建议在使用径向坐标系时对数据进行排序**，以确保视觉准确性。
3. **排序方向控制**：`reverse: true` 会使数据按降序排列，即数值最大的项靠近最外圈；`reverse: false` 则相反。

```javascript
// ✅ 正确：在径向坐标系中按 y 值排序并渲染
chart.options({
  type: 'interval',
  data: [
    { movie: '电影A', rating: 9.2, genre: '科幻' },
    { movie: '电影B', rating: 8.7, genre: '动作' },
    { movie: '电影C', rating: 8.5, genre: '科幻' },
    { movie: '电影D', rating: 7.9, genre: '喜剧' },
    { movie: '电影E', rating: 7.2, genre: '动作' },
    { movie: '电影F', rating: 6.8, genre: '喜剧' }
  ].sort((a, b) => b.rating - a.rating), // 数据预排序
  coordinate: { type: 'radial', innerRadius: 0.35 },
  encode: {
    x: 'movie',
    y: 'rating',
    color: 'rating',
  },
  scale: {
    y: { domain: [0, 10] },
  },
  style: {
    radius: 5,
    fillOpacity: 0.95,
  },
  labels: [{
    text: 'rating',
    position: 'inside',
    style: { fontWeight: 'bold', fill: 'white' },
  }],
  axis: {
    x: { label: { autoRotate: true, style: { fontSize: 10 } } },
    y: { label: true, grid: false, style: { fontSize: 9 } },
  },
  interaction: [{ type: 'elementHighlightByColor' }],
});
```

## 常见错误与修正

### 错误：用自定义函数代替内置 reducer，且误用不存在的 `{ value }` 参数

`sortX` 没有 `by: ({ value }) => ...` 这种 API。`by` 只接受 **channel 名字符串**，聚合逻辑通过 `reducer` 控制。自定义 `reducer` 函数的签名是 `(GI, V) => number`（`GI` = 该分组的行索引数组，`V` = 整列数值数组），而不是接收数据对象数组。

```javascript
// ❌ 错误：by 不接受函数，({ value }) 参数不存在
transform: [
  {
    type: 'sortX',
    by: ({ value }) => d3.sum(value, (d) => d.sales),   // ❌ by 只能是字符串
    reverse: true,
  },
],

// ❌ 同样错误：即使不用 d3，函数形式也不对
transform: [
  {
    type: 'sortX',
    by: ({ value }) => value.reduce((sum, d) => sum + d.value, 0),  // ❌ by 不支持函数
    reverse: true,
  },
],

// ✅ 正确：按分组总和排序用内置 reducer: 'sum'
transform: [
  {
    type: 'sortX',
    by: 'y',
    reducer: 'sum',   // ✅ 内置聚合，无需自定义函数
    reverse: true,
  },
],
```

### 错误：在任何回调中使用未导入的 `d3`

G2 内部使用 d3，但 `d3` 对象不会暴露到用户代码作用域。调用 `d3.sum()`、`d3.max()` 等会抛出 `ReferenceError: d3 is not defined`。如确需自定义逻辑，用原生 JS 替代：

```javascript
// d3.sum(arr, d => d.v)  →  arr.reduce((s, d) => s + d.v, 0)
// d3.max(arr, d => d.v)  →  Math.max(...arr.map(d => d.v))
// d3.min(arr, d => d.v)  →  Math.min(...arr.map(d => d.v))
// d3.mean(arr, d => d.v) →  arr.reduce((s, d) => s + d.v, 0) / arr.length
```

### 错误：在径向坐标系中错误使用 x/y 映射导致排序无效

在径向坐标系中，如果将本应作为排序依据的字段错误地映射到 x 通道，而将角度映射到 y 通道，则 `sortX` 将无法达到预期效果。正确的做法是将排序依据字段映射到 y 通道，并确保数据已按该字段排序。

```javascript
// ❌ 错误：在径向坐标系中错误地将 rating 映射到 x 通道
chart.options({
  type: 'interval',
  data: [
    { movie: '电影A', rating: 9.2, genre: '科幻' },
    { movie: '电影B', rating: 8.7, genre: '动作' },
    // ...
  ],
  coordinate: { type: 'radial', innerRadius: 0.2 },
  encode: {
    x: 'rating',       // ❌ 错误：rating 应该映射到 y 通道
    y: 'movie',        // ❌ 错误：movie 应该映射到 x 通道
    color: 'rating',
  },
  transform: [
    {
      type: 'sortX',
      by: 'rating',    // ❌ 错误：by 应该是 'y'
      reverse: false,
    },
  ],
});

// ✅ 正确：rating 映射到 y 通道，movie 映射到 x 通道，并预排序
chart.options({
  type: 'interval',
  data: [
    { movie: '电影A', rating: 9.2, genre: '科幻' },
    { movie: '电影B', rating: 8.7, genre: '动作' },
    // ...
  ].sort((a, b) => b.rating - a.rating),
  coordinate: { type: 'radial', innerRadius: 0.35 },
  encode: {
    x: 'movie',        // ✅ 正确：movie 映射到 x 通道（角度）
    y: 'rating',       // ✅ 正确：rating 映射到 y 通道（半径）
    color: 'rating',
  },
});
```

path: references/transforms/g2-transform-group.md
content: ---
id: "g2-transform-group"
title: "G2 Group / GroupX / GroupY 分组聚合变换"
description: |
  Group、GroupX、GroupY 是 G2 v5 中用于分组聚合的 Transform。
  Group 按 x 和 y 通道双维度分组；GroupX 按 x 通道分组；GroupY 按 y 通道分组。
  支持 mean、sum、count、min、max、median、first、last 等聚合函数。
  常用于直方图、统计柱状图、聚合折线图等场景。

library: "g2"
version: "5.x"
category: "transforms"
tags:
  - "group"
  - "groupX"
  - "groupY"
  - "分组聚合"
  - "transform"
  - "统计"
  - "mean"
  - "sum"

related:
  - "g2-transform-bin"
  - "g2-transform-stacky"
  - "g2-mark-interval-basic"

use_cases:
  - "按类别计算平均值（均值柱状图）"
  - "按 X 分组后求和展示总量"
  - "将明细数据聚合为统计摘要"

difficulty: "intermediate"
completeness: "full"
created: "2025-03-24"
updated: "2025-03-24"
author: "antv-team"
source_url: "https://g2.antv.antgroup.com/manual/core/transform/group-x"
---

## 核心概念

| Transform | 分组维度 | 典型场景 |
|-----------|----------|----------|
| `groupX` | x 通道（+ color/series） | 同类别取均值/求和 |
| `groupY` | y 通道 | 按 Y 分组聚合 |
| `group` | x + y 双通道 | 二维分组聚合 |

聚合函数通过 `y: 'mean'` 等形式指定，支持：
`mean`（均值）、`sum`（求和）、`count`（计数）、`min`、`max`、`median`、`first`、`last`

## GroupX 基本用法（按类别求均值）

```javascript
import { Chart } from '@antv/g2';

const chart = new Chart({ container: 'container', width: 640, height: 480 });

chart.options({
  type: 'interval',
  data: [
    { category: 'A', value: 10 },
    { category: 'A', value: 20 },
    { category: 'A', value: 30 },
    { category: 'B', value: 40 },
    { category: 'B', value: 50 },
  ],
  encode: { x: 'category', y: 'value' },
  transform: [
    {
      type: 'groupX',
      y: 'mean',   // 按 x 分组，y 取均值
    },
  ],
});

chart.render();
// 结果：A 显示均值 20，B 显示均值 45
```

## GroupX 聚合函数一览

```javascript
chart.options({
  type: 'interval',
  data: rawData,
  encode: { x: 'category', y: 'value' },
  transform: [
    {
      type: 'groupX',
      y: 'mean',      // 均值
      // y: 'sum',    // 求和
      // y: 'count',  // 计数（忽略 y 通道值，统计条数）
      // y: 'max',    // 最大值
      // y: 'min',    // 最小值
      // y: 'median', // 中位数
    },
  ],
});
```

## 统计计数（频率分布）

```javascript
// 统计各类别出现次数
chart.options({
  type: 'interval',
  data: rawData,
  encode: { x: 'category' },    // 无需 y 通道
  transform: [
    { type: 'groupX', y: 'count' },  // y 会自动生成为计数值
  ],
});
```

## GroupY 用法（按 Y 分组）

```javascript
// 水平方向按 y 分组求均值（常用于横向柱状图）
chart.options({
  type: 'interval',
  data: rawData,
  encode: { y: 'category', x: 'value' },
  transform: [
    { type: 'groupY', x: 'mean' },  // 按 y 分组，x 取均值
  ],
  coordinate: { transform: [{ type: 'transpose' }] },
});
```

## 多字段聚合

```javascript
// 同时对多个字段聚合
chart.options({
  type: 'point',
  data: rawData,
  encode: { x: 'date', y: 'value', size: 'amount' },
  transform: [
    {
      type: 'groupX',
      y: 'mean',       // y 取均值
      size: 'sum',     // size 通道取求和
    },
  ],
});
```

## Cell 图表中的 Group 使用

对于 `cell` 类型的图表，通常需要先对数据进行分组聚合再渲染。例如，按日期的 UTC 日和 UTC 月分组，并取最高温度的最大值：

```javascript
const chart = new Chart({
  container: 'container',
});

chart.options({
  type: 'cell',
  height: 300,
  data: {
    type: 'inline',
    value: [
      { date: '2012-01-01', temp_max: 12.8 },
      { date: '2012-01-02', temp_max: 10.6 },
      // 更多数据...
    ]
  },
  encode: {
    x: (d) => new Date(d.date).getUTCDate(),
    y: (d) => new Date(d.date).getUTCMonth(),
    color: 'temp_max',
  },
  transform: [{ type: 'group', color: 'max' }],
  scale: { color: { type: 'sequential', palette: 'gnBu' } },
  style: { inset: 0.5 },
});

chart.render();
```

## 常见错误与修正

### 错误 1：transform 写成对象而非数组
```javascript
// ❌ 错误
chart.options({ transform: { type: 'groupX', y: 'mean' } });

// ✅ 正确
chart.options({ transform: [{ type: 'groupX', y: 'mean' }] });
```

### 错误 2：count 聚合时仍传 y encode
```javascript
// ❌ count 聚合时不需要 y 通道
chart.options({
  encode: { x: 'category', y: 'someField' },
  transform: [{ type: 'groupX', y: 'count' }],  // y: 'count' 会忽略 encode.y
});

// ✅ count 聚合只需 x 通道
chart.options({
  encode: { x: 'category' },    // 不需要 y
  transform: [{ type: 'groupX', y: 'count' }],
});
```

### 错误 3：Cell 图表未正确使用 Group 聚合
```javascript
// ❌ 错误：没有对重复的 x/y 组合做聚合，导致渲染异常
chart.options({
  type: 'cell',
  data: weatherData,
  encode: {
    x: (d) => new Date(d.date).getUTCDate(),
    y: (d) => new Date(d.date).getUTCMonth(),
    color: 'temp_max'
  },
  transform: []  // 缺少必要的 group 聚合
});

// ✅ 正确：使用 group 并指定 color 聚合方式
chart.options({
  type: 'cell',
  data: weatherData,
  encode: {
    x: (d) => new Date(d.date).getUTCDate(),
    y: (d) => new Date(d.date).getUTCMonth(),
    color: 'temp_max'
  },
  transform: [{ type: 'group', color: 'max' }]  // 必须聚合 color 通道
});
```

path: references/transforms/g2-transform-sorty.md
content: ---
id: "g2-transform-sorty"
title: "G2 SortY 按 Y 值排序变换"
description: |
  sortY 在每个 x 分组内按 y 值对数据记录排序，常用于堆叠图中控制各分类的堆叠顺序，
  确保较大的值在底部或顶部。sortX 按 x 通道值对全局数据排序，
  sortColor 则按颜色通道值排序。

library: "g2"
version: "5.x"
category: "transforms"
tags:
  - "sortY"
  - "sortX"
  - "排序"
  - "堆叠顺序"
  - "transform"

related:
  - "g2-transform-sortx"
  - "g2-transform-stacky"
  - "g2-mark-interval-stacked"

use_cases:
  - "堆叠柱状图中控制各分类的堆叠顺序（大值在底）"
  - "确保视觉上更稳定的堆叠布局"

difficulty: "intermediate"
completeness: "full"
created: "2025-03-24"
updated: "2025-03-24"
author: "antv-team"
source_url: "https://g2.antv.antgroup.com/manual/core/transform/sort"
---

## 最小可运行示例（堆叠柱状图排序）

```javascript
import { Chart } from '@antv/g2';

const data = [
  { month: 'Jan', type: 'A', value: 100 },
  { month: 'Jan', type: 'B', value: 200 },
  { month: 'Jan', type: 'C', value: 50 },
  { month: 'Feb', type: 'A', value: 120 },
  { month: 'Feb', type: 'B', value: 80 },
  { month: 'Feb', type: 'C', value: 180 },
];

const chart = new Chart({ container: 'container', width: 640, height: 400 });

chart.options({
  type: 'interval',
  data,
  encode: { x: 'month', y: 'value', color: 'type' },
  transform: [
    { type: 'sortY', reverse: false },  // 每个 x 分组内按 y 值升序排列
    { type: 'stackY' },                 // 再堆叠（大值在顶部）
  ],
});

chart.render();
```

## sortX（全局按 x 值排序）

```javascript
// 条形图按数值降序排列（最大值在顶）
chart.options({
  type: 'interval',
  data: rankingData,
  encode: { x: 'name', y: 'value' },
  transform: [
    { type: 'sortX', by: 'y', reverse: true },  // 按 y 值降序
  ],
  coordinate: { transform: [{ type: 'transpose' }] },
});
```

## 配置项

```javascript
// sortY：在每个 x 分组内排序
transform: [
  {
    type: 'sortY',
    reverse: false,   // false = 升序（小值先），true = 降序（大值先），默认 false
    by: 'y',          // 排序依据通道，默认 'y'
  },
]

// sortX：全局按 x 通道值排序
transform: [
  {
    type: 'sortX',
    by: 'y',          // 排序依据：'x'（按 x 值）或 'y'（按 y 值大小）
    reverse: true,    // true = 降序，默认 false
  },
]
```

## 常见错误与修正

### 错误：sortY 在 stackY 之后执行——堆叠后 y 值已变化，排序基准错误
```javascript
// ❌ 错误顺序：stackY 之后的 y 值是累积值，sortY 基于累积值排序
transform: [
  { type: 'stackY' },  // ❌ 先堆叠
  { type: 'sortY' },   // ❌ 后排序，此时 y 已是累积值
]

// ✅ 正确：先排序后堆叠
transform: [
  { type: 'sortY' },   // ✅ 先按原始 y 值排序
  { type: 'stackY' },  // ✅ 再堆叠（堆叠顺序按排序结果）
]
```


path: references/transforms/g2-transform-normalizey.md
content: ---
id: "g2-transform-normalizey"
title: "G2 NormalizeY 归一化变换"
description: |
  NormalizeY 将每个 x 分组内的 y 值归一化到 [0, 1]，
  通常跟在 stackY 之后使用，用于创建百分比堆叠图表，
  消除总量差异，聚焦占比分布。

library: "g2"
version: "5.x"
category: "transforms"
tags:
  - "normalizeY"
  - "归一化"
  - "百分比"
  - "transform"
  - "百分比堆叠"
  - "占比"
  - "spec"

related:
  - "g2-mark-interval-normalized"
  - "g2-transform-stacky"

use_cases:
  - "创建百分比堆叠柱状图"
  - "创建百分比堆叠面积图"
  - "消除总量差异，聚焦占比"

difficulty: "beginner"
completeness: "partial"
created: "2024-01-01"
updated: "2025-03-01"
author: "antv-team"
source_url: "https://g2.antv.antgroup.com/manual/core/transform/normalize-y"
---

## 基本用法（必须配合 stackY）

```javascript
import { Chart } from '@antv/g2';

const chart = new Chart({ container: 'container', width: 640, height: 480 });

chart.options({
  type: 'interval',
  data,
  encode: { x: 'month', y: 'value', color: 'type' },
  transform: [
    { type: 'stackY' },       // 第一步：堆叠
    { type: 'normalizeY' },   // 第二步：归一化（顺序不能颠倒！）
  ],
  axis: {
    y: { labelFormatter: (v) => `${(v * 100).toFixed(0)}%` },
  },
});

chart.render();
```

## 配置项

```javascript
transform: [
  { type: 'stackY' },
  {
    type: 'normalizeY',
    basis: 'max',    // 归一化基准：'max'（默认，每组最大值）| 'min' | 'first' | 'last' | 'mean' | 'median'
    series: 'y',     // 指定归一化的通道，默认 'y'
  },
],
```

## 常见错误与修正

### 错误：normalizeY 在 stackY 之前执行
```javascript
// ❌ 错误：先归一化再堆叠，得不到百分比堆叠效果
transform: [{ type: 'normalizeY' }, { type: 'stackY' }],

// ✅ 正确：先堆叠，再归一化
transform: [{ type: 'stackY' }, { type: 'normalizeY' }],
```


path: references/transforms/g2-transform-jitterx.md
content: ---
id: "g2-transform-jitterx"
title: "G2 JitterX Transform"
description: |
  在 X 方向对数据进行抖动处理，避免点重叠。
  常用于散点图中分类数据点的分散显示。

library: "g2"
version: "5.x"
category: "transforms"
tags:
  - "抖动"
  - "jitter"
  - "散点图"
  - "防重叠"
  - "X轴"

related:
  - "g2-transform-jitter"
  - "g2-transform-jittery"
  - "g2-mark-point-scatter"

use_cases:
  - "分类散点图中避免点重叠"
  - "展示分类数据的分布密度"
  - "一维数据分布可视化"

anti_patterns:
  - "连续数值数据不需要抖动"
  - "点数量过少时抖动效果不明显"

difficulty: "beginner"
completeness: "full"
created: "2025-03-26"
updated: "2025-03-26"
author: "antv-team"
source_url: "https://g2.antv.antgroup.com/manual/core/transform"
---

## 核心概念

JitterX Transform 在 X 方向对数据点进行随机偏移，使重叠的点分散显示。这对于分类数据的散点图特别有用。

**工作原理：**
1. 根据 X 轴比例尺确定每个类别的范围
2. 在范围内随机偏移每个点的 X 位置
3. 通过 `padding` 控制偏移范围

## 最小可运行示例

```javascript
import { Chart } from '@antv/g2';

const chart = new Chart({
  container: 'container',
  width: 640,
  height: 480,
});

chart.options({
  type: 'point',
  data: [
    { category: 'A', value: 10 },
    { category: 'A', value: 12 },
    { category: 'A', value: 11 },
    { category: 'B', value: 20 },
    { category: 'B', value: 22 },
  ],
  encode: {
    x: 'category',
    y: 'value',
  },
  transform: [
    { type: 'jitterX' },
  ],
});

chart.render();
```

## 常用变体

### 控制抖动范围

```javascript
chart.options({
  type: 'point',
  data,
  encode: { x: 'category', y: 'value' },
  transform: [
    {
      type: 'jitterX',
      padding: 0.2,  // 抖动范围比例，默认 0
    },
  ],
});
```

### 自定义随机函数

```javascript
chart.options({
  type: 'point',
  data,
  encode: { x: 'category', y: 'value' },
  transform: [
    {
      type: 'jitterX',
      random: () => Math.random(),  // 默认 Math.random
    },
  ],
});
```

### 结合 JitterY 使用

```javascript
chart.options({
  type: 'point',
  data,
  encode: { x: 'category', y: 'category2' },
  transform: [
    { type: 'jitterX' },
    { type: 'jitterY' },
  ],
});
```

## 完整类型参考

```typescript
interface JitterXTransform {
  type: 'jitterX';
  padding?: number;      // 抖动范围的内边距，默认 0
  random?: () => number; // 随机数生成函数，默认 Math.random
}
```

## 与 Jitter/JitterY 的对比

| Transform | 抖动方向 | 常用场景 |
|-----------|---------|---------|
| jitter | X 和 Y | 二维分类数据 |
| jitterX | 仅 X | X 轴分类数据 |
| jitterY | 仅 Y | Y 轴分类数据 |

## 常见错误与修正

### 错误 1：对连续数据使用抖动

```javascript
// ❌ 不推荐：X 轴是连续数值时抖动可能造成误解
chart.options({
  type: 'point',
  data,
  encode: { x: 'continuousValue', y: 'value' },
  transform: [{ type: 'jitterX' }],
});

// ✅ 正确：X 轴是分类数据时使用
chart.options({
  type: 'point',
  data,
  encode: { x: 'category', y: 'value' },
  transform: [{ type: 'jitterX' }],
});
```

### 错误 2：padding 值过大

```javascript
// ❌ 错误：padding 过大会导致点溢出到相邻类别
transform: [{ type: 'jitterX', padding: 0.8 }]

// ✅ 正确：合理的 padding 值
transform: [{ type: 'jitterX', padding: 0.2 }]
```

path: references/transforms/g2-transform-selectx.md
content: ---
id: "g2-transform-selectx"
title: "G2 SelectX Transform"
description: |
  按 X 通道选择数据子集。用于筛选每个 X 类别的特定数据点，
  如最大值、最小值、首个、末个等。

library: "g2"
version: "5.x"
category: "transforms"
tags:
  - "选择"
  - "筛选"
  - "X轴"
  - "极值"

related:
  - "g2-transform-select"
  - "g2-transform-selecty"

use_cases:
  - "只显示每个类别的最大值"
  - "筛选每个 X 分组的首个/末个数据点"
  - "突出显示极值点"

anti_patterns:
  - "需要保留所有数据时不应使用"

difficulty: "beginner"
completeness: "full"
created: "2025-03-26"
updated: "2025-03-26"
author: "antv-team"
source_url: "https://g2.antv.antgroup.com/manual/core/transform"
---

## 核心概念

SelectX Transform 按 X 通道分组，然后从每组中选择特定的数据点。选择器支持：
- `max`：Y 值最大的点
- `min`：Y 值最小的点
- `first`：首个数据点
- `last`：末个数据点
- 自定义选择函数

## 最小可运行示例

```javascript
import { Chart } from '@antv/g2';

const chart = new Chart({
  container: 'container',
  width: 640,
  height: 480,
});

chart.options({
  type: 'point',
  data: [
    { category: 'A', value: 10 },
    { category: 'A', value: 25 },
    { category: 'A', value: 15 },
    { category: 'B', value: 20 },
    { category: 'B', value: 35 },
    { category: 'B', value: 30 },
  ],
  encode: {
    x: 'category',
    y: 'value',
  },
  transform: [
    {
      type: 'selectX',
      selector: 'max',  // 只保留每个类别的最大值点
    },
  ],
});

chart.render();
```

## 常用变体

### 选择最小值

```javascript
chart.options({
  type: 'point',
  data,
  encode: { x: 'category', y: 'value' },
  transform: [
    { type: 'selectX', selector: 'min' },
  ],
});
```

### 选择首个/末个

```javascript
// 选择每个类别的第一个数据点
chart.options({
  type: 'point',
  data,
  encode: { x: 'category', y: 'value' },
  transform: [
    { type: 'selectX', selector: 'first' },
  ],
});

// 选择每个类别的最后一个数据点
chart.options({
  type: 'point',
  data,
  encode: { x: 'category', y: 'value' },
  transform: [
    { type: 'selectX', selector: 'last' },
  ],
});
```

### 自定义选择器

```javascript
chart.options({
  type: 'point',
  data,
  encode: { x: 'category', y: 'value' },
  transform: [
    {
      type: 'selectX',
      selector: (I, Y) => {
        // I: 组内索引数组
        // Y: Y 通道的值数组
        // 返回选中的索引
        return I.reduce((maxIdx, i) => Y[i] > Y[maxIdx] ? i : maxIdx, I[0]);
      },
    },
  ],
});
```

## 完整类型参考

```typescript
interface SelectXTransform {
  type: 'selectX';
  selector: 'max' | 'min' | 'first' | 'last' | ((I: number[], Y: any[]) => number);
}
```

## 与 Select/SelectY 的对比

| Transform | 分组维度 | 常用场景 |
|-----------|---------|---------|
| select | 按指定通道 | 通用选择 |
| selectX | 按 X 通道 | X 轴分类筛选 |
| selectY | 按 Y 通道 | Y 轴分类筛选 |

## 常见错误与修正

### 错误 1：selector 拼写错误

```javascript
// ❌ 错误
transform: [{ type: 'selectX', selector: 'maximum' }]

// ✅ 正确
transform: [{ type: 'selectX', selector: 'max' }]
```

### 错误 2：自定义选择器返回值错误

```javascript
// ❌ 错误：返回了值而非索引
selector: (I, Y) => Math.max(...I.map(i => Y[i]))

// ✅ 正确：返回索引
selector: (I, Y) => I.reduce((maxIdx, i) => Y[i] > Y[maxIdx] ? i : maxIdx, I[0])
```

path: references/transforms/g2-transform-groupcolor.md
content: ---
id: "g2-transform-groupcolor"
title: "G2 GroupColor Transform"
description: |
  按 color 通道对数据进行分组聚合。常用于分类聚合场景，
  如按类别统计总和、平均值等。

library: "g2"
version: "5.x"
category: "transforms"
tags:
  - "分组"
  - "聚合"
  - "color"
  - "分类统计"

related:
  - "g2-transform-groupx"
  - "g2-transform-groupy"
  - "g2-transform-group"

use_cases:
  - "按类别统计总和"
  - "按颜色维度聚合数据"
  - "计算各类别的平均值、最大值"

anti_patterns:
  - "需要保留原始数据时不应使用"

difficulty: "beginner"
completeness: "full"
created: "2025-03-26"
updated: "2025-03-26"
author: "antv-team"
source_url: "https://g2.antv.antgroup.com/manual/core/transform"
---

## 核心概念

GroupColor Transform 按 `color` 通道的值对数据进行分组，然后对每组进行聚合计算。

**聚合函数支持：**
- `sum`：求和
- `mean`：平均值
- `median`：中位数
- `max`：最大值
- `min`：最小值
- `count`：计数
- `first`：取第一个值
- `last`：取最后一个值

## 最小可运行示例

```javascript
import { Chart } from '@antv/g2';

const chart = new Chart({
  container: 'container',
  width: 640,
  height: 480,
});

chart.options({
  type: 'interval',
  data: [
    { category: 'A', type: 'X', value: 10 },
    { category: 'A', type: 'Y', value: 20 },
    { category: 'B', type: 'X', value: 15 },
    { category: 'B', type: 'Y', value: 25 },
  ],
  encode: {
    x: 'category',
    y: 'value',
    color: 'type',  // 按 type 分组
  },
  transform: [
    {
      type: 'groupColor',
      y: 'sum',  // 对每组求和
    },
  ],
});

chart.render();
```

## 常用变体

### 计算平均值

```javascript
chart.options({
  type: 'interval',
  data,
  encode: { x: 'category', y: 'value', color: 'type' },
  transform: [
    { type: 'groupColor', y: 'mean' },
  ],
});
```

### 多通道聚合

```javascript
chart.options({
  type: 'interval',
  data,
  encode: { x: 'category', y: 'value', color: 'type', size: 'count' },
  transform: [
    {
      type: 'groupColor',
      y: 'sum',
      size: 'count',  // 同时聚合 size 通道
    },
  ],
});
```

### 自定义聚合函数

```javascript
chart.options({
  type: 'interval',
  data,
  encode: { x: 'category', y: 'value', color: 'type' },
  transform: [
    {
      type: 'groupColor',
      y: (I, V) => {
        // I: 组内索引数组
        // V: 该通道的值数组
        return I.reduce((sum, i) => sum + V[i], 0) / I.length;
      },
    },
  ],
});
```

## 完整类型参考

```typescript
interface GroupColorTransform {
  type: 'groupColor';
  y?: 'sum' | 'mean' | 'median' | 'max' | 'min' | 'count' | 'first' | 'last' | ((I: number[], V: any[]) => any);
  // 其他通道也可以聚合
  [channel: string]: Reducer;
}

type Reducer = 'sum' | 'mean' | 'median' | 'max' | 'min' | 'count' | 'first' | 'last' | ((I: number[], V: any[]) => any);
```

## 常见错误与修正

### 错误 1：未指定 color 通道

```javascript
// ❌ 错误：没有 color 通道，groupColor 无效
chart.options({
  type: 'interval',
  data,
  encode: { x: 'category', y: 'value' },
  transform: [{ type: 'groupColor', y: 'sum' }],
});

// ✅ 正确：添加 color 通道
chart.options({
  type: 'interval',
  data,
  encode: { x: 'category', y: 'value', color: 'type' },
  transform: [{ type: 'groupColor', y: 'sum' }],
});
```

### 错误 2：聚合函数名拼写错误

```javascript
// ❌ 错误
transform: [{ type: 'groupColor', y: 'average' }]

// ✅ 正确
transform: [{ type: 'groupColor', y: 'mean' }]
```

path: references/transforms/g2-transform-selecty.md
content: ---
id: "g2-transform-selecty"
title: "G2 SelectY Transform"
description: |
  按 Y 通道选择数据子集。用于筛选每个 Y 类别的特定数据点，
  如最大值、最小值、首个、末个等。

library: "g2"
version: "5.x"
category: "transforms"
tags:
  - "选择"
  - "筛选"
  - "Y轴"
  - "极值"

related:
  - "g2-transform-select"
  - "g2-transform-selectx"

use_cases:
  - "水平条形图中筛选极值"
  - "转置坐标系下的数据选择"
  - "按 Y 分类筛选数据点"

anti_patterns:
  - "需要保留所有数据时不应使用"

difficulty: "beginner"
completeness: "full"
created: "2025-03-26"
updated: "2025-03-26"
author: "antv-team"
source_url: "https://g2.antv.antgroup.com/manual/core/transform"
---

## 核心概念

SelectY Transform 按 Y 通道分组，然后从每组中选择特定的数据点。选择器支持：
- `max`：X 值最大的点
- `min`：X 值最小的点
- `first`：首个数据点
- `last`：末个数据点
- 自定义选择函数

## 最小可运行示例

```javascript
import { Chart } from '@antv/g2';

const chart = new Chart({
  container: 'container',
  width: 640,
  height: 480,
});

chart.options({
  type: 'point',
  data: [
    { value: 10, category: 'A' },
    { value: 25, category: 'A' },
    { value: 15, category: 'A' },
    { value: 20, category: 'B' },
    { value: 35, category: 'B' },
    { value: 30, category: 'B' },
  ],
  encode: {
    x: 'value',
    y: 'category',
  },
  transform: [
    {
      type: 'selectY',
      selector: 'max',  // 只保留每个 Y 类别的最大值点
    },
  ],
});

chart.render();
```

## 常用变体

### 选择最小值

```javascript
chart.options({
  type: 'point',
  data,
  encode: { x: 'value', y: 'category' },
  transform: [
    { type: 'selectY', selector: 'min' },
  ],
});
```

### 选择首个/末个

```javascript
// 选择每个 Y 类别的第一个数据点
chart.options({
  type: 'point',
  data,
  encode: { x: 'value', y: 'category' },
  transform: [
    { type: 'selectY', selector: 'first' },
  ],
});

// 选择每个 Y 类别的最后一个数据点
chart.options({
  type: 'point',
  data,
  encode: { x: 'value', y: 'category' },
  transform: [
    { type: 'selectY', selector: 'last' },
  ],
});
```

### 自定义选择器

```javascript
chart.options({
  type: 'point',
  data,
  encode: { x: 'value', y: 'category' },
  transform: [
    {
      type: 'selectY',
      selector: (I, X) => {
        // I: 组内索引数组
        // X: X 通道的值数组
        // 返回选中的索引
        return I.reduce((maxIdx, i) => X[i] > X[maxIdx] ? i : maxIdx, I[0]);
      },
    },
  ],
});
```

## 完整类型参考

```typescript
interface SelectYTransform {
  type: 'selectY';
  selector: 'max' | 'min' | 'first' | 'last' | ((I: number[], X: any[]) => number);
}
```

## 与 Select/SelectX 的对比

| Transform | 分组维度 | 常用场景 |
|-----------|---------|---------|
| select | 按指定通道 | 通用选择 |
| selectX | 按 X 通道 | X 轴分类筛选 |
| selectY | 按 Y 通道 | Y 轴分类筛选 |

## 常见错误与修正

### 错误 1：selector 拼写错误

```javascript
// ❌ 错误
transform: [{ type: 'selectY', selector: 'minimum' }]

// ✅ 正确
transform: [{ type: 'selectY', selector: 'min' }]
```

### 错误 2：自定义选择器返回值错误

```javascript
// ❌ 错误：返回了值而非索引
selector: (I, X) => Math.max(...I.map(i => X[i]))

// ✅ 正确：返回索引
selector: (I, X) => I.reduce((maxIdx, i) => X[i] > X[maxIdx] ? i : maxIdx, I[0])
```

path: references/transforms/g2-transform-sort-color.md
content: ---
id: "g2-transform-sort-color"
title: "G2 SortColor 按颜色分组排序变换"
description: |
  sortColor 是 G2 v5 中的排序 Transform，对颜色（color）通道的比例尺域（domain）进行排序。
  与 sortX（对 x 轴排序）类似，但排序作用于 color 通道的类别顺序。
  常用于图例排序、堆叠图层顺序调整等场景。

library: "g2"
version: "5.x"
category: "transforms"
tags:
  - "sortColor"
  - "颜色排序"
  - "图例顺序"
  - "transform"
  - "sort"
  - "color"

related:
  - "g2-transform-sortx"
  - "g2-transform-sorty"
  - "g2-mark-interval-stacked"

use_cases:
  - "按数值大小对图例顺序排序"
  - "堆叠柱状图的颜色分层顺序调整"
  - "折线图系列颜色分配顺序控制"

difficulty: "intermediate"
completeness: "full"
created: "2025-03-24"
updated: "2025-03-24"
author: "antv-team"
source_url: "https://g2.antv.antgroup.com/manual/core/transform/sort-color"
---

## 核心概念

`sortColor` 通过计算各 color 分组的聚合值（默认 y 通道的均值），
对 color scale 的 domain 进行重新排序，影响：
- 图例的显示顺序
- 堆叠图中各层的叠放顺序
- 颜色分配顺序

## 基本用法

```javascript
import { Chart } from '@antv/g2';

const chart = new Chart({ container: 'container', width: 640, height: 480 });

chart.options({
  type: 'interval',
  data: [
    { month: 'Jan', type: 'A', value: 50 },
    { month: 'Jan', type: 'B', value: 80 },
    { month: 'Jan', type: 'C', value: 30 },
    { month: 'Feb', type: 'A', value: 60 },
    { month: 'Feb', type: 'B', value: 70 },
    { month: 'Feb', type: 'C', value: 40 },
  ],
  encode: { x: 'month', y: 'value', color: 'type' },
  transform: [
    { type: 'stackY' },
    { type: 'sortColor', channel: 'y', order: 'descending' },  // 按 y 均值降序排列颜色
  ],
});

chart.render();
```

## 配置项

```javascript
chart.options({
  transform: [
    {
      type: 'sortColor',
      channel: 'y',           // 用于计算排序依据的通道，默认 'y'
      order: 'ascending',     // 'ascending'（升序）| 'descending'（降序），默认 'ascending'
      reducer: 'mean',        // 聚合方式：'mean' | 'sum' | 'max' | 'min' | 函数，默认 'mean'
      reverse: false,         // 是否反转排序结果
    },
  ],
});
```

## 与 sortX 对比

```javascript
// sortX：对 x 轴类别顺序排序（影响柱子/点的 x 轴位置顺序）
transform: [{ type: 'sortX', channel: 'y', order: 'descending' }]

// sortColor：对颜色分组（图例/堆叠层）排序（不影响 x 轴顺序）
transform: [{ type: 'sortColor', channel: 'y', order: 'descending' }]
```

## 常见错误与修正

### 错误：期望改变柱子位置但用了 sortColor
```javascript
// ❌ 错误：sortColor 只改变颜色/图例顺序，不改变 x 轴柱子位置
chart.options({
  encode: { x: 'type', y: 'value' },
  transform: [{ type: 'sortColor', channel: 'y', order: 'descending' }],
  // x 轴柱子顺序没有变化！
});

// ✅ 要改变 x 轴柱子位置，使用 sortX
chart.options({
  encode: { x: 'type', y: 'value' },
  transform: [{ type: 'sortX', channel: 'y', order: 'descending' }],  // ✅
});
```


path: references/transforms/g2-transform-select.md
content: ---
id: "g2-transform-select"
title: "G2 Select / SelectX / SelectY 筛选变换"
description: |
  select 系列变换从分组数据中筛选出特定的数据行用于标注。
  selectX 按 x 通道分组后筛选（常用于折线图末端标签），
  selectY 按 y 通道分组后筛选。
  selector 支持 'first'、'last'、'min'、'max' 等预设值或自定义函数。

library: "g2"
version: "5.x"
category: "transforms"
tags:
  - "select"
  - "selectX"
  - "selectY"
  - "筛选"
  - "末端标签"
  - "极值标注"
  - "transform"

related:
  - "g2-mark-line-basic"
  - "g2-mark-text"
  - "g2-comp-annotation"

use_cases:
  - "在折线图末端显示最新数据标签"
  - "标注每条线的最大值或最小值"
  - "在特定 x 位置放置注释标签"

difficulty: "intermediate"
completeness: "full"
created: "2025-03-24"
updated: "2025-03-24"
author: "antv-team"
source_url: "https://g2.antv.antgroup.com/manual/core/transform/select"
---

## 最小可运行示例（折线图末端标签）

```javascript
import { Chart } from '@antv/g2';

const data = [
  { month: 'Jan', type: 'A', value: 83 },
  { month: 'Feb', type: 'A', value: 90 },
  { month: 'Mar', type: 'A', value: 76 },
  { month: 'Jan', type: 'B', value: 50 },
  { month: 'Feb', type: 'B', value: 65 },
  { month: 'Mar', type: 'B', value: 72 },
];

const chart = new Chart({ container: 'container', width: 640, height: 400 });

// 主折线图
chart.options({
  type: 'view',
  children: [
    {
      type: 'line',
       data,
      encode: { x: 'month', y: 'value', color: 'type' },
    },
    // 末端标签：text mark + selectX（取每条线的最后一个点）
    {
      type: 'text',
       data,
      encode: { x: 'month', y: 'value', color: 'type', text: 'type' },
      transform: [
        {
          type: 'selectX',
          selector: 'last',   // 取每组（每条线）x 最大的点
        },
      ],
      style: { textAnchor: 'start', dx: 6 },
    },
  ],
});

chart.render();
```

## 标注最大值

```javascript
// 在折线图最高点添加标注
{
  type: 'point',
  data,
  encode: { x: 'date', y: 'value', color: 'type' },
  transform: [
    {
      type: 'selectY',
      selector: 'max',   // 每组取 y 值最大的点
    },
  ],
  style: { r: 6, lineWidth: 2 },
  labels: [{ text: (d) => `最高: ${d.value}`, position: 'top' }],
}
```

## selector 速查

```javascript
// 取最后一个点（常用于末端标签）
transform: [{ type: 'selectX', selector: 'last' }]

// 取第一个点
transform: [{ type: 'selectX', selector: 'first' }]

// 取 y 值最大的点
transform: [{ type: 'selectY', selector: 'max' }]

// 取 y 值最小的点
transform: [{ type: 'selectY', selector: 'min' }]

// 自定义：取第 N 个点
transform: [{ type: 'selectX', selector: (data) => data[Math.floor(data.length / 2)] }]
```

## 常见错误与修正

### 错误：select 用在 line mark 自身——应用在独立的 text/point mark
```javascript
// ❌ 在 line mark 上用 selectX，整条线只剩一个点
chart.options({
  type: 'line',
  data,
  encode: { x: 'month', y: 'value' },
  transform: [{ type: 'selectX', selector: 'last' }],  // ❌ 会把折线变成单点
});

// ✅ select 用在额外的 text 或 point mark，与 line 并列
chart.options({
  type: 'view',
  children: [
    { type: 'line', data, encode: { x: 'month', y: 'value' } },
    {
      type: 'text',
      data,
      encode: { x: 'month', y: 'value', text: 'value' },
      transform: [{ type: 'selectX', selector: 'last' }],  // ✅ 独立 mark
    },
  ],
});
```


path: references/transforms/g2-transform-jittery.md
content: ---
id: "g2-transform-jittery"
title: "G2 JitterY Transform"
description: |
  在 Y 方向对数据进行抖动处理，避免点重叠。
  常用于散点图中分类数据点的分散显示。

library: "g2"
version: "5.x"
category: "transforms"
tags:
  - "抖动"
  - "jitter"
  - "散点图"
  - "防重叠"
  - "Y轴"

related:
  - "g2-transform-jitter"
  - "g2-transform-jitterx"
  - "g2-mark-point-scatter"

use_cases:
  - "分类散点图中避免点重叠"
  - "水平方向分类数据的分布展示"
  - "转置坐标系下的抖动"

anti_patterns:
  - "连续数值数据不需要抖动"
  - "点数量过少时抖动效果不明显"

difficulty: "beginner"
completeness: "full"
created: "2025-03-26"
updated: "2025-03-26"
author: "antv-team"
source_url: "https://g2.antv.antgroup.com/manual/core/transform"
---

## 核心概念

JitterY Transform 在 Y 方向对数据点进行随机偏移，使重叠的点分散显示。与 JitterX 对称，适用于 Y 轴为分类数据的情况。

**工作原理：**
1. 根据 Y 轴比例尺确定每个类别的范围
2. 在范围内随机偏移每个点的 Y 位置
3. 通过 `padding` 控制偏移范围

## 最小可运行示例

```javascript
import { Chart } from '@antv/g2';

const chart = new Chart({
  container: 'container',
  width: 640,
  height: 480,
});

chart.options({
  type: 'point',
  data: [
    { value: 10, category: 'A' },
    { value: 12, category: 'A' },
    { value: 11, category: 'A' },
    { value: 20, category: 'B' },
    { value: 22, category: 'B' },
  ],
  encode: {
    x: 'value',
    y: 'category',
  },
  transform: [
    { type: 'jitterY' },
  ],
});

chart.render();
```

## 常用变体

### 控制抖动范围

```javascript
chart.options({
  type: 'point',
  data,
  encode: { x: 'value', y: 'category' },
  transform: [
    {
      type: 'jitterY',
      padding: 0.2,  // 抖动范围比例
    },
  ],
});
```

### 结合 JitterX 使用

```javascript
chart.options({
  type: 'point',
  data,
  encode: { x: 'categoryX', y: 'categoryY' },
  transform: [
    { type: 'jitterX' },
    { type: 'jitterY' },
  ],
});
```

### 自定义随机函数

```javascript
chart.options({
  type: 'point',
  data,
  encode: { x: 'value', y: 'category' },
  transform: [
    {
      type: 'jitterY',
      random: () => Math.random(),
    },
  ],
});
```

## 完整类型参考

```typescript
interface JitterYTransform {
  type: 'jitterY';
  padding?: number;      // 抖动范围的内边距，默认 0
  random?: () => number; // 随机数生成函数，默认 Math.random
}
```

## 与 Jitter/JitterX 的对比

| Transform | 抖动方向 | 常用场景 |
|-----------|---------|---------|
| jitter | X 和 Y | 二维分类数据 |
| jitterX | 仅 X | X 轴分类数据 |
| jitterY | 仅 Y | Y 轴分类数据 |

## 常见错误与修正

### 错误 1：对连续数据使用抖动

```javascript
// ❌ 不推荐：Y 轴是连续数值时抖动可能造成误解
chart.options({
  type: 'point',
  data,
  encode: { x: 'value', y: 'continuousValue' },
  transform: [{ type: 'jitterY' }],
});

// ✅ 正确：Y 轴是分类数据时使用
chart.options({
  type: 'point',
  data,
  encode: { x: 'value', y: 'category' },
  transform: [{ type: 'jitterY' }],
});
```

### 错误 2：padding 值过大

```javascript
// ❌ 错误：padding 过大会导致点溢出到相邻类别
transform: [{ type: 'jitterY', padding: 0.8 }]

// ✅ 正确：合理的 padding 值
transform: [{ type: 'jitterY', padding: 0.2 }]
```

path: references/transforms/g2-transform-stack-enter.md
content: ---
id: "g2-transform-stack-enter"
title: "G2 StackEnter 入场动画堆叠变换"
description: |
  stackEnter 是 G2 v5 中用于分组入场动画的 Transform，
  将同一分组内的元素按序错开入场时间（enterDelay），
  实现"一组一组依次出现"的入场动画效果。
  常用于柱状图、折线图的分组逐步入场展示。

library: "g2"
version: "5.x"
category: "transforms"
tags:
  - "stackEnter"
  - "入场动画"
  - "enterDelay"
  - "分组动画"
  - "transform"
  - "animation"

related:
  - "g2-animation-intro"
  - "g2-transform-stacky"
  - "g2-mark-interval-grouped"

use_cases:
  - "柱状图各分组逐批入场（X 分组依次出现）"
  - "折线图系列逐条依次绘制"
  - "数据讲述场景中按节奏逐步呈现数据"

difficulty: "intermediate"
completeness: "full"
created: "2025-03-24"
updated: "2025-03-24"
author: "antv-team"
source_url: "https://g2.antv.antgroup.com/manual/core/transform/stack-enter"
---

## 核心概念

`stackEnter` 为每条数据分配 `enterDelay` 值：
- 将数据按 `groupBy` 通道（默认 `['x']`）分组
- 同一组内的元素共享相同的入场延迟
- 不同组之间依次叠加延迟时间

每组的延迟 = 前面所有组的 `enterDuration` 之和。

## 基本用法（柱状图分组入场）

```javascript
import { Chart } from '@antv/g2';

const chart = new Chart({ container: 'container', width: 640, height: 480 });

chart.options({
  type: 'interval',
  data: [
    { month: 'Jan', value: 83 },
    { month: 'Feb', value: 60 },
    { month: 'Mar', value: 95 },
    { month: 'Apr', value: 72 },
    { month: 'May', value: 110 },
  ],
  encode: { x: 'month', y: 'value', color: 'month' },
  transform: [
    {
      type: 'stackEnter',
      groupBy: ['x'],          // 按 x 分组（每个月份一批）
      orderBy: null,           // 不额外排序
      duration: 300,           // 每组动画持续时长（毫秒），默认使用 enterDuration
    },
  ],
  animate: {
    enter: {
      type: 'scaleInY',        // 每组柱子从下往上生长
      duration: 300,
    },
  },
});

chart.render();
```

## 折线图多系列依次入场

```javascript
chart.options({
  type: 'line',
  data: multiSeriesData,
  encode: { x: 'date', y: 'value', color: 'series' },
  transform: [
    {
      type: 'stackEnter',
      groupBy: ['color'],    // 按颜色（系列）分组，每条线依次入场
      duration: 800,
    },
  ],
  animate: {
    enter: {
      type: 'pathIn',        // 折线从左向右绘制
      duration: 800,
    },
  },
});
```

## 配置项

```javascript
chart.options({
  transform: [
    {
      type: 'stackEnter',
      groupBy: ['x'],          // 分组通道，默认 ['x']
                               // 可以是单个字符串或数组：['x', 'color']
      orderBy: null,           // 组间排序依据：null | 'x' | 函数
      reverse: false,          // 是否反转组的顺序
      duration: undefined,     // 每组入场时长（毫秒），不设则使用 animate.enter.duration
    },
  ],
});
```

## 常见错误与修正

### 错误：忘记配置 animate.enter
```javascript
// ❌ 有 stackEnter 但没有 animate.enter，看不到动画效果
chart.options({
  transform: [{ type: 'stackEnter', groupBy: ['x'] }],
  // 缺少 animate 配置！
});

// ✅ 必须配合 animate.enter 使用
chart.options({
  transform: [{ type: 'stackEnter', groupBy: ['x'], duration: 400 }],
  animate: {
    enter: {
      type: 'scaleInY',    // 选择合适的入场动画类型
      duration: 400,
    },
  },
});
```

### 错误：duration 与 animate.enter.duration 不一致导致动画不连贯
```javascript
// ❌ stackEnter duration 与 animate.enter.duration 不匹配
chart.options({
  transform: [{ type: 'stackEnter', duration: 500 }],  // 500ms 每组
  animate: { enter: { type: 'scaleInY', duration: 200 } },  // ❌ 200ms 动画（组还没完成就切换）

// ✅ 保持一致
chart.options({
  transform: [{ type: 'stackEnter', duration: 400 }],
  animate: { enter: { type: 'scaleInY', duration: 400 } },   // ✅ 一致
});
```


path: references/transforms/g2-transform-groupy.md
content: ---
id: "g2-transform-groupy"
title: "G2 GroupY Transform"
description: |
  按 Y 通道对数据进行分组聚合。与 GroupX 对称，
  用于按 Y 维度聚合数据的场景。

library: "g2"
version: "5.x"
category: "transforms"
tags:
  - "分组"
  - "聚合"
  - "Y轴"
  - "统计"

related:
  - "g2-transform-groupx"
  - "g2-transform-groupcolor"
  - "g2-transform-group"

use_cases:
  - "按 Y 维度统计"
  - "水平条形图聚合"
  - "转置坐标系下的分组聚合"

anti_patterns:
  - "Y 通道为连续数值时分组效果不佳"

difficulty: "beginner"
completeness: "full"
created: "2025-03-26"
updated: "2025-03-26"
author: "antv-team"
source_url: "https://g2.antv.antgroup.com/manual/core/transform"
---

## 核心概念

GroupY Transform 按 `y` 通道的值对数据进行分组，同时考虑 `color` 和 `series` 通道，然后对每组进行聚合计算。

**分组维度：**
- 主维度：`y` 通道
- 附加维度：`color`、`series`

## 最小可运行示例

```javascript
import { Chart } from '@antv/g2';

const chart = new Chart({
  container: 'container',
  width: 640,
  height: 480,
});

chart.options({
  type: 'interval',
  coordinate: { transform: [{ type: 'transpose' }] },  // 转置为水平条形图
  data: [
    { category: 'A', group: 'X', value: 10 },
    { category: 'A', group: 'Y', value: 20 },
    { category: 'B', group: 'X', value: 15 },
    { category: 'B', group: 'Y', value: 25 },
  ],
  encode: {
    x: 'value',
    y: 'category',
    color: 'group',
  },
  transform: [
    {
      type: 'groupY',
      x: 'sum',  // 对每组求和
    },
  ],
});

chart.render();
```

## 常用变体

### 计算平均值

```javascript
chart.options({
  type: 'interval',
  coordinate: { transform: [{ type: 'transpose' }] },
  data,
  encode: { x: 'value', y: 'category', color: 'group' },
  transform: [
    { type: 'groupY', x: 'mean' },
  ],
});
```

### 计数统计

```javascript
chart.options({
  type: 'interval',
  coordinate: { transform: [{ type: 'transpose' }] },
  data,
  encode: { x: 'value', y: 'category' },
  transform: [
    { type: 'groupY', x: 'count' },
  ],
});
```

### 多通道聚合

```javascript
chart.options({
  type: 'interval',
  coordinate: { transform: [{ type: 'transpose' }] },
  data,
  encode: { x: 'value', y: 'category', size: 'count' },
  transform: [
    {
      type: 'groupY',
      x: 'sum',
      size: 'count',
    },
  ],
});
```

## 完整类型参考

```typescript
interface GroupYTransform {
  type: 'groupY';
  x?: Reducer;
  // 其他通道也可以聚合
  [channel: string]: Reducer;
}

type Reducer = 'sum' | 'mean' | 'median' | 'max' | 'min' | 'count' | 'first' | 'last' | ((I: number[], V: any[]) => any);
```

## 与 GroupX 的对比

| 特性 | GroupX | GroupY |
|------|--------|--------|
| 分组维度 | x, color, series | y, color, series |
| 常用场景 | 竖向柱状图 | 水平条形图 |
| 聚合通道 | 通常是 y | 通常是 x |

## 常见错误与修正

### 错误 1：在非转置坐标系中使用

```javascript
// ⚠️ 注意：在普通坐标系中，GroupY 通常用于 Y 为分类轴的情况
// 如果 Y 是数值轴，分组可能没有意义

// ✅ 正确：转置坐标系 + GroupY
chart.options({
  type: 'interval',
  coordinate: { transform: [{ type: 'transpose' }] },
  data,
  encode: { x: 'value', y: 'category' },
  transform: [{ type: 'groupY', x: 'sum' }],
});
```

path: references/transforms/g2-transform-binx.md
content: ---
id: "g2-transform-binx"
title: "G2 BinX 分箱变换（直方图）"
description: |
  BinX 将连续 x 值按指定区间（bin）分组，自动统计每个区间内的记录数（或聚合值），
  是绘制频率直方图的核心 Transform。与 Interval Mark 组合直接使用原始数据即可生成直方图。

library: "g2"
version: "5.x"
category: "transforms"
tags:
  - "BinX"
  - "直方图"
  - "histogram"
  - "分箱"
  - "分布"
  - "频率"
  - "transform"
  - "spec"

related:
  - "g2-mark-interval-basic"
  - "g2-transform-stacky"

use_cases:
  - "展示连续数值数据的频率分布"
  - "探索数据的分布形态（正态、偏态等）"

anti_patterns:
  - "数据是离散分类时不需要 binX，直接用 interval 即可"

difficulty: "intermediate"
completeness: "full"
created: "2024-01-01"
updated: "2025-03-01"
author: "antv-team"
source_url: "https://g2.antv.antgroup.com/manual/core/transform/bin-x"
---

## 最小可运行示例（直方图）

```javascript
import { Chart } from '@antv/g2';

const chart = new Chart({
  container: 'container',
  width: 640,
  height: 400,
});

// 原始连续数值数据
const rawData = Array.from({ length: 200 }, () => ({
  value: Math.random() * 100,
}));

chart.options({
  type: 'interval',
  data: rawData,
  encode: { x: 'value' },
  transform: [
    {
      type: 'binX',
      y: 'count',      // 统计每个 bin 内的记录数，结果存入 y 通道
      thresholds: 20,  // 分成约 20 个 bin
    },
  ],
  style: { inset: 0.5 },    // 柱体间留细缝
});

chart.render();
```

## 配置项

```javascript
transform: [
  {
    type: 'binX',
    // 统计目标
    y: 'count',       // 'count'（默认，计数）| 'sum' | 'mean' | 'max' | 'min'
    // 如果是 sum/mean 等，还需指定聚合的字段：
    // y: 'sum', field: 'amount',

    // 分箱数量控制（三选一）
    thresholds: 20,           // 目标分箱数（近似值，库会自动调整）
    // domain: [0, 100],      // 指定值域范围
    // step: 5,               // 每个 bin 的宽度（与 thresholds 互斥）
  },
],
```

## 分组直方图（按类别着色）

```javascript
chart.options({
  type: 'interval',
   data,  // [{ value: 42, group: 'A' }, ...]
  encode: { x: 'value', color: 'group' },
  transform: [
    { type: 'binX', y: 'count', thresholds: 15 },
    { type: 'stackY' },   // 堆叠同一 bin 内不同分组的计数
  ],
});
```

## 常见错误与修正

### 错误：对离散分类数据使用 binX
```javascript
// ❌ 错误：genre 是分类字段，不需要 binX
chart.options({
  type: 'interval',
  data,
  encode: { x: 'genre' },
  transform: [{ type: 'binX', y: 'count' }],  // 不必要！
});

// ✅ 正确：分类数据直接用 interval，不需要 binX
chart.options({
  type: 'interval',
  data,
  encode: { x: 'genre', y: 'count' },
});
```

### 错误：忘记指定 y 统计量
```javascript
// ❌ 错误：没有 y 参数，不知道统计什么
chart.options({ transform: [{ type: 'binX', thresholds: 20 }] });

// ✅ 正确：必须指定 y
chart.options({ transform: [{ type: 'binX', y: 'count', thresholds: 20 }] });
```


path: references/transforms/g2-transform-jitter.md
content: ---
id: "g2-transform-jitter"
title: "G2 Jitter 抖动变换（散开重叠点）"
description: |
  jitter 变换为分类轴上的数据点添加随机偏移，避免相同类别下数据点完全重叠。
  jitter 同时抖动 X 和 Y，jitterX 只抖动 X（常用于条形图上的点），
  jitterY 只抖动 Y。常与 point mark 配合展示分类数据分布。

library: "g2"
version: "5.x"
category: "transforms"
tags:
  - "jitter"
  - "抖动"
  - "点图"
  - "分布"
  - "重叠"
  - "beeswarm"
  - "transform"

related:
  - "g2-mark-point-scatter"
  - "g2-transform-dodgex"
  - "g2-transform-stacky"

use_cases:
  - "展示各类别下数据点的分布密度（比箱线图更详细）"
  - "类别轴上多个数据点防止重叠"
  - "与箱线图叠加，同时显示统计摘要和原始数据"

difficulty: "intermediate"
completeness: "full"
created: "2025-03-24"
updated: "2025-03-24"
author: "antv-team"
source_url: "https://g2.antv.antgroup.com/manual/core/transform/jitter"
---

## 最小可运行示例（jitter 防止分类散点图重叠）

```javascript
import { Chart } from '@antv/g2';

const chart = new Chart({ container: 'container', width: 640, height: 480 });

chart.options({
  type: 'point',
  data: [
    { dept: '研发', salary: 18000 }, { dept: '研发', salary: 22000 },
    { dept: '研发', salary: 15000 }, { dept: '研发', salary: 19000 },
    { dept: '销售', salary: 12000 }, { dept: '销售', salary: 16000 },
    { dept: '销售', salary: 14000 }, { dept: '销售', salary: 11000 },
    { dept: '设计', salary: 17000 }, { dept: '设计', salary: 20000 },
  ],
  encode: {
    x: 'dept',     // 分类轴（会在此方向抖动）
    y: 'salary',
    color: 'dept',
  },
  transform: [{ type: 'jitter' }],   // 在 x 和 y 方向添加随机抖动
  style: { fillOpacity: 0.7, r: 4 },
});

chart.render();
```

## jitterX（仅水平抖动）

```javascript
// 适合竖向分类轴 — 仅在 x 方向展开，y 方向保持精确数值
chart.options({
  type: 'point',
  data,
  encode: { x: 'category', y: 'value', color: 'category' },
  transform: [
    {
      type: 'jitterX',
      padding: 0.1,    // 类别宽度比例（0~0.5），默认 0
      random: Math.random,  // 自定义随机函数（可用固定种子）
    },
  ],
});
```

## 与箱线图叠加

```javascript
chart.options({
  type: 'view',
  children: [
    // 箱线图（显示统计摘要）
    {
      type: 'boxplot',
      data,
      encode: { x: 'dept', y: 'salary' },
      style: { boxFill: 'transparent', lineWidth: 1.5 },
    },
    // 散点（显示原始数据分布）
    {
      type: 'point',
      data,
      encode: { x: 'dept', y: 'salary', color: 'dept' },
      transform: [{ type: 'jitter', padding: 0.1 }],
      style: { r: 3, fillOpacity: 0.6 },
    },
  ],
});
```

## 配置项

```javascript
transform: [
  {
    type: 'jitter',    // 或 'jitterX' / 'jitterY'
    padding: 0,        // 类别边界留白（0~0.5），默认 0
    paddingX: 0,       // 单独设置 X 留白（覆盖 padding）
    paddingY: 0,       // 单独设置 Y 留白（覆盖 padding）
    random: Math.random, // 随机函数，可替换为固定种子伪随机
  },
]
```

## 常见错误与修正

### 错误 1：在数值轴上使用 jitter——两个方向都是数值时效果混乱
```javascript
// ❌ 错误：x 和 y 都是数值时，jitter 会破坏精确的数值关系
chart.options({
  type: 'point',
  encode: { x: 'price', y: 'sales' },  // 都是数值轴
  transform: [{ type: 'jitter' }],      // ❌ 散点图本就不重叠，不需要
});

// ✅ jitter 应用于有分类轴的场景
chart.options({
  encode: { x: 'category', y: 'value' },  // x 是分类
  transform: [{ type: 'jitter' }],         // ✅
});
```

### 错误 2：point mark 数据量大时抖动效果不明显——padding 太小
```javascript
// ❌ 默认 padding: 0 时，所有点只在类别宽度极小范围内抖动
transform: [{ type: 'jitter' }]  // padding 默认 0，抖动范围很小

// ✅ 适当增大 padding
transform: [{ type: 'jitter', padding: 0.15 }]  // 类别宽度 15% 作为抖动范围
```


path: references/transforms/g2-transform-diffy.md
content: ---
id: "g2-transform-diffy"
title: "G2 DiffY 差值区域变换"
description: |
  diffY 计算两条折线之间的差值区间（y0 到 y1），用于绘制差值面积图。
  保持上方折线的 y 值不变，计算下方折线相对于上方的差值作为 y1，
  视觉上展示两系列之间的正/负差异区域。

library: "g2"
version: "5.x"
category: "transforms"
tags:
  - "diffY"
  - "差值"
  - "差异面积"
  - "对比"
  - "transform"
  - "区间面积"

related:
  - "g2-mark-area-stacked"
  - "g2-transform-stacky"
  - "g2-mark-line-basic"

use_cases:
  - "展示两条折线之间的正/负差异区域"
  - "实际值 vs 预测值的偏差可视化"
  - "价格上下区间的差值展示"

difficulty: "intermediate"
completeness: "full"
created: "2025-03-24"
updated: "2025-03-24"
author: "antv-team"
source_url: "https://g2.antv.antgroup.com/manual/core/transform/diff-y"
---

## 最小可运行示例（实际 vs 预测差异）

```javascript
import { Chart } from '@antv/g2';

const data = [
  { month: 'Jan', actual: 83,  forecast: 75 },
  { month: 'Feb', actual: 60,  forecast: 70 },
  { month: 'Mar', actual: 95,  forecast: 85 },
  { month: 'Apr', actual: 72,  forecast: 80 },
  { month: 'May', actual: 110, forecast: 100 },
  { month: 'Jun', actual: 88,  forecast: 95 },
];

// 转换为长表格式
const longData = data.flatMap(d => [
  { month: d.month, value: d.actual,   type: '实际' },
  { month: d.month, value: d.forecast, type: '预测' },
]);

const chart = new Chart({ container: 'container', width: 640, height: 400 });

chart.options({
  type: 'view',
  children: [
    // 差值面积（正差异：实际>预测 显示绿色，负差异：实际<预测 显示红色）
    {
      type: 'area',
       longData,
      encode: { x: 'month', y: 'value', color: 'type', series: 'type' },
      transform: [{ type: 'diffY' }],   // 计算两系列间的差值区间
      style: {
        fillOpacity: 0.3,
      },
    },
    // 主折线
    {
      type: 'line',
       longData,
      encode: { x: 'month', y: 'value', color: 'type' },
      style: { lineWidth: 2 },
    },
  ],
});

chart.render();
```

## 配置项

```javascript
transform: [
  {
    type: 'diffY',
    groupBy: 'x',   // 按 x 通道分组对齐，默认 'x'
  },
]
```

## 常见错误与修正

### 错误：数据没有两个系列——diffY 需要至少两个 series 才能计算差值
```javascript
// ❌ 只有一个系列，diffY 没有对比基准，差值为 0
chart.options({
  type: 'area',
  data: singleSeriesData,
  encode: { x: 'date', y: 'value' },  // ❌ 没有 series/color 区分两组
  transform: [{ type: 'diffY' }],
});

// ✅ 必须有两个系列（通过 color/series 区分）
chart.options({
  type: 'area',
  data: twoSeriesData,
  encode: {
    x: 'date',
    y: 'value',
    color: 'type',   // ✅ 区分两个系列
    series: 'type',
  },
  transform: [{ type: 'diffY' }],
});
```

### 错误：diffY 与 stackY 混淆——stackY 是累积，diffY 是差值
```javascript
// stackY：将多个系列的 y 值累积（适合堆叠图）
transform: [{ type: 'stackY' }]

// diffY：计算两个系列之间的差值区间（适合差值面积图）
transform: [{ type: 'diffY' }]
```


path: references/transforms/g2-transform-symmetryy.md
content: ---
id: "g2-transform-symmetryy"
title: "G2 SymmetryY 对称变换（蝴蝶图 / 人口金字塔）"
description: |
  symmetryY 对 y 通道应用偏移使数据关于 y=0 轴对称，
  典型应用是人口金字塔（两个方向的柱状图对称展示）和蝴蝶图。
  通常与 transpose（转置）坐标系配合，实现水平对称条形图。

library: "g2"
version: "5.x"
category: "transforms"
tags:
  - "symmetryY"
  - "对称"
  - "人口金字塔"
  - "蝴蝶图"
  - "population pyramid"
  - "transform"

related:
  - "g2-transform-stacky"
  - "g2-coord-transpose"
  - "g2-mark-interval-stacked"

use_cases:
  - "人口金字塔（男女年龄分布对称展示）"
  - "A/B 对比的蝴蝶图"
  - "正负值关于中心对称的图表"

difficulty: "intermediate"
completeness: "full"
created: "2025-03-24"
updated: "2025-03-24"
author: "antv-team"
source_url: "https://g2.antv.antgroup.com/manual/core/transform/symmetry-y"
---

## 最小可运行示例（人口金字塔）

```javascript
import { Chart } from '@antv/g2';

const data = [
  { age: '0-9',   gender: '男', value: 8500 },
  { age: '10-19', gender: '男', value: 9200 },
  { age: '20-29', gender: '男', value: 10300 },
  { age: '30-39', gender: '男', value: 9800 },
  { age: '40-49', gender: '男', value: 8900 },
  { age: '0-9',   gender: '女', value: 8100 },
  { age: '10-19', gender: '女', value: 8800 },
  { age: '20-29', gender: '女', value: 9900 },
  { age: '30-39', gender: '女', value: 9500 },
  { age: '40-49', gender: '女', value: 8700 },
];

const chart = new Chart({ container: 'container', width: 640, height: 480 });

chart.options({
  type: 'interval',
  data,
  encode: {
    x: 'age',
    y: 'value',
    color: 'gender',
  },
  transform: [
    { type: 'stackY' },     // 先堆叠
    { type: 'symmetryY' },  // 再对称（以 y=0 为中轴）
  ],
  coordinate: { transform: [{ type: 'transpose' }] },  // 转置为水平条形
  axis: {
    y: {
      labelFormatter: (v) => Math.abs(v).toLocaleString(),  // 负值显示为正数
    },
  },
});

chart.render();
```

## 配置项

```javascript
transform: [
  {
    type: 'symmetryY',
    groupBy: 'x',   // 按哪个通道分组，默认 'x'
  },
]
```

## 蝴蝶图（两个类别左右对称）

```javascript
chart.options({
  type: 'interval',
  data: abTestData,
  encode: { x: 'metric', y: 'value', color: 'group' },
  transform: [
    { type: 'stackY' },
    { type: 'symmetryY' },
  ],
  coordinate: { transform: [{ type: 'transpose' }] },
  style: { fillOpacity: 0.85 },
});
```

## 常见错误与修正

### 错误：symmetryY 前忘记 stackY——分组数据不会对称
```javascript
// ❌ 没有 stackY，两个 gender 的柱体重叠在同侧，对称失效
transform: [
  { type: 'symmetryY' },  // ❌ 少了前置 stackY
]

// ✅ 必须先 stackY 再 symmetryY
transform: [
  { type: 'stackY' },     // ✅ 先堆叠（分组数据叠在一起）
  { type: 'symmetryY' },  // ✅ 再对称（两组各自偏移到两侧）
]
```


path: references/transforms/g2-transform-groupx.md
content: ---
id: "g2-transform-groupx"
title: "G2 GroupX 分组聚合变换"
description: |
  groupX 按 x 通道的值对数据分组，并对 y 通道进行聚合计算（count、sum、mean、min、max 等）。
  常用于从原始行级数据直接计算统计量，无需预先聚合数据。
  groupY、groupColor、groupN 是其变体，分别按 y、color 通道或固定数量分组。

library: "g2"
version: "5.x"
category: "transforms"
tags:
  - "groupX"
  - "分组"
  - "聚合"
  - "count"
  - "sum"
  - "mean"
  - "transform"
  - "统计"

related:
  - "g2-transform-stacky"
  - "g2-transform-binx"
  - "g2-mark-interval-basic"

use_cases:
  - "从行级原始数据统计各类别的数量（频率柱状图）"
  - "从明细数据聚合出各组的均值/求和"
  - "词频统计可视化"

difficulty: "intermediate"
completeness: "full"
created: "2025-03-24"
updated: "2025-03-24"
author: "antv-team"
source_url: "https://g2.antv.antgroup.com/manual/core/transform/group"
---

## 最小可运行示例（计数频率柱状图）

```javascript
import { Chart } from '@antv/g2';

// 原始行级数据，不需要预先统计频次
const rawData = [
  { dept: '研发' }, { dept: '研发' }, { dept: '研发' },
  { dept: '销售' }, { dept: '销售' },
  { dept: '设计' }, { dept: '设计' }, { dept: '设计' }, { dept: '设计' },
  { dept: 'HR' },
];

const chart = new Chart({ container: 'container', width: 640, height: 400 });

chart.options({
  type: 'interval',
  data: rawData,
  encode: {
    x: 'dept',  // 分组字段
    y: '★',     // 占位符，实际 y 值由 groupX 计算
    color: 'dept',
  },
  transform: [
    {
      type: 'groupX',
      y: 'count',    // 对 y 通道的聚合方式：统计每组数量
    },
  ],
});

chart.render();
```

## 聚合方式速查

```javascript
// 计数（每组有多少条记录）
transform: [{ type: 'groupX', y: 'count' }]

// 求和（每组 y 字段的总和）
transform: [{ type: 'groupX', y: 'sum' }]

// 均值（每组 y 字段的平均值）
transform: [{ type: 'groupX', y: 'mean' }]

// 最大值 / 最小值
transform: [{ type: 'groupX', y: 'max' }]
transform: [{ type: 'groupX', y: 'min' }]

// 中位数
transform: [{ type: 'groupX', y: 'median' }]

// 自定义聚合函数
transform: [{ type: 'groupX', y: (values) => values.reduce((a, b) => a + b, 0) / values.length }]
```

## 按颜色分组（groupColor）

```javascript
// 统计各部门男女人数
chart.options({
  type: 'interval',
  data: rawEmployeeData,
  encode: { x: 'dept', y: '★', color: 'gender' },
  transform: [
    { type: 'groupX', y: 'count' },   // 先按 x 和 color 组合分组统计
    { type: 'dodgeX' },               // 再分组并排
  ],
});
```

## 均值折线图（从明细数据直接绘制）

```javascript
chart.options({
  type: 'line',
  data: dailySalesData,
  encode: { x: 'month', y: 'dailySales' },
  transform: [
    { type: 'groupX', y: 'mean' },  // 计算每月平均日销售额
  ],
  style: { lineWidth: 2 },
});
```

## 密度图中的 KDE 分组说明

在使用 `density` 图表类型配合 `kde` 变换时，需要注意 `kde` 变换本身不依赖 `groupX`，而是通过 `groupBy` 参数指定分组字段。例如：

```javascript
chart.options({
  type: 'density',
  data: {
    type: 'inline',
    value: irisData,
    transform: [{
      type: 'kde',
      field: 'y',
      groupBy: ['x', 'species'],  // 按 x 和 species 字段分组进行 KDE 计算
    }],
  },
  encode: {
    x: 'x',
    y: 'y',
    color: 'species',
    size: 'size',
    series: 'species',
  },
});
```

在这种情况下，`kde` 变换会自动完成分组和密度计算，无需额外添加 `groupX`。

## 常见错误与修正

### 错误 1：encode.y 写成实际字段名——groupX 应用后 y 通道被覆盖
```javascript
// ❌ 如果想让 groupX 计算 count，encode.y 不需要写实际字段
chart.options({
  encode: { x: 'dept', y: 'salary' },
  transform: [{ type: 'groupX', y: 'count' }],  // ⚠️  count 会覆盖 salary
});
// 结果：y 轴显示的是 count，不是 salary 的总和

// ✅ 如果想要 count，y 字段名无所谓（通常写 '★' 或任意占位符）
chart.options({
  encode: { x: 'dept', y: '★' },
  transform: [{ type: 'groupX', y: 'count' }],  // ✅ 统计每组数量
});

// ✅ 如果想要 sum(salary)，encode.y 必须写 'salary'
chart.options({
  encode: { x: 'dept', y: 'salary' },
  transform: [{ type: 'groupX', y: 'sum' }],  // ✅ 统计每组 salary 总和
});
```

### 错误 2：与 binX 混淆——groupX 用于已有分类，binX 用于数值分桶
```javascript
// ❌ 对数值 x 用 groupX，每个唯一值都是一组，组太多
chart.options({
  encode: { x: 'age', y: '★' },
  transform: [{ type: 'groupX', y: 'count' }],  // ❌ age 有几十个唯一值
});

// ✅ 数值 x 应该用 binX（先分桶再统计）
chart.options({
  encode: { x: 'age', y: '★' },
  transform: [{ type: 'binX', y: 'count', thresholds: 10 }],  // ✅ 分 10 个桶
});
```

### 错误 3：在 density 图中错误使用 groupX 与 kde 结合
```javascript
// ❌ 错误示例：在 density 图中混用 groupX 和 kde
chart.options({
  type: 'density',
  data: {
    type: 'inline',
    value: irisData,
    transform: [
      { type: 'kde', field: 'y', groupBy: ['x'] },
      { type: 'groupX', y: 'mean' }  // ❌ kde 已经完成了分组和聚合，不需要再用 groupX
    ]
  },
  encode: { x: 'x', y: 'y', color: 'x' }
});

// ✅ 正确做法：只使用 kde 并通过 groupBy 指定分组字段
chart.options({
  type: 'density',
  data: {
    type: 'inline',
    value: irisData,
    transform: [
      { type: 'kde', field: 'y', groupBy: ['x'] }  // ✅ 仅使用 kde 变换
    ]
  },
  encode: { x: 'x', y: 'y', color: 'x', size: 'size' }
});
```

### 错误 4：在 density 图中错误配置 encode 映射字段
```javascript
// ❌ 错误示例：未正确使用 kde 输出的字段
chart.options({
  type: 'density',
  data: {
    type: 'inline',
    value: rawData,
    transform: [{
      type: 'kde',
      field: 'y',
      groupBy: ['x']
    }]
  },
  encode: {
    x: 'x',
    y: 'y',        // ❌ 应该使用 kde 输出的 y 字段（默认为 'y'）
    color: 'x',
    size: 'size'   // ❌ 应该使用 kde 输出的 size 字段（默认为 'size'）
  }
});

// ✅ 正确做法：确保 encode 中使用的字段与 kde 输出字段一致
chart.options({
  type: 'density',
  data: {
    type: 'inline',
    value: rawData,
    transform: [{
      type: 'kde',
      field: 'y',
      groupBy: ['x'],
      as: ['kde_y', 'kde_size']  // ✅ 自定义输出字段名
    }]
  },
  encode: {
    x: 'x',
    y: 'kde_y',      // ✅ 使用自定义的 y 字段名
    color: 'x',
    size: 'kde_size' // ✅ 使用自定义的 size 字段名
  }
});
```

path: references/transforms/g2-transform-dodgex.md
content: ---
id: "g2-transform-dodgex"
title: "G2 DodgeX 分组变换"
description: |
  DodgeX 是 G2 v5 中用于分组展示的 Transform，
  将同一 x 位置的多系列元素在水平方向上错开排列，
  是分组柱状图的核心依赖。

library: "g2"
version: "5.x"
category: "transforms"
tags:
  - "dodgeX"
  - "分组"
  - "并排"
  - "transform"
  - "分组柱状图"
  - "spec"

related:
  - "g2-mark-interval-grouped"
  - "g2-transform-stacky"

use_cases:
  - "创建分组柱状图（并排展示多系列）"
  - "分组散点图"

difficulty: "beginner"
completeness: "partial"
created: "2024-01-01"
updated: "2025-03-01"
author: "antv-team"
source_url: "https://g2.antv.antgroup.com/manual/core/transform/dodge-x"
---

## 基本用法

```javascript
import { Chart } from '@antv/g2';

const chart = new Chart({ container: 'container', width: 640, height: 480 });

chart.options({
  type: 'interval',
  data,
  encode: { x: 'month', y: 'value', color: 'type' },
  transform: [{ type: 'dodgeX' }],
});

chart.render();
```

## 配置项

```javascript
chart.options({
  type: 'interval',
  data: [...],
  encode: { x: 'month', y: 'value', color: 'type' },
  transform: [
    {
      type: 'dodgeX',
      padding: 0,          // 组内各柱之间的间距（相对于每组宽度，0-1），默认 0
      paddingOuter: 0.1,   // 整组与相邻组的外边距
      reverse: false,      // 是否反转分组顺序
    },
  ],
});
```

## 与 stackY 的区别

```javascript
// dodgeX：各系列并排展示，便于直接对比绝对值
chart.options({ transform: [{ type: 'dodgeX' }] });

// stackY：各系列堆叠展示，便于对比总量和占比
chart.options({ transform: [{ type: 'stackY' }] });
```

## 常见错误与修正

### 错误：transform 写成对象
```javascript
// ❌ 错误
chart.options({ transform: { type: 'dodgeX' } });

// ✅ 正确：必须是数组
chart.options({ transform: [{ type: 'dodgeX' }] });
```


path: references/transforms/g2-transform-flexx.md
content: ---
id: "g2-transform-flexx"
title: "G2 FlexX 弹性宽度变换（马赛克图 / 不等宽柱）"
description: |
  flexX 根据数据值动态调整 x 轴比例尺的 flex 属性，使每个柱的宽度与数值成比例。
  常用于马赛克图（Marimekko chart）：柱宽表示一个维度，柱高表示另一个维度。
  通过 field 指定控制宽度的字段，reducer 指定聚合方式。

library: "g2"
version: "5.x"
category: "transforms"
tags:
  - "flexX"
  - "不等宽柱"
  - "马赛克图"
  - "Marimekko"
  - "弹性宽度"
  - "transform"

related:
  - "g2-transform-stacky"
  - "g2-mark-interval-stacked"

use_cases:
  - "马赛克图（双维度占比：柱宽 × 柱高）"
  - "不等宽柱状图（柱宽代表样本量/权重）"
  - "市场份额图（宽度=市场规模，高度=占比）"

difficulty: "advanced"
completeness: "full"
created: "2025-03-24"
updated: "2025-03-24"
author: "antv-team"
source_url: "https://g2.antv.antgroup.com/manual/core/transform/flex-x"
---

## 最小可运行示例（马赛克图）

```javascript
import { Chart } from '@antv/g2';

// 马赛克图：x 类别，y 子类别比例，value 控制柱宽（市场规模）
const data = [
  { region: '华北', type: '线上', revenue: 300, share: 0.6 },
  { region: '华北', type: '线下', revenue: 300, share: 0.4 },
  { region: '华东', type: '线上', revenue: 500, share: 0.7 },
  { region: '华东', type: '线下', revenue: 500, share: 0.3 },
  { region: '华南', type: '线上', revenue: 200, share: 0.5 },
  { region: '华南', type: '线下', revenue: 200, share: 0.5 },
];

const chart = new Chart({ container: 'container', width: 640, height: 400 });

chart.options({
  type: 'interval',
  data,
  encode: {
    x: 'region',
    y: 'share',
    color: 'type',
  },
  transform: [
    {
      type: 'flexX',
      field: 'revenue',    // 控制柱宽的字段
      reducer: 'sum',      // 聚合方式（同一 x 类别可能有多行，需要 sum）
    },
    { type: 'stackY' },   // 再堆叠 y 方向（百分比）
  ],
  axis: {
    y: { labelFormatter: (v) => `${(v * 100).toFixed(0)}%` },
  },
});

chart.render();
```

## 配置项

```javascript
transform: [
  {
    type: 'flexX',
    field: 'sampleSize',  // 控制宽度的字段名（每个 x 类别的权重）
    channel: 'y',         // 依据哪个通道的值计算弹性（默认 'y'）
    reducer: 'sum',       // 同一 x 类别下 field 值的聚合方式（'sum' 是最常用的）
  },
]
```

## 常见错误与修正

### 错误：flexX 和 stackY 顺序错误——先 stackY 后 flexX
```javascript
// ❌ 错误：应该先 flexX 再 stackY
transform: [
  { type: 'stackY' },   // ❌ stackY 先执行，flexX 后调整宽度，比例关系出错
  { type: 'flexX', field: 'revenue' },
]

// ✅ 正确顺序：先 flexX（设置宽度弹性），再 stackY（堆叠高度）
transform: [
  { type: 'flexX', field: 'revenue', reducer: 'sum' },  // ✅ 先设置弹性宽度
  { type: 'stackY' },                                     // ✅ 再堆叠
]
```

### 错误：没有设置 reducer——同一 x 有多行时宽度计算不一致
```javascript
// ❌ 未设置 reducer，同一 region 有多行（线上/线下），flexX 不知如何聚合宽度
transform: [{ type: 'flexX', field: 'revenue' }]  // ❌ 缺少 reducer

// ✅ 设置 reducer: 'sum' 对同一 x 的 field 求和
transform: [{ type: 'flexX', field: 'revenue', reducer: 'sum' }]  // ✅
```


path: references/components/g2-comp-legend-category.md
content: ---
id: "g2-comp-legend-category"
title: "G2 分类图例（LegendCategory）"
description: |
  分类图例组件，用于展示离散类别的图例项。
  是最常用的图例类型，适用于分类数据的可视化。

library: "g2"
version: "5.x"
category: "components"
tags:
  - "图例"
  - "legend"
  - "分类"
  - "category"

related:
  - "g2-comp-legend-config"
  - "g2-scale-ordinal"
  - "g2-interaction-legend-filter"

use_cases:
  - "柱状图的分类图例"
  - "折线图的系列图例"
  - "散点图的分组图例"

anti_patterns:
  - "连续数据应使用连续图例（LegendContinuous）"

difficulty: "beginner"
completeness: "full"
created: "2025-03-26"
updated: "2025-03-26"
author: "antv-team"
source_url: "https://g2.antv.antgroup.com/manual/core/component/legend"
---

## 核心概念

LegendCategory 是分类图例组件：
- 显示离散类别的图例项
- 每项包含图标和标签
- 支持交互（点击筛选、hover 高亮）

**特点：**
- 自动从 color/shape 通道推断
- 支持水平和垂直布局
- 支持自定义图标

## 最小可运行示例

```javascript
import { Chart } from '@antv/g2';

const chart = new Chart({
  container: 'container',
  width: 640,
  height: 480,
});

chart.options({
  type: 'interval',
  data: [
    { category: 'A', type: 'X', value: 100 },
    { category: 'A', type: 'Y', value: 150 },
    { category: 'B', type: 'X', value: 120 },
    { category: 'B', type: 'Y', value: 180 },
  ],
  encode: {
    x: 'category',
    y: 'value',
    color: 'type',
  },
  legend: {
    color: {
      position: 'top',
      layout: {
        justifyContent: 'center',
      },
    },
  },
});

chart.render();
```

## 常用变体

### 垂直布局

```javascript
chart.options({
  type: 'interval',
  data,
  encode: { x: 'category', y: 'value', color: 'type' },
  legend: {
    color: {
      position: 'right',
      layout: {
        flexDirection: 'column',
      },
    },
  },
});
```

### 自定义标签格式

```javascript
chart.options({
  type: 'interval',
  data,
  encode: { x: 'category', y: 'value', color: 'type' },
  legend: {
    color: {
      labelFormatter: (val) => `类型: ${val}`,
    },
  },
});
```

### 添加标题

```javascript
chart.options({
  type: 'interval',
  data,
  encode: { x: 'category', y: 'value', color: 'type' },
  legend: {
    color: {
      title: '类型',
      position: 'top',
    },
  },
});
```

### 自定义图标

```javascript
chart.options({
  type: 'interval',
  data,
  encode: { x: 'category', y: 'value', color: 'type' },
  legend: {
    color: {
      itemMarker: 'square',  // 'circle' | 'square' | 'line' | ...
      itemMarkerSize: 12,
    },
  },
});
```

### 网格布局

```javascript
chart.options({
  type: 'interval',
  data,
  encode: { x: 'category', y: 'value', color: 'type' },
  legend: {
    color: {
      cols: 3,  // 每行显示 3 项
      layout: { justifyContent: 'center' },
    },
  },
});
```

### 禁用交互

```javascript
chart.options({
  type: 'interval',
  data,
  encode: { x: 'category', y: 'value', color: 'type' },
  legend: {
    color: {
      itemMarker: 'circle',
    },
  },
  interaction: {
    legendFilter: false,  // 禁用点击筛选
  },
});
```

## 完整类型参考

```typescript
interface LegendCategoryOptions {
  // 位置和布局
  position?: 'top' | 'bottom' | 'left' | 'right' | 'center';
  layout?: {
    flexDirection?: 'row' | 'column';
    justifyContent?: 'flex-start' | 'center' | 'flex-end';
    flexWrap?: 'wrap' | 'nowrap';
  };
  cols?: number;  // 网格布局列数

  // 标题
  title?: string | string[];

  // 图标
  itemMarker?: string | ((id: any, index: number) => string);
  itemMarkerSize?: number;
  itemMarkerLineWidth?: number;
  itemSpacing?: number;

  // 标签
  labelFormatter?: string | ((val: any) => string);
  maxItemWidth?: number;

  // 样式
  style?: {
    fill?: string;
    fontSize?: number;
    // 更多样式...
  };

  // 其他
  dx?: number;
  dy?: number;
}
```

## 与连续图例的区别

| 特性 | 分类图例 | 连续图例 |
|------|---------|---------|
| 数据类型 | 离散类别 | 连续数值 |
| 显示方式 | 图标 + 标签列表 | 色带 + 刻度 |
| 交互 | 点击筛选 | 无筛选 |
| 适用场景 | 分类数据 | 数值映射 |

## 常见错误与修正

### 错误 1：position 参数错误

```javascript
// ❌ 错误：position 应该是预定义值
legend: { color: { position: 'top-left' } }

// ✅ 正确
legend: { color: { position: 'top' } }
```

### 错误 2：未映射 color 通道

```javascript
// ❌ 错误：没有 color 通道，图例不会显示
chart.options({
  type: 'interval',
  data,
  encode: { x: 'category', y: 'value' },
  legend: { color: { position: 'top' } },
});

// ✅ 正确：添加 color 通道
chart.options({
  type: 'interval',
  data,
  encode: { x: 'category', y: 'value', color: 'type' },
  legend: { color: { position: 'top' } },
});
```

### 错误 3：itemMarker 类型错误

```javascript
// ❌ 错误：itemMarker 应该是预定义形状名或函数
legend: { color: { itemMarker: 'triangle-up' } }

// ✅ 正确：使用支持的形状
legend: { color: { itemMarker: 'triangle' } }
// 或
legend: { color: { itemMarker: (id, i) => i === 0 ? 'circle' : 'square' } }
```

path: references/components/g2-comp-legend-continuous.md
content: ---
id: "g2-comp-legend-continuous"
title: "G2 连续图例（legendContinuous）"
description: |
  连续图例用于展示连续数值到颜色的映射关系，常见于热力图、地理可视化等场景。
  支持色带（ribbon）和块状（block）两种形态，可配置标签格式化、范围等。

library: "g2"
version: "5.x"
category: "components"
tags:
  - "legend"
  - "连续图例"
  - "色带"
  - "color legend"
  - "热力图"

related:
  - "g2-comp-legend-config"
  - "g2-comp-legend-category"
  - "g2-scale-sequential"

use_cases:
  - "热力图的颜色映射说明"
  - "地理可视化的数值范围图例"
  - "连续数值的颜色编码"

anti_patterns:
  - "分类数据应使用分类图例（legendCategory）"

difficulty: "intermediate"
completeness: "full"
created: "2025-03-26"
updated: "2025-03-26"
author: "antv-team"
source_url: "https://g2.antv.antgroup.com/manual/component/legend"
---

## 核心概念

连续图例（Continuous Legend）展示连续数值到视觉通道（通常是颜色）的映射：
- 当 `encode.color` 映射到连续数值字段时，图例自动变为连续图例
- 支持线性比例尺（linear）、阈值比例尺（threshold）、分位数比例尺（quantile/quantize）
- 默认显示为色带（ribbon）形式

## 最小可运行示例

```javascript
import { Chart } from '@antv/g2';

const data = Array.from({ length: 100 }, (_, i) => ({
  x: i % 10,
  y: Math.floor(i / 10),
  value: Math.random() * 100,
}));

const chart = new Chart({ container: 'container', width: 640, height: 400 });

chart.options({
  type: 'cell',
  data,
  encode: { x: 'x', y: 'y', color: 'value' },  // value 是连续数值
  scale: { color: { palette: 'Blues' } },
  legend: {
    color: {
      position: 'right',
      length: 200,
      labelFormatter: (v) => Number(v).toFixed(0),  // 注意：v 可能是 string，需先转换
    },
  },
});

chart.render();
```

## 完整配置项

```javascript
chart.options({
  type: 'cell',
  data,
  encode: { x: 'x', y: 'y', color: 'value' },
  legend: {
    color: {
      // ── 位置 ─────────────────────────────────
      position: 'right',       // 'top' | 'bottom' | 'left' | 'right'
      layout: {
        justifyContent: 'center',
      },

      // ── 尺寸 ─────────────────────────────────
      length: 200,             // 色带长度（px）
      size: 20,                // 色带宽度/高度（px）

      // ── 标题 ─────────────────────────────────
      title: '数值范围',
      titleFontSize: 12,

      // ── 标签 ─────────────────────────────────
      labelFormatter: (v) => Number(v).toFixed(1),  // 注意：v 可能是 string，需先转换
      labelAlign: 'value',     // 'value' | 'range'

      // ── 样式 ─────────────────────────────────
      style: {
        ribbonFill: 'black',   // 默认色带填充色（无颜色映射时）
      },
    },
  },
});
```

## 常用变体

### 阈值图例（分段色带）

```javascript
// 使用 threshold/quantize/quantile 比例尺时，图例自动变为分段
chart.options({
  type: 'cell',
  data,
  encode: { x: 'x', y: 'y', color: 'value' },
  scale: {
    color: {
      type: 'quantize',       // 分段比例尺
      domain: [0, 100],
      range: ['#f7fbff', '#6baed6', '#08519c'],  // 3 段颜色
    },
  },
  legend: {
    color: {
      position: 'right',
    },
  },
});
```

### 水平色带

```javascript
chart.options({
  type: 'cell',
  data,
  encode: { x: 'x', y: 'y', color: 'value' },
  legend: {
    color: {
      position: 'bottom',
      length: 400,
      size: 15,
      layout: { justifyContent: 'center' },
    },
  },
});
```

### 自定义色带颜色

```javascript
chart.options({
  type: 'cell',
  data,
  encode: { x: 'x', y: 'y', color: 'value' },
  scale: {
    color: {
      type: 'linear',
      domain: [0, 100],
      range: ['#e6f5ff', '#0066cc'],  // 渐变范围
    },
  },
  legend: {
    color: {
      position: 'right',
      labelFormatter: (v) => `${Number(v)}°C`,  // 注意：v 可能是 string，需先转换
    },
  },
});
```

### size 通道图例

```javascript
// size 通道也会生成连续图例
chart.options({
  type: 'point',
  data,
  encode: { x: 'x', y: 'y', size: 'value' },
  legend: {
    size: {
      position: 'right',
      title: '大小',
    },
  },
});
```

## 完整类型参考

```typescript
interface LegendContinuousOptions {
  position?: 'top' | 'bottom' | 'left' | 'right';
  layout?: FlexLayout;
  title?: string | string[];
  length?: number;           // 色带长度
  size?: number;             // 色带宽度
  labelFormatter?: string | ((value: number) => string);
  labelAlign?: 'value' | 'range';
  style?: {
    ribbonFill?: string;
    [key: string]: any;
  };
}
```

## 连续图例 vs 分类图例

| 特性 | 连续图例 | 分类图例 |
|------|----------|----------|
| 数据类型 | 连续数值 | 离散分类 |
| 视觉形式 | 色带/块状 | 图例项列表 |
| 比例尺 | linear, threshold, quantize | band, ordinal |
| 适用场景 | 热力图、地图、气泡图 | 柱状图、折线图 |

## 常见错误与修正

### 错误 1：分类数据使用连续图例

```javascript
// ❌ 问题：category 是分类字段，不应使用连续图例
encode: { color: 'category' }  // 分类数据
// 连续图例显示效果不佳

// ✅ 正确：分类数据自动使用分类图例
// G2 会根据数据类型自动选择图例类型
```

### 错误 2：labelFormatter 参数类型错误

```javascript
// ❌ 问题：labelFormatter 的参数 v 可能是 string 类型（不是 number）
// G2 连续图例传入的刻度值为字符串，直接调用 .toFixed() 会报错
labelFormatter: (v) => v.toFixed(1)   // ❌ TypeError: v.toFixed is not a function
labelFormatter: (v) => v * 100        // ❌ 返回数字而不是字符串

// ✅ 正确：先转换为数字，再格式化，最终返回字符串
labelFormatter: (v) => Number(v).toFixed(1)          // ✅ 保留 1 位小数
labelFormatter: (v) => `${(Number(v) * 100).toFixed(0)}%`  // ✅ 百分比格式
labelFormatter: (v) => `${parseFloat(v).toFixed(0)}m`      // ✅ 带单位
```

### 错误 3：length 设置过小

```javascript
// ❌ 问题：色带长度太小，标签重叠
legend: { color: { length: 50 } }  // 太短

// ✅ 正确：根据标签数量设置合适长度
legend: { color: { length: 200 } }  // 合适
```

## 与 legendCategory 的选择

- **使用连续图例**：当 color/size 通道映射到连续数值字段
- **使用分类图例**：当 color 通道映射到分类字段

G2 会根据比例尺类型自动选择正确的图例类型，无需手动指定。

path: references/components/g2-comp-legend-config.md
content: ---
id: "g2-comp-legend-config"
title: "G2 图例配置（legend）"
description: |
  详解 G2 v5 Spec 模式中 legend 字段的配置，
  涵盖图例位置、布局、标题、颜色图例、过滤交互及隐藏图例等用法。

library: "g2"
version: "5.x"
category: "components"
tags:
  - "legend"
  - "图例"
  - "位置"
  - "过滤"
  - "颜色图例"
  - "spec"

related:
  - "g2-core-chart-init"
  - "g2-interaction-legend-filter"
  - "g2-comp-axis-config"
  - "g2-comp-legend-category"
  - "g2-comp-legend-continuous"

use_cases:
  - "调整图例位置和布局"
  - "自定义图例标题和样式"
  - "隐藏不需要的图例"
  - "配置连续颜色图例（色带）"

difficulty: "beginner"
completeness: "full"
created: "2024-01-01"
updated: "2025-03-26"
author: "antv-team"
source_url: "https://g2.antv.antgroup.com/manual/component/legend"
---

## 基本用法

```javascript
import { Chart } from '@antv/g2';

const chart = new Chart({ container: 'container', width: 640, height: 480 });

chart.options({
  type: 'interval',
  data,
  encode: { x: 'month', y: 'value', color: 'type' },
  legend: {
    color: {               // 对应 encode.color 通道的图例
      position: 'bottom',  // 'top'(默认) | 'bottom' | 'left' | 'right'
    },
  },
});

chart.render();
```

---

## 增量修改配置

如果已有图表，只想修改某个配置项（如图例位置），可以使用以下方式：

```javascript
// 方式一：重新调用 options，只传需要修改的配置
chart.options({
  legend: {
    color: {
      position: 'right',  // 只修改位置
    },
  },
});
chart.render();  // 需要重新渲染

// 方式二：完整配置后修改
const options = {
  type: 'interval',
  data,
  encode: { x: 'month', y: 'value', color: 'type' },
  legend: { color: { position: 'top' } },
};
chart.options(options);

// 后续修改
options.legend = { color: { position: 'bottom' } };
chart.options(options);
chart.render();
```

---

## 完整配置项参考

### 通用配置（分类图例 & 连续图例）

| 属性 | 描述 | 类型 | 默认值 |
|------|------|------|--------|
| `position` | 图例位置 | `'top' \| 'right' \| 'left' \| 'bottom'` | `'top'` |
| `orientation` | 图例朝向 | `'horizontal' \| 'vertical'` | `'horizontal'` |
| `layout` | Flex 布局配置 | `{ justifyContent, alignItems, flexDirection }` | - |
| `size` | 图例容器尺寸 | `number` | - |
| `length` | 图例容器长度 | `number` | - |
| `crossPadding` | 图例到图表区域的距离 | `number` | `12` |
| `order` | 布局排序 | `number` | `1` |
| `title` | 图例标题 | `string \| string[]` | - |

### 分类图例配置

| 属性 | 描述 | 类型 | 默认值 |
|------|------|------|--------|
| `cols` | 每行显示的图例项数量 | `number` | - |
| `colPadding` | 图例项横向间隔 | `number` | `12` |
| `rowPadding` | 图例项纵向间隔 | `number` | `8` |
| `maxRows` | 图例最大行数 | `number` | `3` |
| `maxCols` | 图例最大列数 | `number` | `3` |
| `itemWidth` | 图例项宽度 | `number` | - |
| `itemSpan` | 图例项图标、标签、值的空间划分 | `number \| number[]` | `[1, 1, 1]` |
| `itemSpacing` | 图例项内部间距 | `number \| number[]` | `[8, 8, 4]` |
| `focus` | 是否启用图例聚焦 | `boolean` | `false` |
| `focusMarkerSize` | 图例聚焦图标大小 | `number` | `12` |
| `defaultSelect` | 默认选中的图例项 | `string[]` | - |

### 图例项图标样式（itemMarker）

| 属性 | 描述 | 类型 | 默认值 |
|------|------|------|--------|
| `itemMarker` | 图例项图标 | `string \| (datum, index, data) => string` | - |
| `itemMarkerSize` | 图标大小 | `number` | `8` |
| `itemMarkerFill` | **图标填充色** | `string` | - |
| `itemMarkerFillOpacity` | 图标填充透明度 | `number` | - |
| `itemMarkerStroke` | 图标描边 | `string` | - |
| `itemMarkerStrokeOpacity` | 图标描边透明度 | `number` | - |
| `itemMarkerLineWidth` | 图标描边宽度 | `number` | - |
| `itemMarkerRadius` | 图标圆角 | `number` | - |

### 图例项标签样式（itemLabel）

| 属性 | 描述 | 类型 | 默认值 |
|------|------|------|--------|
| `itemLabelFill` | **标签文字填充色** | `string` | `#333` |
| `itemLabelFillOpacity` | 标签文字填充透明度 | `number` | - |
| `itemLabelFontSize` | 标签文字大小 | `number` | `12` |
| `itemLabelFontFamily` | 标签文字字体 | `string` | - |
| `itemLabelFontWeight` | 标签字体粗细 | `number \| string` | - |
| `itemLabelTextAlign` | 标签水平对齐方式 | `string` | - |
| `itemLabelTextBaseline` | 标签垂直基线 | `string` | - |
| `itemLabelStroke` | 标签文字描边 | `string` | - |
| `itemLabelLineWidth` | 标签文字描边宽度 | `number` | - |
| `itemLabelDx` | 标签水平偏移量 | `number` | - |
| `itemLabelDy` | 标签垂直偏移量 | `number` | - |

### 图例项值样式（itemValue）

图例项右侧可以额外显示一个"值"列（通过 `formatter` 或数据字段），适合显示数量、百分比等辅助信息。

| 属性 | 描述 | 类型 | 默认值 |
|------|------|------|--------|
| `itemValueFill` | 值文字填充色 | `string` | `#1D2129` |
| `itemValueFillOpacity` | 值文字填充透明度 | `number` | `0.65` |
| `itemValueFontSize` | 值文字大小 | `number` | `12` |
| `itemValueFontFamily` | 值文字字体 | `string` | - |
| `itemValueFontWeight` | 值文字字体粗细 | `number \| string` | - |
| `itemValueStroke` | 值文字描边 | `string` | - |
| `itemValueLineWidth` | 值文字描边宽度 | `number` | - |

### 图例项背景样式（itemBackground）

| 属性 | 描述 | 类型 | 默认值 |
|------|------|------|--------|
| `itemBackgroundFill` | 图例项背景填充色 | `string` | - |
| `itemBackgroundFillOpacity` | 图例项背景填充透明度 | `number` | - |
| `itemBackgroundStroke` | 图例项背景描边 | `string` | - |
| `itemBackgroundStrokeOpacity` | 图例项背景描边透明度 | `number` | - |
| `itemBackgroundLineWidth` | 图例项背景描边宽度 | `number` | - |
| `itemBackgroundRadius` | 图例项背景圆角 | `number` | - |

### 图例标题样式（title）

| 属性 | 描述 | 类型 | 默认值 |
|------|------|------|--------|
| `titleFill` | **标题填充色** | `string` | `#666` |
| `titleFillOpacity` | 标题填充透明度 | `number` | - |
| `titleFontSize` | 标题字体大小 | `number` | `12` |
| `titleFontFamily` | 标题字体 | `string` | - |
| `titleFontWeight` | 标题字体粗细 | `number \| string` | - |
| `titleStroke` | 标题描边 | `string` | - |
| `titleLineWidth` | 标题描边宽度 | `number` | - |
| `titleSpacing` | 标题与图例项的间距 | `number` | - |

### 连续图例配置

| 属性 | 描述 | 类型 | 默认值 |
|------|------|------|--------|
| `color` | 色带颜色 | `string[]` | - |
| `block` | 是否按区间显示 | `boolean` | `false` |
| `type` | 连续图例类型 | `'size' \| 'color'` | `'color'` |

---

## 常用配置示例

### 隐藏图例

```javascript
// 隐藏特定通道的图例
legend: { color: false }

// 全部隐藏（不常用）
legend: false
```

### 修改图例位置和布局

```javascript
chart.options({
  legend: {
    color: {
      position: 'bottom',
      layout: {
        justifyContent: 'center',  // 水平居中
        alignItems: 'center',      // 垂直居中
      },
    },
  },
});
```

### 修改图例项图标颜色

```javascript
chart.options({
  legend: {
    color: {
      itemMarkerFill: 'red',       // 图标填充色
      itemMarkerSize: 10,          // 图标大小
      itemMarkerStroke: 'darkred', // 图标描边
    },
  },
});
```

### 修改图例标签颜色

```javascript
chart.options({
  legend: {
    color: {
      itemLabelFill: '#333',
      itemLabelFontSize: 14,
      itemLabelFontWeight: 'bold',
    },
  },
});
```

### 修改图例标题样式

```javascript
chart.options({
  legend: {
    color: {
      title: '产品类型',
      titleFill: '#1D2129',
      titleFontSize: 14,
      titleFontWeight: 'bold',
      titleSpacing: 12,
    },
  },
});
```

### 饼图图例放底部居中

```javascript
chart.options({
  type: 'interval',
  data,
  encode: { y: 'value', color: 'type' },
  transform: [{ type: 'stackY' }],
  coordinate: { type: 'theta', outerRadius: 0.8 },
  legend: {
    color: {
      position: 'bottom',
      layout: { justifyContent: 'center' },
    },
  },
});
```

### 连续颜色图例（色带）

当 `color` 通道映射连续数值时，图例自动变为色带形式。

```javascript
chart.options({
  type: 'cell',
  data,
  encode: { x: 'x', y: 'y', color: 'value' },  // value 是连续数值
  scale: { color: { palette: 'Blues' } },
  legend: {
    color: {
      position: 'right',
      length: 200,
      labelFormatter: (v) => Number(v).toFixed(0),  // 注意：v 可能是 string，需先转换为数字
    },
  },
});
```

> **更多连续图例配置**：[连续图例详细文档](g2-comp-legend-continuous.md) 涵盖阈值图例、size 通道图例、自定义色带等高级用法。

---

## 常见错误与修正

### 错误 1：legend 写成数组

```javascript
// ❌ 错误：legend 是对象，不是数组
chart.options({ legend: [{ color: { position: 'bottom' } }] });

// ✅ 正确
chart.options({ legend: { color: { position: 'bottom' } } });
```

### 错误 2：legend.color 与 encode.color 不对应

```javascript
// ❌ 错误：encode 没有 color 通道，配置 legend.color 无效
chart.options({
  encode: { x: 'month', y: 'value' },  // 没有 color
  legend: { color: { position: 'bottom' } },
});

// ✅ 正确：只有 encode.color 有映射时，legend.color 才有效
chart.options({
  encode: { x: 'month', y: 'value', color: 'type' },
  legend: { color: { position: 'bottom' } },
});
```

### 错误 3：样式属性名错误

```javascript
// ❌ 错误的属性名
legend: { color: { markerFill: 'red' } }  // 不存在

// ✅ 正确的属性名（带前缀）
legend: { color: { itemMarkerFill: 'red' } }  // 正确
```

### 错误 4：混淆图例标题与图表标题

```javascript
// ❌ 图例标题写在 axis 里
axis: { x: { title: '产品类型' } }  // 这是 X 轴标题

// ✅ 图例标题在 legend 里
legend: { color: { title: '产品类型' } }  // 这是图例标题
```

Security scan verdicts — ClawScan: clean | static analysis: clean | VirusTotal: clean | SkillSpector: suspicious (HIGH)