Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ G2Plot v3 covers 36+ common chart types. PRs for more types are welcome:
|---|---|---|---|---|
| line | column | pie | scatter | dual-axes |
| area | bar | | heatmap | funnel |
| | | | | radar |
| | radial-bar | | | radar |


## 🏗️ How It Works
Expand Down
2 changes: 1 addition & 1 deletion README.zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ G2Plot v3 覆盖 36+ 个常用图表类型,更多类型,欢迎提交 PR:
|---|---|---|---|---|
| line 折线图 | column 柱状图 | pie 饼图 | scatter 散点图 | dual-axes 双轴图 |
| area 面积图 | bar 条形图 | | heatmap 热力图 | funnel 漏斗图 |
| | | | | radar 雷达图 |
| | radial-bar 玉珏图 | | | radar 雷达图 |


## 🏗️ 工作原理
Expand Down
163 changes: 163 additions & 0 deletions charts/radial-bar.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
# Radial Bar 玉珏图(径向条形图)

> 组件用途:在极坐标下用同心圆弧的长度对比多个类别的数值,兼顾排名与整体观感。

## 何时使用

- 类别排名/对比(5~8 个类别):渠道转化率、部门目标完成度、产品评分
- KPI 看板、大屏:环形造型节省横向空间,视觉记忆点强
- 数值同量纲且非负(百分比、分数、金额),需要固定基准对比
- 强调“完成度/达成率”:配合 `scale.y.domain` 固定 0~100,弧长即完成比例

## 何时不用

- 类别多(> 10)或类别名称长 → 用 bar 条形图,标签更好排布
- 需要精确比较细微差异 → 用 column / bar,直线长度比弧长更易读
- 展示整体构成(各块加起来 100%)→ 用 pie 饼图;玉珏图每环独立,不构成整体
- 时间序列趋势 → 用 line 折线图
- 数据含负值 → 用 bar 或发散条形图;弧长无法表达正负方向

## 数据要求

- 长表数据,每行一个类别,见 [数据模式](../references/data-patterns.md)
- `x` 字段:类别字符串(如 `channel`、`department`)
- `y` 字段:数值(number),必须非负;百分比统一用 0~1 或 0~100,并与 `scale.y.domain` 一致
- 数据顺序决定各环的排列顺序;排名场景先在渲染前对数据排序,不要依赖图例顺序
- 单指标多类别场景不要传多系列字段;多系列堆叠不是玉珏图的常规用法

## 基础实现(G2 v5)

玉珏图 = `type: 'interval'` + `coordinate: { type: 'radial' }`。在 radial 坐标系下,`y` 数值映射为圆弧角度,`x` 类别映射为同心环半径,因此弧越长数值越大。

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

/**
* 基础玉珏图(径向条形图)
* 场景:对比各获客渠道的转化率,用同心圆弧长度表达数值大小
* 要点:interval + radial 坐标系;y 轴固定 0~1 映射百分比;隐藏坐标轴,用图例标识类别
*/
const data = [
{ channel: '自然搜索', conversionRate: 0.42 },
{ channel: '社交推荐', conversionRate: 0.68 },
{ channel: '广告投放', conversionRate: 0.35 },
{ channel: '邮件营销', conversionRate: 0.24 },
{ channel: '直接访问', conversionRate: 0.55 },
];

const chart = new Chart({
container: 'container',
autoFit: true,
});

chart.options({
type: 'interval',
data,
encode: { x: 'channel', y: 'conversionRate', color: 'channel' },
coordinate: { type: 'radial', innerRadius: 0.25 },
scale: { y: { domain: [0, 1] } },
axis: {
x: { title: false, label: false, tick: false, line: false },
y: { title: false, label: false, tick: false, line: false, grid: false },
},
legend: { color: { position: 'right' } },
tooltip: {
title: 'channel',
items: [
{
channel: 'y',
name: '转化率',
valueFormatter: (v) => `${(Number(v) * 100).toFixed(1)}%`,
},
],
},
style: { radius: 4 },
});

chart.render();
```

## 常见变体

| 变体 | 关键改动 | 完整案例 |
|---|---|---|
| 数值标签 | `labels` 的 `text` 回调拼单位,`position: 'outside'` 定位到弧末端 | [with-label.ts](../examples/radial-bar/with-label.ts) |
| 半圆造型 | `coordinate.startAngle / endAngle` 限定角度范围 | [semi-circle.ts](../examples/radial-bar/semi-circle.ts) |
| 自定义品牌色板 | `scale: { color: { range: [...] } }` | [semi-circle.ts](../examples/radial-bar/semi-circle.ts) |

半圆玉珏图核心差异(只画右半圆,中心留白更大):

```ts-snippet
coordinate: {
type: 'radial',
innerRadius: 0.35,
startAngle: -Math.PI / 2,
endAngle: Math.PI / 2,
},
```

数值标签核心差异(`autoRotate: false` 让文字保持水平,避免沿弧旋转):

```ts-snippet
labels: [
{
text: (d: { nps: number }) => `${d.nps} 分`,
position: 'outside',
autoRotate: false,
},
],
```

## 样式自定义

| 定制项 | 写法 | 说明 |
|---|---|---|
| 中心留白 | `coordinate: { type: 'radial', innerRadius: 0.35 }` | 0~1 相对值;越大中心空洞越大,可放 HTML 中心文字 |
| 外圈留白 | `coordinate: { type: 'radial', outerRadius: 0.9 }` | 避免图例/标签贴边 |
| 角度范围 | `coordinate: { startAngle, endAngle }` | 默认 `-Math.PI/2` ~ `Math.PI*3/2`;半圆用 `-Math.PI/2` ~ `Math.PI/2` |
| 弧端圆角 | `style: { radius: 4 }` | 让每段圆弧两端变圆,数值小的时候更明显 |
| 类别色板 | `scale: { color: { range: ['#5B8FF9', '#5AD8A6'] } }` | 自定义颜色数组用 `range`,不要传给 `palette` |
| 固定数值范围 | `scale: { y: { domain: [0, 100] } }` | 保证所有弧从同一基准起算 |
| 图例位置 | `legend: { color: { position: 'top' } }` | `'top'` / `'bottom'` / `'left'` / `'right'`,或 `legend: false` 关闭 |
| 隐藏坐标轴 | `axis: { x: false, y: false }` | 玉珏图通常只留图例与标签 |
| 数值标签 | `labels: [{ text, position: 'outside', autoRotate: false }]` | 显示原始数值或格式化文案 |
| 主题 | `theme: 'classicDark'` | 大屏暗色场景,文字与网格自动适配 |

图表标题与 tooltip 格式化:

```ts-snippet
title: { title: '渠道转化率', subtitle: '统计周期:2026-10', align: 'left' },
tooltip: {
title: 'channel',
items: [{ channel: 'y', name: '转化率', valueFormatter: (v) => `${(Number(v) * 100).toFixed(1)}%` }],
},
```

## 易错点(v4 → v5)

| ❌ 错误(幻觉高发) | ✅ 正确 |
|---|---|
| `type: 'radial-bar'` / `type: 'radialBar'` | 无此 mark;用 `type: 'interval'` + `coordinate: { type: 'radial' }` |
| 把 `radial` 写进 `type` | `radial` 是坐标系:`coordinate: { type: 'radial' }` |
| 忘记 `coordinate` | 不加坐标系会渲染成普通直角坐标柱状图 |
| 用 `coordinate: { type: 'polar' }` 代替 `radial` | polar 得到的是玫瑰图/扇形,不是同心环的玉珏图 |
| `radius: 0.8, innerRadius: 0.2` 写在顶层(v2 配置) | 放进坐标系:`coordinate: { type: 'radial', innerRadius: 0.2 }` |
| `startAngle` / `maxAngle` 写在顶层(v4 配置) | 放进坐标系:`coordinate: { startAngle, endAngle }` |
| `seriesField: 'channel'`(v2 配置) | `encode: { color: 'channel' }` |
| `.coordinate('radial')`(v4 链式) | spec 中 `coordinate: { type: 'radial' }` |
| 百分比数据不固定 `domain` | 显式 `scale: { y: { domain: [0, 1] } }` 或 `[0, 100]`,否则自适应范围会夸大差异 |
| 用玉珏图表达正负增长 | 负值没有弧长方向;改用 bar 或发散条形图 |
| 类别超过 10 个仍用玉珏图 | 环太薄、不可读;改用 bar 条形图 |
| `new RadialBar('container', {...})`(G2Plot v2) | `new Chart({ container })` + `type: 'interval'` + radial |

更多对照见 [v4-to-v5-migration.md](../references/v4-to-v5-migration.md)。

## 完整案例

每个案例的元信息(场景与关键配置),代码均通过 `npm run validate` 校验:

| 案例 | 场景 | 关键配置 |
|---|---|---|
| [basic.ts](../examples/radial-bar/basic.ts) | 各渠道转化率对比,最简起步 | `type: 'interval'` + `coordinate.type: 'radial'`、`innerRadius: 0.25`、`scale.y.domain: [0, 1]` |
| [with-label.ts](../examples/radial-bar/with-label.ts) | 弧末端直接标出产品 NPS 分数 | `labels` text 回调 + `position: 'outside'` + `autoRotate: false` |
| [semi-circle.ts](../examples/radial-bar/semi-circle.ts) | 大屏半圆造型 + 品牌色板 | `startAngle` / `endAngle` 半圆、`scale.color.range`、`legend.position: 'top'` |
16 changes: 16 additions & 0 deletions evals/cases/radial-bar.jsonl
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{"id": "radial-basic-channel", "prompt": "我想用环形图对比自然搜索、社交推荐、广告投放、邮件营销四个渠道的转化率,弧越长表示转化率越高", "groundTruth": "使用 type: 'interval';coordinate 设置 type: 'radial';encode x 映射渠道字段、y 映射转化率字段、color 映射渠道;scale.y 固定 domain [0, 1]"}
{"id": "radial-inner-radius", "prompt": "玉珏图中间空出一大块,我想在圆心位置放一个总数文字", "groundTruth": "coordinate 设置 type: 'radial' 并增大 innerRadius(如 0.35~0.45)留出圆心空间;type 为 'interval';中心文字用 HTML 叠加,不要伪造不存在的 mark"}
{"id": "radial-fixed-domain", "prompt": "各渠道转化率都是百分比,希望所有圆弧从 0 开始按 100% 为满格来画,不要自动缩放", "groundTruth": "scale.y 显式设置 domain: [0, 100](或 [0, 1]);coordinate.type 为 'radial';type: 'interval'"}
{"id": "radial-rounded", "prompt": "玉珏图每段圆弧的两端想要圆角效果,看起来更柔和", "groundTruth": "style 设置 radius 为大于 0 的数值(如 4~8);coordinate.type: 'radial';type: 'interval'"}
{"id": "radial-label-outside", "prompt": "每段圆弧末端直接标出具体分数,文字不要跟着弧旋转,保持水平", "groundTruth": "labels 配置 text 回调拼出数值与单位,position: 'outside',autoRotate: false;coordinate.type: 'radial'"}
{"id": "radial-semi-circle", "prompt": "做一个半圆形的玉珏图,只占 180 度,放在大屏顶部", "groundTruth": "coordinate 设置 type: 'radial',并用 startAngle: -Math.PI / 2、endAngle: Math.PI / 2(或等价半圆角度);type: 'interval'"}
{"id": "radial-custom-palette", "prompt": "玉珏图不要默认配色,用我们品牌的蓝绿黄红紫五个颜色", "groundTruth": "scale.color.range 传入自定义颜色数组;encode.color 映射类别字段;type: 'interval' + coordinate.type: 'radial';不要用 palette 传数组"}
{"id": "radial-legend-top", "prompt": "玉珏图的图例放到顶部,横向排开,别占右侧空间", "groundTruth": "legend.color.position 设置为 'top';coordinate.type: 'radial';type: 'interval'"}
{"id": "radial-hide-axis", "prompt": "玉珏图只要圆环和图例,坐标轴、刻度、网格线都隐藏掉", "groundTruth": "axis 中把 x、y 的 label/tick/line/grid 设为 false,或直接 axis: { x: false, y: false };type: 'interval' + coordinate.type: 'radial'"}
{"id": "radial-tooltip-percent", "prompt": "悬停圆弧时 tooltip 显示渠道名和百分比,保留一位小数", "groundTruth": "tooltip.title 映射渠道字段,tooltip.items 的 valueFormatter 把数值转成百分比并 toFixed(1);type: 'interval' + coordinate.type: 'radial'"}
{"id": "radial-vs-pie", "prompt": "我要展示各部门在总营收中的占比,用玉珏图还是饼图?希望一眼看出整体构成", "groundTruth": "看整体构成和 100% 加总用 pie 饼图;玉珏图适合类别间排名/对比、每环独立不成整体;应说明选型理由而不是硬套 radial-bar"}
{"id": "radial-sort-desc", "prompt": "玉珏图的圆环顺序很乱,想按完成度从高到低排列", "groundTruth": "渲染前对 data 按数值字段降序排序(或用 data.transform 的 sort),再传给 interval;不要依赖图例顺序;coordinate.type: 'radial'"}
{"id": "radial-dark-theme", "prompt": "大屏是暗色背景,玉珏图也要暗色主题,文字和网格自动适配", "groundTruth": "设置 theme: 'classicDark' 或 'dark';type: 'interval' + coordinate.type: 'radial';无需逐项改文字颜色"}
{"id": "radial-negative-values", "prompt": "有几个月是负增长,想用玉珏图同时表达正负", "groundTruth": "玉珏图用弧长表达数值,无法表达正负方向;负值应改用 bar 条形图或发散条形图,并说明原因"}
{"id": "radial-many-categories", "prompt": "我有 20 个城市的销售额想用玉珏图对比", "groundTruth": "类别过多时玉珏图每环过薄不可读;建议改用 bar 条形图,或先取 Top N 收敛到 5~8 个类别;若坚持用 radial 需说明类别上限"}
{"id": "radial-v2-migration", "prompt": "旧项目里用的是 G2Plot v2 的 RadialBar,现在要迁移到 G2 v5", "groundTruth": "改为 import { Chart } from '@antv/g2';new Chart({ container, autoFit: true });options 中使用 type: 'interval' + coordinate: { type: 'radial', innerRadius };xField/yField/seriesField 改为 encode,radius/innerRadius 移入 coordinate"}
45 changes: 45 additions & 0 deletions examples/radial-bar/basic.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { Chart } from '@antv/g2';

/**
* 基础玉珏图(径向条形图)
* 场景:对比各获客渠道的转化率,用同心圆弧长度表达数值大小
* 要点:interval + radial 坐标系;y 轴固定 0~1 映射百分比;隐藏坐标轴,用图例标识类别
*/
const data = [
{ channel: '自然搜索', conversionRate: 0.42 },
{ channel: '社交推荐', conversionRate: 0.68 },
{ channel: '广告投放', conversionRate: 0.35 },
{ channel: '邮件营销', conversionRate: 0.24 },
{ channel: '直接访问', conversionRate: 0.55 },
];

const chart = new Chart({
container: 'container',
autoFit: true,
});

chart.options({
type: 'interval',
data,
encode: { x: 'channel', y: 'conversionRate', color: 'channel' },
coordinate: { type: 'radial', innerRadius: 0.25 },
scale: { y: { domain: [0, 1] } },
axis: {
x: { title: false, label: false, tick: false, line: false },
y: { title: false, label: false, tick: false, line: false, grid: false },
},
legend: { color: { position: 'right' } },
tooltip: {
title: 'channel',
items: [
{
channel: 'y',
name: '转化率',
valueFormatter: (v) => `${(Number(v) * 100).toFixed(1)}%`,
},
],
},
style: { radius: 4 },
});

chart.render();
53 changes: 53 additions & 0 deletions examples/radial-bar/semi-circle.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import { Chart } from '@antv/g2';

/**
* 半圆玉珏图 + 自定义色板
* 场景:大屏顶部展示各业务线的月度目标完成度,半圆造型更省纵向空间
* 要点:startAngle / endAngle 限定半圆范围;scale.color.range 自定义品牌色;图例放顶部
*/
const data = [
{ department: '电商', completion: 86 },
{ department: '本地生活', completion: 72 },
{ department: '云计算', completion: 94 },
{ department: '广告', completion: 63 },
{ department: '金融', completion: 78 },
];

const chart = new Chart({
container: 'container',
autoFit: true,
});

chart.options({
type: 'interval',
data,
encode: { x: 'department', y: 'completion', color: 'department' },
coordinate: {
type: 'radial',
innerRadius: 0.35,
startAngle: -Math.PI / 2,
endAngle: Math.PI / 2,
},
scale: {
y: { domain: [0, 100] },
color: { range: ['#5B8FF9', '#5AD8A6', '#5D7092', '#F6BD16', '#E8684A'] },
},
axis: {
x: { title: false, label: false, tick: false, line: false },
y: { title: false, label: false, tick: false, line: false, grid: false },
},
legend: { color: { position: 'top' } },
tooltip: {
title: 'department',
items: [
{
channel: 'y',
name: '完成度',
valueFormatter: (v) => `${v}%`,
},
],
},
style: { radius: 6 },
});

chart.render();
43 changes: 43 additions & 0 deletions examples/radial-bar/with-label.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { Chart } from '@antv/g2';

/**
* 带数值标签的玉珏图
* 场景:展示各产品线的 NPS 得分,需要在每段圆弧末端直接读出分数
* 要点:labels 用 text 回调拼接单位,position: 'outside' 定位到弧末端,autoRotate: false 保持文字水平
*/
const data = [
{ product: '智能手表', nps: 72 },
{ product: '无线耳机', nps: 85 },
{ product: '运动手环', nps: 64 },
{ product: '智能音箱', nps: 58 },
{ product: '平板电脑', nps: 77 },
];

const chart = new Chart({
container: 'container',
autoFit: true,
});

chart.options({
type: 'interval',
data,
encode: { x: 'product', y: 'nps', color: 'product' },
coordinate: { type: 'radial', innerRadius: 0.25 },
scale: { y: { domain: [0, 100] } },
labels: [
{
text: (d: { nps: number }) => `${d.nps} 分`,
position: 'outside',
autoRotate: false,
style: { fontSize: 12, fill: '#555' },
},
],
axis: {
x: { title: false, label: false, tick: false, line: false },
y: { title: false, label: false, tick: false, line: false, grid: false },
},
legend: { color: { position: 'right' } },
style: { radius: 4 },
});

chart.render();
1 change: 1 addition & 0 deletions skills/g2plot-v3/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ description: G2Plot v3 可视化组件库,提示词即组件。当用户需要
| 多系列趋势对比 | x + y + 系列字段 | line(color 分组) | [charts/line.md](../../charts/line.md) |
| 类别数值对比 | 类别 x + 数值 y | column 柱状图 | [charts/column.md](../../charts/column.md) |
| 类别数值对比(横向) | 类别多/名称长 | bar 条形图 | [charts/bar.md](../../charts/bar.md) |
| 环形类别对比、排名 | 类别 + 非负数值(5~8 类) | radial-bar 玉珏图 | [charts/radial-bar.md](../../charts/radial-bar.md) |
| 占比、构成 | 类别 + 数值 | pie 饼图 | [charts/pie.md](../../charts/pie.md) |
| 趋势 + 累积量 | 时间 x + 数值 y | area 面积图 | [charts/area.md](../../charts/area.md) |
| 双变量相关性 | 两个数值字段 | scatter 散点图 | [charts/scatter.md](../../charts/scatter.md) |
Expand Down
Loading