最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

html - Connect Line Between 2 Elements - Javascript - Stack Overflow

programmeradmin0浏览0评论

I'm currently working on a way to create a single-line diagram using Javascript. I'm currently able to connect two html elements using the following function:

adjustLine (from, to, line) {
  var fT = from.offsetTop  + from.offsetHeight/2;
  var tT = to.offsetTop      + to.offsetHeight/2;
  var fL = from.offsetLeft + from.offsetWidth/2;
  var tL = to.offsetLeft     + to.offsetWidth/2;

  var CA   = Math.abs(tT - fT);
  var CO   = Math.abs(tL - fL);
  var H    = Math.sqrt(CA*CA + CO*CO);
  var ANG  = 180 / Math.PI * Math.acos( CA/H );

  if(tT > fT){
      var top  = (tT-fT)/2 + fT;
  }else{
      var top  = (fT-tT)/2 + tT;
  }
  if(tL > fL){
      var left = (tL-fL)/2 + fL;
  }else{
      var left = (fL-tL)/2 + tL;
  }

  if(( fT < tT && fL < tL) || ( tT < fT && tL < fL) || (fT > tT && fL > tL) || (tT > fT && tL > fL)){
    ANG *= -1;
  }
  top-= H/2;

  line.style["-webkit-transform"] = 'rotate('+ ANG +'deg)';
  line.style["-moz-transform"] = 'rotate('+ ANG +'deg)';
  line.style["-ms-transform"] = 'rotate('+ ANG +'deg)';
  line.style["-o-transform"] = 'rotate('+ ANG +'deg)';
  line.style["-transform"] = 'rotate('+ ANG +'deg)';
  line.style.top    = top+'px';
  line.style.left   = left+'px';
  line.style.height = H + 'px';
}

The function takes 3 parameters:

  • 1) the first html element
  • 2) the second html element
  • 3) the line that connects the elements.

This works fine and outputs the following result:

However, the lines that connect the html elements are a straight line, I would like to have a more natural flow for a diagram, something like the following:

Any advice on how I could go about doing this? Any help is greatly appreciated!

Edit

I'm using html2canvas to create an image of the generated output. This is my code:

var myDiv = document.getElementById('content');

html2canvas(myDiv, {useCORS: true, allowTaint : true}).then(function (canvas) {

            var imgData = canvas.toDataURL("image/png", 1.0);
            var imgData2 = canvas2.toDataURL("image/png", 1.0);

            var pdf = new jsPDF('l', 'pt', [HTML_Width, HTML_Height]);
            pdf.internal.scaleFactor = 30;
            pdf.addImage(imgData, 'PNG', 0, 0, HTML_Width, HTML_Height);

            pdf.addPage();
            pdf.addImage(imgData2, 'PNG', 0, 0, HTML_Width_2, HTML_Height_2);

            pdf.save("my_file.pdf");
});

I'm currently working on a way to create a single-line diagram using Javascript. I'm currently able to connect two html elements using the following function:

adjustLine (from, to, line) {
  var fT = from.offsetTop  + from.offsetHeight/2;
  var tT = to.offsetTop      + to.offsetHeight/2;
  var fL = from.offsetLeft + from.offsetWidth/2;
  var tL = to.offsetLeft     + to.offsetWidth/2;

  var CA   = Math.abs(tT - fT);
  var CO   = Math.abs(tL - fL);
  var H    = Math.sqrt(CA*CA + CO*CO);
  var ANG  = 180 / Math.PI * Math.acos( CA/H );

  if(tT > fT){
      var top  = (tT-fT)/2 + fT;
  }else{
      var top  = (fT-tT)/2 + tT;
  }
  if(tL > fL){
      var left = (tL-fL)/2 + fL;
  }else{
      var left = (fL-tL)/2 + tL;
  }

  if(( fT < tT && fL < tL) || ( tT < fT && tL < fL) || (fT > tT && fL > tL) || (tT > fT && tL > fL)){
    ANG *= -1;
  }
  top-= H/2;

  line.style["-webkit-transform"] = 'rotate('+ ANG +'deg)';
  line.style["-moz-transform"] = 'rotate('+ ANG +'deg)';
  line.style["-ms-transform"] = 'rotate('+ ANG +'deg)';
  line.style["-o-transform"] = 'rotate('+ ANG +'deg)';
  line.style["-transform"] = 'rotate('+ ANG +'deg)';
  line.style.top    = top+'px';
  line.style.left   = left+'px';
  line.style.height = H + 'px';
}

The function takes 3 parameters:

  • 1) the first html element
  • 2) the second html element
  • 3) the line that connects the elements.

This works fine and outputs the following result:

However, the lines that connect the html elements are a straight line, I would like to have a more natural flow for a diagram, something like the following:

Any advice on how I could go about doing this? Any help is greatly appreciated!

Edit

I'm using html2canvas to create an image of the generated output. This is my code:

var myDiv = document.getElementById('content');

html2canvas(myDiv, {useCORS: true, allowTaint : true}).then(function (canvas) {

            var imgData = canvas.toDataURL("image/png", 1.0);
            var imgData2 = canvas2.toDataURL("image/png", 1.0);

            var pdf = new jsPDF('l', 'pt', [HTML_Width, HTML_Height]);
            pdf.internal.scaleFactor = 30;
            pdf.addImage(imgData, 'PNG', 0, 0, HTML_Width, HTML_Height);

            pdf.addPage();
            pdf.addImage(imgData2, 'PNG', 0, 0, HTML_Width_2, HTML_Height_2);

            pdf.save("my_file.pdf");
});
Share Improve this question edited Mar 3, 2020 at 18:40 Brian Moreno asked Feb 23, 2020 at 16:30 Brian MorenoBrian Moreno 1,0274 gold badges12 silver badges40 bronze badges 7
  • Could you upload rest of the code? – Jacek Rojek Commented Feb 23, 2020 at 16:55
  • @JacekRojek sure, here's the code in CodePen: codepen.io/MejorCodigo/pen/YzXpmYY. – Brian Moreno Commented Feb 23, 2020 at 17:16
  • this looks overkill but I would go like this. Consider the somehow manhattan distance: x axis counts double. Then do Astar from point A to B. (this would favor going to the top first). Then for point C to B you have painted the canvas, A star will avoid crossing the line and should stay "next" to it. This is a random thought of 5min though – grodzi Commented Feb 23, 2020 at 18:58
  • A* implementation would be overkill for this, but would definitely work. Is there limitations on how many lines a single object would have? How dynamic would this system need to be? Can user interaction move the elements around, updating the lines in realtime? Are the number of connected elements all known upfront at once or dynamically added? – Phaelax Commented Feb 25, 2020 at 16:45
  • @Phaelaxz hi, each object is added dynamically to a <div>... using a json file I know how many elements should be printed and the parent/child of each element, also the user should not move the elements around thus the lines don't need to be updated in real time, and ideally there should be 2 lines per connection. – Brian Moreno Commented Feb 25, 2020 at 16:52
 |  Show 2 more ments

2 Answers 2

Reset to default 6 +100

So heree is my solution with SVG!

const xmlns   = 'http://www.w3/2000/svg'
  ,   svgLink = 'http://www.w3/1999/xlink'
  ,   elmSVG  = document.getElementById('elmSVG')
  ,   bot_inversor  = 80
  ,   top_fotovolta = 300
  ;
for (let i=0;i<4;i++)
  {
  let x = 10 + (i*60)
    , fotovolta = document.createElementNS(xmlns, 'use');

  fotovolta.setAttributeNS(svgLink, 'xlink:href', '#fotovoltaico');

  fotovolta.setAttributeNS(null, 'x', x);
  fotovolta.setAttributeNS(null, 'y', top_fotovolta);
  fotovolta.setAttributeNS(null, 'width', '50');
  fotovolta.setAttributeNS(null, 'height', '70'); 

  elmSVG.appendChild(fotovolta);

  adjustLines(i);
  }
function adjustLines(item)
  {
  let left = (item<2)    // the hard part...  
    , b1 = 25 + (item *60) + (left?0:20)
    , b2 = b1 + (left?20:-20)
    , a1 = 105 + (item *10) + (left?0:10)
    , a2 = a1 + (left?5:-5)
    , l1 = 50 + (left?item:3-item) *30
    , l2 = l1 + 10
    ;
  let jLine1 = document.createElementNS(xmlns, 'polyline');
  jLine1.setAttributeNS(null, 'points', `${b1},${top_fotovolta} ${b1},${bot_inversor+l1} ${a1},${bot_inversor+l1} ${a1},${bot_inversor}`);
  elmSVG.appendChild(jLine1);   

  let jLine2 = document.createElementNS(xmlns, 'polyline');
  jLine2.setAttributeNS(null, 'points', `${b2},${top_fotovolta} ${b2},${bot_inversor+l2} ${a2},${bot_inversor+l2} ${a2},${bot_inversor}`);
  elmSVG.appendChild(jLine2);   
  }
#elmSVG {
  width: 250px;
  height: 380px;
  background-color: #b4f0f0;
  margin: 1em;
}
#elmSVG * {
  fill:none;
  stroke:#2f363d;
  stroke-width:2px;
}
.curveSVG {
  stroke-linecap:round;
}
<h2>Connecting Lines</h2>

<svg id="elmSVG" xmlns="http://www.w3/2000/svg" viewBox="0 0 250 380">
  <defs>
    <symbol id="inversor" viewBox="0 0 70 70">
      <rect x="0" y="0" width="70" height="70" />
      <line x1="70" x2="0" y1="0" y2="70"  />
      <line x1="10" x2="30" y1="15" y2="15" />
      <line x1="10" x2="30" y1="20" y2="20"  />
      <path d="M 40,55 Q 45,45 50,55 T 60,55" class="curveSVG" />
    </symbol>
    <symbol id="fotovoltaico"  viewBox="0 0 50 70">
      <rect x="0" y="0" width="50" height="70" />
      <line x1="0" x2="25" y1="0" y2="20"  />
      <line x1="50" x2="25" y1="0" y2="20"  />
    </symbol>
  </defs>

  <use xlink:href="#inversor" x="90"  y="10" width="70" height="70" />
</svg>

other solution with <canvas>

const eCanvas = document.getElementById('elmCanvas')
  ,   cCanvas = eCanvas.getContext('2d')
  ,   bot_inversor  = 80
  ,   top_fotovolta = 300
  ;

// set background color
cCanvas.fillStyle = 'rgb(178, 240, 240)'
cCanvas.fillRect(0, 0, eCanvas.width, eCanvas.height )

Draw_Inversor( cCanvas, (eCanvas.width/2 - 70/2), 10)

for (let i=0;i<4;i++)
  {
  Draw_Fotovoltaico( cCanvas, 10+(i*60), top_fotovolta );
  Draw_connectsLines( cCanvas, i, 4, (eCanvas.width/2), (10+(i*60)+25) );
  }

function Draw_Inversor( ctx, x, y )
  {
  ctx.beginPath();
  ctx.moveTo(x+70, y);
  ctx.lineTo(x, y+70);
  ctx.lineTo(x, y);
  ctx.lineTo(x+70, y);
  ctx.lineTo(x+70, y+70);
  ctx.lineTo(x, y+70);
  ctx.moveTo(x+10, y+15);
  ctx.lineTo(x+30, y+15);
  ctx.moveTo(x+10, y+20);
  ctx.lineTo(x+30, y+20);
  ctx.moveTo(x+50, y+55);
  ctx.arc(x+45, y+55, 5, 0, Math.PI, true);
  ctx.moveTo(x+60, y+55);
  ctx.arc(x+55, y+55, 5, 0, Math.PI, false);
  ctx.stroke();
  }
function Draw_Fotovoltaico( ctx, x, y )
  {
  ctx.beginPath();
  ctx.moveTo(x, y);
  ctx.lineTo(x+25, y+20);
  ctx.lineTo(x+50, y);
  ctx.lineTo(x, y);
  ctx.lineTo(x, y+70);
  ctx.lineTo(x+50, y+70);
  ctx.lineTo(x+50, y);
  ctx.stroke();
  }
function Draw_connectsLines( ctx, no, maxOcc, topCenter, botCenter )
  {
  let mid   = maxOcc/2
    , left  = no < mid
    , decal = left ? no :(maxOcc/2)-no+1 
    , topL1 = 50+ (decal *30)
    , topL2 = topL1 +10
    , topX1 = topCenter + (left? -(mid-decal)*10 : +(mid-decal)*10 )
    , topX2 = topX1 +(left?+5:-5)
    , botX1 = botCenter + (left?-10:+10)
    , botX2 = botCenter + (left?+10:-10) 
    ;
  ctx.beginPath();
  ctx.moveTo(topX1, bot_inversor);
  ctx.lineTo(topX1, bot_inversor +topL1 );
  ctx.lineTo(botX1, bot_inversor +topL1 );
  ctx.lineTo(botX1, top_fotovolta );
  ctx.stroke();

  ctx.beginPath();
  ctx.moveTo(topX2, bot_inversor);
  ctx.lineTo(topX2, bot_inversor +topL2 );
  ctx.lineTo(botX2, bot_inversor +topL2 );
  ctx.lineTo(botX2, top_fotovolta );
  ctx.stroke();
  }
#elmCanvas {
  margin: 1em;
}
<canvas id="elmCanvas" width="250" height="400"></canvas>

发布评论

评论列表(0)

  1. 暂无评论