使用canvas画树形结构关系图

效果图:

  1.使用canvas画边框,文本内容居中

 getCtx(){

        let cs = document.getElementById('canvas');

        let cxt = cs.getContext('2d');

        return cxt;

      },

      drawTextItem({

        startX,

        startY,

        width,

        height,

        title,

      }) {

        let ctx = this.getCtx();

        // 重新开辟一个线路

        ctx.beginPath();

        // 画一个外边框

        ctx.rect(startX, startY, width, height);

        ctx.strokeStyle = "red";

        ctx.fillStyle = "pink";

        ctx.stroke();

        ctx.fill();

       

        // 写字

        ctx.fillStyle = "red";

        ctx.textBaseline = "middle";

        let { width:textWidth } = ctx.measureText(title);

        // fillText(text, x, y, maxWidth),maxWidth最大宽度

        ctx.fillText(title, startX+(width-textWidth)/2, startY+height/2, textWidth);

      },

 

2.思路:

1.判断是否有子节点,有则继续执行画边框操作

2.如何画第二层子节点?难点在于获取子节点的起始节点

如果不考虑最下层节点的话,画到最后结果会发现有节点存在覆盖情况,所以需要轮询查询第二层节点下最多有多少个节点

 这里以左边为例,查看得出有4个,以一个边框固定宽100,高50,高度间距12为例,计算设置杭州市居中的位置,杭州市下面的节点执行相同操作

//计算节点下占据空间多少个节点,没有子节点则标记为1

getAllCount(node){

        let count = 0;

        node.children.reduce((pre,cue)=>{

          count = pre+(cue.children && cue.children.length>0?this.getAllCount(cue):1)

          return count;

        },0)

        return count;

      },

 如何计算节点起点:

比如左右子节点共用8个节点空间,左边4个,右边4个的话,

先计算最左侧起点: let start = startX - (nodeWidth*count+(nodeMargin*(count-1)))/2;//起点

然后占5个节点空间: childrenStartX = start+(nodeWidth*4+(nodeMargin*(4-1)))/2;考虑间距

if(node.children && node.children.length>0){

          let count = 0;

          count = this.getAllCount(node);//计算最底层节点个数

          console.log('count',count);

          let start = startX - (nodeWidth*count+(nodeMargin*(count-1)))/2;//起点

          node.children.forEach((item,index) => {

              let childrenStartX = 0;

             

              let childrenStartY = startY+nodeMargin+nodeHeight;

              let nodeLenth = 0;

              if(item.children && item.children.length>0){

                nodeLenth= this.getAllCount(item)

              }else{

                nodeLenth = 1;

              }

              childrenStartX = start+(nodeWidth*nodeLenth+(nodeMargin*(nodeLenth-1)))/2;

              start = start+(nodeWidth*nodeLenth+(nodeMargin*(nodeLenth-1)))+nodeMargin;

             

              this.drawLine(startX,startY,childrenStartX,childrenStartY);//划线

              this.render(childrenStartX,childrenStartY,item);//划子级

          });

         

        }  

 完整代码:

<div class="canvas">

        <canvas height="1000" width="1000" id="canvas"></canvas>

  </div>

 

let node = {

        title: "浙江省",

        children: [

          {

            title: "杭州市",

            children: [

              {

                title: "拱墅区",

                children: [

                  {

                    title: "北部软件园",

                  },

                ],

              },

              {

                title: "余杭区",

                children: [

                  {

                    title: "梦想小镇",

                  },

                  {

                    title: "欧美金融城",

                  },

                  {

                    title: "华容科技城",

                  },

                ],

              },

            ],

          },

          {

            title: "宁波市",

            children: [

              {

                title: "海曙区",

                children: [

                  {

                    title: "疆良小镇",

                  },

                  {

                    title: "欧美金融城",

                  },

                ],

              },

              {

                title: "江北区",

                children: [

                  {

                    title: "华兴小镇",

                  },

                  {

                    title: "感电东方城",

                  },

                ],

              },

            ],

          },

        ],

      };

      this.render(500,10,node);

 

getCtx(){

        let cs = document.getElementById('canvas');

        let cxt = cs.getContext('2d');

        return cxt;

      },

      drawTextItem({

        startX,

        startY,

        width,

        height,

        title,

      }) {

        let ctx = this.getCtx();

        // 重新开辟一个线路

        ctx.beginPath();

        // 画一个外边框

        ctx.rect(startX, startY, width, height);

        ctx.strokeStyle = "red";

        ctx.fillStyle = "pink";

        ctx.stroke();

        ctx.fill();

       

        // 写字

        ctx.fillStyle = "red";

        ctx.textBaseline = "middle";

        let { width:textWidth } = ctx.measureText(title);

        // fillText(text, x, y, maxWidth),maxWidth最大宽度

        ctx.fillText(title, startX+(width-textWidth)/2, startY+height/2, textWidth);

      },

      drawLine(nodeX,nodeY,childX,childY){

        let ctx = this.getCtx();

        let nodeWidth = 100;

        let nodeHeight = 50;

        let nodeMargin = 12;

        // 重新开辟一个线路

        ctx.beginPath();

        ctx.moveTo(nodeX+(nodeWidth/2),nodeY+nodeHeight);

        ctx.lineTo(nodeX+(nodeWidth/2),nodeY+nodeHeight+(nodeMargin/2));

        ctx.lineTo(childX+(nodeWidth/2),childY-(nodeMargin/2));

        ctx.lineTo(childX+(nodeWidth/2),childY);

        ctx.stroke() // 将起点和终点连接起来

      },

      render(startX,startY,node){

        let nodeWidth = 100;

        let nodeHeight = 50;

        let nodeMargin = 12;

        this.drawTextItem({startX,startY,width:nodeWidth,height:nodeHeight,title:node.title});

        if(node.children && node.children.length>0){

          let count = 0;

          count = this.getAllCount(node);//计算最底层节点个数

          console.log('count',count);

          let start = startX - (nodeWidth*count+(nodeMargin*(count-1)))/2;//起点

          node.children.forEach((item,index) => {

              let childrenStartX = 0;

             

              let childrenStartY = startY+nodeMargin+nodeHeight;

              let nodeLenth = 0;

              if(item.children && item.children.length>0){

                nodeLenth= this.getAllCount(item)

              }else{

                nodeLenth = 1;

              }

              childrenStartX = start+(nodeWidth*nodeLenth+(nodeMargin*(nodeLenth-1)))/2;

              start = start+(nodeWidth*nodeLenth+(nodeMargin*(nodeLenth-1)))+nodeMargin;

             

              this.drawLine(startX,startY,childrenStartX,childrenStartY);//划线

              this.render(childrenStartX,childrenStartY,item);//划子级

          });

         

        }  

      },

      getAllCount(node){

        let count = 0;

        node.children.reduce((pre,cue)=>{

          count = pre+(cue.children && cue.children.length>0?this.getAllCount(cue):1)

          return count;

        },0)

        return count;

      },

 参考文章:[canvas]原来画树状图没那么难,下次别找库了 - 掘金

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值