2020-03-20 css canvas绘制流程图(2)

0.效果图:(内容自己填充)


WechatIMG9.jpeg

1.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();
  }

2.DEMO

                              <!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="ss.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>

3.效果图:


粘贴图片.png
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,172评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,346评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,788评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,299评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,409评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,467评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,476评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,262评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,699评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,994评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,167评论 1 343
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,827评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,499评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,149评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,387评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,028评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,055评论 2 352

推荐阅读更多精彩内容

  • 2.CSS Sprite(CSS 精灵), 也称作 雪碧图;如华为官网右侧提示栏: 图标字体是个比较大的技术讨论点...
    萧强阅读 7,694评论 1 16
  • //1.引入js 2.demo
    Mylovesunshine阅读 2,586评论 0 0
  • 大家好!我是三年级四班的邱硕程,我今天又来写作文了。因为昨天和一个朋友玩了很久,所以忘记给大家写作文了,那今天我就...
    宝鼎木门李晓静阅读 244评论 0 0
  • 道路两旁的树,繁茂的枝叶,告诉你夏天来了。去年夏天的炎热,一度让这个城市的空调脱销,今年这个时候,很多家已经在安空...
    包小鱼的姐姐阅读 199评论 0 2
  • 董卿曾说过:“从某种意义上来说:世间一切,都是遇见,就像冷遇见暖,就有了雨;春遇见冬,有了岁月;天遇见地,有了永恒...
    錡錡落落阅读 231评论 0 2