蓝凌流程引擎流程图格式化实现原理全解
在开发流程应用系统的过程中,流程图格式化是最基础且关键的一步。本文将全面分析 JavaScript 流程图格式化的实现原理,分段解释它如何利用层级和宽度进行布局,并最终实现节点和连线的美观排列。
一、格式化总体思路
想要实现自动排列流程图节点,需要分三步进行计算:
- 计算节点层级 Level
- 计算节点布局宽度 Wide
- 根据层级和宽度,确定节点坐标 x/y
- 根据节点坐标,计算连线抵达路径
二、层级计算原理 (Level)
层级描述节点在流程中所处的第几层,通常以 DFS 递归的方式得出:
- 开始节点设为 level = 0
- 对每个出线节点向下递归,level + 1
- 如果遇到回路,别递归,标记 IsInCycle = true
**目的:**确定每个节点置于终端流程线路的第几层

三、节点宽度计算 (Wide)
宽度是指一个节点在横向属于它的子节点总布局需要的平面空间。
主要原理:
- 普通节点:Wide = 节点默认宽度
- splitNode:按照子节点数 * 单节点宽度
- joinNode:同理,往上逆向传播
/**********************************************************功能:计算最大占宽入线/出线的最大占宽>=并行分支占宽(取大的一方),若再次遇到并行分支,跳到对应结束/开始并行分支节点避免向上/下将宽度传给所遇分支内的节点**********************************************************/
function FlowChart_calcWide(node,type,number){if(type=="up"){if(node.LineIn.length>0){for(var i=0;i<node.LineIn.length;i++){var startNode = node.LineIn[i].StartNode;while(startNode.Type == 'joinNode'){startNode.Wide = startNode.Wide<FlowChartObject.Nodes.Wide*number?FlowChartObject.Nodes.Wide*number:startNode.Wide;var splitNode = FlowChartObject.Nodes.GetNodeById(startNode.Data.relatedNodeIds);splitNode.Wide = splitNode.Wide<FlowChartObject.Nodes.Wide*number?FlowChartObject.Nodes.Wide*number:splitNode.Wide;startNode = splitNode.LineIn[0].StartNode;}startNode.Wide = startNode.Wide<FlowChartObject.Nodes.Wide*number?FlowChartObject.Nodes.Wide*number:startNode.Wide;FlowChart_calcWide(startNode,type,number);}}}else{if(node.LineOut.length>0){for(var i=0;i<node.LineOut.length;i++){var endNode = node.LineOut[i].EndNode;while(endNode.Type == 'splitNode'){endNode.Wide = endNode.Wide<FlowChartObject.Nodes.Wide*number?FlowChartObject.Nodes.Wide*number:endNode.Wide;var joinNode = FlowChartObject.Nodes.GetNodeById(endNode.Data.relatedNodeIds);joinNode.Wide = joinNode.Wide<FlowChartObject.Nodes.Wide*number?FlowChartObject.Nodes.Wide*number:joinNode.Wide;endNode = joinNode.LineOut[i].EndNode;}endNode.Wide = endNode.Wide<FlowChartObject.Nodes.Wide*number?FlowChartObject.Nodes.Wide*number:endNode.Wide;FlowChart_calcWide(endNode,type,number);}}}
}

四、节点坐标计算
根据 层级 Level 和 宽度 Wide 确定 X/Y
var x = parent.x - (parent.Wide - node.Wide)/2 + offset;
var y = baseTop + node.Level * verticalSpacing;
- **x 座标:**在父节点左侧居中排列
- **y 座标:**根据层级 Level 线性增长
- 初始 baseTop = 60, verticalSpacing = 80
五、连线路径计算
**目标:**使连线不交叉,地方明确,回路区分显著
基本思路
- 第一点:出发节点口
- 第二点:线垂直向下/上错差
- 第三点:接近节点输入口
psStart[0] = node.GetPointByPosition("3");
psStart[1] = { x: ..., y: ... }
psStart[2] = nextNode.GetPointByPosition("1");
回路连线设计:
- 如果 nextNode.IsInCycle == true
- 设置为虚线 + 曲线路径
六、实际应用
- 每个节点的实际宽度 = 120 px
- 逻辑宽度 = 160 px (Margin + Padding)
- 实际 x 坐标 = 添加应用 offset
七、总结
通过层级 + 宽度 的分析,配合 DFS 阻止回路,可以在最简单的前端开发环境下,实现美观、通用、高效的流程图格式化系统。
格式化后流程图:
