antV X6 2.0 自定义节点

前序:X6 和 G6 是孪生兄弟,那x6和g6怎么去选择呢?
X6:X6更注重图编辑
拖拽加入节点、从锚点拖拽创建边、编辑边的形状、编辑过程中对齐等重图编辑应用,特别是流程图编辑,用 X6。
图编辑一般是在「编辑修改数据」或「从 0-1 搭建数据」的过程,所以图规模不会太大。X6 是基于 HTML 的, 所以使用者可以像写 HTML 一样去写一个节点,但是在较大规模的图上性能不佳。
G6:图可视化与分析应用的场景,用G6。
G6 是基于 Canvas 的,在较大规模图上可以保持流畅交互。在移动端,需要支持展示和简单交互,对性能的要求更高,因此这种需求优选 G6。

所以,如果你有大于 200 个节点的场景,优选 G6。如果你的数据量不大,但是组成一个节点的图元非常多(图元指一个图形,如矩形、文字等,在 HTML 里就是一个 DOM 节点),优选 G6。其余情况下的图编辑场景,X6 绝对首选。
AntV X6
不同于第一篇的基础文章,我们将从以下方面完整阐述一张自定义DAG图的形成过程:
选择哪种图实现我们的需求?
为何、如何自定义节点?
如何更好分配在画布中的布局?
事件传递过程?

1、选择那种图实现我们的需求?
在这里插入图片描述

首先基于前序中描述的场景,我们的节点量不会太多,并且节点中的功能包含了checkbox框复选、不同节点中内容不同、右键展开菜单栏,因此此场景下X6更优于G6。
使用G6去实现血缘图与DAG图同样也是可以的,但是因为我们使用的技术栈是vue3,G6文档中对于自定义节点的支持没那么好,也就是所我们想以单独组件的形式实现(例如在其中写antd的checkbox,下拉菜单)并没有x6友好(2023.3.9)
G6血缘关系图与DAG图:https://g6.antv.antgroup.com/design/template
G6树节点增加删除插件:https://g6.antv.antgroup.com/zh/examples/tree/customItemTree#customEdgeTree
G6(react)组件式自定义节点https://g6.antv.antgroup.com/manual/middle/elements/nodes/react-node
补充:版本问题
x6在2022-11-26发布了v2.0新的版本(连官网地址都换了),对当前文档中的功能影响不大,注册新节点的方法有所改动:
https://x6.antv.antgroup.com/tutorial/update#x6-vue-shape-%E4%BD%BF%E7%94%A8
版本:更新后我的版本是,注意:x6的大版本一定要与x6-vue-shape相同,也就是1.X对应1.X,2.X对应2.X
“@antv/layout”: “^0.3.17”,
“@antv/x6”: “^2.5.6”,
“@antv/x6-vue-shape”: “^2.0.10”,

2、为何、如何自定义节点?
为何自定义节点:
功能中包含了复杂元素与图片样式
hover效果:使用事件系统中提供的node:mouseenter和node:mouseleave 去改变节点的hover效果样式不符合预期,且出现了样式混乱的情况
文字过长展示…: 上一篇文章中实现了此功能,但是实现方式复杂度远超过预期,通过css去实现或许是更好的选择

安装:(1.x版本)
npm install @antv/x6-vue-shape

// 节点注册
import ‘@antv/x6-vue-shape’ // 导入vue-shape,不导入会报’vue-shape’ does not exist.
import Count from ‘Count.vue’ // 导入自定义组件

// 注册节点只能全局注册依次,所以我将注册放到了app.vue文件中
Graph.registerNode(“my-count”, {
inherit: “vue-shape”,
x: 200,
y: 150,
width: 150,
height: 100,
component: {
template: <Count />,
components: {
Count, // 自定义节点组件
},
},
});
报错:
不导入会报’vue-shape’ does not exist.:import ‘@antv/x6-vue-shape’
注册后依然会报错:Uncaught TypeError: Cannot read properties of undefined (reading ‘ToolItem’);,需要在vite.config.js中增加配置

// vite.config.js
resolve: {,
alias: [
{
find: ‘@antv/x6’,
replacement: ‘@antv/x6/lib’
},
{
find: ‘@antv/x6-vue-shape’,
replacement: ‘@antv/x6-vue-shape/lib’,
},
]
}

v2.0+版本更新:调整注册方式
https://x6.antv.antgroup.com/tutorial/update#x6-vue-shape-%E4%BD%BF%E7%94%A8
npm install @antv/x6-vue-shape

import { register } from ‘@antv/x6-vue-shape’
import Count from ‘Count.vue’ // 导入自定义组件

// 全局注册血缘关系节点(第二次注册会报错)
const initNode = () => {
register({
shape: ‘blood-node’,
width: 100,
height: 100,
component: BloodNode,
})
}

原理:在 SVG 中有一个特殊的 元素,在该元素中可以内嵌任何 XHTML 元素,所以我们可以借助该元素来渲染 HTML 元素和 vue 组件到需要位置。
在这里插入图片描述

Hello World

节点宽度问题: 对于节点宽度和高度,有两种需求 1、节点宽度需要根据内容宽度自适应,超出一定宽度展示...,这个操作相对比较复杂,我这里的实现依旧是第一篇文章中的在创建节点前确定文字的宽度,用...替换字符,然后根据宽度创建节点来实现 之所以要这么实现,是因为节点宽度决定了连接节点的箭头的位置,这个是在连线开始前就必须建立好的,如果单纯自定义节点中定义宽度改变不了节点外层包裹的宽度,连接点就会有空白或者超出 2、固定宽度,内部超出展示... 这种方案完美的适配了自定义节点,只需要在自定义节点中通过css控制样式即可

3、如何更好分配在画布中的布局?
上篇文章中提到过我是通过计算中心节点的左侧和右侧节点,分配每个节点的位置,这种方式可以实现需求,但是不够灵活。
因此我们利用布局的替换自己计算的方式:
// 安装布局:
npm install @antv/layout --save

// 导入布局方式
import { DagreLayout } from ‘@antv/layout’

// 数据结构为nodes(所有点)和edges(所有边)
const data: Model.FromJSONData = {
nodes: [],
edges: [],
}

// 边为点的id连接的二维数组
const edges = [
[‘1’, ‘2’],
[‘2’, ‘3’],
[‘2’, ‘4’],
[‘4’, ‘5’],
[‘4’, ‘6’],
[‘4’, ‘7’],
[‘4’, ‘8’],
[‘5’, ‘9’],
[‘6’, ‘10’],
[‘7’, ‘11’],
[‘8’, ‘12’],
]

const graph = new Graph({
container: document.getElementById(‘container’)!,
})

// 使用DagreLayout层次布局
const dagreLayout = new DagreLayout({
type: ‘dagre’,
rankdir: ‘LR’,
align: ‘UR’,
ranksep: 35,
nodesep: 15,
})
const model = dagreLayout.layout(data)

graph.fromJSON(model)
在这里插入图片描述

在这里插入图片描述

从配置项中我们可以看到可以修改:(我这里是需要中心左右布局,中心节点在中间,左右两侧分别展示上下游节点)
rankdir来调整layout方向,改为RL左右方向
align调整节点对齐方式,如果UL、UR、DL、DR都无法满足我们的布局方式,试试undefined
在这里插入图片描述

4、事件传递过程?
1、节点展示数据:从画布层传到节点层

// 画布层无需注入,节点层通过inject可以直接获取到注入的函数getNode
const nodeInfo = ref<any>()
let getNode: any = inject('getNode')
 
onMounted(() => {
  nodeInfo.value = getNode() // 获取到的数据如下图,其中data为传递过来的节点数据data
  console.log('000', getNode())
})
这样就可以将所有数据以nodeInfo的方式展示到页面中了

在这里插入图片描述

2、选择下拉菜单
在这里插入图片描述

我们的需求中会有邮件点击节点打开菜单项这样的需求:
方案1:文档中有提供事件系统,我们在画布层点击事件中绑定下拉菜单
https://antv-x6.gitee.io/zh/docs/tutorial/advanced/components
但是问题是 提供的下拉菜单等组件的插件是react,这就很尴尬了
方案2:在自定义节点中绑定一个下拉菜单,右键时触发此菜单,因为右键后操作的是页面数据,所以我们需要将数据传递到画布层
不可行:没有向上传递的事件,组件是在app.vue中注册,且上面并没有@emit或者其他可以传出的方法
方案3:给每个节点绑定事件系统:node:contextmenu,触发事件系统时,更新传入节点的数据(一个标志位openMenu:true)开启下拉菜单
不可行:可以开启菜单,但是无法包括所有关闭方式
方案4:事件系统中提供了一种节点层和画布层传递数据的方法
可行
https://antv-x6.gitee.io/zh/docs/tutorial/intermediate/events
在这里插入图片描述

// 节点层
let num = 0;
const addNode = () => {
nodeInfo.value.setData({
id: nodeInfo.value.id,
type: ‘add’,
num: num++, // 这里为什么需要num++呢,因为如果不修改数据,多次点击同一事件,画布层无法检测到data变化,就无法触发画布层change事件,所以只要让num改变就会触发更新
})
}

// 画布层
graphView.value.on(‘node:change:data’, (data) => {
console.log(‘11111’, data)
})
这样我们就可以在画布层拿到所有我们想要的事件了,

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值