canvas绘制流程图
最近在做一个需求,根据数据动态生成以下类似的流程图,需要可以设置每个节点的颜色,每个节点可添加点击移动等相关的事件
代码中有做很多的注释和说明,相关的文档说明链接:https://9eb75i.axshare.com
drawFlowChart.js
//画流程图
//画所有的图形:画图和画对应的箭头
function drawFlowChart(context,canvas,flowdata,initTop, initSpaceH){
//1、判断是否有需要平均计算x的数据
flowdata.forEach(function(row){
if(row.isAverage){
row.data = calChartX(canvas.width,row.data, row.y);
}
});
//2、先要画完所有的图形
flowdata.forEach(function(row,rowIndex){
row.y = row.y ? row.y : ( rowIndex==0 ? initTop + initSpaceH : initTop + initSpaceH*rowIndex);
row = drawRowChart(context, row); //画图形
});
//3、添加要指向的对象,必须要在画完所有图形之后
flowdata.forEach(function(row){
row.data.forEach(function(item){
if(item.arrowArr && item.arrowArr.length){
item.arrowArr.forEach(function(mItem){
mItem = addToObj(mItem,flowdata);
})
}
})
});
//4、给所有图形画上对应的画箭头,必须要在前两步完成之后
flowdata.forEach(function(row,rowIndex){
row.data.forEach(function(item){
if(item.arrowArr && item.arrowArr.length){
drawSingleArrow(context,item);//画箭头
}
})
});
//5、给所有元素添加点击和悬浮事件
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(ev){
var ev = ev || window.event;
var ua = navigator.userAgent.toLowerCase();
var isIE = ua.indexOf("compatible") > -1 && ua.indexOf("msie") > -1 && !ua.indexOf("opera") > -1; //IE浏览器
var isEdge= ua.indexOf("Edge") > -1;
var isIE11 = ua.toLowerCase().match(/rv:([\d.]+)\) like gecko/);
var curx = (isIE || isEdge || isIE11) ? ev.offsetX : ev.layerX;
var cury = (isIE || isEdge || isIE11) ? ev.offsetY : ev.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(ev){
var ev = ev || window.event;
var ua = navigator.userAgent.toLowerCase();
var isIE = ua.indexOf("compatible") > -1 && ua.indexOf("msie") > -1 && !ua.indexOf("opera") > -1; //IE浏览器
var isEdge= ua.indexOf("Edge") > -1;
var isIE11 = ua.toLowerCase().match(/rv:([\d.]+)\) like gecko/);
var curx = (isIE || isEdge || isIE11) ? ev.offsetX : ev.layerX;
var cury = (isIE || isEdge || isIE11) ? ev.offsetY : ev.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(ev){
var ev = ev || window.event;
var ua = navigator.userAgent.toLowerCase();
var isIE = ua.indexOf("compatible") > -1 && ua.indexOf("msie") > -1 && !ua.indexOf("opera") > -1; //IE浏览器
var isEdge= ua.indexOf("Edge") > -1;
var isIE11 = ua.toLowerCase().match(/rv:([\d.]+)\) like gecko/);
var curx = (isIE || isEdge || isIE11) ? ev.offsetX : ev.layerX;
var cury = (isIE || isEdge || isIE11) ? ev.offsetY : ev.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) {
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 微软雅黑';
context.fillStyle = item.color ? (item.color.fontColor ? item.color.fontColor: '#5B9BD5'):'#5B9BD5' ; //文字颜色
context.textAlign="center";
context.fillText(item.text, x+w/2, y+h/2+6);
context.stroke();
}
//画菱形
function drawRhombus(context,x, y, l, h, item) {
context.beginPath();
context.moveTo(x, y + h);
context.lineTo(x - l * 2-40, y);
context.lineTo(x, y - h);
context.lineTo(x + l * 2+40, 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 = item.color ? (item.color.fontColor ? item.color.fontColor: '#5B9BD5'):'#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';
var textStyle = textSize('14px','微软雅黑',item.text);
w = parseInt(textStyle.width)+30;
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';
var textStyle = textSize('14px','微软雅黑',item.text);
w = parseInt(textStyle.width)+30;
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);
}
//Step 矩形对象
function Step(context,x, y, item) {
this.flag = "step";
var textStyle = textSize('14px','微软雅黑',item.text);
var w = parseInt(textStyle.width)+30;
this.h = 40;
this.w = w;
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";
var textStyle = textSize('14px','微软雅黑',item.text);
var w = parseInt(textStyle.width)/4;
this.l = w;
this.h = 40;
this.w = w;
this.x = x;
this.y = y;
this.text = item.text;
this.range = calRange(this);
drawRhombus(context,x, y, this.l,this.h, 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+40 , 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-40 , 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.h);
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.h, 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/////////////////////////////////////
html
<!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绘制流程图的更多相关文章
- 记录使用echarts的graph类型绘制流程图全过程(二)- 多层关系和圆形图片的设置
本文主要记录在使用echarts的graph类型绘制流程图时候遇到的2个问题:对于圆形图片的剪切和多层关系的设置 图片的设置 如果用echarts默认的symbol参数来显示图片,会显示图片的原始状态 ...
- 玩转控件:GDI+动态绘制流程图
前言 今天,要跟大家一起分享是"GDI+动态生成流程图"的功能.别看名字高大上(也就那样儿--!),其实就是动态生成控件,然后GDI+绘制直线连接控件罢了.实际项目效果图如下 ...
- HTML5学习总结——canvas绘制象棋(canvas绘图)
一.HTML5学习总结——canvas绘制象棋 1.第一次:canvas绘制象棋(笨方法)示例代码: <!DOCTYPE html> <html> <head> & ...
- 用canvas绘制折线图
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...
- 封装 用canvas绘制直线的函数--面向对象
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...
- 学习笔记:HTML5 Canvas绘制简单图形
HTML5 Canvas绘制简单图形 1.添加Canvas标签,添加id供js操作. <canvas id="mycanvas" height="700" ...
- canvas绘制经典折线图(一)
最终效果图如下: 实现步骤如下:注-引用了jQuery HTML代码 <!doctype html> <html lang="en"> <head&g ...
- Canvas绘制图形
1.Canvas绘制一个蓝色的矩形 <!DOCTYPE html> <html> <head lang="en"> <meta chars ...
- [canvas]利用canvas绘制自适应的折线图
前段时间学习了用canvas绘制折现图,且当画布变换大小,折现图会随之变化,现附上代码 <!DOCTYPE html> <html lang="en"> & ...
随机推荐
- js格式化JSON数据
前言: 最近做的项目中遇到个需要在前端页面中将某个设备需要的数据格式展示出来,方便用户配置.一开始单纯的将数据格式写入到pre标签中, 但是通过pre标签写入的数据格式在代码可视化上不是很优雅.由于上 ...
- iOS-基于TCP连接<Scoket-服务端>
一:前言(本文为TCP服务端) TCP首先要服务器开放一个端口 然后客户端去连接服务端的IP地址和端口号 连接成功之后再进行数据传输 要经历三次握手 二:代码在GitHub 1.需要的工具类 自行下载 ...
- 【Gradle】Java Gradle 插件
Java Gradle 插件 如何应用 apply plugin:'java' Java插件约定的项目结构 Project |--build.gradle |--src |--main |--java ...
- Recover With Until Time fails With RMAN-20207 When Time Is Before Last RESETLOGS (Doc ID 159405.1)
Recover With Until Time fails With RMAN-20207 When Time Is Before Last RESETLOGS (Doc ID 159405.1) A ...
- Linux-3.14.12内存管理笔记【建立内核页表(3)
前面已经分析了内核页表的准备工作以及内核低端内存页表的建立,接着回到init_mem_mapping()中,低端内存页表建立后紧随着还有一个函数early_ioremap_page_table_ran ...
- TensorFlow从1到2(十一)变分自动编码器和图片自动生成
基本概念 "变分自动编码器"(Variational Autoencoders,缩写:VAE)的概念来自Diederik P Kingma和Max Welling的论文<Au ...
- Spring Cloud 服务之间调用
微服务之多个服务间调用 现在又一个学生微服务 user 和 学校微服务 school,如果user需要访问school,我们应该怎么做? 1.使用RestTemplate方式 添加config imp ...
- C语言解决汉诺塔问题!
很难受,看了很多资料才明白..... 对这个问题分析,发现思路如下:有n个黄金盘,要先把n-1个弄到B柱上,再把第n个弄到C柱上,然后把n-1个借助A柱弄到C柱上. 实现的函数如下: void f(i ...
- 扎心一问!你凭什么成为top1%的Java工程师?
目录 1.解决生产环境里的突发故障 2.对棘手的线上性能问题进行优化 3.锻造区别于普通码农的核心竞争力 4.打磨架构设计能力 5.你凭什么成为 top1%? 你工作几年了? 是否天天CRUD到吐 ...
- LeetCode 387: 字符串中的第一个唯一字符 First Unique Character in a String
题目: 给定一个字符串,找到它的第一个不重复的字符,并返回它的索引.如果不存在,则返回 -1. Given a string, find the first non-repeating charact ...