我正在参加「掘金·启航计划」
系列文章:
- 手摸手使用G6实现(轻)图编辑应用系列-初识G6
- 手摸手使用G6实现(轻)图编辑应用系列-自定义节点
- 手摸手使用G6实现(轻)图编辑应用系列-自定义边、箭头
- 手摸手使用G6实现(轻)图编辑应用系列-自定义行为
- 手摸手使用G6实现(轻)图编辑应用系列-后续优化
需求:对数据进行拓扑展示,简单的交互效果;支持编辑、查看节点间关系
简单效果展示
实现功能时,遇到许多问题,将官方文档、github翻阅了无数次;想着通过这个系列demo实现进行记录,也避免其他人重复犯错,节约时间
主要分两个区域:物料区、画布区;支持切换查看、编辑模式;编辑模式可从物料区拖拽物料,进行布局以及连线建立关系,建立关系时可指定箭头指向;无连线的节点可通过删除回到物料区;
框架比较
目前 AntV 有两个关于图的库:G6、X6(详细区别)
- G6
- 探索数据、获得洞察以及其他的辅助能力
- 底层canvas、svg都支持(可选),大量节点优选
- 节点内部图元复杂的优先
- X6
- 创建、编辑数据样式与形状
- 底层基于svg,200以内节点的图编辑场景优选
- 节点内部图元简单的优先
场景:节点数量较大,节点间线数量无限制;节点内图元实现时有十个,并且对于图编辑的需求比较轻,调研发现基于G6的扩展机制可满足需求,最终决定使用G6实现
实现过程
- 数据处理
- 自定义节点
- 自定义线、箭头
- 节点、线的交互
- 自定义行为
- 辅助插件使用
- 后续优化
数据处理
- G6支持的数据格式
<script>
const graphData = {
// 点集
nodes: [
{
id: 'node1', // 节点的唯一标识
x: 100, // 节点横坐标
y: 200, // 节点纵坐标
label: '起始点', // 节点文本
},
{
id: 'node2',
x: 300,
y: 200,
label: '目标点',
},
],
// 边集
edges: [
// 表示一条从 node1 节点连接到 node2 节点的边
{
source: 'node1', // 起始点 id
target: 'node2', // 目标点 id
label: '我是连线', // 边的文本
},
],
};
</script>
- 利用样式优先级实现每个线不同的箭头指向(数据样式 > 全局图样式)
graphData.edges.forEach(item =>{
item.style = {
startArrow: item.direction === 'double' ? arrowStyle['startArrow'][item.type] : false,
endArrow: item.direction === 'double' ? arrowStyle['endArrow'][item.type]: item.direction === 'single' ? arrowStyle['endArrow'][item.type]:false,
}
})
- 针对节点间线类型处理,使用G6.Util.processParallelEdges公共方法处理
- 使用
G6.Util.processParallelEdges(graphData.edges, 30, 'customQuadratic', 'customLine', 'customLoop');
- 目的
- 改变边数据上的类型,以及获取到对应类型相应的配置字段
- 入参
- 边数组
- 平行边间的距离 - offsetDiff
- 节点间存在多条边的边类型
- 节点间存在一条边的边类型
- 自环边的边类型
- 处理过程
- 将边数组转换为 key 为源-目标节点 val 为 edge 对象数组的 edgeMap
- 找出节点间 startPoint、 endPoint 相反的 edge
- 遍历 edgeMap
- 节点间存在多条边,将指定边类型赋值到 edge 对象上;
- 主要是计算边的控制点
- curveOcffset 值(控制点距离两端点连线的距离)
- curvePosition 弯曲的位置使用默认的0.5
- 保证总边数奇数边,有一个边 curveOcffset 为 0,其余的边基于 offsetDiff * n * (k % 2 === 0 ? 1 : -1) 进行计算,此外与两端点连接的直线方向相反的需要 * -1
- 总边数偶数边,除没有两端点连接的直线,其余的同上
- 主要是计算边的控制点
- 节点间存在一条边,将指定边类型赋值到 edge 对象上
- 自环边,将指定边类型赋值到 edge 对象上;按照固定的 8 个方向划边,每超过8自环边,自环的曲度增加20
- 节点间存在多条边,将指定边类型赋值到 edge 对象上;
- 创建图实例
graph = new G6.Graph({
container,
width,
height,
modes: {
default: ['drag-canvas', 'zoom-canvas'],
},
// 默认节点样式
defaultNode: styleObj.defaultNode,
// 默认线样式
defaultEdge: styleObj.defaultEdge,
});
- 加载并渲染数据
graph.data(graphData);
graph.render();
结尾
截至到此,数据处理完毕,通过创建Graph实例可渲染出拓扑关系,下一节会介绍实现自定义节点的步骤
希望看完的朋友可以点个喜欢/关注,您的支持是对我最大的鼓励