Canvas 绘制三段式审批流程图

该流程图默认有且只有三段,分别是:发起申请-》审核-》结果

若存在流程且每段必定是有数据的,由于是用原生js绘制的流程图,在引入项目中之后可以尝试根据数据的多少动态调节canvas画布的大小。

一:简单的设置一下css样式

<style type="text/css">
        html body{
            height:100%;
            width:100%;
        }
        .approvalProcess{
            margin:0 auto;
            width:1000px;
            height:500px;
            border:1px solid black;
        }
        .top{
            width:100%;
            height:40px;
            display: flex;
        }
        .top >div{
            width:100px;
            height:100%;
        }
        .state1 >span{
            display: inline-block;
            width:20px;
            height:8px;
            background:rgba(37, 137, 255, 1);
        }
        .state2 >span{
            display: inline-block;
            width:20px;
            height:8px;
            background:rgba(53, 195, 185, 1);
        }
        .state3 >span{
            display: inline-block;
            width:20px;
            height:8px;
            background:rgba(245, 172, 86, 1);
        }
        .state4 >span{
            display: inline-block;
            width:20px;
            height:8px;
            background:rgba(212, 48, 48, 1);
        }
        .bottom{
            width:100%;
            height:calc( 100% - 40px);
        }
        canvas{
            border:1px solid black;
        }
    </style>

二:简单的添加一下dom元素

 <div class="approvalProcess">
        <div class="top">
            <div class="state1">
                <span></span>
                发起人
            </div>
            <div class="state2">
                <span></span>
                通过
            </div>
            <div class="state3">
                <span></span>
                待审批
            </div>
            <div class="state4">
                <span></span>
                驳回
            </div>
        </div>
        <div class="bottom">
            <canvas id="processImage" width="1000" height="460"></canvas>
        </div>
    </div>

三:简单的数据结构及自行封装的功能函数

1,声明的变量

let canvas = document.getElementById('processImage')
        let ctx = canvas.getContext('2d');
        let flowData = [
            [{
                userName:'刘备',
                state:"发起申请",
                color:"rgba(37, 137, 255, 1)",
            }],
            [
                {
                    userName:'张飞',
                    state:"同意",
                    color:'rgba(53, 195, 185, 1)'
                },
                {
                    userName:'赵云',
                    state:'待审批',
                    color:"rgba(245, 172, 86, 1)"
                },
                {
                    userName:'曹操',
                    state:'驳回',
                    color:"rgba(212, 48, 48, 1)"
                },
            ],
            [
                {
                    state:"通过",
                    color:'rgba(53, 195, 185, .5)'
                }
            ]
        ]
        let drawStartX = 50;    //第一层元素起始中心X坐标
        let drawStartY = 200    //第一层元素起始中心Y坐标
        
        let arrowWidth = 5;    //绘制箭头的宽度

        let horizontalLineSpacing = 50  //分组横线间距
        let verticalLineSpacing = 110    //分组纵线间距

        let borderWidth = 120    //元素边框宽度
        let borderHeight = 90   //元素边框高度
        let lineColor = "rgba(37, 137, 255, 1)";

2,封装的功能方法

1,绘制流程节点

function drawProcessNode(startX,startY,stateColor,userName,state){
                //绘制圆角边框
                ctx.strokeStyle = 'rgba(222, 225, 231, 1)';
                ctx.beginPath();
                ctx.arc(startX+10,startY-35,10,-Math.PI/2,Math.PI,true);
                ctx.stroke();
                ctx.closePath();
                ctx.beginPath();
                ctx.moveTo(startX+10,startY-45);
                ctx.lineTo(startX+110,startY-45);
                ctx.stroke();
                ctx.closePath();
                ctx.beginPath();
                ctx.arc(startX+110,startY-35,10,-Math.PI/2,0,false);
                ctx.stroke();
                ctx.closePath();
                ctx.beginPath();
                ctx.moveTo(startX+borderWidth,startY-35)
                ctx.lineTo(startX+borderWidth,startY+35)
                ctx.stroke()
                ctx.closePath()
                ctx.beginPath()
                ctx.arc(startX+110,startY+35,10,Math.PI/2,0,true);
                ctx.stroke()
                ctx.closePath()
                ctx.beginPath();
                ctx.moveTo(startX+110,startY+45);
                ctx.lineTo(startX+10,startY+45);
                ctx.stroke();
                ctx.closePath();
                ctx.beginPath();
                ctx.arc(startX+10,startY+35,10,Math.PI,Math.PI/2,true);
                ctx.stroke();
                ctx.closePath();
                ctx.beginPath();
                ctx.moveTo(startX,startY+35);
                ctx.lineTo(startX,startY-35);
                ctx.stroke();
                ctx.closePath();

                //绘制填充圆形 头像背景
                ctx.beginPath();
                ctx.arc(startX+20,startY-20,12,0,Math.PI*2,true)
                ctx.fillStyle = stateColor;
                ctx.fill();
                ctx.closePath();

                ctx.beginPath();
                ctx.arc(startX+20,startY-24,4,0,Math.PI*2,true)
                ctx.fillStyle = 'white';
                ctx.fill();
                ctx.closePath();

                ctx.beginPath();
                ctx.arc(startX+20,startY-8,10,0,Math.PI/2,true)
                ctx.fillStyle = 'white';
                ctx.fill();
                ctx.closePath();

                ctx.beginPath();
                ctx.lineWidth = 3;
                ctx.arc(startX+20,startY-20,12,0,Math.PI*2,true)
                ctx.strokeStyle = stateColor;
                ctx.stroke();
                ctx.closePath();

                //填充文字
                ctx.fillStyle="rgba(66, 76, 87, 1)";
                ctx.font="14px serif";
                ctx.fillText(userName,startX+40,startY-18);

                ctx.fillStyle="rgba(66, 76, 87, 1)";
                ctx.font="14px serif";
                ctx.fillText(state,startX+40,startY+20);
        }

2,绘制纵线

//绘制纵线
function drawVerticalLine(startX,startY,endX,endY,color){
     ctx.beginPath();
     ctx.strokeStyle=color
     ctx.moveTo(startX,startY);
     ctx.lineTo(endX,endY);
     ctx.stroke();
 }

3,绘制横线

//绘制横线
function drawHorizontalLine(startX,startY,endX,endY,color){
     ctx.strokeStyle=color;
     ctx.beginPath();
     ctx.moveTo(startX,startY);
     ctx.lineTo(endX,endY);
     ctx.stroke();
 }

4,绘制右箭头

//绘制右箭头
function drawRightArrow(startX,startY,color){
      ctx.beginPath();
      ctx.moveTo(startX+5,startY);
      ctx.lineTo(startX,startY+5);
      ctx.lineTo(startX,startY-5);
      ctx.fillStyle = color;
      ctx.fill();
 }

5,绘制圆点

//绘制圆点
function drawDot(centerX,centerY,rediuce,color){
     ctx.beginPath();
     ctx.lineWidth = 1;
     ctx.fillStyle = color;
     ctx.arc(centerX,centerY,rediuce,0,Math.PI*2,true)
     ctx.fill()
}

6,绘制流程结束节点


       //绘制流程结束节点
        function drawNullNode(startX,startY,state,color){
                ctx.fillStyle = color;
                ctx.beginPath();
                ctx.arc(startX+10,startY-15,10,-Math.PI/2,Math.PI,true);
                ctx.lineTo(startX+10,startY-25);
                ctx.lineTo(startX+40,startY-25);
                ctx.arc(startX+40,startY-15,10,-Math.PI/2,0,false);
                ctx.lineTo(startX+50,startY-15)
                ctx.lineTo(startX+50,startY+15)
                ctx.arc(startX+40,startY+15,10,Math.PI/2,0,true);
                ctx.lineTo(startX+40,startY+25);
                ctx.lineTo(startX+10,startY+25);
                ctx.arc(startX+10,startY+15,10,Math.PI,Math.PI/2,true);
                ctx.lineTo(startX,startY+15);
                ctx.lineTo(startX,startY-15);
                ctx.fill();
                ctx.closePath();
                ctx.fillStyle = "rgba(66, 76, 87, 1)"
                ctx.font="12px"
                ctx.fillText(state,startX+10,startY+5)
        }

三:主体代码逻辑

 //绘制主体逻辑
 //流程节点默认只有三段
 function draw(){
     //绘制发起节点
     drawProcessNode(drawStartX,drawStartY,flowData[0][0].color,flowData[0][0].userName,flowData[0][0].state)
     //绘制圆点
     drawDot(drawStartX+borderWidth,drawStartY,4,lineColor)
     //绘制连接发起节点的横线
     let appliStartX = drawStartX+borderWidth;
     let appliEndX = appliStartX+horizontalLineSpacing;

     drawHorizontalLine(appliStartX,drawStartY,appliEndX,drawStartY,lineColor)

     let endLineStartX = appliEndX+arrowWidth+horizontalLineSpacing*2+borderWidth;
     //绘制第二流程的节点
     if(flowData[1].length===1){
          //绘制前半线
         let centerStartX = appliEndX+horizontalLineSpacing;
         drawHorizontalLine(appliEndX,drawStartY,centerStartX,drawStartY,lineColor)
         //绘制右箭头
         drawRightArrow(centerStartX,drawStartY,lineColor)
         //绘制元素
         drawProcessNode(centerStartX+arrowWidth,drawStartY,flowData[1][0].color,flowData[1][0].userName,flowData[1][0].state)
          //绘制圆点
         let centerNodeStartX = centerStartX+arrowWidth+borderWidth;
         drawDot(centerNodeStartX,drawStartY,4,lineColor)
         //绘制后半线
          drawHorizontalLine(centerNodeStartX,drawStartY,centerNodeStartX+horizontalLineSpacing,drawStartY,lineColor)
        }else{

            drawDot(appliEndX,drawStartY,4,lineColor)
            //绘制左侧纵线
            //获取左侧纵线起始终止XY坐标
                
            let leftStartY = drawStartY - verticalLineSpacing*(flowData[1].length-1)/2;
            let leftEndY = drawStartY + verticalLineSpacing*(flowData[1].length-1)/2;
            drawVerticalLine(appliEndX,leftStartY,appliEndX,leftEndY,lineColor);
            //绘制右侧纵线
            //获取右侧纵线起始终止XY坐标
            let rightStartX = appliEndX+arrowWidth+horizontalLineSpacing*2+borderWidth;//120是节点元素边框的宽度
            drawVerticalLine(rightStartX,leftStartY,rightStartX,leftEndY,lineColor);

            for(let i = 0; i<flowData[1].length;i++){
               //绘制前半线
               //获取每个审批节点前半线的起始Y坐标
               let centerFromtStartY = leftStartY+i*verticalLineSpacing;
               //获取每个审批节点前半线的终止X坐标
               let centerFrontEndX = appliEndX+horizontalLineSpacing;
              drawHorizontalLine(appliEndX,centerFromtStartY,centerFrontEndX,centerFromtStartY,lineColor)

                //绘制右箭头
                drawRightArrow(centerFrontEndX,centerFromtStartY,lineColor);

                //绘制节点元素
               drawProcessNode(centerFrontEndX+arrowWidth,centerFromtStartY, flowData[1][i].color,flowData[1][i].userName,flowData[1][i].state)
                //绘制圆点
                let centerDotX = centerFrontEndX+arrowWidth+borderWidth;

                drawDot(centerDotX,centerFromtStartY,4,lineColor)

                //绘制右半线
                //获取右半线终点X坐标
                let centerBehindEndX = centerDotX+horizontalLineSpacing;
                 drawHorizontalLine(centerDotX,centerFromtStartY,centerBehindEndX,centerFromtStartY,lineColor)
              }
              drawDot(endLineStartX,drawStartY,4,lineColor)
        }
         //绘制结束节点,以及链接结束节点的半线箭头

         let endLineEndX = endLineStartX+horizontalLineSpacing;
         drawHorizontalLine(endLineStartX,drawStartY,endLineEndX,drawStartY,lineColor)
         drawRightArrow(endLineEndX,drawStartY,lineColor);
         drawNullNode(endLineEndX+arrowWidth,drawStartY,flowData[2][0].state,flowData[2][0].color)
  }
  draw();

四:效果图

 

  • 3
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值