前言
这一小节来讲一下如何自定义布局,首先说下为什么要自定义布局,没错,就是因为 Antv 自带的层次布局并不能满足我们的需求,如下是采用 Antv 自带的层次布局实现的效果。
使用 Antv 自带的层次布局实现存在以下几点问题:
- 从图中可以明显看到有部分表重叠了
- 节点之间的距离有时也不相等
- 整个图并没有完全沿中轴线对称
所以为了解决上述的几个问题,我们需要自定义布局,先来看下我们自定义布局之后的效果
可见完美的解决了上述的三个问题,自定义布局有如下几个步骤:
1. 确定布局方式
我们还是采用层次布局,我们希望达到的效果由图一变为图二
图一
图二
2. 在构建节点时加入布局需要的参数
{
"id": key,
"key": key,
"label": key,
"x": 100,
"y": 100,
"level": level,
"order": order,
"attrs": attrs,
"size": [400, height]
}
其中 id、key、label、x、y、attrs、size 是节点必须要有的属性,level 和 order 是我们自定义布局需要的属性,level 用在实现分层,order 用来实现同层排序。
3. 编码实现
class CustomDagreLayout extends Base {
/** 布局的起始(左上角)位置 */
public begin: number[] = [0, 0];
/** 节点水平间距(px) */
public nodesep: number = 50;
/** 每一层节点之间间距 */
public ranksep: number = 50;
constructor(options?: DagreLayoutOptions) {
super();
this.updateCfg(options);
}
public getDefaultCfg() {
return {
nodesep: 50, // 节点水平间距(px)
ranksep: 50, // 每一层节点之间间距
begin: [0, 0], // 布局的起点位置
};
}
/**
* 执行布局
*/
public execute() {
const self = this;
const { nodes, edges, ranksep, nodesep, begin } = self;
if (!nodes) return;
const layerMap: Map<number, Node[]> = new Map();
nodes.forEach((item: any, index, arr) => {
if (!layerMap.has(item.level)) {
layerMap.set(
item.level,
arr.filter((node: any) => node.level === item.level)
);
}
});
// TODO 重新调整层级
const startX = begin[0];
const startY = begin[1];
const size = layerMap.size;
const maxWidth = size * nodeWidth + (size - 1) * ranksep;
const hr = Array.from(layerMap.values()).map((list: any[]) => {
const sum = list.reduce((pre: any, curr: any) => {
return pre + curr.size[1];
}, 0);
return sum + (list.length - 1) * nodesep;
});
const maxHeight = Math.max(...hr);
const offsetX = startX + maxWidth;
const offsetY = startY + maxHeight;
const centerLine = offsetY - maxHeight / 2;
layerMap.forEach((value, key) => {
let d = key === maxLevel ? size - 1 : key;
const x = offsetX - d * (nodeWidth + ranksep);
const y = centerLine + hr[d] / 2;
const sortNodes = value.sort((x: any, y: any) => y.order - x.order);
let preY = y;
sortNodes.forEach((e: any, index) => {
const { size } = e;
const margin = index === 0 ? 0 : nodesep;
preY = preY - size[1] - margin;
e.x = x;
e.y = preY;
});
});
if (self.onLayoutEnd) self.onLayoutEnd();
}
public getType() {
return 'lineageLayout';
}
}
export default CustomDagreLayout;
至此,自定义布局完结。