How to update the lines so that they do not break away from the rectangles after dragging?

Hi!. I beg you to help me. I’m creating a small chart and it’s not working
make the connecting lines between the rectangles so that they stretch
after moving the rectangle-nodes. What we have:

  1. 3 rectangles created in OOR js svg;
  2. created connecting lines with rectangles (in OOP js svg);
  3. function for moving rectangle-nodes.
    What is the problem. When dragging nodes, the lines break away from the rectangles.
    An example of how a diagram works here/
    I ask for help in creating a function for updating the line when dragging the nodes of the diagram.
    I think that it needs to put the value of the points of contact, put the name of the update function
    to the code of the drag/drop function

Can you share a screenshot and a live example of what you have so far? Thanks!

This post was flagged by the community and is temporarily hidden.

The short answer is to have your code for drawing the lines to be updated as part of each drag operation.

Can you share the JS you are using for drawing the rectangles and the lines?

:slight_smile:

<!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>breaking-the-line</title>
  <style>
    .background {
      fill: #eee;
    }
    .static {
      cursor: not-allowed;
    }
    .draggable {
      cursor: move;
    }
  </style>
</head>
<body>
  <h1>Dynamic diagram. Breaking connecting lines after dragging rectangles</h1>
    <h2>How to update the lines so that they do not break away from the rectangles after dragging?</h2>
<svg xmlns="http://www.w3.org/2000/svg" id="forDraw" viewBox="0 0 1330 420" onload="makeDraggable(evt)">
  </svg>
    <script type="text/javascript">   
      function makeDraggable(evt) {
        var svg = evt.target;
        svg.addEventListener('mousedown', startDrag);
        svg.addEventListener('mousemove', drag);
        svg.addEventListener('mouseup', endDrag);
        function getMousePosition(evt) {
          var CTM = svg.getScreenCTM();
          return {
            x: (evt.clientX - CTM.e) / CTM.a,
            y: (evt.clientY - CTM.f) / CTM.d
          };
        }
        var selectedElement, offset;
        function startDrag(evt) {
          if (evt.target.classList.contains('draggable')) {
            selectedElement = evt.target;
            offset = getMousePosition(evt);
            offset.x -= parseFloat(selectedElement.getAttributeNS(null, "x"));
            offset.y -= parseFloat(selectedElement.getAttributeNS(null, "y"));
          }
        }
        function drag(evt) {
          if (selectedElement) {
            var coord = getMousePosition(evt);
            selectedElement.setAttributeNS(null, "x", coord.x - offset.x);
            selectedElement.setAttributeNS(null, "y", coord.y - offset.y);
          }
        }
        function endDrag(evt) {
          selectedElement = null;
        }
      }   
    var recWidth = '200';
    var recHeight = '80';
    function Rectangle(width,height, location={x: 400, y: 50},style='draggable', fill = 'red', stroke = 'green',id='ivan') {
   this.width = recWidth;
   this.height =  recHeight;  
   this.location = location;
   this.style = style; 
   this.fill = fill;
   this.stroke = stroke;
   this.id = id;
   
     this.draw = function() { 
 forDraw.innerHTML += `<rect  width="${this.width}" height="${this.height}"  x="${this.location.x}" y="${this.location.y}" 
  class="${this.style}" fill="${this.fill}" stroke="${this.stroke}" id="${this.id}" />`;

     }	
}
Rectangle.prototype.draw = function() {
   if (forDraw.getElementById(this.id)) forDraw.getElementById(this.id).remove();
   forDraw.innerHTML += `<rect  width="${this.width}" height="${this.height}"  x="${this.location.x}" y="${this.location.y}" 
  class="${this.style}" fill="${this.fill}" stroke="${this.stroke}" id="${this.id}" />`;
}
var rec1 = new Rectangle();
rec1.draw();
var rec2 = new Rectangle(150,90, {x: 300, y:300}, style='draggable','yellow', 'red','petro');
rec2.draw();
var rec3 = new Rectangle(150,90, {x: 550, y:300}, style='draggable','green', 'blue','dima');
rec3.draw();
function Lines(x1=rec1.location.x+100, y1=rec1.location.y+80, x2=rec2.location.x+100, y2=rec2.location.y, stroke='blue',id='l1') {
     this.x1 = x1;
   this.y1 = y1;
   this.x2 = x2;
   this.y2 = y2;   
   this.stroke = stroke;
   this.id = id;
    this.draw = function() {
      forDraw.innerHTML += `<line x1="${this.x1}" y1="${this.y1}"  x2="${this.x2}" y2="${this.y2}" 
 stroke="${this.stroke}" id="${this.id}" />`;   
    }
   }
  
var connect1 = new Lines();
connect1.draw();
var x3  = rec3.location.x+100;
  var y3  = rec3.location.y; 
 /////
var connect2 = new Lines(rec1.location.x+100,rec1.location.y+80, rec3.location.x+100, rec3.location.y,stroke='green',id='l2');
connect2.draw();
    </script>  
</body>
</html>

Summary

This text will be hidden

To add to what Kirupa was saying…
If you created a way of having groups you could update all items in the group on drag. So drag by the rectangle and then update all other items by the same offset. Then you could add a circle (can be invisible if you want) as the point where the line starts/ends. Then have another object/list that keeps track of what points join up with other points and then draw the lines appropriately. Im not sure how the drag is working right now so cant make an example just yet and have to go out in a sec but might make something when I get back coz svg is something I really want to learn.
Oh and I had a quick look at grouping in svg…its a pain. “g” doesnt have x y cords and cant automatically drag things together (such a shame). One person used the idea of adding an svg element that can, here, that seems like a kinda good way of doing it. Or your going to have to implement something yourself, which wouldnt be to bad…thats why I added the data-group attribute.

But now to what really got my interest. Why are you using innerHTML to get stuff done? Why not use the dom when theres so much there to use, its prolly more performant, etc. Like I said, I still have to learn this stuff but I had a quick look around and made this…


    // Creates and mods an svg elements attributes
    function svgElement(element, attributes) {
      if (typeof element == 'string') element = document.createElementNS('http://www.w3.org/2000/svg', element);
      for (prop in attributes) {
        element.setAttribute(prop, attributes[prop])
      }
      return element;
    }

      var rec = svgElement('rect', {
      width: 200,
      height: 80,
      x: 400,
      y: 50,
      class: 'draggable',
      fill: 'red',
      stroke: 'green',
      id: 'ivan',
      "data-group": "one"
    });

    var circle1 = svgElement('circle', {
      cx: rec.x.baseVal.value + rec.width.baseVal.value / 2,
      cy: rec.y.baseVal.value + rec.height.baseVal.value,
      r: 10,
      "data-group": "one"
    });

    forDraw.appendChild(rec);
    forDraw.appendChild(circle1);

This will add the first rectangle and the point where the line starts. Am I doing it the right/best way, no idea, got lots more to learn myself.
Like I said Ill prolly add more to this when I get back.

EDIT: Oh, and the best advice I can give you is to use a library. Theres a few around and they have already gone through all this pain and worked out all the problems. But then if your trying to really learn SVG then I totally get it.

1 Like