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