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 @@ -45,7 +45,7 @@ G2Plot v3 covers 36+ common chart types. PRs for more types are welcome:
| Trend | Comparison | Proportion | Distribution | Relationship/Other |
|---|---|---|---|---|
| line | column | pie | scatter | dual-axes |
| area | bar | | heatmap | funnel |
| area | bar | progress | heatmap | funnel |
| | | | | radar |


Expand Down
2 changes: 1 addition & 1 deletion README.zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ G2Plot v3 覆盖 36+ 个常用图表类型,更多类型,欢迎提交 PR:
| 趋势 | 对比 | 占比 | 分布 | 关系/其他 |
|---|---|---|---|---|
| line 折线图 | column 柱状图 | pie 饼图 | scatter 散点图 | dual-axes 双轴图 |
| area 面积图 | bar 条形图 | | heatmap 热力图 | funnel 漏斗图 |
| area 面积图 | bar 条形图 | progress 进度条 | heatmap 热力图 | funnel 漏斗图 |
| | | | | radar 雷达图 |


Expand Down
180 changes: 180 additions & 0 deletions charts/progress.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
# Progress 进度条

> 组件用途:展示单一指标相对目标的完成进度,突出「已完成 / 剩余」的比例关系。

## 何时使用

- 完成度对比目标:季度目标完成率、KPI 达成度、任务进度
- 资源消耗比例:存储已用 / 总容量、预算消耗
- 少量指标(1~6 条)横向对比进度,且每条都有明确的目标上限
- 仪表盘、任务详情页里替代裸数字,让人一眼看出「还差多少」

## 何时不用

- 多类别构成占比(各部分拼成整体,但不是「完成 / 剩余」)→ 用 pie 饼图
- 没有目标值的类别数值对比 → 用 bar 条形图
- 指标随时间变化 → 用 line 折线图
- 主要诉求是精确比较多个数值,进度只是附属信息 → 用 column 柱状图
- 二维交叉的密度分布 → 用 heatmap 热力图

## 数据要求

- 长表数据,每行一个进度项,见 [数据模式](../references/data-patterns.md)
- `x` 字段:进度项名称(字符串);单条进度也要给语义化标签(如 `'季度目标完成率'`)
- `y` 字段:当前值(`number`),与 `target` 同单位;**不要**写成 `'72%'` 字符串
- `target` 字段:目标值 / 上限(`number`),用于画背景轨道;不需要轨道时改用 `scale.y.domain` 固定上限
- 百分比不要在数据里拼成字符串,标签用 `text` 回调格式化

## 基础实现(G2 v5)

G2 v5 **没有** `type: 'progress'` mark:进度条 = `interval` + 转置坐标系,背景轨道用第二个 `interval` 叠在底层。

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

const data = [
{ metric: '季度目标完成率', completed: 72, target: 100 },
];

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

chart.options({
type: 'view',
data,
coordinate: { transform: [{ type: 'transpose' }] },
scale: { y: { domain: [0, 100] } },
axis: { x: false, y: false },
legend: false,
children: [
{
type: 'interval',
encode: { x: 'metric', y: 'target', size: 28 },
style: { fill: '#EEF2F7', radius: 14 },
tooltip: false,
},
{
type: 'interval',
encode: { x: 'metric', y: 'completed', size: 28 },
style: { fill: '#5B8FF9', radius: 14 },
labels: [
{
text: (d: { completed: number }) => `${d.completed}%`,
position: 'right',
style: { fontSize: 14, fontWeight: 600, fill: '#1d1d1d' },
},
],
},
],
});

chart.render();
```

关键点:

- `coordinate: { transform: [{ type: 'transpose' }] }` 把竖向柱转成横向条
- `scale: { y: { domain: [0, 100] } }` 固定值域,轨道与进度条长度才可比;不设会按数据自适应,72% 也可能撑满整条
- `encode: { size: 28 }` 控制条高(px,转置后即厚度);`style.radius` 取条高一半得到胶囊形
- 轨道与进度条必须共享同一个 `x` 字段和 y 域(放在同一 `type: 'view'` 下即可自动共享 scale)
- 单条进度关闭坐标轴(`axis: { x: false, y: false }`)

## 常见变体

| 变体 | 关键改动 | 完整案例 |
|---|---|---|
| 多指标进度对比 | 多条数据 + `encode.size` 调细,保留 x 轴展示名称 | [multi-progress.ts](../examples/progress/multi-progress.ts) |
| 按达标状态着色 | `encode.color` 映射状态字段,`scale.color.domain/range` 固定颜色顺序 | [multi-progress.ts](../examples/progress/multi-progress.ts) |
| 环形进度 | `transform: [{ type: 'stackY' }]` + `coordinate: { type: 'theta', innerRadius }` | [ring-progress.ts](../examples/progress/ring-progress.ts) |

极简版(不需要背景轨道,靠固定值域表达剩余量):

```ts-snippet
chart.options({
type: 'interval',
data: [{ metric: '任务完成度', completed: 72 }],
encode: { x: 'metric', y: 'completed', size: 16 },
coordinate: { transform: [{ type: 'transpose' }] },
scale: { y: { domain: [0, 100] } },
style: { fill: '#5B8FF9', radius: 8 },
axis: false,
legend: false,
});
```

分段进度(已完成 / 进行中 / 未开始,同一 x 上堆叠):

```ts-snippet
chart.options({
type: 'interval',
data: [
{ stage: '上线进度', status: '已完成', value: 60 },
{ stage: '上线进度', status: '进行中', value: 25 },
{ stage: '上线进度', status: '未开始', value: 15 },
],
encode: { x: 'stage', y: 'value', color: 'status' },
transform: [{ type: 'stackY' }],
coordinate: { transform: [{ type: 'transpose' }] },
scale: { y: { domain: [0, 100] } },
style: { radius: 8 },
axis: { x: false, y: false },
legend: { color: { position: 'bottom' } },
});
```

## 样式自定义

| 定制项 | 写法 | 说明 |
|---|---|---|
| 条高 | `encode: { size: 16 }` | px;转置后即进度条厚度,多条目建议 12~20 |
| 圆角 | `style: { radius: 8 }` | 取条高一半即为胶囊形 |
| 进度色 | 单色 `style: { fill: '#5B8FF9' }`;按状态 `encode: { color: 'status' }` + `scale.color.range` | 状态色需显式给 `scale.color.domain` 固定顺序 |
| 轨道色 | 轨道 interval 的 `style: { fill: '#EEF2F7' }` | 明显浅于进度色,暗色主题可换 `'#2A2A2A'` |
| 百分比标签 | `labels: [{ text: (d) => d.completed + '%', position: 'right' }]` | 转置后 `'right'` 即条尾外侧 |
| 关闭坐标轴 / 图例 | `axis: false`、`legend: false` | 单条进度常用 |
| 内置主题 | `theme: 'classicDark'` | 暗色大屏一行切换 |

tooltip 与标题(轨道 interval 设 `tooltip: false`,避免两条重叠时提示重复):

```ts-snippet
title: {
title: '季度目标完成情况',
subtitle: '数据截至 2026-09-30',
align: 'left',
},
tooltip: {
title: 'metric',
items: [
{ channel: 'y', name: '已完成', valueFormatter: (v) => `${v}%` },
],
},
```

## 易错点(v4 → v5)

| ❌ 错误(幻觉高发) | ✅ 正确 |
|---|---|
| `type: 'progress'`(G2 v5 无此 mark,白屏/报错) | `type: 'interval'` + `coordinate: { transform: [{ type: 'transpose' }] }` |
| `coordinate: { type: 'transpose' }` | 转置是坐标系变换:`coordinate: { transform: [{ type: 'transpose' }] }` |
| `new Progress('container', { percent: 0.72 })`(G2Plot v2) | `new Chart({ container })` + `chart.options(spec)` |
| `import { Progress } from '@antv/g2plot'` | `import { Chart } from '@antv/g2'` |
| 把 `percent` / `progress` 当配置项传 | 数据字段 `completed: 72` + `encode: { y: 'completed' }` |
| 不设 `scale.y.domain` 就画进度条 | 固定 `domain: [0, target]`,否则当前值自动撑满,看不出剩余量 |
| 用 `style: { width }` / `style: { height }` 调条高 | 条高用 `encode: { size }`;`width` / `height` 是图表尺寸 |
| 百分比存成 `'72%'` 字符串 | 存 `number`,`labels.text` 回调拼 `%` |
| 轨道和进度条各自用不同数据域 / 不同 x 字段 | 放在同一 `type: 'view'` 下共享 scale,长度才对得齐 |
| `chart.source(data)`(v4) | `chart.options({ data })` |

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

## 完整案例

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

| 案例 | 场景 | 关键配置 |
|---|---|---|
| [basic.ts](../examples/progress/basic.ts) | 单指标完成度(季度目标完成率),最简起步 | `type: 'view'` + 双 interval、`transpose`、`scale.y.domain`、`labels` |
| [multi-progress.ts](../examples/progress/multi-progress.ts) | 多业务线完成率横向对比,按达标状态着色 | `encode.size`、`encode.color` + `scale.color.domain`、x 轴保留 |
| [ring-progress.ts](../examples/progress/ring-progress.ts) | 大屏 / 移动端单指标环形进度 | `stackY`、`coordinate: { type: 'theta', innerRadius: 0.72 }`、`labels` |
16 changes: 16 additions & 0 deletions evals/cases/progress.jsonl
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{"id": "progress-basic", "prompt": "任务详情页要展示本季度的目标完成率 72%,用一条横向进度条表示", "groundTruth": "使用 type: 'interval',coordinate: { transform: [{ type: 'transpose' }] } 转成横向;数据含 completed 与 target(100)字段;scale.y.domain 固定为 [0, 100];背景轨道用第二条 interval 叠加在同一 type: 'view' 下,共享 scale 保证长度对齐"}
{"id": "progress-kpi-card", "prompt": "大屏 KPI 卡片里放一个完成率 86% 的进度条,不要坐标轴和图例", "groundTruth": "interval + transpose 进度条;axis: false、legend: false;labels 显示 86%;scale.y.domain 固定 [0, 100],条高用 encode.size 控制"}
{"id": "progress-multi", "prompt": "对比华东、华南、华北、西南四个大区今年的目标完成率,画在一张图上", "groundTruth": "多条 interval + transpose(横向条形)或多组进度条;x 映射大区字段,y 映射完成率,scale.y.domain 为 [0, 100];每条的条高用 encode.size 控制,不需要 color 分组"}
{"id": "progress-threshold-color", "prompt": "完成率低于 60% 的进度条标红,其余用绿色,方便一眼看出风险", "groundTruth": "数据中增加状态字段(如 status: '预警' / '正常'),encode.color 映射该字段,并用 scale.color.domain 和 range 固定颜色顺序;或对 interval 用 style.fill 回调按值返回颜色"}
{"id": "progress-ring", "prompt": "用环形进度展示完成率 68%,中间是空的", "groundTruth": "type: 'interval' + transform: [{ type: 'stackY' }] + coordinate: { type: 'theta', innerRadius: 0.72 };数据两段「已完成 68 / 未完成 32」,未完成段用浅灰充当轨道;axis: false、legend: false"}
{"id": "progress-storage", "prompt": "展示存储使用情况:已用 320GB,总容量 1024GB", "groundTruth": "interval + transpose 进度条;y 为已用量 320,scale.y.domain 固定 [0, 1024](用总量作上限,而不是写死 100);轨道 interval 用浅灰填充;tooltip 中同时给出已用与剩余(1024-320)"}
{"id": "progress-label-decimal", "prompt": "进度条尾部显示 72.5%,保留一位小数", "groundTruth": "labels 中 text 使用回调如 (d) => `${d.completed.toFixed(1)}%`,position 为 'right'(转置后即条尾外侧);数据里 completed 存 number 72.5,不要存字符串"}
{"id": "progress-tooltip", "prompt": "鼠标悬停时显示已完成 72%、剩余 28%", "groundTruth": "tooltip.items 配置 channel: 'y' 与 valueFormatter,输出「已完成 / 剩余」两个百分比;背景轨道 interval 设置 tooltip: false 避免两条重叠时重复提示"}
{"id": "progress-dark", "prompt": "暗色大屏用的进度条,配色要适配深色背景", "groundTruth": "设置 theme: 'classicDark' 或 'dark';轨道 interval 的 fill 换成深灰(如 '#2A2A2A'),进度条用高亮色;关闭坐标轴与图例"}
{"id": "progress-title", "prompt": "进度图要有标题「季度目标完成情况」和副标题「数据截至 2026-09」,左对齐", "groundTruth": "title 必须是对象,包含 title 与 subtitle 字段,align: 'left';不能把 title 直接写成字符串"}
{"id": "progress-brand-color", "prompt": "品牌色是 #FF6B35,画一个胶囊圆角的进度条", "groundTruth": "进度 interval 的 style.fill 设为 '#FF6B35';style.radius 取 encode.size 的一半(如 size: 24 / radius: 12);轨道用浅色 fill 并保持同样的 radius"}
{"id": "progress-thin", "prompt": "进度条细一点,高度 6px 就够了", "groundTruth": "用 encode.size: 6 控制条高(不是 style.width / style.height);style.radius 取 3;scale.y.domain 仍固定 [0, 100]"}
{"id": "progress-segmented", "prompt": "一个进度条拆成已完成、进行中、未开始三段,用不同颜色区分", "groundTruth": "同一 x 下三段 interval 堆叠:encode.color 映射状态字段 + transform: [{ type: 'stackY' }] + coordinate: { transform: [{ type: 'transpose' }] },scale.y.domain 为 [0, 100],legend.color.position 设为 'bottom'"}
{"id": "progress-vertical", "prompt": "画一个竖向进度条,从下往上增长", "groundTruth": "interval 不转置坐标系(去掉 transpose),encode x 为进度项名称、y 为当前值,scale.y.domain 固定 [0, target];用 encode.size 控制条宽;axis 与 legend 关闭"}
{"id": "progress-type-antipattern", "prompt": "直接用 type: 'progress' 画一个进度条", "groundTruth": "G2 v5 没有 progress mark(写 type: 'progress' 会报错/白屏);必须用 type: 'interval' + coordinate: { transform: [{ type: 'transpose' }] } 实现,并用 scale.y.domain 固定 [0, 100]"}
{"id": "progress-percent-string", "prompt": "我数据里的 percent 字段是 '72%' 这种字符串,直接拿来画进度条", "groundTruth": "先把字符串转成 number(parseFloat 或去掉 %),再用 encode: { y: 'completed' } 映射;百分比符号只在 labels.text 回调里拼接,数值字段必须保持 number 类型"}
46 changes: 46 additions & 0 deletions examples/progress/basic.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { Chart } from '@antv/g2';

/**
* 基础进度条
* 场景:单一指标相对目标的完成度(如季度目标完成率),需要一眼看出已完成与剩余部分
* 要点:interval + transpose 横向排布;view 叠加灰色轨道与彩色进度;scale.y 固定 [0, target] 保证长度可比
*/
const data = [
{ metric: '季度目标完成率', completed: 72, target: 100 },
];

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

chart.options({
type: 'view',
data,
coordinate: { transform: [{ type: 'transpose' }] },
scale: { y: { domain: [0, 100] } },
axis: { x: false, y: false },
legend: false,
children: [
{
type: 'interval',
encode: { x: 'metric', y: 'target', size: 28 },
style: { fill: '#EEF2F7', radius: 14 },
tooltip: false,
},
{
type: 'interval',
encode: { x: 'metric', y: 'completed', size: 28 },
style: { fill: '#5B8FF9', radius: 14 },
labels: [
{
text: (d: { completed: number }) => `${d.completed}%`,
position: 'right',
style: { fontSize: 14, fontWeight: 600, fill: '#1d1d1d' },
},
],
},
],
});

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

/**
* 多指标进度对比
* 场景:看板中横向对比多条业务线的目标完成率,并按是否达标着色
* 要点:interval + transpose 多条并列;view 叠加轨道;color 通道映射达标状态,需显式给 scale.color.domain 固定颜色顺序
*/
const data = [
{ team: '华东大区', completed: 92, target: 100, status: '达成' },
{ team: '华南大区', completed: 78, target: 100, status: '达成' },
{ team: '华北大区', completed: 64, target: 100, status: '预警' },
{ team: '西南大区', completed: 45, target: 100, status: '预警' },
];

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

chart.options({
type: 'view',
data,
coordinate: { transform: [{ type: 'transpose' }] },
scale: {
y: { domain: [0, 100] },
color: { domain: ['达成', '预警'], range: ['#52C41A', '#FAAD14'] },
},
axis: { x: { title: false }, y: false },
legend: false,
children: [
{
type: 'interval',
encode: { x: 'team', y: 'target', size: 16 },
style: { fill: '#EEF2F7', radius: 8 },
tooltip: false,
},
{
type: 'interval',
encode: { x: 'team', y: 'completed', size: 16, color: 'status' },
style: { radius: 8 },
labels: [
{
text: (d: { completed: number }) => `${d.completed}%`,
position: 'right',
style: { fontSize: 12, fill: '#595959' },
},
],
},
],
});

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

/**
* 环形进度(Donut Progress)
* 场景:大屏 / 移动端单指标完成度,环形比条状更省空间且视觉更聚焦
* 要点:interval + stackY + theta 坐标系;已完成/未完成两段堆叠,未完成段用浅灰充当轨道;innerRadius 挖空成环
*/
const data = [
{ status: '已完成', value: 68 },
{ status: '未完成', value: 32 },
];

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

chart.options({
type: 'interval',
data,
encode: { y: 'value', color: 'status' },
transform: [{ type: 'stackY' }],
coordinate: { type: 'theta', innerRadius: 0.72 },
scale: { color: { domain: ['已完成', '未完成'], range: ['#5B8FF9', '#EEF2F7'] } },
axis: false,
legend: false,
labels: [
{
// 只标注「已完成」一段,避免白字落在浅灰轨道上看不清
text: (d: { status: string; value: number }) =>
d.status === '已完成' ? `${d.value}%` : '',
position: 'inside',
style: { fontSize: 14, fontWeight: 600, fill: '#fff' },
},
],
});

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 @@ -32,6 +32,7 @@ description: G2Plot v3 可视化组件库,提示词即组件。当用户需要
| 多维能力对比 | 多维度数值 | radar 雷达图 | [charts/radar.md](../../charts/radar.md) |
| 密度/频率分布 | 两个类别 + 数值 | heatmap 热力图 | [charts/heatmap.md](../../charts/heatmap.md) |
| 流程转化 | 阶段 + 数值 | funnel 漏斗图 | [charts/funnel.md](../../charts/funnel.md) |
| 完成度 / 目标进度 | 单指标 0~100% 或少量进度项 | progress 进度条 | [charts/progress.md](../../charts/progress.md) |

选型拿不准时,读对应 `charts/<chart>.md` 的「何时使用 / 何时不用」小节。

Expand Down
Loading