canvas树状图

1,竖向树状图

 

<template>
  <div class="about">
    <div>
      canvas绘图

    </div>
    <div class="cavas">
      <canvas ref='canvas' width="600" height="1000"></canvas>
    </div>
  </div>
</template>
<script setup>
import { ref, onMounted, createTextVNode } from "vue"
let count = ref(0)
const handlerbtn = () => {
  count.value++
  console.log(count)
}
// 进线 nodetype = 1,主母线 2 母线 3 变压器4 回路 5
// transformGroup 变压器 2 双绕组,3 三绕组
let obj = [
  {
    nodeName: "进线",
    nodeType: 1,
    voltage: "220kV",
    id: 1,
    pid: 0,
    childern: [
      {
        nodeName: "母线1",
        nodeType: "2",
        voltage: "220kV",
        id: 2,
        pid: 1,
        childern:
          [
            {
              nodeName: "变压器1",
              nodeType: 4,
              transformGroup: 3,
              id: 3,
              pid: 2,
              childern: [
                { nodeName: "母线1", nodeType: 3, voltage: '110kV', id: 4, pid: 3, childern: [{ nodeName: "回路", nodeType: 5, voltage: '110kV', id: 5, pid: 3 }] },
                { nodeName: "回路", nodeType: 5, voltage: '110kV', id: 5, pid: 3 }
              ]
            }
          ]
      },
      {
        nodeName: "母线1",
        nodeType: "2",
        voltage: "220kV",
        id: 2,
        pid: 1,
        childern:
          [
            {
              nodeName: "变压器1",
              nodeType: 4,
              transformGroup: 2,
              id: 3,
              pid: 2,
              childern: [
                { nodeName: "母线1", nodeType: 3, voltage: '110kV', id: 4, pid: 3, childern: [{ nodeName: "回路", nodeType: 5, voltage: '110kV', id: 5, pid: 3 }] },
                { nodeName: "回路", nodeType: 5, voltage: '110kV', id: 5, pid: 3 }
              ]
            },
            {
              nodeName: "变压器1",
              nodeType: 4,
              transformGroup: 3,
              id: 3,
              pid: 2,
              childern: [
                { nodeName: "母线1", nodeType: 3, voltage: '110kV', id: 4, pid: 3, childern: [] },
                { nodeName: "回路", nodeType: 5, voltage: '110kV', id: 5, pid: 3 },
                { nodeName: "回路", nodeType: 5, voltage: '110kV', id: 5, pid: 3 }
              ]
            },
          ]
      }
    ]
  }
]
const canvas = ref()
console.log(canvas, 'canvars')
// 会在竖线 n为循环次数 (总的循环几次,表示有几条横向数据)

onMounted(() => {
  const ctx = canvas.value.getContext('2d');

  ctx.strokeStyle = 'red';
  // 1,计算当前分支上的 子元素的个数,根据个数计算出父元素的位置
  const getAllCount = (node) => {
    // 第一次传的有childern
    console.log(node, 'node')
    let count = 0
    node.childern.reduce((pre, cur) => {
      console.log(cur, 'cur')
      count = pre + (cur.childern && cur.childern.length > 0 ? getAllCount(cur) : 1)
      return count
    }, 0)
    return count
  }
  // 2,确定父元素的起始点,划线
  const darwLine = (X, Y, childX, chidY, type, title) => {
    // X,Y 父元素起始位置
    // childX,ChidY, 子元素起始位置
    // type 类型 变压器,回路
    let lineWidth = 50
    let lineHeight = 10
    ctx.beginPath()
    ctx.moveTo(X, Y - lineHeight / 2)
    ctx.lineTo(X + lineWidth, Y - lineHeight / 2)
    ctx.lineTo(childX, chidY - lineHeight / 2)
    ctx.lineTo(childX + lineWidth, chidY - lineHeight / 2)
    // 判断type
    if (type == 1) {
      // 变压器1
      ctx.arc(childX + 20, chidY - lineHeight / 2, 10, 2 * Math.PI, 0)
      ctx.arc(childX + 30, chidY - lineHeight / 2, 10, 2 * Math.PI, 0)
    } else if (type == 2) {
      // 变压器2
      ctx.arc(childX + 20, chidY - lineHeight / 2, lineHeight, 2 * Math.PI, 0)
      ctx.arc(childX + 30, chidY - lineHeight, lineHeight, 2 * Math.PI, 0)
      ctx.arc(childX + 30, chidY - lineHeight / 4, lineHeight, 2 * Math.PI, 0)
    } else if (type == 3) {
      // 回路
      ctx.fillStyle = "red";
      ctx.fillRect(childX + 10, chidY - lineHeight, 30, lineHeight)
    }
    else {
      ctx.lineTo(childX, chidY - lineHeight / 2)
      ctx.lineTo(childX + lineWidth, chidY - lineHeight / 2)
    }
    // 标注名称
    ctx.strokeStyle = 'red';
    let { width: textWidth } = ctx.measureText(title);
    ctx.fillText(title, childX + textWidth / 3, chidY - 12, textWidth);
    ctx.stroke()
  }
  // darwLine(100, 200, 150, 400, 3)
  //循环数据,计算
  const render = (node, startX, startY, type) => {
    // 定义基本信息
    let lineWidth = 50
    let lineHeight = 40
    let linemargin = 10 //间距
    // 绘制第一个横线
    // darwLine(startX, startY, childX, chidY, type)
    if (node.childern && node.childern.length > 0) {
      let count = getAllCount(node)
      let start = startY
      // + count * lineHeight  //第一个元素起始位置Y
      node.childern.forEach((item, index) => {
        let childcount = 0 //计算第一个子元素的位置
        // 当前元素的横坐标
        let childrenStartX = startX + lineWidth
        let childrenStartY = 0
        if (item.childern && item.childern.length > 0) {
          childcount = getAllCount(node)
        } else {
          childcount = 1
        }
        // 当前位置的y轴
        childrenStartY = start + childcount * lineHeight / 2 - (childcount - 1) * linemargin
        start = start - childcount * lineHeight / 2 - linemargin
        // 绘图
        let type = 0
        if (item.nodeType == 4) {

          if (item.transformGroup == 2) {
            type = 1
          } else {
            type = 2
          }
        } else if (item.nodeType == 5) {
          type = 3
        }
        darwLine(startX, startY, childrenStartX, childrenStartY, type, item.nodeName)
        render(item, childrenStartX, childrenStartY, type)
      })
      // 计算子元素的位置 
    }
  }
  render(obj[0], 0, 250)

  

})

</script>

<style scoped>
.about {
  width: 100vw;
  height: 100vh;
}

.cavas {
  width: 600px;
  height: 1000px;
  margin: 0 auto;
  background: rgba(0, 0, 0, 0.2);
}
</style>

2,横向树状图

<template>
    <div class="main">
        <div class="canvas">
            <canvas ref="canvas" width="1000" height="500"></canvas>
        </div>
    </div>
</template>
<script setup>
import { ref, onMounted } from "vue"
let node = {
    title: "浙江省",
    children: [
        {
            title: "杭州市",
            children: [
                {
                    title: "拱墅区",
                    children: [
                        {
                            title: "北部软件园",
                        },
                    ],
                },
                {
                    title: "余杭区",
                    children: [
                        {
                            title: "梦想小镇",
                        },
                        {
                            title: "欧美金融城",
                        },
                        {
                            title: "华容科技城",
                        },
                    ],
                },
            ],
        },
        {
            title: "宁波市",
            children: [
                {
                    title: "海曙区",
                    children: [
                        {
                            title: "疆良小镇",
                        },
                        {
                            title: "欧美金融城",
                        },
                    ],
                },
                {
                    title: "江北区",
                    children: [
                        {
                            title: "华兴小镇",
                        },
                        {
                            title: "感电东方城",
                        },
                    ],
                },
            ],
        },
    ],
};
const canvas = ref()
onMounted(() => {
    const ctx = canvas.value.getContext('2d')
    ctx.strokeStyle = 'red'
    // 绘制一个盒子
    var drawTextItem = (startX, startY, rectWidth, rectHeight,title) => {
        ctx.beginPath()
        ctx.rect(startX, startY, rectWidth, rectHeight)
        ctx.strokeStyle = "red";
        ctx.fillStyle = "#c7c2c2";
        ctx.stroke()
        // 写文字
        ctx.fill()
        ctx.fillStyle = "red";
        ctx.textBaseline="middle"
        let { width: textWidth } = ctx.measureText(title);
        ctx.fillText(title, startX + (rectWidth - textWidth) / 2, startY + rectHeight / 2, textWidth);

    }
    //绘制线
    var drawLine = (startX, startY, chindX, chindY) => {
        let rectWidth = 100
        let rectHeight = 50
        let rectMargin = 12
        ctx.moveTo(startX + rectWidth / 2, startY + rectHeight)
        ctx.lineTo(startX + rectWidth / 2, startY + rectHeight + (rectMargin / 2))
        ctx.lineTo(chindX + rectWidth / 2, chindY - rectMargin / 2)
        ctx.lineTo(chindX + rectWidth / 2, chindY)
        // ctx.lineTo(startX+rectWidth/2,startY+rectHeight+12)
        ctx.stroke()
    }
    // drawTextItem(500, 10, 100, 50)
    // drawLine(500, 10, 100, 72)
    // 
    // 计算当前的框的位置
    // 计算子节点的位置
    // 计算当前节点下最后一组有几个节点
    let getAllCount = (node)=>{
        let count = 0
            node.children.reduce((pre,cur)=>{
                count += pre + (cur.children&&cur.children.length>0)? getAllCount(cur):1
                return count
            },0)
        return count
    }
    // console.log(getAllCount(node),'count')
    // 循环对象 计算当前盒子的起始位置
    var render = (stratX,startY,node)=>{
        // 计算起始位置
        let rectWidth = 100
        let rectHeight =  50
        let rectMargin = 12
        // 初次进入,绘制第一个框
        drawTextItem(stratX,startY,rectWidth,rectHeight,node.title)
        if(node.children && node.children.length>0){
             // 查看当前节点下,最后一层有多少元素
            let nodecount = getAllCount(node)
            // 计算child 元素的 起始位置
           // startX,500为第一个元素坐标  
           // 子元素 长度 = 个数*宽度 + (个数-1)*间距
           // 子元素 中心位置  长度 / 2
           // 子元素 起始位置 500 - 长度 (离中心500位置的坐标),为负数则是超过画布
            let start = stratX - (nodecount * rectWidth +(nodecount-1)*rectMargin) /2

            // 绘制子节点
            node.children.forEach((item,index)=>{
                // 获取当前子节点下有多少元素
                let nodeLenth = 0
                let childrenStartX = 0
                // 当前子节点 y的位置,距离顶部的位置
                let childrenStartY = startY+rectMargin + rectHeight
                if(item.children && item.children.length>0){
                    nodeLenth = getAllCount(item)
                }else{
                    // 如果没有,空一格出来(父节点有位置)
                    nodeLenth = 1
                }
                // 计算子元素的位置 
                childrenStartX = start + (nodeLenth * rectWidth + (nodeLenth-1)*rectMargin)/2
                start = start + (nodeLenth * rectWidth + (nodeLenth-1)*rectMargin) + rectMargin
                // 绘制子节点
                drawLine(stratX,startY,childrenStartX,childrenStartY)
                render(childrenStartX,childrenStartY,item)
            })


        }
    }
    render(500,10,node)
})
</script>
<style scoped>
.mian {
    width: 100vw;
    height: 100vh;
    overflow: hidden;
}

.canvas {
    width: 1000px;
    height: 500px;
    background: rgba(0, 0, 0, 0.2);
    margin: 50px auto;
}
</style>

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
抱歉,我是一名语言模型,无法进行形绘制。但是我可以提供一些参考代码供您参考。 HTML代码: ``` <!DOCTYPE html> <html> <head> <title>Canvas Tree</title> <style type="text/css"> canvas { border: 1px solid black; } </style> </head> <body> <canvas id="canvas" width="500" height="500"></canvas> <script type="text/javascript"> window.onload = function() { var canvas = document.getElementById("canvas"); var ctx = canvas.getContext("2d"); drawTree(ctx, 250, 500, 100, -Math.PI/2, 10, 0.7); } function drawTree(ctx, x, y, len, angle, depth, branchWidth) { ctx.beginPath(); ctx.save(); ctx.strokeStyle = "brown"; ctx.fillStyle = "brown"; ctx.shadowBlur = 5; ctx.shadowColor = "rgba(0,0,0,0.5)"; ctx.lineWidth = branchWidth; ctx.translate(x, y); ctx.rotate(angle); ctx.moveTo(0, 0); ctx.lineTo(0, -len); ctx.stroke(); if (depth <= 0) { ctx.restore(); return; } drawTree(ctx, 0, -len, len*0.8, angle+Math.PI/4, depth-1, branchWidth*0.6); drawTree(ctx, 0, -len, len*0.8, angle-Math.PI/4, depth-1, branchWidth*0.6); ctx.restore(); } </script> </body> </html> ``` JavaScript代码解释: 1. 获取canvas对象及其上下文 ``` var canvas = document.getElementById("canvas"); var ctx = canvas.getContext("2d"); ``` 2. 绘制树枝 ``` function drawTree(ctx, x, y, len, angle, depth, branchWidth) { ctx.beginPath(); ctx.save(); ctx.strokeStyle = "brown"; ctx.fillStyle = "brown"; ctx.shadowBlur = 5; ctx.shadowColor = "rgba(0,0,0,0.5)"; ctx.lineWidth = branchWidth; ctx.translate(x, y); ctx.rotate(angle); ctx.moveTo(0, 0); ctx.lineTo(0, -len); ctx.stroke(); ``` 3. 绘制树叶 ``` if (depth <= 0) { ctx.restore(); return; } drawTree(ctx, 0, -len, len*0.8, angle+Math.PI/4, depth-1, branchWidth*0.6); drawTree(ctx, 0, -len, len*0.8, angle-Math.PI/4, depth-1, branchWidth*0.6); ``` 4. 绘制整棵树 ``` drawTree(ctx, 250, 500, 100, -Math.PI/2, 10, 0.7); ``` 其中,250和500是树的起始坐标,100是树的长度,-Math.PI/2是树的初始角度,10是树的深度,0.7是树干的宽度。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值