转:超级好⽤的流程图js框架
⽀叫(Graph Theroy)。利⽤图我们可以做很多⼯具,⽐如思维导图,流程图,状态机,组织架构图,等等。今天我要做的是⽤开源的HTML5⼯具来快速构造⼀个做图的⼯具。
⼯具选择
预先善其事,必先利其器。第⼀件事是选择⼀件合适的⼯具,开源时代,程序员还是很幸福的,选择很多。
最终,我选择了jsPlumb,因为它完全开源,使⽤很简单,⽤D3的话可能会多花很多功夫。joint.js也不错。⼤家可以根据⾃⼰的需要选择。构建静态应⽤
下⾯我们⼀步⼀步的来使⽤jsPlumb来创建我们的流程图⼯具。
第⼀步是等待DOM和jsPlumb初始化完毕,类似ady()和ady(), 要使⽤jsPlumb, 需要把代码放在这个函数⾥:
1 ady(function() {
// ... your code goes here ... }
创建⼀个jsPlumb的实例,并初始化jsPlumb的配置参数:
1 2 3 4 5 6 7 8 9 10 11 12 13//Initialize JsPlumb
var color = "#E8C870";
var instance = Instance({
// notice the 'curviness' argument to this Bezier curve.  the curves on this page are far smoother    // than the curves on the first demo, which use the default curviness value.
Connector : [ "Bezier", { curviness:50 } ],
DragOptions : { cursor: "pointer", zIndex:2000 },
PaintStyle : { strokeStyle:color, lineWidth:2 },
EndpointStyle : { radius:5, fillStyle:color },
HoverPaintStyle : {strokeStyle:"#7073EB"},
EndpointHoverStyle : {fillStyle:"#7073EB"},
Container:"container-id"
});
这⾥给给出了⼀些配置包括,连接线(这⾥配置了⼀个贝塞尔曲线),线的风格,连接点得风格。Container需要配置⼀个对应的DIV容器的id。(这⾥也可以使⽤setContainer的⽅法)
下⾯我们要创建⼀个节点(node),每⼀个节点可以⽤⼀个DIV来实现。我这⾥提供了⼀个函数来创建节点。
1
2 3 4 5 6 7 8 9 10 11function addNode(parentId, nodeId, nodeLable, position) {  var panel = d3.select("#"+ parentId);
panel.append('div').style('width','120px').style('height','50px')    .style('position','absolute')
.style('top',position.y).style('left',position.x)
.style('border','2px #9DFFCA solid').attr('align','center')
.
attr('id',nodeId).classed('node',true)
.text(nodeLable);
Selector('#'+ nodeId)[0];
}
这⾥做的事情就是创建了⼀个DIV元素,并放在对应的容器的制定位置上,注意为了⽀持拖拽的功能,必须使⽤position:absolute 。
我使⽤D3来操作DOM,⼤家可能会更习惯JQuery,这纯属个⼈喜好的问题。
最后返回创建节点的实例引⽤,这是的selector使⽤了Selector()⽅法,它和JQuery的selector是⼀样的,这样⽤的好处是你可以使⽤不同的DOM操作库,例如Vanilla
下⾯我使⽤⼀个函数来创建端点/锚点(anchor),锚点就是节点上的连接点,⽤于连接不同的节点。
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33function ad
dPorts(instance, node, ports, type) {
//Assume horizental layout
var number_of_ports = ports.length;
var i = 0;
var height = $(node).height();  //Note, jquery does not include border for height  var y_offset = 1 / ( number_of_ports + 1);
var y = 0;
for( ; i < number_of_ports; i++ ) {
var anchor = [0,0,0,0];
var paintStyle = { radius:5, fillStyle:'#FF8891'};
var isSource = false, isTarget = false;
if( type === 'output') {
anchor[0] = 1;
paintStyle.fillStyle = '#D4FFD6';
isSource = true;
} else{
isTarget =true;
}
anchor[1] = y + y_offset;
y = anchor[1];
instance.addEndpoint(node, {
Attribute("id") + "-"+ ports[i],
paintStyle: paintStyle,
anchor:anchor,
maxConnections:-1,
isSource:isSource,
isTarget:isTarget
});
}
}
instance是jsPlumb的实例
node是我们⽤addNode⽅法创建的Node实例
ports,是⼀个string的数组,指定端点的个数和名字
type,可能是output或者input,指定端点的种类,⼀个节点的输出端⼝可以连接另⼀个节点的输⼊端⼝。
这⾥anchor是⼀个四维数组,0维和1维分别是锚点在节点x轴和y轴的偏移百分⽐。我这⾥希望把端⼝画在节点的左右两侧,并按照端⼝的数量均匀分布。
最后使⽤instance.addEndpoint来创建端点。注意这⾥只要指定isSource和isTarget就可以⽤drag&drop的⽅式来连接端点,⾮常⽅便。
下⾯⼀步我们提供⼀个函数来连接端点:
jquery框架使用
1
2 3 4 5 6 7 8 9 10 11 12 13function connectPorts(instance, node1, port1, node2 , port2) {
// declare some common values:
var color = "gray";
var arrowCommon = { foldback:0.8, fillStyle:color, width:5 },
// use three-arg spec to create two different arrows with the common values:  overlays = [
[ "Arrow", { location:0.8 }, arrowCommon ],
[ "Arrow", { location:0.2, direction:-1 }, arrowCommon ]
];
var uuid_source = Attribute("id") + "-"+ port1;
var uuid_target = Attribute("id") + "-"+ port2;
14 15  t({uuids:[uuid_source, uuid_target]}); }
node1和node2是源节点和⽬标节点的引⽤,port1和port2是源端⼝和⽬标端⼝的名字。
使⽤t⽅法来创建连接。 overlays⽤来添加连接线的箭头效果或者其他风格,我这⾥没有使⽤,因为觉得都不是很好看。⼤家如果要⽤,只要把overlays加⼊到t的⽅法参数就可以了。
调⽤以上⽅法来创建节点,端点和连接线。
1 2 3 4 5 6 7var node1 = addNode('container-id','node1', 'node1', {x:'80px',y:'20px'}); var node2 = addNode('container-id','node2', 'node2', {x:'280px',y:'20px'});
addPorts(instance, node1, ['out1','out2'],'output');
addPorts(instance, node2, ['in','in1','in2'],'input');
connectPorts(instance, node1, 'out2', node2, 'in');
这⾥我们创建了两个节点,第⼀个节点有两个输出端⼝,第⼆个节点有三个输⼊端⼝,然后把第⼀个节点的out2端⼝连接到第⼆个端点的in 端⼝。效果如下:
最后我们给节点增加drag&drop的功能,这样我们就可以拖动这些节点来改变图的布局了。
1instance.draggable($('.node'));
这⾥似乎依赖于JQuery-UI,我还不是很清楚。
交互式创建节点
我们已经初步具有了创建图的功能,可是节点的创建必须通过程序,我们希望⽤交互的⽅式来创建节点。
我们先创建⼀个tree view:
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19function getTreeData() {
var tree = [
{
text: "Nodes",
nodes: [
{
text: "Node1",
},
{
text: "Node2"
}
]
}
];
return tree;
}
//Initialize Control Tree View
$('#control-panel').treeview({data: getTreeData()});
树上有两个节点:
然后我实现从树上拖拽对应的节点,到流程图上的逻辑。1
2
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27//Handle drag and drop
$('.list-group-item').attr('draggable','true').on('dragstart', function(ev){
//ev.dataTransfer.setData("text", ev.target.id);
console.log('drag start');
});
$('#container-id').on('drop', function(ev){
//avoid event conlict for jsPlumb
if(ev.target.className.indexOf('_jsPlumb') >= 0 ) {
return;
}
ev.preventDefault();
var mx = ''+ ev.originalEvent.offsetX + 'px';
var my = ''+ ev.originalEvent.offsetY + 'px';
console.log('on drop : '+ ev.Data('text'));  var uid = new Date().getTime();
var node = addNode('flow-panel','node'+ uid, 'node', {x:mx,y:my});
addPorts(instance, node, ['out'],'output');
addPorts(instance, node, ['in1','in2'],'input');
instance.draggable($(node));
}).on('dragover', function(ev){
ev.preventDefault();
console.log('on drag over');
});
这⾥要注意的是要避免和jsPlumb拖拽端点的逻辑冲突,当检测到target是jsPlumb对象是需要直接从drop⽅法中退出以执⾏对应的jsPlumb 的drop逻辑。
好了,⼀个绘制流程图的软件⼯具初步完⼯。

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。