antv/G6和antv/X6

1. g6有combo,x6有 parent.addChild(child),实现combo效果,即node外面的框子;
2. g6有tooltip插件,x6用 删除 node,removeNode(node.id),新增 node graph.addNode(node),实现
共同的:er图布局,必须经过计算(暂未实现)

下面是在 X6 官网调试的demo 代码

import { Graph } from ‘@antv/x6’
import dagre from ‘dagre’
import { DagreLayout } from ‘@antv/layout’

const LINE_HEIGHT = 24
const NODE_WIDTH = 150

let nodes = [
{
id: 1,
shape: “er-rect”,
width: 60,
height: 40,
label: Child - 1,
zIndex: 1,
attrs: {
body: {
stroke: ‘none’,
fill: #211,
cursor: “auto”,
},
label: {
fill: ‘#fff’,
fontSize: 12,
cursor: “auto”,
},
},
},
{
id: 2,
shape: “er-rect”,
width: 60,
height: 40,
label: Child - 2,
zIndex: 1,
attrs: {
body: {
stroke: ‘none’,
fill: #422,
cursor: “auto”,
},
label: {
fill: ‘#fff’,
fontSize: 12,
cursor: “auto”,
},
},
},
{
id: 3,
shape: “er-rect”,
width: 60,
height: 40,
label: Child - 3,
zIndex: 1,
attrs: {
body: {
stroke: ‘none’,
fill: #633,
cursor: “auto”,
},
label: {
fill: ‘#fff’,
fontSize: 12,
cursor: “auto”,
},
},
},
{
id: 4,
shape: “er-rect”,
width: 60,
height: 40,
label: Child - 4,
zIndex: 1,
attrs: {
body: {
stroke: ‘none’,
fill: #844,
cursor: “auto”,
},
label: {
fill: ‘#fff’,
fontSize: 12,
cursor: “auto”,
},
},
},
{
id: 5,
shape: “er-rect”,
width: 60,
height: 40,
label: Child - 5,
zIndex: 1,
attrs: {
body: {
stroke: ‘none’,
fill: #1055,
cursor: “auto”,
},
label: {
fill: ‘#fff’,
fontSize: 12,
cursor: “auto”,
},
},
},
{
id: 6,
shape: “er-rect”,
width: 60,
height: 40,
label: Child - 6,
zIndex: 1,
attrs: {
body: {
stroke: ‘none’,
fill: #1266,
cursor: “auto”,
},
label: {
fill: ‘#fff’,
fontSize: 12,
cursor: “auto”,
},
},
},
{
id: 7,
shape: “er-rect”,
width: 60,
height: 40,
label: Child - 7,
zIndex: 1,
attrs: {
body: {
stroke: ‘none’,
fill: #1477,
cursor: “auto”,
},
label: {
fill: ‘#fff’,
fontSize: 12,
cursor: “auto”,
},
},
},
{
id: 8,
shape: “er-rect”,
width: 60,
height: 40,
label: Child - 8,
zIndex: 1,
attrs: {
body: {
stroke: ‘none’,
fill: #1688,
cursor: “auto”,
},
label: {
fill: ‘#fff’,
fontSize: 12,
cursor: “auto”,
},
},
},
{
id: 9,
shape: “er-rect”,
width: 60,
height: 40,
label: Child - 9,
zIndex: 1,
attrs: {
body: {
stroke: ‘none’,
fill: #1899,
cursor: “auto”,
},
label: {
fill: ‘#fff’,
fontSize: 12,
cursor: “auto”,
},
},
}
]

let edges = [
{
“id”: “edge1”,
“shape”: “edge”,
“source”: {
“cell”: “1”,
},
“target”: {
“cell”: “2”,
},
“attrs”: {
“line”: {
“stroke”: “#A2B1C3”,
“strokeWidth”: 2
}
},
“zIndex”: 1
},
{
“id”: “edge2”,
“shape”: “edge”,
“source”: {
“cell”: “3”,
},
“target”: {
“cell”: “4”,
},
“attrs”: {
“line”: {
“stroke”: “#A2B1C3”,
“strokeWidth”: 2
}
},
“zIndex”: 1
},
{
“id”: “edge3”,
“shape”: “edge”,
“source”: {
“cell”: “5”,
},
“target”: {
“cell”: “4”,
},
“attrs”: {
“line”: {
“stroke”: “#A2B1C3”,
“strokeWidth”: 2
}
},
“zIndex”: 1
},
{
“id”: “edge4”,
“shape”: “edge”,
“source”: {
“cell”: “4”,
},
“target”: {
“cell”: “6”,
},
“attrs”: {
“line”: {
“stroke”: “#A2B1C3”,
“strokeWidth”: 2
}
},
“zIndex”: 1
},
{
“id”: “edge5”,
“shape”: “edge”,
“source”: {
“cell”: “6”,
},
“target”: {
“cell”: “7”,
},
“attrs”: {
“line”: {
“stroke”: “#A2B1C3”,
“strokeWidth”: 2
}
},
“zIndex”: 1
},
{
“id”: “edge6”,
“shape”: “edge”,
“source”: {
“cell”: “7”,
},
“target”: {
“cell”: “8”,
},
“attrs”: {
“line”: {
“stroke”: “#A2B1C3”,
“strokeWidth”: 2
}
},
“zIndex”: 1
},
{
“id”: “edge7”,
“shape”: “edge”,
“source”: {
“cell”: “7”,
},
“target”: {
“cell”: “9”,
},
“attrs”: {
“line”: {
“stroke”: “#A2B1C3”,
“strokeWidth”: 2
}
},
“zIndex”: 1
}
]

let parentNode = {
id: ‘parent’,
x: 10,
y: 10,
width: 20,
height: 20,
zIndex: 0,
label: ‘Parent’,
attrs: {
body: {
fill: ‘#fffbe6’,
stroke: ‘#ffe7ba’,
cursor: “pointer”,
},
label: {
fontSize: 12,
cursor: “pointer”,
},
text: {
refX: 25,
refY: 10,
},
},
}

Graph.registerPortLayout(
‘erPortPosition’,
(portsPositionArgs) => {
return portsPositionArgs.map((_, index) => {
return {
position: {
x: 0,
y: (index + 1) * LINE_HEIGHT,
},
angle: 0,
}
})
},
true,
)

Graph.registerNode(
‘er-rect’,
{
inherit: ‘rect’,
markup: [
{
tagName: ‘rect’,
selector: ‘body’,
},
{
tagName: ‘text’,
selector: ‘label’,
},
],
attrs: {
rect: {
strokeWidth: 1,
stroke: ‘#5F95FF’,
fill: ‘#5F95FF’,
},
label: {
fontWeight: ‘bold’,
fill: ‘#ffffff’,
fontSize: 12,
},
},
ports: {
groups: {
list: {
markup: [
{
tagName: ‘rect’,
selector: ‘portBody’,
},
{
tagName: ‘text’,
selector: ‘portNameLabel’,
},
{
tagName: ‘text’,
selector: ‘portTypeLabel’,
},
],
attrs: {
portBody: {
width: NODE_WIDTH,
height: LINE_HEIGHT,
strokeWidth: 1,
stroke: ‘#5F95FF’,
fill: ‘#EFF4FF’,
magnet: true,
},
portNameLabel: {
ref: ‘portBody’,
refX: 6,
refY: 6,
fontSize: 10,
},
portTypeLabel: {
ref: ‘portBody’,
refX: 95,
refY: 6,
fontSize: 10,
},
},
position: ‘erPortPosition’,
},
},
},
},
true,
)

Graph.registerNode(
“tooltip-node”,
{
width: 600,
height: 450,
x: 200,
y: 50,
attrs: {
body: {
stroke: “#5F95FF”,
strokeWidth: 1,
fill: “rgba(204,204,204,0.2)”,
refWidth: 1,
refHeight: 1,
},
title: {
text: “Node”,
refX: 40,
refY: 14,
fill: “rgba(0,0,0,0.85)”,
fontSize: 12,
“text-anchor”: “start”,
},
text: {
text: “this is content text”,
refX: 40,
refY: 38,
fontSize: 12,
fill: “rgba(0,0,0,0.6)”,
“text-anchor”: “start”,
},
},
markup: [
{
tagName: “rect”,
selector: “body”,
},
{
tagName: “text”,
selector: “title”,
},
{
tagName: “text”,
selector: “text”,
},
],
},
true
);

const graph = new Graph({
container: document.getElementById(‘container’),
// embedding: {
// enabled: true,
// },
highlighting: {
embedding: {
name: ‘stroke’,
args: {
padding: -1,
attrs: {
stroke: ‘#73d13d’,
},
},
},
},
})

function getNodes(nodes) {
const parent = graph.addNode(parentNode)
nodes.forEach(item => {
if (item.shape !== ‘edge’) {
parent.addChild(graph.addNode(item))
}
})
}

function getEdges(edges) {
edges.forEach(item => {
if (item.shape == ‘edge’) {
if (item.source && item.target) {
graph.addEdge({
…item,
// source: item.source,
// target: item.target,
// attrs: item.attrs,
// normal 默认路由,原样返回路径点。
// orth 正交路由,由水平或垂直的正交线段组成。
// oneSide 受限正交路由,由受限的三段水平或垂直的正交线段组成。
// manhattan 智能正交路由,由水平或垂直的正交线段组成,并自动避开路径上的其他节点(障碍)。
// metro 智能地铁线路由,由水平或垂直的正交线段和斜角线段组成,类似地铁轨道图,并自动避开路径上的其他节点(障碍)。
// er 实体关系路由,由 Z 字形的斜角线段组成。
// router: “er”,
connector: {
// normal 简单连接器,用直线连接起点、路由点和终点。
// smooth 平滑连接器,用三次贝塞尔曲线线连接起点、路由点和终点。
// rounded 圆角连接器,用直线连接起点、路由点和终点,并在线段连接处用圆弧链接(倒圆角)。
// jumpover 跳线连接器,用直线连接起点、路由点和终点,并在边与边的交叉处用跳线符号链接。

        name: "normal",
        zIndex: 10000,
      },
    });
  }
}

})
}

// 布局方向
let dir = “LR”; // LR RL TB BT 竖排
// dir = ‘RL’ // LR RL TB BT 竖排
// dir = ‘TB’ // LR RL TB BT 横排
// dir = ‘BT’ // LR RL TB BT 横排
// 自动布局
function layout() {
const nodes = graph.getNodes();
const edges = graph.getEdges();
const g = new dagre.graphlib.Graph();
g.setGraph({ rankdir: dir, nodesep: 16, ranksep: 16 });
g.setDefaultEdgeLabel(() => ({}));
let width = 0;
let height = 0;
nodes.forEach((node, i) => {
if (node.id !== ‘parent’) {
width = 80;
height = 60;
g.setNode(node.id, { width, height });
}
});

edges.forEach((edge) => {
const source = edge.getSource();
const target = edge.getTarget();
g.setEdge(source.cell, target.cell);
});

dagre.layout(g);

graph.freeze();

g.nodes().forEach((id) => {
const node = graph.getCell(id);
if (node) {
const pos = g.node(id);
node.position(pos.x, pos.y);
}
});

edges.forEach((edge) => {
const source = edge.getSourceNode();
const target = edge.getTargetNode();
const sourceBBox = source.getBBox();
const targetBBox = target.getBBox();

if ((dir === "LR" || dir === "RL") && sourceBBox.y !== targetBBox.y) {
  const gap =
    dir === "LR"
      ? targetBBox.x - sourceBBox.x - sourceBBox.width
      : -sourceBBox.x + targetBBox.x + targetBBox.width;
  const fix = dir === "LR" ? sourceBBox.width : 0;
  const x = sourceBBox.x + fix + gap / 2;
  edge.setVertices([
    { x, y: sourceBBox.center.y },
    { x, y: targetBBox.center.y },
  ]);
} else if ((dir === "TB" || dir === "BT") && sourceBBox.x !== targetBBox.x) {
  const gap =
    dir === "TB"
      ? targetBBox.y - sourceBBox.y - sourceBBox.height
      : -sourceBBox.y + targetBBox.y + targetBBox.height;
  const fix = dir === "TB" ? sourceBBox.height : 0;
  const y = sourceBBox.y + fix + gap / 2;
  edge.setVertices([
    { x: sourceBBox.center.x, y },
    { x: targetBBox.center.x, y },
  ]);
} else {
  edge.setVertices([]);
}

});

graph.unfreeze();
}

getNodes(nodes);
getEdges(edges)
layout();

let ctrlPressed = false
const embedPadding = 20
let nodeId = [];

// 计算 parent node 的 width height
let flag = true;
if (flag) {
const nodes = graph.getNodes();
let x = 0;
let y = 0;
let parent = undefined;
nodes.forEach((node) => {
const element = node.getPosition();
if (element.x > x) {
x = element.x;
}
if (element.y > y) {
y = element.y;
}
if (node.id === “parent”) {
parent = node;
}
});

parent.prop(
{
size: { width: x + 100, height: y + 60 },
},
{ skipParentHandler: true }
);

flag = false;
}

// 递归往前找
function getPreEdges(id) {
edges.forEach(item => {
if (item.shape == ‘edge’) {
if (item.target.cell == id) {
item.attrs.line.stroke = ‘#0f0’;
getPreEdges(item.source.cell)
nodeId.push(item.source.cell)
}
}
})
}

// 递归往后找
function getNextEdges(id) {
edges.forEach(item => {
if (item.shape == ‘edge’) {
if (item.source.cell == id) {
item.attrs.line.stroke = ‘#0f0’;
getNextEdges(item.target.cell)
nodeId.push(item.target.cell)
}
}
})
}

graph.on(“edge:click”, ({ e, x, y, edge, view }) => {
// console.log(“edge:click”, edge.store.data);
const nodes = graph.getNodes();
nodes.forEach((node) => {
if (node.store.data.shape == “tooltip-node”) {
console.log(“node”, node.id, node.store.data.shape);
graph.removeNode(node.id);
}
});
graph.addNode({
x,
y: y > 330 ? 330 : y,
shape: “tooltip-node”,
});
});

graph.on(“node:click”, ({ e, x, y, node, view }) => {
if (node.store.data.id == “parent”) {
console.log(“node:click”, node.store.data);
} else {
node.store.data.attrs.body.fill = ‘#f00’;
// 从画布上删除点击的 node
// graph.removeNode(node.id)
// 递归改数据
nodeId.push(${node.id})
getPreEdges(node.id);
getNextEdges(node.id)
graph.clearCells(); // 清空画布所有的 node、edge
nodeId.forEach(ele => {
nodes.forEach(item => {
if (item.id == ele) {
item.attrs.body.fill = ‘#0f0’;
}
})
})
getNodes(nodes)
edges.forEach(item => {
graph.addEdge({
…item,
connector: {
name: “normal”,
},
});
})
// 重新布局
layout();
}
});

graph.on(‘node:change:size’, ({ node, options }) => {
if (options.skipParentHandler) {
return
}

const children = node.getChildren()
if (children && children.length) {
node.prop(‘originSize’, node.getSize())
}
})

graph.on(‘node:change:position’, ({ node, options }) => {
if (options.skipParentHandler || ctrlPressed) {
return
}

const children = node.getChildren()
if (children && children.length) {
node.prop(‘originPosition’, node.getPosition())
}

const parent = node.getParent()
if (parent && parent.isNode()) {
let originSize = parent.prop(‘originSize’)
if (originSize == null) {
originSize = parent.getSize()
parent.prop(‘originSize’, originSize)
}

let originPosition = parent.prop('originPosition')
if (originPosition == null) {
  originPosition = parent.getPosition()
  parent.prop('originPosition', originPosition)
}

let x = originPosition.x
let y = originPosition.y
let cornerX = originPosition.x + originSize.width
let cornerY = originPosition.y + originSize.height
let hasChange = false

const children = parent.getChildren()
if (children) {
  children.forEach((child) => {
    const bbox = child.getBBox().inflate(embedPadding)
    const corner = bbox.getCorner()

    if (bbox.x < x) {
      x = bbox.x
      hasChange = true
    }

    if (bbox.y < y) {
      y = bbox.y
      hasChange = true
    }

    if (corner.x > cornerX) {
      cornerX = corner.x
      hasChange = true
    }

    if (corner.y > cornerY) {
      cornerY = corner.y
      hasChange = true
    }
  })
}

if (hasChange) {
  parent.prop(
    {
      position: { x, y },
      size: { width: cornerX - x, height: cornerY - y },
    },
    { skipParentHandler: true },
  )
}

}
})

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值