From antvis-chart-visualization-skills
Generates code for AntV X6 3.x graph editor, covering nodes, edges, ports, interactions, and plugins for flowcharts, DAGs, ER diagrams, and lineage graphs.
How this skill is triggered — by the user, by Claude, or both
Slash command
/antvis-chart-visualization-skills:antv-x6-editorThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
<!-- CONSTRAINTS:START -->
references/core/x6-core-anchor.mdreferences/core/x6-core-animation.mdreferences/core/x6-core-attr-registry.mdreferences/core/x6-core-background.mdreferences/core/x6-core-cell-data.mdreferences/core/x6-core-connection-point.mdreferences/core/x6-core-connection-strategy.mdreferences/core/x6-core-connector-advanced.mdreferences/core/x6-core-connector-params.mdreferences/core/x6-core-coord.mdreferences/core/x6-core-defs.mdreferences/core/x6-core-edge-anchor.mdreferences/core/x6-core-edge.mdreferences/core/x6-core-events.mdreferences/core/x6-core-filter.mdreferences/core/x6-core-geometry.mdreferences/core/x6-core-graph-init.mdreferences/core/x6-core-grid.mdreferences/core/x6-core-highlighter.mdreferences/core/x6-core-html-shape.mdgraph.render() 不存在:X6 3.x 中 new Graph() / addNode / addEdge / fromJSON 均自动渲染,代码中不得出现 graph.render()。container 变量:运行环境会作为函数参数注入 container。Graph 初始化只使用字符串字面量 container: 'container',禁止 const/let/var container = ...,也禁止 document.getElementById('container')。graph.use(new Plugin(...)) 注册对应插件:graph.toPNG / toSVG / toJPEG 依赖 Export;graph.select / unselect 依赖 Selection;graph.undo / redo 依赖 History;graph.copy / paste / cut 依赖 Clipboard;graph.bindKey 依赖 Keyboard。未注册插件时对应方法不存在。Graph.registerNode(name, def) / Graph.registerEdge(name, def) / Shape.HTML.register({ shape, ... }) 必须在首次 addNode / addEdge 之前完成。@antv/x6 仅导出 11 个插件类:Clipboard、Dnd、Export、History、Keyboard、MiniMap、Scroller、Selection、Snapline、Stencil、Transform。mousewheel、embedding、panning、connecting、translating、interacting、background、grid 是 new Graph() 的构造选项,不是插件,不得 import 同名类、不得 graph.use(new XxxClass())。例:滚轮缩放写在 Graph 构造中 mousewheel: { enabled: true, zoomAtMousePosition: true, modifiers: ['ctrl'] }。cell.animate(keyframes, options)(Web Animations API 风格):X6 3.x 没有 node.transition(path, target, options) 方法。源码中 transition 仅作为 node.translate(tx, ty, { transition }) / node.rotate(deg, { transition }) 的 options 字段存在(boolean | KeyframeEffectOptions),并非独立方法。示例:
// 通用动画
node.animate(
{ fill: ['#fff', '#1890ff'], transform: ['scale(1)', 'scale(1.2)'] },
{ duration: 500, iterations: 1, fill: 'forwards' },
);
// 仅平移过渡
node.translate(120, 0, { transition: { duration: 500, easing: 'ease-in-out' } });
多步属性变更可用 graph.startBatch('animate'); cell.attr(...); graph.stopBatch('animate'); 包装。container 参数必填,必须使用字符串形式 container: 'container',运行时环境会自动解析为 DOM 元素background: { color: '#F2F7FA' },所有画布都需要统一的浅蓝灰色背景grid 配置,除非用户明确要求显示网格width / height,除非用户明确指定画布尺寸;画布默认自适应容器大小import { Graph } from '@antv/x6',仅导入实际使用到的类Shape:仅在使用 Shape.HTML.register() 等 Shape 静态方法时才导入 Shape'@antv/x6' 直接导入,如 import { Graph, Selection, History } from '@antv/x6'@antv/x6-plugin-xxx 独立包导入(已废弃)import { Graph } from '@antv/x6';
const graph = new Graph({
container: 'container',
background: { color: '#F2F7FA' },
});
graph.addNode() 逐个添加节点,而非 graph.fromJSON() 批量导入(除非用户明确要求批量加载数据)'rect'、'circle'、'ellipse'、'polygon'、'polyline'、'path'、'text'、'text-block'、'image'、'html'attrs 配置,遵循 SVG 属性命名x、y 设置(左上角坐标),尺寸通过 width、height 设置attrs: {
body: { stroke: '#8f8f8f', strokeWidth: 1, fill: '#fff', rx: 6, ry: 6 },
}
background-color),必须用 SVG 属性(如 fill)graph.addEdge({ source, target, ... }) 添加边source/target 可以是:节点实例、节点 ID 字符串、{ cell: node, port: 'portId' } 对象、坐标 { x, y }attrs: { line: { stroke, strokeWidth, strokeDasharray, targetMarker, sourceMarker } }attrs: { line: { stroke: '#8f8f8f', strokeWidth: 1 } }targetMarker: 'classic'(经典箭头)、'block'、'circle'、'diamond'router: 'orth'(正交)、'manhattan'、'metro'、'er'connector: 'rounded'(圆角)、'smooth'(贝塞尔曲线)、'jumpover'ports 字段中ports: { groups: { groupName: { position, attrs, ... } }, items: [{ id, group }] }'top'、'bottom'、'left'、'right'attrs: { circle: { magnet: true } } 允许从端口拖出连线magnet: true 才能从该端口发起或接收连线connecting 字段设置connecting: { allowBlank: false, router: 'orth', connector: 'rounded', createEdge() {...} }translating: { restrict: true } 或传函数限制区域embedding: { enabled: true } 允许节点拖入分组@antv/x6 导入,通过 graph.use(new Plugin(options)) 注册Selection、Snapline、History、Clipboard、Keyboard、Scroller、MiniMap、Transform、Export、Stencil、Dndgraph.use(new Selection({ enabled: true, rubberband: true }))graph.use(new Snapline({ enabled: true }))graph.use(new History({ enabled: true }))graph.use(new Clipboard({ enabled: true }))graph.use(new Keyboard({ enabled: true }))graph.use(new Scroller({ enabled: true }))graph.use(new MiniMap({ enabled: true, container: minimapContainer }))graph.use(new Transform({ resizing: { enabled: true }, rotating: { enabled: true } }))graph.use(new Export())(注册后可调用 graph.toPNG() / graph.toSVG())graph.enablePlugins('selection') / graph.disablePlugins('selection')selecting、snapline 等选项(3.x 不支持)const data = graph.toJSON() 返回 { cells: [...] } 对象graph.fromJSON(data) 加载整个图数据graph.clearCells() 清除所有元素zIndex、parent),应通过 API 操作graph.on('node:click', ({ node, e }) => {...})graph.on('edge:click', ({ edge, e }) => {...})graph.on('blank:click', ({ e }) => {...})graph.on('node:moved', ({ node }) => {...})({ node, e }) 而非 (node, e)Selection,必须 import { Graph, Selection } from '@antv/x6'Graph.Selection、Graph.Keyboard 等命名空间写法(不存在)new Selection(...) 必须对应 import { Selection } from '@antv/x6'new XxxYyy(...) 调用,XxxYyy 必须字面出现在第一行 import { ..., XxxYyy } from '@antv/x6' 的花括号内。常见漏写:Selection、Keyboard、History、Clipboard、Snapline、MiniMap、Transform、Scroller、Export、Stencil、Dnd、Shape(用到 Shape.HTML.register 时)。Illegal constructor 等运行时错:评测/Playground 环境用 UMD 构建(window.X6)执行代码,而不是真正的 ES Module。Selection、Keyboard 等会根据 import 列表从 window.X6 解构出来;如果 import 漏写,标识符 Selection 会回退到 window.Selection(浏览器原生 Selection 接口),new Selection({...}) 会抛 Failed to construct 'Selection': Illegal constructor。Keyboard / History 等同理(会 is not a constructor)。import { Graph, Selection, Keyboard, History, ... } from '@antv/x6';,不要分多行 import,也不要遗漏。node.addTools([{ name: 'button-remove', args: { x: 0, y: 0 } }]) 或 graph.addTools(node, [...])node.removeTools() 或 graph.removeTools(node)node.hasTools()node.hideTools() / node.showTools()(3.x 不存在此 API)graph.on('node:mouseenter', ({ node }) => {
node.addTools([{ name: 'button-remove', args: { x: 0, y: 0 } }]);
});
graph.on('node:mouseleave', ({ node }) => {
node.removeTools();
});
fill 支持渐变对象语法,禁止直接操作 graph.defs 或 document.createElementNS 创建 SVG 渐变attrs: {
body: {
fill: {
type: 'linearGradient',
stops: [
{ offset: '0%', color: '#0000ff' },
{ offset: '100%', color: '#00ff00' },
],
},
},
}
private、类型注解 : string、as 类型断言)Shape.HTML.register({ shape, html, effect }) 注册自定义 shape,禁止使用 class extends Node 方式Graph.registerHTMLComponent(name, factory) —— 这是 X6 2.x 旧 API,3.x 源码已无此方法。所有 HTML 节点统一通过 Shape.HTML.register 注册(详见 references/core/x6-core-html-shape.md)effect 数组指定哪些属性变化时触发重新渲染(如 ['data']);纯静态展示节点不要加 effectimport { Graph, Shape } from '@antv/x6';
Shape.HTML.register({
shape: 'my-html',
effect: ['data'],
html(node) {
const div = document.createElement('div');
div.style.width = '100%';
div.style.height = '100%';
div.innerHTML = node.getData().content || '';
return div;
},
});
const graph = new Graph({ container: 'container' });
graph.addNode({ shape: 'my-html', x: 100, y: 100, width: 200, height: 80, data: { content: '<div>Hello</div>' } });
graph.use(new Stencil({ target: graph, groups: [...] })) 注册graph.getPlugin('stencil') 获取实例,将 stencil.container 挂载到 DOMgraph.createNode(...) 创建(非 graph.addNode),再通过 stencil.load(nodes, groupName) 加载node.addPort() 动态添加端口时,必须在节点初始化时预定义对应的 ports.groupsconst node = graph.addNode({
...,
ports: {
groups: {
in: { position: 'left', attrs: { circle: { r: 4, magnet: true, stroke: '#8f8f8f', fill: '#fff' } } },
out: { position: 'right', attrs: { circle: { r: 4, magnet: true, stroke: '#8f8f8f', fill: '#fff' } } },
},
},
});
node.addPort({ id: 'port1', group: 'out' });
registerNode + addNode 的 ports 合并陷阱(强约束):Graph.registerNode(name, { ports: { items: [{ id: 'in1', group: 'in' }] } }) 之后,如果再 graph.addNode({ shape: name, ports: { items: [{ id: 'in1', group: 'in' }] } }),X6 内部 Cell 构造时 ObjectExt.merge(defaults, metadata) 会按数组下标合并、node.addPorts 走的也是 [...current, ...new] 简单拼接,不会去重,运行时直接抛 Error: Duplicitied port id.。正确做法二选一:
registerNode 里只声明 ports.groups,把 ports.items 留给 addNode / 后续 node.addPort 提供;registerNode 里完整声明 ports.items,addNode 时不再传 ports.items(如需追加端口,调用 node.addPort({ id: '新id', group: 'xxx' }),且新 id 不能与 registry 里已声明的重名)。html(node) 回调里给 DOM 设样式时,禁止直接写连字符属性:el.style.box-sizing = '...'、el.style.font-size = '...' 会被 JS 解析为 el.style.box - sizing = ...,抛 Invalid left-hand side in assignment。正确写法二选一:
el.style.boxSizing = 'border-box'、el.style.fontSize = '14px'、el.style.backgroundColor = '#fff';el.style['box-sizing'] = 'border-box'、el.style['font-size'] = '14px';el.style.cssText = 'box-sizing:border-box;font-size:14px;' 或 Object.assign(el.style, { boxSizing: 'border-box', fontSize: '14px' })。el.classList.add('...') / el.setAttribute('data-x', '...') 是合法 API;禁止 el.class = '...' / el['class-name'] = ...。graph.scrollToCell() → 正确方式:graph.centerCell(cell) 滚动并居中到指定 cellgraph.highlightCell() / graph.highlightNode() → 正确方式:通过 node.attr('body/stroke', '#f00') 或添加 CSS class 实现高亮Shape.Cylinder / Shape.Diamond 等不存在的内置 Shape → 用 'rect' + rx/ry 或 'polygon' 自定义Shape.Edge.define() / Shape.Node.define() → 正确方式:Graph.registerEdge() / Graph.registerNode()Shape.Group / Shape.Group.define() / new Shape.Group() → X6 3.x 的 Shape 命名空间没有 Group 导出(实际只有 Circle / Edge / Ellipse / HTML / Image / Path / Polygon / Polyline / Rect / TextBlock,运行时会报 Cannot read properties of undefined (reading 'define'))。父子分组的正确方式:直接 graph.addNode({ shape: 'rect', ... }) 创建一个普通节点作为父节点,再通过 parent.addChild(child) / parent.embed(child) 建立父子关系;或用 Graph.registerNode('my-group', { inherit: 'rect', markup: [...], attrs: {...} }) 注册一个自定义分组形状再 addNode({ shape: 'my-group' })。Embedding 当插件 import / new / graph.use(new Embedding(...)) → X6 3.x 没有 Embedding 插件类(运行时会报 Embedding is not a constructor)。节点嵌入是 Graph 构造选项:new Graph({ container, embedding: { enabled: true, findParent: 'bbox', frontOnly: false, validate: ({ child, parent }) => true } })。Hover 高亮通过 highlighting.embedding 配置,嵌入/解除嵌入事件为 node:embedding / node:embedded。history.batch() → 正确方式:graph.startBatch('custom'); ...; graph.stopBatch('custom'); 或 graph.batchUpdate(() => { ... })graph.defs / graph.svgDoc / document.createElementNS('...', 'linearGradient' | 'defs' | 'marker') → X6 3.x 不暴露 graph.defs / graph.svgDoc,运行时会报 Cannot read properties of undefined。正确方式:
fill 渐变:直接用 attrs 中的渐变对象语法(fill: { type: 'linearGradient', stops: [...] })const id = graph.defineGradient({ type: 'linearGradient', stops: [{ offset: 0, color: '#f00' }, { offset: 1, color: '#0f0' }] }),再在 marker 对象里写 fill: \url(#${id})``graph.defineMarker(options) / graph.defineFilter(options)graph.addNode / graph.addEdge / graph.fromJSON 调用,确保画布有可视内容。即使用户 query 只描述了交互(panning / mousewheel / 插件等)配置,也必须自行补 2~3 个示例节点 + 1 条边作为渲染载体,否则视觉验证会被判定为「白屏」。graph.centerContent()(或在画布需要随内容缩放时使用 graph.zoomToFit({ padding: 20, maxScale: 1 }))。X6 默认不会自动居中,缺失该调用会导致内容偏向左上角、视觉评分不通过。两者二选一,不可同时调用。panning + mousewheel + Selection rubberband)同时启用时,必须用 modifiers 错开触发条件(例如:panning 用 'shift',mousewheel 用 'ctrl',rubberband 留空)。禁止把 'mouseWheel' 放进 panning.eventTypes 同时又启用 mousewheel,两者会争抢滚轮事件。// 错误:独立插件包已废弃
import { Selection } from '@antv/x6-plugin-selection';
import { History } from '@antv/x6-plugin-history';
// 正确:从 @antv/x6 直接导入
import { Graph, Selection, History } from '@antv/x6';
const graph = new Graph({ container: 'container' });
graph.use(new Selection({ enabled: true, rubberband: true }));
graph.use(new History({ enabled: true }));
// 错误:3.x 不支持构造函数选项模式
const graph = new Graph({
container: 'container',
selecting: { enabled: true }, // ❌
snapline: { enabled: true }, // ❌
history: { enabled: true }, // ❌
});
// 正确:使用 graph.use() 注册插件
import { Graph, Selection, Snapline, History } from '@antv/x6';
const graph = new Graph({ container: 'container' });
graph.use(new Selection({ enabled: true }));
graph.use(new Snapline({ enabled: true }));
graph.use(new History({ enabled: true }));
// 错误:使用 CSS 属性名
attrs: {
body: {
'background-color': '#fff', // ❌
'border-radius': '6px', // ❌
}
}
// 正确:使用 SVG 属性名
attrs: {
body: {
fill: '#fff', // ✅ 背景色
rx: 6, // ✅ 圆角
ry: 6,
stroke: '#8f8f8f', // ✅ 边框色
strokeWidth: 1, // ✅ 边框宽度
}
}
// 错误:遗漏 container
const graph = new Graph({});
// 正确:container 必填
const graph = new Graph({ container: 'container' });
// 错误:端口无法连线
ports: {
items: [{ id: 'port1', group: 'out' }],
groups: {
out: { position: 'right', attrs: { circle: { r: 5 } } }
}
}
// 正确:设置 magnet: true
ports: {
items: [{ id: 'port1', group: 'out' }],
groups: {
out: { position: 'right', attrs: { circle: { r: 5, magnet: true, stroke: '#8f8f8f' } } }
}
}
// 错误:参数不是位置传递
graph.on('node:click', (node, e) => { ... });
// 正确:解构对象参数
graph.on('node:click', ({ node, e }) => { ... });
import { Graph } from '@antv/x6';
const graph = new Graph({
container: 'container',
background: { color: '#F2F7FA' },
});
const source = graph.addNode({
shape: 'rect',
x: 40,
y: 40,
width: 100,
height: 40,
label: 'Source',
attrs: {
body: { stroke: '#8f8f8f', strokeWidth: 1, fill: '#fff', rx: 6, ry: 6 },
},
});
const target = graph.addNode({
shape: 'rect',
x: 300,
y: 200,
width: 100,
height: 40,
label: 'Target',
attrs: {
body: { stroke: '#8f8f8f', strokeWidth: 1, fill: '#fff', rx: 6, ry: 6 },
},
});
graph.addEdge({
source,
target,
attrs: {
line: { stroke: '#8f8f8f', strokeWidth: 1 },
},
});
// 内容居中:所有节点/边添加完成后调用,使画布内容相对于容器居中显示
// 如需缩放以适应容器,使用 graph.zoomToFit({ padding: 20, maxScale: 1 }) 替代
graph.centerContent();
| 场景 | 推荐配置 | 关键特性 |
|---|---|---|
| DAG 数据管道 | ports + orth router + connecting | 有向无环、端口连线 |
| ER 实体关系图 | HTML 节点 + er router | 表格式节点、字段展示 |
| 流程图/审批流 | 菱形判断节点 + 分支边 | 条件分支、多路径 |
| 组织架构图 | orth router + 树形布局 | 层级关系、折叠 |
| 血缘分析 | 左右布局 + smooth connector | 多层流转、端口 |
| 网络拓扑 | 圆形节点 + 星型结构 | 设备类型、连接状态 |
| 状态机 | 圆形节点 + 边标签 | 状态转换、事件触发 |
| shape | 形状 | 适用场景 |
|---|---|---|
rect | 矩形 | 通用节点、流程步骤 |
circle | 圆形 | 状态节点、端点 |
ellipse | 椭圆 | 通用强调 |
polygon | 多边形 | 菱形(判断)、六边形 |
text | 纯文本 | 标注、注释 |
image | 图片 | 图标节点 |
html | HTML | 富文本、表格式节点 |
| 类型 | 效果 | 适用场景 |
|---|---|---|
normal | 直线(默认) | 简单图 |
orth | 正交折线 | 流程图、DAG |
manhattan | 智能正交(绕障) | 复杂布局 |
metro | 地铁线风格 | 地铁图 |
er | ER 图专用 | 实体关系图 |
| 类型 | 效果 | 适用场景 |
|---|---|---|
normal | 直线段(默认) | 简单图 |
rounded | 圆角折线 | 流程图(推荐) |
smooth | 贝塞尔曲线 | 血缘图、关系图 |
jumpover | 跨线跳跃 | 复杂交叉 |
| 插件 | 注册方式 | 功能 |
|---|---|---|
| Selection | graph.use(new Selection({ enabled: true, rubberband: true })) | 框选节点 |
| Snapline | graph.use(new Snapline({ enabled: true })) | 对齐辅助线 |
| History | graph.use(new History({ enabled: true })) | 撤销/重做 |
| Clipboard | graph.use(new Clipboard({ enabled: true })) | 复制/粘贴 |
| Keyboard | graph.use(new Keyboard({ enabled: true })) | 快捷键绑定 |
| Scroller | graph.use(new Scroller({ enabled: true })) | 滚动画布 |
| MiniMap | graph.use(new MiniMap({ enabled: true, container })) | 小地图导航 |
| Transform | graph.use(new Transform({ resizing: { enabled: true }, rotating: { enabled: true } })) | 节点缩放/旋转 |
| Export | graph.use(new Export()) | 导出 PNG/SVG |
| Stencil | graph.use(new Stencil({ target: graph, groups: [...] })) | 侧边栏拖拽面板 |
| Dnd | graph.use(new Dnd({ target: graph })) | 拖拽创建节点 |
npx claudepluginhub antvis/chart-visualization-skills --plugin chart-visualizationGenerates G6 v5 graph visualization code for network, tree, and flow diagrams. Covers initialization, layout, interactions, plugins, and avoids v4 API pitfalls.
Creates custom React Flow nodes, edges, and handles with React components, TypeScript types, Tailwind styling, and registration. For building interactive node-based editors and diagrams.
Builds node-based graphs and workflow editors using @xyflow/react. Covers custom nodes, edges, handles, viewport control, and built-in node/edge types.