最近在作一個需求,根據數據動態生成如下相似的流程圖,須要能夠設置每一個節點的顏色,每一個節點可添加點擊移動等相關的事件javascript
代碼中有作不少的註釋和說明,相關的文檔說明連接:https://9eb75i.axshare.comcss
drawFlowChart.jshtml
//畫全部的圖形:畫圖和畫對應的箭頭 function drawFlowChart(context,canvas,flowdata,initTop, initSpaceH){ //一、判斷是否有須要平均計算x的數據 flowdata.forEach(function(row){ if(row.isAverage){ row.data = calChartX(canvas.width,row.data, row.y); } }); //二、先要畫完全部的圖形 flowdata.forEach(function(row,rowIndex){ row.y = row.y ? row.y : ( rowIndex==0 ? initTop + initSpaceH : initTop + initSpaceH*rowIndex); row = drawRowChart(context, row); //畫圖形 }); //三、添加要指向的對象,必需要在畫完全部圖形以後 flowdata.forEach(function(row){ row.data.forEach(function(item){ if(item.arrowArr && item.arrowArr.length){ item.arrowArr.forEach(function(mItem){ mItem = addToObj(mItem,flowdata); }) } }) }); //四、給全部圖形畫上對應的畫箭頭,必需要在前兩步完成以後 flowdata.forEach(function(row,rowIndex){ row.data.forEach(function(item){ if(item.arrowArr && item.arrowArr.length){ drawSingleArrow(context,item);//畫箭頭 } }) }); //五、給全部元素添加點擊和懸浮事件 addMethod(canvas,flowdata) } //當一行有n個圖形而且須要平均排列時用此方法計算每一個圖形的x function calChartX(canvasW,data, dataY){ var startW = 80; var stepW = 120; var CondW = 30; var count = 0; for(var i=0;i<data.length;i++){ if(data[i].type == 'Step'){ count += stepW; }else if(data[i].type == 'Start' || data[i].type == 'End'){ count += startW; }else if(data[i].type == 'Condition'){ count += CondW; } } //spaceW 計算一行中每一個圖形的平均間距 var spaceW = parseInt((canvasW - count)/(data.length+1)); //計算座標x var prevDiv = [], curW = 0; for(var i=0;i<data.length;i++){ if(data[i].type == 'Step'){ prevDiv.push(stepW); curW = stepW/2; }else if(data[i].type == 'Start' || data[i].type == 'End'){ prevDiv.push(startW); curW = startW/2; }else if(data[i].type == 'Condition'){ prevDiv.push(CondW); curW = CondW/2; } var preLength = 0; for(var j=0;j<i;j++){ preLength += prevDiv[j]; } var x = spaceW*(i+1)+preLength+curW; var y = data[i].y; data[i]['x'] = x; data[i]['y'] = y ? y : dataY; } return data; } //生成每列對應的圖形 function drawRowChart(context, row){ row.data.forEach(function(item,index){ var s = null; item.y = item.y ? item.y : row.y; if(item.type == 'Step'){ s = new Step(context,item.x,item.y,item); }else if(item.type == 'Condition'){ s = new Condition(context,item.x,item.y,item); }else if(item.type == 'End'){ s = new End(context,item.x,item.y,item); }else if(item.type == 'Start'){ s = new Start(context,item.x,item.y,item); } item.chartObj = s; }) return row; } //繪製單個的圖形 function drawSingleChart(context,item){ var s = ''; if(item.type == 'Step'){ s = new Step(context,item.x,item.y,item); }else if(item.type == 'Condition'){ s = new Condition(context,item.x,item.y,item); }else if(item.type == 'End'){ s = new End(context,item.x,item.y,item); }else if(item.type == 'Start'){ s = new Start(context,item.x,item.y,item); } item.chartObj = s; return item; } //每一個對象的座標範圍 function calRange(obj){ var newObj = { minX:obj.x-obj.w/2, maxX:obj.x+obj.w/2, minY:obj.y-obj.h/2, maxY:obj.y+obj.h/2 } return newObj; } //處理每個箭頭須要指向的對象 function addToObj(arrObj,flowData){ flowData.forEach(function(rows){ rows.data.forEach(function(item){ if(item.name == arrObj.to){ arrObj.to = item.chartObj; } }) }) return arrObj; } //話每一個圖形的箭頭指向 function drawSingleArrow(context,data){ var step1 = data.chartObj; if(data.arrowArr && data.arrowArr.length){ data.arrowArr.forEach(function(item){ step1[item.arrow](item.to,context); }) } } //清除單個圖形 function repaintSingleChart(context,item){ var range = item.chartObj.range; //清除以前畫的圖形 context.clearRect(range.minX-1,range.minY-1,item.chartObj.w+2,item.chartObj.h+3); } //給全部圖形添加事件 function addMethod(canvas,flowData){ //給全部圖形添加點擊事件 canvas.onclick=function(e){ var curx = e.layerX; var cury = e.layerY; flowData.forEach(function(row,listIndex){ row.data.forEach(function(item){ var range = item.chartObj.range; if(curx>=range.minX && curx<=range.maxX && cury>=range.minY && cury<=range.maxY){ var clickMethod = null; if(row.method && row.method.onclick){ //若是每行定義了事件 //判斷每一個元素是否有單獨定義事件,若是有,取改元素定義的事件,若是沒有取每行定義的事件 if(item.method && item.method.onclick){ clickMethod = item.method.onclick }else{ clickMethod = row.method.onclick } }else{ if(item.method && item.method.onclick){ clickMethod = item.method.onclick }else{ clickMethod = null; } } if(clickMethod instanceof Function){ clickMethod(item); } } }) }); }; var timer = null; //給全部圖形添加mousemove事件 canvas.onmousemove=function(e){ var curx = e.layerX; var cury = e.layerY; clearTimeout(timer); flowData.forEach(function(row,listIndex){ row.data.forEach(function(item){ var range = item.chartObj.range; if(curx>=range.minX && curx<=range.maxX && cury>=range.minY && cury<=range.maxY){ var clickMethod = null; if(row.method && row.method.onmousemove){ //若是每行定義了事件 //判斷每一個元素是否有單獨定義事件,若是有,取改元素定義的事件,若是沒有取每行定義的事件 if(item.method && item.method.onmousemove){ clickMethod = item.method.onmousemove }else{ clickMethod = row.method.onmousemove } }else{ if(item.method && item.method.onmousemove){ clickMethod = item.method.onmousemove }else{ clickMethod = null; } } if(clickMethod instanceof Function){ timer = setTimeout(function(){ clickMethod(item); },1000) } } }) }); } //給全部圖形添加mouseleave事件 canvas.onmouseleave=function(e){ var curx = e.layerX; var cury = e.layerY; clearTimeout(timer); flowData.forEach(function(row){ row.data.forEach(function(item){ var range = item.chartObj.range; if(curx>=range.minX && curx<=range.maxX && cury>=range.minY && cury<=range.maxY){ var clickMethod = null; if(row.method && row.method.onmouseleave){ //若是每行定義了事件 //判斷每一個元素是否有單獨定義事件,若是有,取改元素定義的事件,若是沒有取每行定義的事件 if(item.method && item.method.onmouseleave){ clickMethod = item.method.onmouseleave }else{ clickMethod = row.method.onmouseleave } }else{ if(item.method && item.method.onmouseleave){ clickMethod = item.method.onmouseleave }else{ clickMethod = null; } } if(clickMethod instanceof Function){ clickMethod(item); } } }) }); } } /////////////////////////////////////////基本畫圖形start//////////////////////////////////////////////////////// //畫圓角矩形 function drawRoundRect(context, x, y, w, h, item, radius, tsw) { radius = radius || 20; context.beginPath(); context.arc(x + radius, y + radius, radius, Math.PI, Math.PI * 3 / 2); context.lineTo(w - radius + x, y); context.arc(w - radius + x, radius + y, radius, Math.PI * 3 / 2, Math.PI * 2); context.lineTo(w + x, h + y - radius); context.arc(w - radius + x, h - radius + y, radius, 0, Math.PI * 1 / 2); context.lineTo(radius + x, h +y); context.arc(radius + x, h - radius + y, radius, Math.PI * 1 / 2, Math.PI); context.closePath(); context.fillStyle = item.color ? (item.color.bgColor ? item.color.bgColor: 'rgba(91,155,213,0.5)'):'white' ; //背景顏色 context.fill(); context.strokeStyle= item.color ? (item.color.borderColor ? item.color.borderColor: '#5B9BD5'):'#5B9BD5' ; //邊框顏色 context.font = 'normal 14px 微軟雅黑'; var textStyle = textSize('14px','微軟雅黑',item.text); tsw = tsw ? tsw : parseInt(textStyle.width/2); context.fillStyle = item.color ? (item.color.fontColor ? item.color.fontColor: '#5B9BD5'):'#5B9BD5' ; //文字顏色 context.fillText(item.text, x+w/2-tsw, y+h/2+6); context.stroke(); } //畫菱形 function drawRhombus(context,x, y, l, item) { context.beginPath(); context.moveTo(x, y + l); context.lineTo(x - l * 2, y); context.lineTo(x, y - l); context.lineTo(x + l * 2, y); context.closePath(); context.strokeStyle= item.color ? (item.color.borderColor ? item.color.borderColor: '#5B9BD5'):'#5B9BD5' ; //邊框顏色 context.fillStyle = item.color ? (item.color.bgColor ? item.color.bgColor: 'rgba(91,155,213,0.5)'):'white' ; //背景顏色 context.fill(); context.fillStyle = '#5B9BD5'; //文字顏色 context.font = 'normal 14px 微軟雅黑'; context.fillText(item.text, x, y+3); context.stroke(); } //計算文本的寬高 function textSize(fontSize,fontFamily,text){ var span = document.createElement("span"); var result = {}; result.width = span.offsetWidth; result.height = span.offsetHeight; span.style.visibility = "hidden"; span.style.fontSize = fontSize; span.style.fontFamily = fontFamily; span.style.display = "inline-block"; document.body.appendChild(span); if(typeof span.textContent != "undefined"){ span.textContent = text; }else{ span.innerText = text; } result.width = parseFloat(window.getComputedStyle(span).width) - result.width; result.height = parseFloat(window.getComputedStyle(span).height) - result.height; return result; } //Start 圓角矩形對象 function Start(context,x, y, item, h, w) { this.flag = 'start'; this.h = h || 40; this.w = w || 2 * this.h; this.x = x; this.y = y; this.text = item.text; this.range = calRange(this); drawRoundRect(context,x - this.w / 2, y - this.h / 2, this.w, this.h, item); } //End 圓角矩形對象 function End(context, x, y, item, h, w) { this.flag = 'end'; this.h = h || 40; this.w = w || 2 * this.h; this.x = x; this.y = y; this.text = item.text; this.range = calRange(this); drawRoundRect(context, x - this.w / 2, y - this.h / 2, this.w, this.h, item, 20, -2); } //Step 矩形對象 function Step(context,x, y, item) { this.flag = "step"; this.h = 40; this.w = 2 * 60; this.x = x; this.y = y; this.text = item.text; this.range = calRange(this); context.strokeStyle= item.color ? (item.color.borderColor ? item.color.borderColor: '#5B9BD5'):'#5B9BD5' ; //邊框顏色 context.strokeRect(x - this.w / 2, y - this.h / 2, this.w, this.h); context.fillStyle = item.color ? (item.color.bgColor ? item.color.bgColor: 'rgba(91,155,213,0.5)'):'white' ; //背景顏色 context.fillRect(x - this.w / 2, y - this.h / 2,this.w,this.h); context.fillStyle = item.color ? (item.color.fontColor ? item.color.fontColor: '#5B9BD5'):'#5B9BD5' ; //文字顏色 context.textAlign = 'center'; context.font = 'normal 14px 微軟雅黑'; if(item.text){context.fillText(item.text,x, y+4);} } //Condition 菱形對象 function Condition(context, x, y, item) { this.flag = "condition"; this.l = 30; this.h = 30; this.w = 30; this.x = x; this.y = y; this.text = item.text; this.range = calRange(this); drawRhombus(context,x, y, this.l, item); } /////////////////////////////////////////基本畫圖形end//////////////////////////////////////////////////////// ////////////////////////////////////////////畫箭頭///////////////////////////////////////////////////////////// //箭頭從start圓角矩形bottom——>top Start.prototype.drawBottomToTop = function(obj,context) { if(obj.flag == "step") { var arrow = new Arrow(this.x, this.y + this.h / 2, obj.x, obj.y - obj.h / 2); arrow.drawBottomToTop(context); } else if(obj.flag == "condition") { var arrow = new Arrow(this.x, this.y + this.h / 2, obj.x, obj.y - obj.l); arrow.drawBottomToTop(context); } } //箭頭從step矩形bottom——>right Step.prototype.drawBottomToRight = function(obj,context) { var arrow = null; if(obj.flag == "step") { arrow = new Arrow(this.x, this.y + this.h / 2, obj.x + obj.w / 2 , obj.y); } else if(obj.flag == "condition") { arrow = new Arrow(this.x , this.y + this.h / 2, obj.x + obj.l*2 , obj.y); }else if(obj.flag == "start" || obj.flag == "end"){ arrow = new Arrow(this.x , this.y + this.h / 2, obj.x + obj.w/2 , obj.y); } arrow.drawBottomToRight(context); } //箭頭從step矩形bottom——>left Step.prototype.drawBottomToLeft = function(obj,context) { var arrow = null; if(obj.flag == "step") { arrow = new Arrow(this.x, this.y + this.h / 2, obj.x - obj.w / 2 , obj.y); } else if(obj.flag == "condition") { arrow = new Arrow(this.x , this.y + this.h / 2, obj.x - obj.l*2 , obj.y); }else if(obj.flag == "start" || obj.flag == "end"){ arrow = new Arrow(this.x , this.y + this.h / 2, obj.x - obj.w/2 , obj.y); } arrow.drawBottomToRight(context); } //箭頭從step矩形bottom——>top Step.prototype.drawBottomToTop = function(obj,context) { if(obj.flag == "step") { var arrow = new Arrow(this.x, this.y + this.h / 2, obj.x, obj.y - obj.h / 2); arrow.drawBottomToTop(context); } else if(obj.flag == "condition") { var arrow = new Arrow(this.x, this.y + this.h / 2, obj.x, obj.y - obj.l); arrow.drawBottomToTop(context); } } //箭頭從step矩形right——>left Step.prototype.drawRightToLeft = function(obj,context) { var arrow = null; if(obj.flag == "step") { arrow = new Arrow(this.x + this.w / 2, this.y, obj.x - obj.w / 2 , obj.y); } else if(obj.flag == "condition") { arrow = new Arrow(this.x + this.w / 2, this.y, obj.x-obj.l*2 , obj.y); }else if(obj.flag == "start" || obj.flag == "end"){ arrow = new Arrow(this.x + this.w / 2, this.y, obj.x-obj.w/2 , obj.y); } arrow.drawLeftToRightOrRightToLeft(context); } //箭頭從Condition菱形Bottom——>top Condition.prototype.drawBottomToTop = function(obj,context) { if(obj.flag == "step") { var arrow = new Arrow(this.x, this.y + this.l, obj.x, obj.y - obj.h / 2); arrow.drawBottomToTop(context); } else if(obj.flag == "condition") { var arrow = new Arrow(this.x, this.y + this.l, obj.x, obj.y - obj.l); arrow.drawBottomToTop(context); } } //箭頭從Condition菱形right——>top Condition.prototype.drawRightToTop = function(obj, context) { if(obj.flag == "step") { var arrow = new Arrow(this.x + this.l * 2, this.y, obj.x, obj.y - obj.h / 2); arrow.drawLeftOrRightToTop(context); } else if(obj.flag == "condition") { var arrow = new Arrow(this.x + this.l * 2, this.y, obj.x, obj.y - obj.l); arrow.drawLeftOrRightToTop(context); } } //箭頭從Condition菱形left——>top Condition.prototype.drawLeftToTop = function(obj,context) { if(obj.flag == "step") { var arrow = new Arrow(this.x - this.l * 2, this.y, obj.x, obj.y - obj.h / 2); arrow.drawLeftOrRightToTop(context); } else if(obj.flag == "condition") { var arrow = new Arrow(this.x - this.l * 2, this.y, obj.x, obj.y - obj.l); arrow.drawLeftOrRightToTop(context); } } //箭頭從Condition菱形right——>left Condition.prototype.drawRightToLeft = function(obj,context) { if(obj.flag == "step") { var arrow = new Arrow(this.x + this.l * 2, this.y, obj.x - this.w / 2, obj.y); arrow.drawLeftToRightOrRightToLeft(context); } else if(obj.flag == "condition") { var arrow = new Arrow(this.x + this.l * 2, this.y, obj.x - this.l * 2, obj.y); arrow.drawLeftToRightOrRightToLeft(context); } } //箭頭從Condition菱形left——>right Condition.prototype.drawLeftToRight = function(obj, context) { if(obj.flag == "step") { var arrow = new Arrow(this.x - this.l * 2, this.y, obj.x + this.w / 2, obj.y); arrow.drawLeftToRightOrRightToLeft(context); } else if(obj.flag == "condition") { var arrow = new Arrow(this.x - this.l * 2, this.y, obj.x + this.l * 2, obj.y); arrow.drawLeftToRightOrRightToLeft(context); } } /////////////////////////////////////////畫箭頭start///////////////////////////////////// function Arrow(x1, y1, x2, y2) { this.x1 = x1; this.y1 = y1; this.x2 = x2; this.y2 = y2; this.tmpX1 = null; this.tmpY1 = null; this.tmpX2 = null; this.tmpY2 = null; this.color = "#5B9BD5"; } Arrow.prototype.setColor = function(color) { this.color=color; } /** * * @param {Object} x1起始點橫座標 * @param {Object} y1起始點縱座標 * @param {Object} x2結束點橫座標 * @param {Object} y2結束點縱座標 */ Arrow.prototype.setP = function(x1, y1, x2, y2) { this.x1 = x1; this.y1 = y1; this.x2 = x2; this.y2 = y2; } //第一個拐點 Arrow.prototype.setP1 = function(tmpX1,tmpY1) { this.tmpX1=tmpX1; this.tmpY1=tmpY1; } //第二個拐點 Arrow.prototype.setP2 = function(tmpX2,tmpY2) { this.tmpX2=tmpX2; this.tmpY2=tmpY2; } Arrow.prototype.drawBottomToTop = function(ctx) { if (this.x1 != this.x2) { this.setP1(this.x1,(this.y1+this.y2)/2); this.setP2(this.x2,(this.y1+this.y2)/2); this.draw(ctx); }else{ this.draw(ctx); } } Arrow.prototype.drawLeftOrRightToTop = function(ctx) { this.setP1(this.x2,this.y1); this.draw(ctx); } Arrow.prototype.drawLeftToRightOrRightToLeft = function(ctx) { if (this.y1 != this.y2) { this.setP1((this.x1+this.x2)/2,this.y1); this.setP2((this.x1+this.x2)/2,this.y2); this.draw(ctx); }else{ this.draw(ctx); } } Arrow.prototype.drawBottomToRight = function(ctx) { if (this.y1 != this.y2) { this.setP1(this.x1,this.y2); this.draw(ctx); }else{ this.draw(ctx); } } Arrow.prototype.draw = function(ctx) { // arbitrary styling ctx.strokeStyle = this.color; ctx.fillStyle = this.color; // draw the line ctx.beginPath(); ctx.moveTo(this.x1, this.y1); if(this.tmpX1 != null && this.tmpY1 != null && this.tmpX2 != null && this.tmpY2 != null) { ctx.lineTo(this.tmpX1, this.tmpY1); ctx.closePath(); ctx.stroke(); ctx.beginPath(); ctx.moveTo(this.tmpX1, this.tmpY1) ctx.lineTo(this.tmpX2, this.tmpY2); ctx.closePath(); ctx.stroke(); ctx.beginPath(); ctx.moveTo(this.tmpX2, this.tmpY2); ctx.lineTo(this.x2, this.y2); ctx.closePath(); ctx.stroke(); var endRadians = Math.atan((this.y2 - this.tmpY2) / (this.x2 - this.tmpX2)); endRadians += ((this.x2 >= this.tmpX2) ? 90 : -90) * Math.PI / 180; this.drawArrowhead(ctx, this.x2, this.y2, endRadians); } else if(this.tmpX1 != null && this.tmpY1 != null && this.tmpX2 == null && this.tmpY2 == null) { ctx.lineTo(this.tmpX1, this.tmpY1); ctx.closePath(); ctx.stroke(); ctx.beginPath(); ctx.moveTo(this.tmpX1, this.tmpY1) ctx.lineTo(this.x2, this.y2); ctx.closePath(); ctx.stroke(); var endRadians = Math.atan((this.y2 - this.tmpY1) / (this.x2 - this.tmpX1)); endRadians += ((this.x2 >= this.tmpX1) ? 90 : -90) * Math.PI / 180; this.drawArrowhead(ctx, this.x2, this.y2, endRadians); }else if(this.tmpX1 == null && this.tmpY1 == null && this.tmpX2 == null && this.tmpY2 == null){ ctx.lineTo(this.x2, this.y2); ctx.closePath(); ctx.stroke(); var endRadians = Math.atan((this.y2 - this.y1) / (this.x2 - this.x1)); endRadians += ((this.x2 >= this.x1) ? 90 : -90) * Math.PI / 180; this.drawArrowhead(ctx, this.x2, this.y2, endRadians); } } Arrow.prototype.drawArrowhead = function(ctx, x, y, radians) { ctx.save(); ctx.beginPath(); ctx.translate(x, y); ctx.rotate(radians); ctx.moveTo(0, 0); ctx.lineTo(5, 10); ctx.lineTo(-5, 10); ctx.closePath(); ctx.restore(); ctx.fill(); } /////////////////////////////////////////畫箭頭end/////////////////////////////////////
htmljava
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <link rel="stylesheet" href="public.css"> </head> <body> <div class="box"> <canvas id="myCanvas" width="1000" height="800"></canvas> </div> <script type="text/javascript" src="drawFlowChart.js" ></script> <script> var canvas = document.getElementById("myCanvas"); var cxt = canvas.getContext('2d'); var canWidth = cxt.canvas.clientWidth; var init = {top: 32, spaceH: 70}; var row2 = { y:init.top+init.spaceH, data:[ { type:'Step', text:'業務名稱1', name:'step_2_1', arrowArr:[ { arrow:'drawBottomToTop', to:'step_3_1' } ], x:'', y:'', requestData:{} }, { type:'Step', text:'業務名稱2', name:'step_2_2', arrowArr:[ { arrow:'drawBottomToLeft', to:'step_3_2' } ] }, { type:'Step', text:'業務名稱3', name:'step_2_3', arrowArr:[ { arrow:'drawBottomToRight', to:'step_3_2' } ] }, { type:'Step', name:'step_2_4', text:'業務名稱4', arrowArr:[ { arrow:'drawBottomToRight', to:'step_7_1' } ] } ] }; row2.data = calChartX(canWidth,row2.data, row2.y); var flowData = [ { row:1, y:init.top, data:[ { type:'Start', text:'開始', name:'step_1_1', arrowArr:[ { arrow:'drawBottomToTop', to:'step_2_1' }, { arrow:'drawBottomToTop', to:'step_2_2' }, { arrow:'drawBottomToTop', to:'step_2_3' }, { arrow:'drawBottomToTop', to:'step_2_4' } ], x:canWidth/2, y:'' } ] }, { row:2, y:init.top+init.spaceH, data:row2.data, method:{ onmousemove:null, onmouseleave:null, onclick:hoverSingleChart } }, { row:3, y:'', data:[{ type:'Step', text:'業務名稱4', x:row2.data[0].x, name:'step_3_1', arrowArr:[ { arrow:'drawBottomToTop', to:'step_4_1' } ] },{ type:'Step', text:'業務名稱5', name:'step_3_2', x:canWidth/2, arrowArr:[ { arrow:'drawBottomToTop', to:'step_4_2' } ] }] }, { row:4, y:'', data:[{ type:'Step', text:'業務名稱6', x:row2.data[0].x, name:'step_4_1', arrowArr:[ { arrow:'drawBottomToTop', to:'step_5_1' } ] },{ type:'Step', text:'業務名稱7', name:'step_4_2', x:canWidth/2, arrowArr:[ { arrow:'drawBottomToTop', to:'step_5_2' } ] }] }, { row:5, y:'', data:[{ type:'Step', text:'業務名稱8', x:row2.data[0].x, name:'step_5_1', arrowArr:[ { arrow:'drawBottomToTop', to:'step_6_1' } ] },{ type:'Step', text:'業務名稱9', name:'step_5_2', x:canWidth/2, arrowArr:[ { arrow:'drawBottomToTop', to:'step_6_2' }, { arrow:'drawBottomToTop', to:'step_6_3' } ] }] }, { row:6, y:'', data:[{ type:'Step', text:'業務名稱10', x:row2.data[0].x, name:'step_6_1', arrowArr:[ { arrow:'drawBottomToLeft', to:'step_7_1' } ] },{ type:'Step', text:'業務名稱11', name:'step_6_2', x:row2.data[1].x, arrowArr:[ { arrow:'drawBottomToTop', to:'step_7_1' } ] },{ type:'Step', text:'業務名稱12', name:'step_6_3', x:row2.data[2].x, arrowArr:[ { arrow:'drawBottomToTop', to:'step_7_1' } ] }] }, { row:7, y:init.top+init.spaceH*6+10, data:[{ type:'Condition', text:'判斷條件', x:canWidth/2, name:'step_7_1', arrowArr:[ { arrow:'drawBottomToTop', to:'step_8_1' } ] }] }, { row:8, y:init.top+init.spaceH*7+30, isAverage:true, //平均計算x data:[ { type:'Step', text:'業務名稱12', name:'step_8_1', arrowArr:[ { arrow:'drawRightToLeft', to:'step_8_2' } ], requestData:{}, method:{ onmousemove:null, onmouseleave:null, onclick:null } }, { type:'Step', text:'業務名稱4', name:'step_8_2', arrowArr:[ { arrow:'drawRightToLeft', to:'step_8_3' } ] }, { type:'Step', text:'業務名稱4', name:'step_8_3', arrowArr:[ { arrow:'drawRightToLeft', to:'step_8_4' } ] }, { type:'Step', name:'step_8_4', text:'業務名稱4', arrowArr:[ { arrow:'drawRightToLeft', to:'step_8_5' } ] }, { type:'Step', name:'step_8_5', text:'業務名稱4', arrowArr:[ { arrow:'drawRightToLeft', to:'step_8_6' } ] }, { type:'Step', name:'step_8_6', text:'業務名稱4', arrowArr:[ { arrow:'drawBottomToTop', to:'step_9_1' } ] } ] }, { row:9, y:init.top+init.spaceH*8+30, isAverage:true, data:[ { type:'Step', text:'業務名稱4', name:'step_9_1', arrowArr:[ { arrow:'drawRightToLeft', to:'step_9_2' } ], requestData:{}, method:{ onmousemove:null, onmouseleave:null, onclick:hoverSingleChart } }, { type:'Step', text:'業務名稱4', name:'step_9_2', arrowArr:[ { arrow:'drawRightToLeft', to:'step_9_3' } ] }, { type:'Step', text:'業務名稱4', name:'step_9_3', arrowArr:[ { arrow:'drawRightToLeft', to:'step_9_4' } ] }, { type:'Step', name:'step_9_4', text:'業務名稱4', arrowArr:[ { arrow:'drawRightToLeft', to:'step_9_5' } ] }, { type:'End', name:'step_9_5', text:'結束', arrowArr:[] } ] } ]; drawFlowChart(cxt,canvas,flowData, init.top, init.spaceH); function hoverSingleChart(singleData){ console.log("---------鼠標事件-----------"); console.log(singleData); } </script> </body> </html>
參考博文:https://www.cnblogs.com/DurantSimpson/p/6146038.html/canvas