一、前言
G6-Editor 是 AntV 官方提供的、专注于图可视化编辑器的类库,也是市面上完成度较高的图可视化编辑器。然而令人诟病的是其文档对新手极度不友好,我一度怀疑此文档只有他们自己开发人员才能看得懂,回首学习的过程里如同是漆黑中前行,苦不堪言。为了让后来者不重蹈覆辙,我愿化为萤火用微弱的光点指引前行的道路。
目标
1、组件使用
2、自定义节点
3、数据关联
4、自定义命令
开发环境
这里使用的框架是Vue,UI使用Element-ui,以及主角G6-Editor,建议clone文章最下面的github项目结合阅读。
二、进入实战
2.1Editor 是整个编辑器的主控类,其主要职责是将编辑器的各个组件协同起来。
用法:
import G6Editor from '@antv/g6-editor' const editor = new G6Editor() // 元素面板栏 Itempannel const itempannel = new G6Editor.Itempannel({ container: 'itempannel', }) // 工具栏 Toolbar const toolbar = new G6Editor. Toolbar({ container: 'toolbar', }) // 详细面板 Detailpannel const detailpannel = new G6Editor.Detailpannel ({ container: 'detailpannel' }) // 缩略图 Minimap const minimap = new G6Editor.Minimap({ container: 'minimap', height: 226, width: 226 }) // 组件挂载到Editor editor.add(page) editor.add(itempannel) editor.add(toolbar) editor.add(detailpannel) editor.add(minimap) 复制代码
Tips:先实例化组件,然后再挂载到Editor
△ 以上。

Toolbar 工具栏类,负责工具栏按钮的命令绑定、可用禁用状态控制。
G6-Editor内置了多种命令,亦可以自定义命令。
用法:
<!-- Toolbar -->
<div id="toolbar" class="toolbar"> <i data-command="delete" class="command el-icon el-icon-delete" title="删除"></i> <i data-command="zoomIn" class="command el-icon el-icon-zoom-in" title="放大"></i> <i data-command="save" class="command el-icon el-icon-upload" title="保存"></i> </div> 复制代码
// Command
const Command = G6Editor.Command
// 自定义Save命令
Command.registerCommand('save', { // 命令是否进入队列,默认是 true queue: false, // 命令是否可用 enable(eidtor) { return true }, // 正向命令 execute(eidtor) { // 获取当前page const page = this.editor.getCurrentPage() const data = page.save() console.log(data) }, // 快捷键:Ctrl+shirt+s shortcutCodes : [['ctrlKey', 'shiftKey', 's']] }) 复制代码
Tips:工具栏的控件标签必须要有 data-command="delete"
和 class="command"
否则无效。其中delete
为内置或自定义命令名称。
△ 以上。

用法:
<!-- 元素面板栏 -->
<div id="itempannel" class="ph left"> <div class="getItem" data-type="node" data-shape="flow-rect" data-size="120*48" data-label="常规节点" data-color="#1890FF"> <img draggable="false" src="https://gw.alipayobjects.com/zos/rmsportal/wHcJakkCXDrUUlNkNzSy.svg" alt="" srcset=""> </div> <div class="getItem" data-type="node" data-shape="flow-circle" data-size="72*72" data-label="起止节点" data-color="#FA8C16"> <img draggable="false" src="https://gw.alipayobjects.com/zos/rmsportal/ZnPxbVjKYADMYxkTQXRi.svg" alt="" srcset=""> </div> <div class="getItem" data-type="node" data-shape="flow-rhombus" data-size="80*72" data-label="分叉节点" data-color="#13C2C2"> <img draggable="false" src="https://gw.alipayobjects.com/zos/rmsportal/SnWIktArriZRWdGCnGfK.svg" alt="" srcset=""> </div> <div class="getItem" data-type="node" data-shape="flow-capsule" data-size="80*48" data-label="模型节点" data-color="#722ED1"> <img draggable="false" src="https://gw.alipayobjects.com/zos/rmsportal/rQMUhHHSqwYsPwjXxcfP.svg" alt="" srcset=""> </div> <!-- 注意!我跟别人不一样,我是自定义的节点 --> <div class="getItem" data-type="node" data-shape="customNode" data-size="80*48" data-label="我是自定义的" data-color="#722ED1"> <img draggable="false" src="https://user-gold-cdn.xitu.io/2019/3/15/169809645b016da6?w=114&h=128&f=png&s=1893" alt="" srcset=""> </div> </div> 复制代码
自定义节点
// 注意!这里不能使用new G6Editor.Flow()的形式,是无效的。
// 应使用 const Flow = G6Editor.Flow
const Flow = G6Editor.Flow
Flow.registerNode('customNode', { draw(item){ const group = item.getGraphicGroup() const model = item.getModel() group.addShape('text', { attrs: { x: 0, y: 0, fill: '#333', text: model.label } }) group.addShape('text', { attrs: { x: 0, y: 0, fill: '#333', text: ' ('+model.x+', '+model.y+') \n 原点是组的图坐标', textBaseline: 'top' } }) return group.addShape('rect', { attrs: { x: 0, y: 0, width: 100, height: 100, stroke: 'red' } }) } }) 复制代码
含class="getItem"
的元素会被当做节点,可以往编辑器拖动。
data-*
所有 * 都会被设置进添加图项的数据模型。
data-type
元素类型
data-shape
元素图形
data-size
元素大小
data-label
元素标签
data-color
元素颜色
draggable
禁止、启动拖拽默认行为
△ 以上。

用法:
<!-- 详细面板 -->
<div id="detailpannel" class="detailpannel"> <div data-status="node-selected" class="panel" id="node_detailpanel"> <div class="panel-title">属性详情</div> <div class="block-container"> <el-input v-model="nodeLabel" size="mini" @change="changeNodeLabel" placeholder="请输入内容"></el-input> </div> </div> </div> 复制代码
data-status
标识不同页面状态下,各个右键菜单容器的显示隐藏。
<div data-status="node-selected">节点属性栏</div> <div data-status="edge-selected">边属性栏</div> <div data-status="group-selected">群组属性栏</div> <div data-status="canvas-selected">画布属性栏</div> <div data-status="multi-selected">多选时属性栏</div> 复制代码
△ 以上。

<!-- 缩略图 -->
<div class="minimap"> <div class="panel-title">缩略图</div> <div id="minimap"></div> </div> 复制代码
这个倒没什么好说的,可以参考官方的文档和实例。
△ 以上。
高能预警! 最后一个也是最重要的一个!数据关联!!!不和业务数据关联的一切都是耍流氓。
还记得Detailpannel
属性栏类吗? 里面有一个输入框,事件绑定了changeNodeLabel
,用来修改节点的label
属性。
// methods changeNodeLabel(value) { const editor = this.editor // 执行命令 editor.executeCommand(() => { const page = editor.getCurrentPage() const selectedItems = page.getSelected() selectedItems.forEach(item => { // 更新属性 page.update(item.id, { label: value }) }) }) } 复制代码
另外选择节点,也需要将label
的值设置到输入框里。
// 获取当前page
const currentPage = editor.getCurrentPage()
// 监听选择变化
currentPage.on('afteritemselected', ev => { // 选择对象为Node节点 if (ev.item.isNode) { // 获取属性 const nm = ev.item.getModel() _this.nodeLabel = nm.label } // 选择对象为Edge节点 if (ev.item.isEdge) { // 获取属性 const nm = ev.item.getModel() _this.nodeLabel = nm.label } }) 复制代码
△ 以上。
后话
由于代码段都是比较零散,最好结合下面提供在github上的项目食用,如果文章对你有帮助,请随手一个赞或者收藏。另外文章写得仓促有很多不足的地方,希望大家轻拍,哈哈哈~
github仓库 :github.com/leeggco/g6-…