$npx -y skills add antvis/chart-visualization-skills --skill antv-g6-graphG6 v5 图可视化代码生成技能,支持网络图、树形图、流程图等多种图类型的初始化、布局、交互和插件配置
| 1 | # G6 v5 图可视化代码生成技能 |
| 2 | |
| 3 | ## 核心约束(必须遵守) |
| 4 | |
| 5 | ### 初始化规范 |
| 6 | - `container` 参数必填,传入 DOM 元素 ID 字符串或 DOM 元素对象 |
| 7 | - 使用 `new Graph({...})` 构造函数,**不得使用** `new G6.Graph()` (v4 写法) |
| 8 | - 所有配置在构造函数中一次性完成,不得事后多次调用配置方法覆盖 |
| 9 | - `graph.render()` 返回 Promise,异步渲染;若需等待完成请 `await graph.render()` |
| 10 | |
| 11 | ### 数据结构规范 |
| 12 | - 数据格式:`{ nodes: [...], edges: [...], combos?: [...] }` |
| 13 | - 每个节点必须有唯一 `id`(字符串);业务数据放在 `data` 字段 |
| 14 | - 边必须有 `source` 和 `target`,值为节点 `id` |
| 15 | - **禁止**使用 v4 的 `graph.data()` 方法传数据 |
| 16 | |
| 17 | ### 节点/边样式规范 |
| 18 | - 样式通过 `node.style` / `edge.style` 配置,支持静态值和回调函数 |
| 19 | - 回调函数签名:`(datum: NodeData | EdgeData) => value` |
| 20 | - 标签文本通过 `style.labelText` 设置(**不是** `label` 或 `labelCfg`) |
| 21 | - 节点大小通过 `style.size` 设置(单个数值或 [width, height] 数组) |
| 22 | |
| 23 | ### 布局规范 |
| 24 | - `layout` 配置放在 Graph 选项中:`{ type: 'force', ... }` |
| 25 | - `force` 布局**不支持** `preventOverlap` / `nodeSize`(G6 v4 参数,v5 静默忽略);防重叠请改用 `d3-force` + `collide` |
| 26 | - 树形布局(mindmap, compact-box, dendrogram, indented)需要树形数据或 `treeToGraphData()` 转换 |
| 27 | - 力导向布局异步运行,`graph.render()` 后会持续迭代 |
| 28 | - **`nodeStrength` 必须为非负数**(≥ 0),负值会导致布局计算异常或节点行为不可预测 |
| 29 | |
| 30 | ### 交互行为规范 |
| 31 | - `behaviors` 为字符串数组或配置对象数组 |
| 32 | - 常用行为字符串简写:`'drag-canvas'`, `'zoom-canvas'`, `'drag-element'`, `'click-select'` |
| 33 | - G6 v5 **移除了 Mode(模式)概念**,所有 behavior 直接在数组中配置 |
| 34 | - 复杂配置使用对象形式:`{ type: 'click-select', multiple: true }` |
| 35 | |
| 36 | ### 插件规范 |
| 37 | - `plugins` 为数组,与 `behaviors` 类似 |
| 38 | - 简写:`'minimap'`, `'grid-line'`, `'tooltip'` |
| 39 | - 复杂配置:`{ type: 'tooltip', getContent: (e, items) => '...' }` |
| 40 | |
| 41 | --- |
| 42 | |
| 43 | ## 禁止的错误模式 |
| 44 | |
| 45 | ### ❌ 使用 v4 API |
| 46 | |
| 47 | ```javascript |
| 48 | // 错误:v4 chainable API |
| 49 | const graph = new G6.Graph({ ... }); |
| 50 | graph.data(data); |
| 51 | graph.render(); |
| 52 | graph.node((node) => ({ ... })); // v4 回调 |
| 53 | |
| 54 | // 正确:v5 构造函数 |
| 55 | const graph = new Graph({ |
| 56 | container: 'container', |
| 57 | data: { nodes: [...], edges: [...] }, |
| 58 | node: { style: { ... } }, |
| 59 | }); |
| 60 | graph.render(); |
| 61 | ``` |
| 62 | |
| 63 | ### ❌ 错误的节点 data 结构 |
| 64 | |
| 65 | ```javascript |
| 66 | // 错误:直接在顶层放业务属性 |
| 67 | { id: 'node1', label: 'Node 1', value: 100 } |
| 68 | |
| 69 | // 正确:业务属性放在 data 字段 |
| 70 | { id: 'node1', data: { label: 'Node 1', value: 100 } } |
| 71 | ``` |
| 72 | |
| 73 | ### ❌ 错误的标签配置 |
| 74 | |
| 75 | ```javascript |
| 76 | // 错误:v4 labelCfg |
| 77 | node: { |
| 78 | labelCfg: { style: { fill: '#333' } } |
| 79 | } |
| 80 | |
| 81 | // 正确:v5 style.labelText |
| 82 | node: { |
| 83 | style: { |
| 84 | labelText: (d) => d.data.label, |
| 85 | labelFill: '#333', |
| 86 | labelFontSize: 14, |
| 87 | } |
| 88 | } |
| 89 | ``` |
| 90 | |
| 91 | ### ❌ behaviors 使用 Mode 概念 |
| 92 | |
| 93 | ```javascript |
| 94 | // 错误:v4 modes |
| 95 | modes: { |
| 96 | default: ['drag-canvas', 'zoom-canvas'], |
| 97 | edit: ['create-edge'], |
| 98 | } |
| 99 | |
| 100 | // 正确:v5 直接 behaviors 数组 |
| 101 | behaviors: ['drag-canvas', 'zoom-canvas', 'drag-element'], |
| 102 | ``` |
| 103 | |
| 104 | ### ❌ 自定义节点 render() 中读取 attributes.data → 白屏 |
| 105 | |
| 106 | ```javascript |
| 107 | // 错误:attributes 是计算后的样式对象,不含节点 data,访问 data.color 抛 TypeError |
| 108 | render(attributes, container) { |
| 109 | const { data } = attributes; // undefined |
| 110 | const fill = data.color; // TypeError → 白屏 |
| 111 | } |
| 112 | |
| 113 | // 正确:通过 node.style 回调把 data 字段映射为自定义样式属性 |
| 114 | // ① Graph 配置 |
| 115 | node: { |
| 116 | type: 'my-node', |
| 117 | style: { color: (d) => d.data.color }, |
| 118 | }, |
| 119 | // ② render() 中直接从 attributes 读取 |
| 120 | render(attributes, container) { |
| 121 | const { color = '#1783FF' } = attributes; // ✅ |
| 122 | } |
| 123 | ``` |
| 124 | |
| 125 | ### ❌ 使用 extend 注册自定义节点 |
| 126 | |
| 127 | ```javascript |
| 128 | // 错误:extend 已从 G6 v5 正式版移除,导入后调用会报 "extend is not a function" |
| 129 | import { Graph, extend } from '@antv/g6'; |
| 130 | const extendedGraph = extend(Graph, { |
| 131 | nodes: { 'my-node': MyNodeFn }, |
| 132 | }); |
| 133 | |
| 134 | // 错误:v4 的 group.addShape() API |
| 135 | const MyNode = (node) => (model) => { |
| 136 | const group = node.group(); |
| 137 | group.addShape('circle', { attrs: { r: 20 } }); |
| 138 | }; |
| 139 | |
| 140 | // 正确:BaseNode 类 + register() |
| 141 | import { BaseNode, Circle, ExtensionCategory, Graph, register } from '@antv/g6'; |
| 142 | class MyNode extends BaseNode { |
| 143 | render(attributes, container) { |
| 144 | super.render(attributes, container); |
| 145 | this.upsert('key', Circle, { cx: 0, cy: 0, r: 20, fill: '#1783FF' }, container); |
| 146 | } |
| 147 | } |
| 148 | register(ExtensionCategory.NODE, 'my-node', MyNode); |
| 149 | const graph = new Graph({ node: { type: 'my-node' } }); |
| 150 | ``` |
| 151 | |
| 152 | ### ❌ 缺少 container |
| 153 | |
| 154 | ```javascript |
| 155 | // 错误:遗漏 container |
| 156 | const graph = new Graph({ }); |
| 157 | |
| 158 | // 正确:container 必填,值为字符串 ID 或 DOM 元素 |
| 159 | const graph = new Graph({ container: 'container' }); |
| 160 | // 或传入 DOM 元素 |
| 161 | const graph = new Graph({ container: document.getElementById('container') }); |
| 162 | ``` |
| 163 | |
| 164 | > 常见变体错误:`container: container`(把字符串 ID 当变量名使用,变量未定义 → ReferenceError → 白屏) |
| 165 | |
| 166 | ### ❌ autoFit: 'view' 配合异步力导向布局导致白屏 |
| 167 | |
| 168 | ```javascript |
| 169 | // 错误:combo-combined / force / d3-force 等布局是异步迭代的 |
| 170 | // autoFit 在布局迭代开始前执行,节点全堆在原点,包围盒为零 → 缩放异常 → 白屏 |
| 171 | const graph = new Graph({ |
| 172 | autoFit: 'view', // ❌ 异步布局下不能在此设置 |
| 173 | layout: { type: 'combo-combined' }, |
| 174 | }); |
| 175 | graph.render(); |
| 176 | |
| 177 | // 正确:不设置 autoFit,在 AFTER_LAYOUT 事件后调用 fitView |
| 178 | import { Graph, GraphEvent } from '@antv/g6'; |
| 179 | const graph = new Graph({ |
| 180 | layout: { type: 'combo-combined' }, |
| 181 | }); |
| 182 | graph.on(GraphEvent.AFTER_LAYOUT, () => graph.fitView({ padding: 20 })); |
| 183 | graph.render(); |
| 184 | ``` |
| 185 | |
| 186 | > 同步布局(`dagre`、`grid`、`circular` 等)不受此影响,可以直接用 `autoFit: 'view'`。 |
| 187 | |
| 188 | --- |
| 189 | |
| 190 | ## 基础结构模板 |
| 191 | |
| 192 | ```javascript |
| 193 | import { Graph } from '@antv/g6'; |
| 194 | |
| 195 | const graph = new Graph({ |
| 196 | // 1. 容器 |
| 197 | container: 'container', // DOM id 或 HTMLElement |
| 198 | autoFit: 'view', // 可选:'center' | 'view' | false |
| 199 | |
| 200 | // 2. 数据 |
| 201 | data: { |
| 202 | nodes: [ |
| 203 | { id: 'n1', data: { label: '节点1' } |