两个盒子之间的指引线请问怎么实现?

clipboard.png
请问图中这种对应关系的指引线怎么实现,右边的盒子可能会有好几个,怎样动态的和左边的盒子建立指引线的联系?请了解的大佬给个实现的思路和方法

阅读 5.1k
5 个回答

额,我的方法有点笨,就是将线与块放在一个容器内,计算他们在容器内的左标,然后连线。c 可以拖拽,线在右侧可以跟随,但是拖动不好用,,,
大概思路如下:

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8" />
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <title>Page Title</title>
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <script src="https://cdn.jsdelivr.net/npm/lodash@4.17.10/lodash.min.js"></script>
  <style>
    body {
      position: relative;
      border: 1px solid #ccc;
      height: 500px;
      width: 500px;
    }
    body > div {
      position: absolute;
      width: 100px;
      height: 50px;
      background: blueviolet;
      color: white;
    }
    div.line {
      border-top: 2px dashed red;
      height: 0;
    }
    #a {
      top: 200px;
      left: 100px;
    }
    #b {
      top: 100px;
      left: 300px;
    }
    #c {
      top: 300px;
      left: 300px;
    }
  </style>
</head>
<body>
  <div id='a'>A</div>
  <div id='b'>B</div>
  <div id='c' style="background: teal">C, drag me</div>
  <canvas id='anc' style='position: absolute; z-index: 1'></canvas>
  <div id='anb' class='line'></div>
  <script>
    const ela = document.getElementById('a');
    const elb = document.getElementById('b');
    const elc = document.getElementById('c');
    const lineAToB = document.getElementById('anb');
    const lineAToC = document.getElementById('anc');
    const container = document.body;
    let lineCtx = lineAToC.getContext('2d');

    const drawPaddingX = 10;
    const drawPaddingY = 10;

    // 元素左上角,在容器内的坐标
    let pEla = getCordInContainer(ela);
    let pElb = getCordInContainer(elb);
    let pElc = getCordInContainer(elc);

    // 元素本身的长宽
    let sEla = { width: ela.offsetWidth, height: ela.offsetHeight };
    let sElb = { width: elb.offsetWidth, height: elb.offsetHeight };
    let sElc = { width: elc.offsetWidth, height: elc.offsetHeight };

    function getCordInContainer(elem) {
      let eX = elem.offsetLeft;
      let eY = elem.offsetTop;
      return { eX, eY };
    }

    // 三角函数忘得差不多了,这里面角度的符号没有确定,随便写的。。。
    function setLine() {
      // 这个坐标差,应该从两个连接点换直角三角形计算
      let diffXAB = (pElb.eX - sElb.width )- pEla.eX ;
      let diffXAC = (pElb.eX -sElb.width) - pEla.eX ;
      let diffYAB = pEla.eY + sEla.height/2 - sElb.height/2 - pElb.eY ;
      let diffYAC = pEla.eY + sEla.height/2 - sElb.height/2 - pElb.eY ;

      // 求夹角
      let lineABDeg = Math.atan2(diffYAB, diffXAB) * 180 / Math.PI;
      let lineABRad = Math.atan2(diffYAB, diffXAB);
      let lineWidth = diffYAB/Math.sin(lineABRad);
      // 求初始位置,主要是旋转后的偏移量
      let lineStartX = pEla.eX + sEla.width - (lineWidth/2 - lineWidth/2*Math.cos(lineABRad));
      let lineStartY = pEla.eY + sEla.height/2 - lineWidth/2*Math.sin(lineABRad);

      let str = `width: ${lineWidth}px; top: ${lineStartY}px; left: ${lineStartX}px`;
      console.log(str);
      lineAToB.style.cssText= str;
      lineAToB.style.transform = `rotate(${-lineABDeg}deg)`;
    }
    // 将线放置到正确位置
    setLine();
    function drawLine(canElem, ctx) {
      pElc = getCordInContainer(elc);
      sElc = { width: elc.offsetWidth, height: elc.offsetHeight };
      // 这个坐标差,应该从两个连接点换直角三角形计算
      // 调整这个就可一移动到左面
      let diffXAC = (pElc.eX -sElc.width) - pEla.eX + 2 * drawPaddingX;
      let diffYAC = Math.abs(pEla.eY + sEla.height/2 - sElc.height/2 - pElc.eY) + 2 * drawPaddingY;

      let lineStartX = pEla.eX + sEla.width;
      let lineStartY = pEla.eY + sEla.height/2;
      let lineEndX = pElc.eX;
      let lineEndY = pElc.eY + sElc.height/2;
      let lineCordX = Math.min(lineStartX, lineEndY) - drawPaddingX;
      let lineCordY = Math.min(lineStartY, lineEndY) - drawPaddingY;

      
      canElem.width = Math.abs(diffXAC);
      canElem.height = Math.abs(diffYAC);
      canElem.style.setProperty('left', lineCordX + 'px', '');
      canElem.style.setProperty('top', lineCordY + 'px', '');

      ctx.clearRect(0,0, canElem.width, canElem.height);
      ctx.beginPath();
      ctx.moveTo(lineStartX - lineCordX, lineStartY - lineCordY);
      ctx.lineTo(lineEndX - lineCordX, lineEndY - lineCordY);
      ctx.stroke();
    }
    drawLine(lineAToC, lineCtx);
    let draging = false;
    elc.onmousedown = () => {
      draging = true;
    }
    container.onmousemove = _.throttle((e) => {
      if (draging) {
        elc.style.setProperty('left', elc.offsetLeft + e.movementX + 'px', '');
        elc.style.setProperty('top', elc.offsetTop + e.movementY + 'px', '');
        drawLine(lineAToC, lineCtx);
      }
    }, 10);
    container.onmouseleave = () => {
      draging = false;
    }
    container.onmouseup = () => {
      draging = false;
      console.log(draging);
    }
  </script>
</body>
</html>

::after ::before

  1. 计算两个盒子的中心点
  2. 用伪元素画一个 <div> ,用它的某个边框作为连线
  3. 用三角函数计算盒子的位置,放过去

涉及到自适应吗,如果涉及到自适应,建议把三个盒子放到一个容器里面,如果不涉及到自适应,方式就很多了,只有位置确定好,之间的线条可以用盒子去模拟,也可以用图片等等

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题