name: antv-x6-editor
description: X6 图编辑引擎代码生成技能,支持流程图、DAG、ER图、血缘图等图编辑场景的节点/边/端口/交互/插件配置
version: 3.x
X6 图编辑引擎代码生成技能
核心约束(必须遵守)
X6 3.x 关键约束(强制)
graph.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。未注册插件时对应方法不存在。
- 自定义 shape 必须先注册再使用:
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'); 包装。
初始化规范
节点操作规范
- 优先使用
graph.addNode() 逐个添加节点,而非 graph.fromJSON() 批量导入(除非用户明确要求批量加载数据)
- 内置 shape:
'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 },
}
- 禁止在 attrs 中使用 CSS 属性名(如
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 字段中
- 端口组:
ports: { groups: { groupName: { position, attrs, ... } }, items: [{ id, group }] }
- position 取值:
'top'、'bottom'、'left'、'right'
- 端口是连线的锚点,设置
attrs: { circle: { magnet: true } } 允许从端口拖出连线
- 必须设置
magnet: true 才能从该端口发起或接收连线
交互配置规范
- 连线交互在 Graph 配置中通过
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、Dnd
- 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: minimapContainer }))
- Transform:
graph.use(new Transform({ resizing: { enabled: true }, rotating: { enabled: true } }))
- Export:
graph.use(new Export())(注册后可调用 graph.toPNG() / graph.toSVG())
- 动态控制:
graph.enablePlugins('selection') / graph.disablePlugins('selection')
- 禁止在 Graph 构造函数中直接传入
selecting、snapline 等选项(3.x 不支持)
序列化规范
- 导出:
const data = graph.toJSON() 返回 { cells: [...] } 对象
- 导入:
graph.fromJSON(data) 加载整个图数据
- 清空:
graph.clearCells() 清除所有元素
- 禁止手动构造 cells 数组中的内部字段(如
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)
导入规范
- 所有使用到的类都必须出现在 import 语句中:如使用
Selection,必须 import { Graph, Selection } from '@antv/x6'
- 禁止使用
Graph.Selection、Graph.Keyboard 等命名空间写法(不存在)
- 禁止使用未导入的类:
new Selection(...) 必须对应 import { Selection } from '@antv/x6'
- import 自检清单(强制,输出代码前必须逐行核对):对代码中每一个
new XxxYyy(...) 调用,XxxYyy 必须字面出现在第一行 import { ..., XxxYyy } from '@antv/x6' 的花括号内。常见漏写:Selection、Keyboard、History、Clipboard、Snapline、MiniMap、Transform、Scroller、Export、Stencil、Dnd、Shape(用到 Shape.HTML.register 时)。
- import 漏写为何会变成
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 漏写的标准修法:把所有用到的插件类合并到同一行
import { Graph, Selection, Keyboard, History, ... } from '@antv/x6';,不要分多行 import,也不要遗漏。
节点工具(Tools)规范
- 添加工具:
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();
});
渐变色规范
- X6 attrs 中
fill 支持渐变对象语法,禁止直接操作 graph.defs 或 document.createElementNS 创建 SVG 渐变
- 线性渐变正确写法:
attrs: {
body: {
fill: {
type: 'linearGradient',
stops: [
{ offset: '0%', color: '#0000ff' },
{ offset: '100%', color: '#00ff00' },
],
},
},
}
代码输出规范
- 必须输出纯 JavaScript,禁止使用 TypeScript 语法(如
private、类型注解 : string、as 类型断言)
- HTML 自定义节点使用
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']);纯静态展示节点不要加 effect
- HTML 节点正确写法:
import { 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>' } });
Stencil 插件规范
- Stencil 通过
graph.use(new Stencil({ target: graph, groups: [...] })) 注册
- 注册后通过
graph.getPlugin('stencil') 获取实例,将 stencil.container 挂载到 DOM
- Stencil 内的节点模板使用
graph.createNode(...) 创建(非 graph.addNode),再通过 stencil.load(nodes, groupName) 加载
动态端口规范
- 使用
node.addPort() 动态添加端口时,必须在节点初始化时预定义对应的 ports.groups
- 如果没有预定义 group,端口无法正确定位,可能导致渲染异常
- 正确写法:
const 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 里已声明的重名)。
DOM/CSS 操作规范(HTML 节点 / Stencil / 自定义工具)
- HTML 节点
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'] = ...。
不存在的 API(禁止使用)
- 禁止
graph.scrollToCell() → 正确方式:graph.centerCell(cell) 滚动并居中到指定 cell
- 禁止
graph.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: [...] })
- 自定义 marker 需要渐变填充:先
const id = graph.defineGradient({ type: 'linearGradient', stops: [{ offset: 0, color: '#f00' }, { offset: 1, color: '#0f0' }] }),再在 marker 对象里写 fill: \url(#${id})``
- 自定义 marker / filter 同理使用
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 属性和 SVG 属性
// 错误:使用 CSS 属性名
attrs: {
body: {
'background-color': '#fff', // ❌
'border-radius': '6px', // ❌
}
}
// 正确:使用 SVG 属性名
attrs: {
body: {
fill: '#fff', // ✅ 背景色
rx: 6, // ✅ 圆角
ry: 6,
stroke: '#8f8f8f', // ✅ 边框色
strokeWidth: 1, // ✅ 边框宽度
}
}
❌ 缺少 container
// 错误:遗漏 container
const graph = new Graph({});
// 正确:container 必填
const graph = new Graph({ container: 'container' });
❌ 连接桩未设置 magnet
// 错误:端口无法连线
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 |
富文本、表格式节点 |
路由器与连接器
路由器(Router)— 决定边的路径走向
| 类型 |
效果 |
适用场景 |
normal |
直线(默认) |
简单图 |
orth |
正交折线 |
流程图、DAG |
manhattan |
智能正交(绕障) |
复杂布局 |
metro |
地铁线风格 |
地铁图 |
er |
ER 图专用 |
实体关系图 |
连接器(Connector)— 决定边的线条样式
| 类型 |
效果 |
适用场景 |
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 })) |
拖拽创建节点 |