1

I am implementing a patch-cord based system for connecting boxes. I would like to know what my best options are for rendering the patch-cords, here is a screen shot, the cable is a mock-up in Gimp:

enter image description here

The patcher background now is a <div>, so are the boxes and so are the "ports" (the blueish little squares inside the boxes that are the terminals of the cables).

Should I go straight to making the background a canvas or dynamically updated SVG? Or is it better to use an HTML element for each cord. I could see these advantage over canvas:

  • perhaps the CSS can be massaged to make the coords automatically move when the boxes move
  • I'm given a spatial partitioning for free, i.e. I will have an easier job detecting clicks on the coords.
  • I could use z-index to solve the layering on top of the boxes

The disadvantages of layered HTML elements might be

  • performance when there are many cords?
  • what happens when cords overlap. Any problems with transparent backgrounds?

Edit: In terms of interactivity, I am concluding that inline SVG would be the best approach. However, I am worried about the performance. For example, this simple demo where you can drag some SVG components around is ridiculously slow on a modern computer. Is it just bad programming or an inherent problem of SVG?

0__
  • 66,707
  • 21
  • 171
  • 266
  • I built something very similar recently, ended up using D3 over canvas. (will give a fuller answer asap) – atmd Jul 21 '15 at 08:41
  • Ok, thanks. I have looked at [this question](http://stackoverflow.com/questions/7034/graph-visualization-library-in-javascript) in the meantime. So far, [jPlump](https://github.com/sporritt/jsplumb) seems the most light weight and reduced solution. But it's mixed open source/commercial project, something I usually avoid at all costs. – 0__ Jul 21 '15 at 08:46
  • SVG is a good choice if the combined number of boxes+connectors is small or medium. SVG elements are full DOM elements so you will get built-in eventing on each element. As the number of elements becomes large, the cost of built-in eventing also becomes large so you might then switch to Canvas. Canvas receives events but it's drawings do not, so you will have to hit-test the mouse against your boxes in code (not difficult). I would echo @atmd in saying that d3 is an excellent library and is worth checking out as a tool for your project. – markE Jul 21 '15 at 17:09

2 Answers2

1

I ended up using <div> elements for the boxes and a single <svg> for all the patch cords.

0__
  • 66,707
  • 21
  • 171
  • 266
1

I wanted to make a working example with svg lines.
This is how far i got (i hope it is of some use).
But its going to take a lot of time to add all the creating of paths etc.

$(document).ready(function() {
  /*******
  Setting random position on the boxes
  ********/
  $('.box').each(function() {
    $(this).css({
      top: Math.random() * (document.body.clientHeight - $(this).height()),
      left: Math.random() * (document.body.clientWidth - $(this).width())
    });
  });
  /*****
  Handles behavior. click and svg create/place
  ******/
  $('.handle').click(function(e) {
    $(this).css("background-color", "red");
    var x = e.pageX;
    var y = e.pageY;
    console.log(x + " " + y);
  });

  /*******
  Grabbing and moving boxes
  *******/
  var $dragging = null;
  var offsetpos = [0.0, 0.0];

  $(document.body).on("mousemove", function(e) {
    if ($dragging) {
      var y = e.pageY - offsetpos[1];
      var x = e.pageX - offsetpos[0];
      if (x < 0 || y < 0) return;
      if (x > document.body.clientWidth - $dragging.width()) return;
      if (y > document.body.clientHeight - $dragging.height()) return;
      $dragging.offset({
        top: y,
        left: x
      });
    }
  });

  $(document.body).on("mousedown", ".box", function(e) {
    var $e = $(e.target);
    if ($e.hasClass("handle")) return;
    $dragging = $(e.target);
    offsetpos = [e.pageX - this.offsetLeft,
      e.pageY - this.offsetTop
    ];
  });

  $(document.body).on("mouseup", ".box", function(e) {
    $dragging = null;
  });
});
.network-wrapper {
  border: 5px solid fireBrick;
  border-radius: 2px;
  height: 200px;
  width: 90vw;
}
.field {
  width: 100%;
  height: 100%;
  position: relative;
}
.box {
  position: absolute;
  border: 2px solid black;
  width: 100px;
  height: 30px;
  cursor: move;
}
.box p {
  pointer-events: none;
  position: absolute;
  margin: 0;
  text-indent: 5px;
  margin-top: 5px;
}
.box .handle {
  cursor: pointer;
  position: absolute;
  background-color: #666;
  width: 10px;
  height: 10px;
}
.handle.top {
  top: 0;
}
.handle.left {
  left: 0;
}
.handle.bottom {
  bottom: 0;
}
.handle.right {
  right: 0;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<section class="network-wrapper">
  <div class="field">
    <svg width="100%" height="100%">
    </svg>
    <div class="box">
      <div class="handle top left"></div>
      <div class="handle top right"></div>
      <div class="handle bottom left"></div>
      <div class="handle bottom right"></div>
      <p>some info</p>
    </div>
    <div class="box">
      <div class="handle top left"></div>
      <div class="handle top right"></div>
      <div class="handle bottom left"></div>
      <div class="handle bottom right"></div>
      <p>some info</p>
    </div>
    <div class="box">
      <div class="handle top left"></div>
      <div class="handle top right"></div>
      <div class="handle bottom left"></div>
      <div class="handle bottom right"></div>
      <p>some info</p>
    </div>
  </div>
</section>
Persijn
  • 14,624
  • 3
  • 43
  • 72