基于canvas画元素之间的连接线

81 阅读1分钟

image.png

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>用canvas画连接线(直角边)</title>
    <style>
        #app {
            margin: 50px 80px;
        }

        .row {
            position: relative;
            display: flex;
            justify-content: space-between;
        }

        .row .col {
            z-index: 2;
            width: 140px;
        }

        .col .item {
            width: 140px;
            height: 80px;
            margin-right: 30px;
            border: 1px solid #ccc;
            margin-bottom: 30px;
            background-color: pink;
        }

        canvas {
            border: 1px solid #d3d3d3;
            position: absolute;
            left: 0;
            top: 0;
            width: 100%;
            height: 100%;
            z-index: 1;
        }
    </style>
</head>

<body>
    <div id="app">
        <div class="row" id="dots-container">
            <div class="col">
                <div class="item">1</div>
                <div class="item">2</div>
                <div class="item">3</div>
                <div class="item">4</div>
            </div>
            <div class="col">
            </div>
            <div class="col">
                <div class="item">5</div>
                <div class="item">6</div>
                <div class="item">7</div>
            </div>
            <div class="col">
                <div class="item">8</div>
            </div>
            <div class="col">
                <div class="item"></div>
                <div class="item"></div>
                <div class="item"></div>
                <div class="item"></div>
            </div>
            <div class="col">
            </div>
            <div class="col">
            </div>
            <canvas id="bg-line"></canvas>
        </div>
    </div>
    <script>
        // 画线连接两个元素
        const drawLine = (eleFrom, eleTo, eleParent, ctx, {
            col2colWidth
        }) => {
            const rectFrom = eleFrom.getBoundingClientRect();
            const rectTo = eleTo.getBoundingClientRect();
            const rectParent = eleParent.getBoundingClientRect();
            const relativeCenterFrom = {
                x: rectFrom.x - rectParent.x + rectFrom.width / 2,
                y: rectFrom.y - rectParent.y + rectFrom.height / 2
            };
            const relativeCenterTo = {
                x: rectTo.x - rectParent.x + rectTo.width / 2,
                y: rectTo.y - rectParent.y + rectTo.height / 2
            };
            // 同个垂直线上 或 同个水平线上
            if (relativeCenterFrom.x === relativeCenterTo.x || relativeCenterFrom.y === relativeCenterTo.y) {
                ctx.moveTo(relativeCenterFrom.x, relativeCenterFrom.y);
                ctx.lineTo(relativeCenterTo.x, relativeCenterTo.y);
                ctx.stroke();
                return;
            }
            // 不在同个水平或垂直线上,画转角
            const borderRadius = 20;
            const arcFrom = {
                x: relativeCenterFrom.x + col2colWidth / 2,
                y: relativeCenterFrom.y
            };
            const arcTo = {
                x: relativeCenterFrom.x + col2colWidth / 2,
                y: relativeCenterTo.y
            };
            ctx.moveTo(relativeCenterFrom.x, relativeCenterFrom.y);
            ctx.lineTo(arcFrom.x - borderRadius, arcFrom.y);
            ctx.arcTo(arcFrom.x, arcFrom.y, arcFrom.x, arcFrom.y - borderRadius, borderRadius); // 创建弧
            ctx.lineTo(arcTo.x, arcTo.y + borderRadius);
            ctx.arcTo(arcTo.x, arcTo.y, arcTo.x + borderRadius, arcTo.y, borderRadius); // 创建弧
            ctx.lineTo(relativeCenterTo.x, relativeCenterTo.y);
            ctx.stroke();
        }
        // 连接指定的节点
        const connectLines = (itemSelector, colSelector, parentSelector, canvasId, {
            lineColor
        } = {
            lineColor: "#ccc"
        }) => {
            const canvasEle = document.getElementById(canvasId);
            const ctx = canvasEle.getContext("2d");
            const items = document.querySelectorAll(itemSelector);
            ctx.strokeStyle = lineColor;
            ctx.lineWidth = 1;
            const cols = document.querySelectorAll(colSelector);
            let col2colWidth = 0;
            if (cols.length > 1) {
                col2colWidth = cols[1].getBoundingClientRect().left - cols[0].getBoundingClientRect().left;
            }
            for (let index = 0; index < items.length; index++) {
                if (index >= items.length - 1) {
                    break;
                }
                drawLine(items[index], items[index + 1], document.querySelector(parentSelector), ctx, {
                    col2colWidth
                });
            }
        }
        // 初始化canvas,需设置具体宽高
        const initCanvas = (canvasId) => {
            const canvasEle = document.getElementById(canvasId);
            if (!canvasEle) {
                return;
            }
            const width = canvasEle.clientWidth;
            const height = canvasEle.clientHeight;
            canvasEle.setAttribute("width", `${width}px`);
            canvasEle.setAttribute("height", `${height}px`);
        }

        initCanvas("bg-line");
        connectLines(".item", ".col", ".row", "bg-line", {
            lineColor: "blue"
        });
    </script>
</body>

</html>

总结:

  1. canvas需要设置width和height属性,不然会有默认宽高,绘制的图形会被放大或压缩;
  2. canvas绘制线条api:moveTo、lineTo、artTo、stroke;