還記得剛進入資訊業沒多久就被指派要寫一個流程塑模器,當時為了實作它還需要拼命的尋找第三方現成的套件,評估到最後還要報告到總裁那邊去...經過這幾年的成長沒想到已經可以直接用jQuery+SVG再加上一些邏輯完成流程設計器的基本外框,自己都覺得感動.......
1.引用
jquery.js
jquery-ui.js
2.CSS
.body{
padding:0px;
margin:0px;
}
.node{
position: absolute;
}
.task{
font-size:12px;
border : 1px solid black;
border-radius : 8px;
width : 100px;
height: 60px;
line-height: 60px;
text-align: center;
background-color: #CFF0FB;
}
.event{
background-color: white;
width : 30px;
height: 30px;
border-radius : 30px;
}
.gateway {
width:60px;
height:60px;
}
.gateway .shape{
width: 40px;
height: 40px;
border : 1px solid black;
background-color: #CFF0FB;
margin-left:30px;
margin-top:17px;
}
.rotate-45{
-webkit-transform-origin: 0 100%;
-moz-transform-origin: 0 100%;
-o-transform-origin: 0 100%;
-ms-transform-origin: 0 100%;
transform-origin: 0 100%;
-webkit-transform:rotate(-45deg);
-moz-transform:rotate(-45deg);
-o-transform:rotate(-45deg);
-ms-transform:rotate(-45deg);
transform:rotate(-45deg);
}
.gateway .shape .exclusive-gateway{
font-size : 60px;
margin-top : -19px;
margin-left : 28px;
}
.start-event{
border : 2px solid black;
}
.end-event{
border : 6px solid black;
}
#diagram{
position: absolute;
top: 0px;
left: 0px;
}
#linkDiagram{
position: absolute;
top: 0px;
left: 0px;
}
3.HTML
<body>
<!--連接線的區塊 -->
<svg id="linkDiagram" width="1600" height="1600">
</svg>
<!--所有圖形元件的區塊-->
<div id="diagram" style="width:1600px; height:1600px;">
</div>
</body>
4.JS
$(function() {
//建立任務與事件
createEvent('startEvent_1', 50, 165 , 'start-event');
createTask('task_1', 200, 150, '發起者');
createTask('task_2', 350, 150, '直屬主管');
createGateway('exclusive_gateway_1', 500, 150, 'exclusive');
createTask('task_3', 650, 50, '總經理');
createTask('task_4', 850, 150, '財務群組');
createEvent('endEvent_1', 1050, 165 , 'end-event');
//建立連結線
createLink('startEvent_1', 'task_1');
createLink('task_1', 'task_2');
createLink('task_2', 'exclusive_gateway_1');
createLink('exclusive_gateway_1', 'task_3');
createLink('exclusive_gateway_1', 'task_4');
createLink('task_3', 'task_4');
createLink('task_4', 'endEvent_1');
});
// 將元件綁定拖曳等事件
function bindDragEvent(pNodeId){
$("#"+pNodeId).draggable({
start: function( event, ui ) {
},
stop: function( event, ui ) {
refreshLink();
},
drag:function( event, ui ) {
refreshLink();
},
grid: [ 5, 5 ]
}).mousedown(function(){
});
}
// 重新刷新線段邏輯
function refreshLink(){
for(var i=0; i<gAllLinks.length; i++){
var tLink = gAllLinks[i];
var tFormNodeId = tLink.formId;
var tToNodeId = tLink.toId;
drawLink(tFormNodeId, tToNodeId);
}
}
// 建立事件元件
function createEvent(pNodeId, pLeft, pTop, pType){
var tNode =
'<div class="event node '+pType+'" id="'+pNodeId+'">'+
'</div>';
tNode = $(tNode);
tNode.css({
'top': pTop + 'px',
'left' : pLeft + 'px'
});
$("#diagram").append(tNode);
bindDragEvent(pNodeId);
}
// 建立任務元件
function createTask(pNodeId, pLeft, pTop, pContent){
var tNode =
'<div class="node task" id="'+pNodeId+'">'+
pContent+
'</div>';
tNode = $(tNode);
tNode.css({
'top': pTop + 'px',
'left' : pLeft + 'px'
});
$("#diagram").append(tNode);
bindDragEvent(pNodeId);
}
// 建立閘道元件
function createGateway(pNodeId, pLeft, pTop, pType){
var tHtml = '';
if(pType === 'exclusive'){
tHtml = '<div class="exclusive-gateway rotate-45">+</div>';
}
var tNode =
'<div id="'+pNodeId+'" class="node gateway">'+
'<div class="shape rotate-45">'+
tHtml+
'</div>'+
'</div>';
tNode = $(tNode);
tNode.css({
'top': pTop + 'px',
'left' : pLeft + 'px'
});
$("#diagram").append(tNode);
bindDragEvent(pNodeId);
}
var gAllLinks = [];
// 建立連接線
function createLink(pFormNodeId, pToNodeId){
var tLineId = pFormNodeId+'_'+pToNodeId;
var tLine = document.createElementNS("http://www.w3.org/2000/svg", "polyline");
tLine = $(tLine);
tLine.attr({
id : tLineId
}).css({
'fill' : 'none',
'stroke' : 'black',
'stroke-width' : 1
});
$("#linkDiagram").append(tLine);
// 產生連接線的箭頭
var tArrowId = "arrow_"+tLineId;
var tArrow = document.createElementNS("http://www.w3.org/2000/svg", "polygon");
tArrow = $(tArrow);
tArrow.attr({
id : tArrowId
}).css({
'fill' : 'black'
});
$("#linkDiagram").append(tArrow);
drawLink(pFormNodeId, pToNodeId);
gAllLinks.push({
'formId' : pFormNodeId,
'toId' : pToNodeId
});
}
// 判斷連接線的模式
function findLineMode(pFormNode, pToNode){
var tFormNodeWidth = parseInt(pFormNode.css('width').replace('px',''));
var tFormNodeHeight = parseInt(pFormNode.css('height').replace('px',''));
var tFormNodeTop = parseInt(pFormNode.css('top').replace('px',''));
var tFormNodeLeft = parseInt(pFormNode.css('left').replace('px',''));
var tToNodeWidth = parseInt(pToNode.css('width').replace('px',''));
var tToNodeHeight = parseInt(pToNode.css('height').replace('px',''));
var tToNodeTop = parseInt(pToNode.css('top').replace('px',''));
var tToNodeLeft = parseInt(pToNode.css('left').replace('px',''));
var tMode = '';
if(tFormNodeLeft+tFormNodeWidth <= tToNodeLeft){
tMode = 'RIGHT'; //目標在右側
}else if (tToNodeLeft+tToNodeWidth <= tFormNodeLeft){
tMode = 'LEFT'; //目標在左側
}else if (tFormNodeLeft >= tToNodeLeft && tFormNodeLeft <= (tToNodeLeft+tToNodeWidth)){
if (tToNodeTop >= tFormNodeTop+tToNodeHeight){
tMode = 'BOTTOM'; //目標在下方
} else {
tMode = 'TOP'; //目標在上方
}
}else{
if (tToNodeTop + tToNodeHeight >= tFormNodeTop){
tMode = 'BOTTOM'; //目標在下方
} else {
tMode = 'TOP'; //目標在上方
}
}
return tMode;
}
// 劃出連接線
function drawLink(pFormNodeId, pToNodeId){
// 將元件與元件的關係區分四種情境
// 1.預連接的元件在此元件的右方 (RIGHT模式)
// 2.預連接的元件在此元件的左方 (LEFT模式)
// 3.預連接的元件在此元件的上方 (TOP模式)
// 4.預連接的元件在此元件的下方 (BOTTOM模式)
var tFormNode = $("#"+pFormNodeId);
var tToNode = $("#"+pToNodeId);
var tMode = findLineMode(tFormNode, tToNode);
var tStartPointObj;
var tEndPointObj;
//線的四個節點
var tStartPoint;
var tBreakPoint_1;
var tBreakPoint_2;
var tEndPoint;
//畫箭頭的三個節點
var tArrowPoint_1;
var tArrowPoint_2;
var tArrowPoint_3;
var tArrowSize = 10;
if(tMode === 'RIGHT' || tMode === 'LEFT'){
if(tMode === 'RIGHT'){
tStartPointObj = getRightPoint(tFormNode);
tEndPointObj = getLeftPoint(tToNode);
tArrowPoint_1 = tEndPointObj.left + ',' + tEndPointObj.top;
tArrowPoint_2 = (tEndPointObj.left-10) + ',' + (tEndPointObj.top+10);
tArrowPoint_3 = (tEndPointObj.left-10) + ',' + (tEndPointObj.top-10);
}else if(tMode === 'LEFT'){
tStartPointObj = getLeftPoint(tFormNode);
tEndPointObj = getRightPoint(tToNode);
tArrowPoint_1 = tEndPointObj.left + ',' + tEndPointObj.top;
tArrowPoint_2 = (tEndPointObj.left+10) + ',' + (tEndPointObj.top+10);
tArrowPoint_3 = (tEndPointObj.left+10) + ',' + (tEndPointObj.top-10);
}
tStartPoint = tStartPointObj.left + ',' + tStartPointObj.top;
tBreakPoint_1 = (tStartPointObj.left + (tEndPointObj.left - tStartPointObj.left)/2) + ',' + tStartPointObj.top;
tBreakPoint_2 = (tStartPointObj.left + (tEndPointObj.left - tStartPointObj.left)/2) + ',' + tEndPointObj.top;
tEndPoint = tEndPointObj.left + ',' + tEndPointObj.top;
} else if (tMode === 'TOP' || tMode === 'BOTTOM'){
if(tMode === 'TOP'){
tStartPointObj = getTopPoint(tFormNode);
tEndPointObj = getBottomPoint(tToNode);
tArrowPoint_1 = tEndPointObj.left + ',' + tEndPointObj.top;
tArrowPoint_2 = (tEndPointObj.left+10) + ',' + (tEndPointObj.top+10);
tArrowPoint_3 = (tEndPointObj.left-10) + ',' + (tEndPointObj.top+10);
}else if(tMode === 'BOTTOM'){
tStartPointObj = getBottomPoint(tFormNode);
tEndPointObj = getTopPoint(tToNode);
tArrowPoint_1 = tEndPointObj.left + ',' + tEndPointObj.top;
tArrowPoint_2 = (tEndPointObj.left+10) + ',' + (tEndPointObj.top-10);
tArrowPoint_3 = (tEndPointObj.left-10) + ',' + (tEndPointObj.top-10);
}
tStartPoint = tStartPointObj.left + ',' + tStartPointObj.top;
tBreakPoint_1 = tStartPointObj.left+ ',' + ( tEndPointObj.top + (tStartPointObj.top - tEndPointObj.top)/2 );
tBreakPoint_2 = tEndPointObj.left + ',' + ( tEndPointObj.top + (tStartPointObj.top - tEndPointObj.top)/2 );
tEndPoint = tEndPointObj.left + ',' + tEndPointObj.top;
}
var tLineId = pFormNodeId+'_'+pToNodeId;
var tLine = $("#"+tLineId);
tLine.attr({
points : tStartPoint + ' ' +tBreakPoint_1 + ' ' + tBreakPoint_2 + ' ' + tEndPoint
});
var tArrow = $("#arrow_"+tLineId);
tArrow.attr({
points : tArrowPoint_1 + ' ' +tArrowPoint_2 + ' ' + tArrowPoint_3
});
}
// 找出元件右側的中心點座標
function getRightPoint(pNode){
var tNodeWidth = parseInt(pNode.css('width').replace('px',''));
var tNodeHeight = parseInt(pNode.css('height').replace('px',''));
var tNodeTop = parseInt(pNode.css('top').replace('px',''));
var tNodeLeft = parseInt(pNode.css('left').replace('px',''));
return {
left : tNodeLeft + tNodeWidth,
top : tNodeTop + (tNodeHeight/2)
};
}
// 找出元件左側的中心點座標
function getLeftPoint(pNode){
var tNodeWidth = parseInt(pNode.css('width').replace('px',''));
var tNodeHeight = parseInt(pNode.css('height').replace('px',''));
var tNodeTop = parseInt(pNode.css('top').replace('px',''));
var tNodeLeft = parseInt(pNode.css('left').replace('px',''));
return {
left : tNodeLeft,
top : tNodeTop + (tNodeHeight/2)
};
}
// 找出元件上方的中心點座標
function getTopPoint(pNode){
var tNodeWidth = parseInt(pNode.css('width').replace('px',''));
var tNodeHeight = parseInt(pNode.css('height').replace('px',''));
var tNodeTop = parseInt(pNode.css('top').replace('px',''));
var tNodeLeft = parseInt(pNode.css('left').replace('px',''));
return {
left : tNodeLeft + (tNodeWidth/2),
top : tNodeTop
};
}
// 找出元件下方的中心點座標
function getBottomPoint(pNode){
var tNodeWidth = parseInt(pNode.css('width').replace('px',''));
var tNodeHeight = parseInt(pNode.css('height').replace('px',''));
var tNodeTop = parseInt(pNode.css('top').replace('px',''));
var tNodeLeft = parseInt(pNode.css('left').replace('px',''));
return {
left : tNodeLeft + (tNodeWidth/2),
top : tNodeTop + tNodeHeight
};
}