概述
分层布局弄好之后,每一层的节点高低的绝对地位根本确定了,咱们最简略的形式,就是每一层平铺所有节点,节点宽度固定以及间距固定,每个节点的地位根本就确定了。咱们只有基于这些节点进行连线就能够了。
计划思路
直线形式
最简略的形式就是直线,咱们只有确定两个节点之间的绝对具体坐标,而后两点之间画一条直线就能够了。成果如下
从上图能够看出,直线实现起来是比较简单的,然而节点很少的状况,还是能够勉强能够承受的,如果节点太多,线条简单,根本没方法看了,成果不是很好。
曲线形式
曲线是比拟常见的形式,这里能够用 3 次贝塞尔曲线或者 2 次贝塞尔曲线,算好相应的控制点即可。然而这些控制点不是很好算,并且通用的一些控制点,在一些简单状况下,展现的成果也不是很好,这里也没打算应用这个。
折线形式
就是通过折线,曼哈顿的形式,在空白的中央进行拐点,进行连线,这种形式能够很好利用空间,并且能够躲避节点,不会呈现 线和节点的互相遮蔽,这里我就采纳了这种形式。因为咱们的业务场景,节点比拟多,并且层级不是很多,这里我投机采纳了一种比较简单的形式来画这个折线。首先看下成果
在上述情况,咱们思考最简略的场景,就是最多用 4 个点来画这条折线,这种画法应该是最简略的。因为图是从上往下画的,咱们只有思考 两头节点 Y 坐标即可。然而要思考尽可能的不要让线条重叠。咱们能够把每层节点依照 x 坐标排序,而后划线的时候,依照 x 轴,从左到右的顺次划线,并且保障每条线没有重叠的局部。
具体实现
节点排序
function divideNodesIntoLayer(nodeList){
// 清空缓存的线段联合
nodeLines = {};
var lineNodes = {};
for(var layer = 1; layer <= maxGraphLayer; layer++){lineNodes[layer] = [];
for(var j = 0; j < nodeList.length; j++){if(nodeList[j].layer === layer){lineNodes[layer].push(nodeList[j]);
}
}
lineNodes[layer].sort(function(a, b){return a.x - b.x;})
}
return lineNodes;
}
从间隔上层节点 40px 的中央,往上遍历,每次减少一个步长,判断是否用重叠,直到找到一个不重叠的 Y 值为止。
function calcMidY(){
var midY = endY - 40;
while (true) {
var flag = false;
if (nodeLines[layer]) {for (var i = 0; i < nodeLines[layer].length; i++) {var line = nodeLines[layer][i];
if (line.startY === midY) {
// 判断是否重叠
if (checkCross(startX, endX, line.startX, line.endX)) {flag = true;}
}
if (flag) break;
}
} else {nodeLines[layer] = [];}
if (!flag) break;
midY -= lineDis;
}
if (startX !== endX) {
// 缓存曾经画的线段
nodeLines[layer].push({
startX: startX,
startY: midY,
endX: endX,
endY: midY
})
}
}
找到了拐点的 Y 值,整条折线的坐标都清晰了,依据 4 个点划线即可。
总结
以上是一种很简略的实现形式,遇到简单场景,还是会有线条的重叠,更精确的做法是要躲避所有阻碍,寻找门路,这种简单的做法,后续会进一步尝试。
本文由华为云公布