qml中canvas绘制简单程序流程图

简单程序流程图

import QtQuick 2.12
import QtQuick.Window 2.12

Window {
    visible: true
    width: 640
    height: 700
    title: qsTr("Hello World")
//style 0圆角矩形 1矩形 2菱形 非012的为平行四边形
    property var padding: 20
    property var bgc: "#0C283C"
    property var fs: 30
    property var name: ""
    property var lc: "#ffffff"
    property var fc: "#ffffff"
    property var strokeColor: "#ffffff"
    property var fillColor: "#00BFFF"
    property var data1: JSON.stringify([
       {
          "nextNodeID": "1",
//           "lc": "255,0,0",
//           "fc": "255,0,0",
          "style": "0",
          "nodeText": "开\t始",
          "nodeID": "0"
       },
       {
          "nextNodeID": "2",
          "linetext": "",
//           "lc": "255,0,0",
//           "fc": "255,0,0",
          "style": "4",
          "nodeText": "初始数据",
          "nodeID": "1"
       },
       {
          "nextNodeID": "3",
          "linetext": "否",
//           "lc": "255,0,0",
//           "fc": "255,0,0",
          "style": "2",
          "nodeText": "初始数据?",
          "nodeID": "2"
       },
       {
          "nextNodeID": "4",
          "linetext": "是",
//           "lc": "255,0,0",
          "style": "2",
          "nodeText": "",
          "nodeID": "2"
       },
       {
          "nextNodeID": "5",
//           "lc": "255,0,0",
//           "fc": "255,0,0",
          "style": "1",
          "nodeText": "初始数据",
          "nodeID": "3"
       },
       {
          "nextNodeID": "6",
//           "lc": "255,0,0",
//           "fc": "255,0,0",
          "style": "0",
          "nodeText": "初始数据",
          "nodeID": "4"
       },
       {
          "nextNodeID": "6",
          "linetext": "是",
//           "lc": "255,0,0",
//           "fc": "255,0,0",
          "style": "2",
          "nodeText": "初始数据",
          "nodeID": "5"
       },
       {
          "nextNodeID": "3",
          "linetext": "否",
//           "lc": "255,0,0",
          "style": "2",
          "nodeText": "",
          "nodeID": "5"
       },
       {
          "nextNodeID": "7",
          "linetext": "",
//           "lc": "255,0,0",
//           "fc": "255,0,0",
          "style": "1",
          "nodeText": "初始数据",
          "nodeID": "6"
       },
       {
          "nextNodeID": "-1",
          "linetext": "",
//           "lc": "255,0,0",
//           "fc": "255,0,0",
          "style": "0",
          "nodeText": "结\t束",
          "nodeID": "7"
       }
    ])
    function repeatNum(item,arr){
        var count =0;
        for(var i=0;i<arr.length;i++){
            if(arr[i]==item){
                count++
            }
        }
        return count;
    }

    function colorChange(rgbColor) {
        // RGB颜色值的正则
        if(rgbColor==undefined){
            return "#ffffff"
        }

        rgbColor = "rgb("+rgbColor+")";
        var reg = /^(rgb|RGB)/;
        var color = rgbColor;
        if (reg.test(color)) {
            var strHex = "#";
            // 把RGB的3个数值变成数组
            var colorArr = color.replace(/(?:\(|\)|rgb|RGB)*/g, "").split(",");
            // 转成16进制
            for (var i = 0; i < colorArr.length; i++) {
                var hex = Number(colorArr[i]).toString(16);
                if(hex.length==1){
                    hex = "0"+hex
                }
                if (hex === "0") {
                    hex += hex;
                }
                strHex += hex;
            }
            return strHex;
        } else {
            return String(color);
        }
    }

    Rectangle{
        id:bg
        width: 1000
        height: 700
        color: "#0C283C"
        anchors.centerIn: parent

        Canvas{
            id:myCanvas
            width: parent.width
            height: parent.height
            anchors.fill: parent
            onPaint: {
                var ctx = getContext("2d");
                ctx.clearRect(0,0,myCanvas.width,myCanvas.height)
                //先对json处理分离成主干-分支-多连线
                //存储主干节点

                var dataJson = JSON.parse(data1)
                console.log("canvasjson:",data1)
                var gridModel = [];
                var norepeatArr = []
                var repeatArr = []
                var repeatNextNodeID = []
                var leftArr = []
                var rightArr = []
                for(var i=0;i<dataJson.length;i++){
                    if(repeatNum(dataJson[i].nodeID,norepeatArr)==0){
                        if(repeatNum(dataJson[i].nodeID,repeatNextNodeID)==0){
                            norepeatArr.push(dataJson[i].nodeID)
                            gridModel.push(dataJson[i])
                        }else{
                            rightArr.push(dataJson[i])
                        }
                    }else{
                        repeatArr.push(dataJson[i])
                        repeatNextNodeID.push(dataJson[i].nextNodeID)
                    }
                }


                for(var i=0;i<repeatArr.length;i++){
                    if(repeatNum(repeatArr[i].nextNodeID,norepeatArr)!=0){
                        leftArr.push(repeatArr[i])
                    }else{
                        for(var j=0;j<rightArr.length;j++){
                            if(repeatArr[i].nextNodeID==rightArr[j].nodeID){
                                rightArr[j].parentID = repeatArr[i].nodeID
                                rightArr[j].linetext = repeatArr[i].linetext

                                rightArr[j].plc = repeatArr[i].lc
                                rightArr[j].pfc = repeatArr[i].fc
                            }
                        }
                    }
                }

                console.log(JSON.stringify(gridModel))
                console.log(JSON.stringify(norepeatArr))
                console.log(JSON.stringify(repeatArr))
                console.log(JSON.stringify(repeatNextNodeID))
                console.log(JSON.stringify(leftArr))
                console.log(JSON.stringify(rightArr))



                //文字宽高 间隔 字体大小 左右分支距离间隔 箭头长度
                var w = myCanvas.width/8
                var h = myCanvas.height/gridModel.length/2
                var interval = myCanvas.height/gridModel.length
                var fontpx = h/2
                var branchInterval = myCanvas.width/4
                var arrowLen = interval/6


                //绘制主干
                for(var i=0;i<gridModel.length;i++){
                    var val = gridModel[i]
                    if(val.style == 0){
                        ctx.beginPath()
                        drawRadiusRec(ctx,myCanvas.width/2-w/2,i*interval,w,h,5,val.nodeText,fontpx,colorChange(val.fc))
                        if(parseInt(val.nextNodeID)>0){
                            drawArrow(ctx,myCanvas.width/2, interval*i+h, myCanvas.width/2,(i+1)*interval,30,arrowLen,2,colorChange(val.lc),"m",val.linetext?val.linetext:"",fontpx,colorChange(val.fc));
                        }
                        ctx.closePath()
                    }else if(val.style == 1){
                        ctx.beginPath()
                        drawRec(ctx,myCanvas.width/2-w/2,i*interval,w,h,val.nodeText,fontpx,colorChange(val.fc))
                        drawArrow(ctx,myCanvas.width/2, interval*i+h, myCanvas.width/2,(i+1)*interval,30,arrowLen,2,colorChange(val.lc),"m",val.linetext?val.linetext:"",fontpx,colorChange(val.fc));
                        ctx.closePath()
                    }
                    else if(val.style == 2){
                        ctx.beginPath()
                        drawLing(ctx,myCanvas.width/2-w/2,i*interval,w,h,val.nodeText,fontpx,colorChange(val.fc))
                        drawArrow(ctx,myCanvas.width/2, interval*i+h, myCanvas.width/2,(i+1)*interval,30,arrowLen,2,colorChange(val.lc),"m",val.linetext?val.linetext:"",fontpx,colorChange(val.fc));
                        ctx.closePath()
                    }
                    else {
                        ctx.beginPath()
                        drawSibianxing(ctx,myCanvas.width/2-w/2,i*interval,w,h,val.nodeText,fontpx,colorChange(val.fc))
                        drawArrow(ctx,myCanvas.width/2, interval*i+h, myCanvas.width/2,(i+1)*interval,30,arrowLen,2,colorChange(val.lc),"m",val.linetext?val.linetext:"",fontpx,colorChange(val.fc));
                        ctx.closePath()
                    }
                }

//需要一左一右显示,合并左右数组,间隔添加属性pos为左还是右
               var ary1 = JSON.parse(JSON.stringify(leftArr));//深拷贝,否则改变原数组
               var ary2 = JSON.parse(JSON.stringify(rightArr));
               for (var i = 0; i < ary2.length; i++) {
                 ary1.splice(1 + 2*i, 0, ary2[i]);
               }
               console.log(ary1);

               for(var i=0;i<ary1.length;i++){
                if(i%2==0){
                    ary1[i].pos="left";
                }else{
                    ary1[i].pos="right";
                }
               }
                //绘制带节点分支
                for(var n=0;n<rightArr.length;n++){
                    var pos = "left";

                    for(var m=0;m<ary1.length;m++){
                        if(ary1[m].nodeID==rightArr[n].nodeID){
                            pos = ary1[m].pos
                        }
                    }

                    var posVal = pos =="left"?-1:1;
                        var rightX = myCanvas.width/2 +branchInterval*(n+1)*posVal
                        //暂定分支只能是矩形1 否则再判断是画哪种图形
                        ctx.beginPath()

                        //取父节点和子节点在主干的下标,除2取整
                        var pInd,sInd;
                        for(var i=0;i<gridModel.length;i++){
                            if(rightArr[n].parentID==gridModel[i].nodeID){
                                pInd = i
                                rightArr[n].parentNodedata = gridModel[i].nodeText

                            }else if(gridModel[i].nodeID==rightArr[n].nextNodeID){
                                sInd = i
                                rightArr[n].sonNodedata = gridModel[i].nodeText
                            }
                        }
                        var midInd = parseInt((parseFloat(pInd)+parseFloat(sInd))/2)

                        //通过rightarr的下标,更改下y,实现同侧避让


                        //绘制右分支上箭头
                        var pX = [rightX+w/2*posVal]
                        var pY = [interval*pInd+h/2]


                        //解决因为节点图形因为文字自适应宽度导致箭头起点不对

                        var fromX = myCanvas.width/2+w/2*posVal

                        rightArr[n].parentNodedata = rightArr[n].parentNodedata.replace("\t","   ")
                        if(getTextLen(rightArr[n].parentNodedata,fontpx)+80>w){

                            fromX = fromX+(getTextLen(rightArr[n].parentNodedata,fontpx)+80-w)/2*posVal
                        }

                        //参数说明:canvas对象,起点x,起点y,终点x,终点y,箭头角度,箭头长度,线宽,颜色,节点位置,线文字,拐点个数,拐点x(array),拐点y(array)
                        drawArrow(ctx,fromX, interval*pInd+h-h/2,rightX+w/2*posVal,interval*midInd,30,arrowLen,2,colorChange(rightArr[n].plc),"r",rightArr[n].linetext,fontpx,colorChange(rightArr[n].pfc),1,pX,pY);

                        //绘制右分支下箭头
                        var sX = [rightX+w/2*posVal]
                        var sY = [interval*sInd+h/2]

                        var toX = myCanvas.width/2+w/2*posVal
                        rightArr[n].sonNodedata = rightArr[n].sonNodedata.replace("\t","   ")
                        if(getTextLen(rightArr[n].sonNodedata,fontpx)>w){

                            toX = toX+(getTextLen(rightArr[n].sonNodedata,fontpx)-w)/2*posVal
                        }


                        //下箭头方向改变,起点终点xy调换
                        drawArrow(ctx,rightX+w/2*posVal,interval*midInd,toX, interval*sInd+h/2, 30,arrowLen,2,colorChange(rightArr[n].lc),"r","",fontpx,colorChange(rightArr[n].fc),1,sX,sY);

                        var x1 = posVal==1?rightX:rightX-w
                        drawRec(ctx,x1,midInd*interval,w,h,rightArr[n].nodeText,fontpx,colorChange(rightArr[n].fc))
                        ctx.closePath()
                }

                //绘制无节点分支,只有箭头连线
                for(var i=0;i<leftArr.length;i++){

                    var pos1 = "left";

                    for(var m=0;m<ary1.length;m++){
                        if(ary1[m].nodeID==leftArr[i].nodeID){
                            pos1 = ary1[m].pos
                        }
                    }

                    var posVal1 = pos1 =="left"?1:-1;
                    var leftX = myCanvas.width/2 -branchInterval*(i+1)*posVal1
                    //取父节点和子节点在主干的下标,除2取整
                    var tInd,bInd;
                    for(var j=0;j<gridModel.length;j++){
                        if(leftArr[i].nodeID==gridModel[j].nodeID){
                            leftArr[i].parentNodedata = gridModel[j].nodeText
                            bInd = j
                        }else if(gridModel[j].nodeID==leftArr[i].nextNodeID){
                            leftArr[i].sonNodedata = gridModel[j].nodeText
                            tInd = j
                        }
                    }


                    var fromX1 = myCanvas.width/2-w/2*posVal1

                    leftArr[i].parentNodedata = leftArr[i].parentNodedata.replace("\t","   ")
                    if(getTextLen(leftArr[i].parentNodedata,fontpx)+80>w){

                        fromX1 = fromX1-(getTextLen(leftArr[i].parentNodedata,fontpx)+80-w)/2*posVal1
                    }

                    var toX1 = myCanvas.width/2-w/2*posVal1
                    leftArr[i].sonNodedata = leftArr[i].sonNodedata.replace("\t","   ")
                    if(getTextLen(leftArr[i].sonNodedata,fontpx)>w){

                        toX1 = toX1-(getTextLen(leftArr[i].sonNodedata,fontpx)-w)/2*posVal1
                    }


                    //无节点的情况下为两拐点
                    var lX = [leftX-w/2*posVal1,leftX-w/2*posVal1]
                    var lY = [interval*bInd+h/2,interval*tInd+h/2]
                    drawArrow(ctx,fromX1,interval*bInd+h/2,toX1, interval*tInd+h/2, 30,arrowLen,2,colorChange(leftArr[i].lc),"l",leftArr[i].linetext,fontpx,colorChange(leftArr[i].fc),2,lX,lY);
                }


            }

        }



    }

    function getTextLen(str,fs){
        if(str == undefined||fs==undefined){
            return 0
        }
        return str.replace(/[\u0391-\uFFE5]/g,"aa").length*fs/2;   //先把中文替换成两个字节的英文,在计算长度
    }


    // (x,y):圆角矩形起始坐标; width: 矩形宽度; height: 矩形高度; r: 矩形圆角;
    function drawRadiusRec(ctx, x, y, width, height, r,text,ifs,ifc){
        console.log("indexof",text.indexOf("\t")!=-1)
        if(text.indexOf("\t")!=-1){
            text = text.replace("\t","   ")
        }

        if(ifs == undefined){
            ifs = fs
        }
        if(ifc == undefined){
            if(typeof fc == "object"){
                ifc = fc
            }else{
                if(fc.indexOf(",")!=-1){
                    ifc = colorChange(fc)
                }else{
                    ifc = fc
                }
            }



        }

        if(getTextLen(text,ifs)>width){
            x -=(getTextLen(text,ifs)-width)/2
            width = getTextLen(text,ifs)
        }

      ctx.beginPath();
      ctx.moveTo(x + r, y);
      ctx.lineWidth = 2;
      ctx.strokeStyle = strokeColor;//矩形填充颜色
      ctx.fillStyle = fillColor;//矩形填充颜色
      ctx.lineTo(x + width - r, y);
      ctx.arc(x + width - r, y + r, r, Math.PI*1.5, Math.PI*2);
      ctx.lineTo(x + width, y + height - r);
      ctx.arc(x + width - r, y + height - r, r, 0, Math.PI*0.5);
      ctx.lineTo(x + r, y + height);
      ctx.arc(x + r, y + height - r, r, Math.PI*0.5, Math.PI);
      ctx.lineTo(x, y + r);
      ctx.arc(x + r, y + r, r, Math.PI, Math.PI*1.5);
      ctx.stroke();
      ctx.fill();
        ctx.font = ifs+"px bold 黑体";
        // 设置颜色
        ctx.fillStyle = ifc;
        // 设置水平对齐方式
        ctx.textAlign = "center";
        // 设置垂直对齐方式
        ctx.textBaseline = "middle";
        // 绘制文字(参数:要写的字,x坐标,y坐标)
        ctx.fillText(text, x+width/2, y+height/2);
    }

    function drawRec(ctx,x,y,width,height,text,ifs,ifc){
        if(text.indexOf("\t")!=-1){
            text = text.replace("\t","   ")
        }
        if(ifs == undefined){
            ifs = fs
        }
        if(ifc == undefined){
            if(typeof fc == "object"){
                ifc = fc
            }else{
                if(fc.indexOf(",")!=-1){
                    ifc = colorChange(fc)
                }else{
                    ifc = fc
                }
            }
        }
        if(getTextLen(text,ifs)>width){
            x -=(getTextLen(text,ifs)-width)/2
            width = getTextLen(text,ifs)
        }


        ctx.fillStyle=fillColor; //fillStyle设置填充颜色
        ctx.strokeStyle=strokeColor; //strokeStyle设置边框颜色
        ctx.lineWidth=2;
        ctx.fillRect(x,y,width,height);
        ctx.strokeRect(x,y,width,height);
        ctx.font = ifs+"px bold 黑体";
        // 设置颜色
        ctx.fillStyle = ifc;
        // 设置水平对齐方式
        ctx.textAlign = "center";
        // 设置垂直对齐方式
        ctx.textBaseline = "middle";
        // 绘制文字(参数:要写的字,x坐标,y坐标)
        ctx.fillText(text, x+width/2, y+height/2);
    }

    //画四边形
    function drawSibianxing(ctx,x,y,width,height,text,ifs,ifc){
        if(text.indexOf("\t")!=-1){
            text = text.replace("\t","   ")
        }


        if(ifs == undefined){
            ifs = fs
        }
        if(ifc == undefined){
            if(typeof fc == "object"){
                ifc = fc
            }else{
                if(fc.indexOf(",")!=-1){
                    ifc = colorChange(fc)
                }else{
                    ifc = fc
                }
            }
        }
        if(getTextLen(text,ifs)>width){
            x -=(getTextLen(text,ifs)-width)/2
            width = getTextLen(text,ifs)
        }


        ctx.lineWidth = 2;
        ctx.fillStyle = fillColor;
        ctx.strokeStyle = strokeColor;
        ctx.beginPath();
        ctx.moveTo(x,y+height);
        ctx.lineTo(x+10,y);
        ctx.lineTo(x+10+width,y);
        ctx.lineTo(x+width,y+height);
        ctx.closePath();
        ctx.stroke();
        ctx.fill();
        ctx.font = ifs+"px bold 黑体";
        // 设置颜色
        ctx.fillStyle = ifc;
        // 设置水平对齐方式
        ctx.textAlign = "center";
        // 设置垂直对齐方式
        ctx.textBaseline = "middle";
        // 绘制文字(参数:要写的字,x坐标,y坐标)
        ctx.fillText(text, x+width/2, y+height/2);
    }

    //画菱形
    function drawLing(ctx,x,y,width,height,text,ifs,ifc){

        if(text.indexOf("\t")!=-1){
            text = text.replace("\t","   ")
        }

        if(ifs == undefined){
            ifs = fs
        }
        if(ifc == undefined){
            if(typeof fc == "object"){
                ifc = fc
            }else{
                if(fc.indexOf(",")!=-1){
                    ifc = colorChange(fc)
                }else{
                    ifc = fc
                }
            }
        }
        if(getTextLen(text,ifs)+80>width){
            x -=(getTextLen(text,ifs)+80-width)/2
            width = getTextLen(text,ifs)+80
        }



        ctx.lineWidth = 2;
        ctx.fillStyle = fillColor;
        ctx.strokeStyle = strokeColor;
        ctx.beginPath();
        ctx.moveTo(x,y+height/2);
        ctx.lineTo(x+width/2,y);
        ctx.lineTo(x+width,y+height/2);
        ctx.lineTo(x+width/2,y+height);
        ctx.closePath();
        ctx.stroke();
        ctx.fill();
        ctx.font = ifs+"px bold 黑体";
        // 设置颜色
        ctx.fillStyle = ifc;
        // 设置水平对齐方式
        ctx.textAlign = "center";
        // 设置垂直对齐方式
        ctx.textBaseline = "middle";
        // 绘制文字(参数:要写的字,x坐标,y坐标)
        ctx.fillText(text, x+width/2, y+height/2);
    }

    //    画箭头
    //参数说明:canvas对象,起点x,起点y,终点x,终点y,箭头角度,箭头长度,线宽,线色,节点位置,线文字,字体大小,字体颜色,拐点个数,拐点x(array),拐点y(array)当字体大小及颜色未传值时,root属性fc及lc才会生效
    function drawArrow(ctx, fromX, fromY, toX, toY, theta, headlen, width, color,nodePox,lineText,ifs,ifc,pointNum,pointX,pointY) {

            theta = typeof (theta) != 'undefined' ? theta : 30;
            headlen = typeof (theta) != 'undefined' ? headlen : 10;
            width = typeof (width) != 'undefined' ? width : 1;
//            color = typeof (color) != 'color' ? color : '#000';

            if(ifs == undefined){
                ifs = fs
            }
            if(ifc == undefined){
                if(typeof fc == "object"){
                    ifc = fc
                }else{
                    if(fc.indexOf(",")!=-1){
                        ifc = colorChange(fc)
                    }else{
                        ifc = fc
                    }
                }
            }

            if(color == undefined){
                if(typeof lc == "object"){
                    color = lc
                }else{
                    if(lc.indexOf(",")!=-1){
                        color = colorChange(lc)
                    }else{
                        color = lc
                    }
                }
            }

            // 计算各角度和对应的P2,P3坐标
            var angle;
            if(pointX==undefined){
                angle = Math.atan2(fromY - toY, fromX - toX) * 180 / Math.PI
            }else{
                angle = Math.atan2(parseInt(pointY[pointY.length-1])-toY, parseInt(pointX[pointX.length-1]) - toX)* 180 / Math.PI
            }

            var angle1 = (angle + theta) * Math.PI / 180,
                angle2 = (angle - theta) * Math.PI / 180,
                topX = headlen * Math.cos(angle1),
                topY = headlen * Math.sin(angle1),
                botX = headlen * Math.cos(angle2),
                botY = headlen * Math.sin(angle2);

            ctx.save();
            ctx.beginPath();

            var arrowX = fromX - topX,
                arrowY = fromY - topY;

            ctx.moveTo(arrowX, arrowY);
            ctx.moveTo(fromX, fromY);
            if(pointNum!=undefined){
                for(var i=0;i<pointX.length;i++){
                    ctx.lineTo(pointX[i], pointY[i]);
                }
            }
            ctx.lineTo(toX, toY);
            arrowX = toX + topX;
            arrowY = toY + topY;
            ctx.moveTo(arrowX, arrowY);
            ctx.lineTo(toX, toY);
            arrowX = toX + botX;
            arrowY = toY + botY;
            ctx.lineTo(arrowX, arrowY);
            ctx.strokeStyle = color;
            ctx.lineWidth = width;
            ctx.stroke();
            ctx.restore();

            ctx.font = ifs+"px bold 黑体";
            // 设置水平对齐方式
            ctx.textAlign = nodePox=="r"?"right":"left";
            // 设置颜色
            ctx.fillStyle = ifc;
            // 绘制文字(参数:要写的字,x坐标,y坐标)
            var posX;
            console.log('drawText');
            if(pointNum!=undefined){
                posX = pointX[0]
            }else {
                posX = (toX-fromX)/2+fromX
            }

            ctx.fillText(lineText, posX, (toY-fromY)/2+fromY);
        }

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值