最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • canvas绘制组织架构图

    正文概述 掘金(LeeCC)   2021-01-08   863

    目的: 绘制组织架构图

    大概效果如下图: canvas绘制组织架构图

    需求拆分

    1. 默认相对画布居中
    2. 一级只有一个节点,二级水平分布,二级以下,垂直分布
    3. 矩形框框、每一层级的宽度、高度固定
    4. 父子节点,通过线条链接
    5. 父子节点、兄弟节点存在一定的间距
    6. 支持点击小圆圈折叠展开
    7. 每个矩形元素,会显示文字,文字居中对齐,过长则自动换行

    准备工作

    先学习一下canvas,了解canvas绘图的基本套路

    根据以上的需求拆分,先用canvas的API画出各个单独的元素

    1. 首先画矩形框

    canvas绘制组织架构图

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Rect</title>
    </head>
    <body>
    <canvas id="canvas" width="1024" height="2768" ></canvas>
    </body>
    <script>
        const canvas = document.getElementById('canvas');
        const ctx = canvas.getContext('2d');
        const width = canvas.width;
        const centerX = width / 2
        // 绘制的方法
        const drawDept = (ctx, config) => {
            ctx.fillStyle = config.fillStyle;
            ctx.rect(config.x, config.y, config.width, config.height);
            ctx.fill();
            ctx.lineWidth = 1;
            ctx.strokeStyle = '#222';
            ctx.rect(config.x, config.y, config.width, config.height);
            ctx.stroke()
        }
        const firstLevelItem = {
            width: 150,
            height: 75,
            x: centerX - 150/2,
            y: 10
        }
        const firstLevelConfig = {
            width: firstLevelItem.width,
            height: firstLevelItem.height,
            x: firstLevelItem.x,
            y: firstLevelItem.y,
            fillStyle: "transparent",
        }
    drawDept(ctx,firstLevelConfig)
    
    </script>
    </html>
    
    

    2. 画圆圈

    canvas绘制组织架构图

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Circle</title>
    </head>
    <body>
    <canvas id="canvas" width="1024" height="2768" ></canvas>
    </body>
    <script>
        const canvas = document.getElementById('canvas');
        const ctx = canvas.getContext('2d');
        const width = canvas.width;
        // 绘制的方法
        const drawCollapseButton = (ctx,config)=>{
            ctx.fillStyle = '#fff'
            ctx.beginPath();
            ctx.arc(config.x,config.y,config.r,0,2*Math.PI);
            ctx.stroke();
    
            ctx.fill();
            drawText(ctx,{
                x:config.x,
                y:config.y + 8,
                text: '+',
                fontSize: 20,
                lineHeight: 20,
                containerHeight: config.r * 2,
                color: '#222',
                maxWidth: config.r * 2
            })
        }
    
        drawCollapseButton(ctx,{
            x: 20,
            y: 20,
            r: 10
        })
    
    </script>
    </html>
    
    

    3. 画连线

    只要有水平和垂直两种布局,于是连线也有两种。 两种连线的共同点是,有起始点和结束点,0-n个中间点

    canvas绘制组织架构图

    代码有点多,只给出连线的函数

    const drawLine = (ctx, config) => {
        ctx.strokeStyle = config.borderColor;
        ctx.lineWidth = 1;
        ctx.beginPath();
        ctx.moveTo(config.startPoint.x, config.startPoint.y);
        config.middlePoint.forEach(item => {
            ctx.lineTo(item.x, item.y)
        })
        ctx.lineTo(config.endPoint.x, config.endPoint.y)
    
        ctx.stroke();
    }
        
    

    4. 写文字

    canvas有提供些文字的API,但是不支持换行,于是,自己加了换行的代码 思路就是,

    1. 根据换行符 \n 切成数组
    2. 遍历数据,将每个元素,切成单个字符
    3. 使用canvas的apimeasureText测量字符的宽度
    4. 达到一行的宽度则作为一个字符串,存入数组
    5. 遍历数据画出字符

    canvas绘制组织架构图 canvas绘制组织架构图

    const workBreak = (ctx, text, maxWidth) => {
            const objText = ctx.measureText(text);
            let arrFillText = []
            if (objText.width > maxWidth) {
                const arrText = text.split('')
    
                let newText = '';
                arrText.forEach((world, index) => {
                    const currentText = `${newText}${world}`
                    const {width} = ctx.measureText(currentText);
                    if (width >= maxWidth) {
                        arrFillText.push(newText)
                        newText = world
                    } else {
                        newText = currentText
                        if (index === arrText.length - 1) {
                            arrFillText.push(newText)
                        }
                    }
                })
            } else {
                arrFillText = [text]
            }
    
            return arrFillText
        }
    
        const drawText = (ctx, config) => {
            ctx.font = `${config.fontSize}px serif`
            ctx.fillStyle = config.color
            ctx.textAlign = 'center'
            const arrText = config.text.split('\n');
    
            const arrDrawText = []
            arrText.forEach((item) => {
                const arr = workBreak(ctx, item, config.maxWidth)
                arr.forEach((text) => {
                    arrDrawText.push(text)
                })
            })
    
            const h = arrDrawText.length * config.lineHeight;
            const gap = config.containerHeight - h
    
            arrDrawText.forEach((text, index) => {
                ctx.fillText(text, config.x, config.y + index * (config.lineHeight) + gap / 2, config.maxWidth)
            })
        }
        
    

    5. 判断鼠标在当前区域内

    isPointInPath用于判断在当前路径中是否包含检测点的方法

    ctx.isPointInPath(mousePoint.x,mousePoint.y)
    

    canvas绘制组织架构图

    6. 处理数据

    因为架构图,展示出来,就是一颗树的形状,所以处理的过程中,递归会使用的比较频繁。
    与html的常用元素,如div等有各种定位,canvas画的每个元素,都需要自己计算坐标位置。所以,必须在处理数据的同时,也把坐标确定下来。
    接下来一节,数据处理的一些问题和思路
    

    处理数据

    先画个图,元素之间的各种关系会更清晰

    canvas绘制组织架构图 canvas绘制组织架构图

    宽度计算

    1. 因为第一层只有一个节点,所以,第一层的最大宽度maxWidth=[所有二层]maxWidth之和+(gapV * [子元素数量-1])与第二层相同即可
    2. 第二层某个元素的最大宽度maxWidth=[maxWidth最大的第三层子元素的]maxWidth
    3. 从第三层开始,当前元素的maxWidth=[子]maxWidth+(gapV * [当前级别-2])

    canvas绘制组织架构图

    高度计算

    1. 水平间距gapH
    2. 第一层的高度maxHeight=[第二层中,最高的一组]maxHeight+gapH+[第一层的]height
    3. 第二层的高度及以下maxHeight=[所有子元素]height*gapH*[子元素-1]数量+[自身的]height+gapH

    元素起始点坐标

    有了最大宽度和高度,就可以确定各个元素的坐标

    连接点确定

    1. 水平布局的连接点在元素的中间

    与父元素的连接点parentLinkPoint

    起始点=父节点的childLinkPoint.x - (父节点的maxWidth)/2
    index 在兄弟中的排位,0开始
    x = 起始点 + index * gapV + 前面几个兄弟的maxWidth 之和
    y = 父节点的childLinkPoint.y + gapH
    

    与子元素的连接点 childLinkPoint

    x = x + 当前节点的实际宽度[上图灰色块]的一半
    y = y
    
    1. 垂直布局的连接点

    与父元素的连接点parentLinkPoint

    x 在于父childLinkPoint.x 往右偏移gapV一半的位置
    y 在于父childLinkPoint.y 往下偏移gapH的位置
    

    与子元素的连接点 childLinkPoint

    x = x
    y = y 当前节点实际高度的一半
    

    起源地下载网 » canvas绘制组织架构图

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元