Small SVG app - JavaScript-to-HTML architecture?
11:41 09 Jan 2017

I am on my path learning JavaScript and SVG and recently I was working on this little widget where its user would "drag & drop" various arbitrary SVG paths into canvas space and then double click and manipulate those further.

In my previous question I got very decent reply as to how to code actual draggables in SVG with good mouse handlers/listeners.

Now I am blocked by lack of understanding on how does JavaScript "talks" to HTML?

In this example below as I see it:

  • we generated 6 javascript objects "from" HTML elements.
  • these 6 shapes sort of turned into interactive elements and now can be dragged
  • but when I attempt to doubleclick such a shape using that Select(elem) function - I can't access the pathPoints property. I guess I understand that still the HTML element and the js object are not really linked together or something.
  1. Can someone hint me - what am I missing here?
  2. How do I learn such things in broad sense, what terms I should google? "organizing javascript" "html to javascript binding". I want to learn these things and not even sure how these are called..

var selectedShape = null;

var src = document.querySelectorAll(".inventory");
for (var h = 0; h < src.length; h++) {
  var o = new Draggable(src[h]);
  inventory[h] = o; //not sure://
}


function Draggable(elem) {
  this.target = elem
  this.clickPoint = this.target.ownerSVGElement.createSVGPoint()
  this.lastMove = this.target.ownerSVGElement.createSVGPoint()
  this.currentMove = this.target.ownerSVGElement.createSVGPoint()
  this.dpath = this.target.getAttribute("d")
  this.subclass = this.target.getAttribute("subclass")
  this.pathPoints = parsePathToPoints(this.dpath, this.subclass)
  this.target.addEventListener("dblclick", this)
  this.target.addEventListener("mousedown", this)
  this.handleEvent = function(evt) {
    switch (evt.type) {
      case 'mousedown':
        evt.preventDefault()
        this.clickPoint = globalToLocalCoords(evt.clientX, evt.clientY)
        this.target.classList.add("dragged")
        this.target.ownerSVGElement.addEventListener("mousemove", this.move)
        this.target.ownerSVGElement.addEventListener("mouseup", this.endMove)
          //this.target.setAttribute("pointer-events", "all")
          // bummer - tried all I could but if I use the line above - dblclick is no longer recognized/ 
        break;
      case 'dblclick':
        evt.preventDefault()
        if (selectedShape) {
          if (selectedShape != this.target) {
            selectedShape.classList.remove("selected")
            this.target.classList.add("selected")
            selectedShape = elem;
          } else {
            return;
          }
        } else {
          selectedShape = elem
          this.target.classList.add("selected")
        }
        Select(this.target);
        break;
    }
  }
  this.move = function(evt) {
    evt.preventDefault()
    var p = globalToLocalCoords(evt.clientX, evt.clientY)
    this.currentMove.x = this.lastMove.x + (p.x - this.clickPoint.x)
    this.currentMove.y = this.lastMove.y + (p.y - this.clickPoint.y)
    this.target.setAttribute("transform", "translate(" + this.currentMove.x + "," + this.currentMove.y + ")")
  }.bind(this)
  this.endMove = function(evt) {
    this.lastMove.x = this.currentMove.x
    this.lastMove.y = this.currentMove.y
    this.target.classList.remove("dragged")
    this.target.setAttribute("pointer-events", "all")
    this.target.ownerSVGElement.removeEventListener("mousemove", this.move)
    this.target.ownerSVGElement.removeEventListener("mouseup", this.endMove)
  }.bind(this)

  function globalToLocalCoords(x, y) {
    var p = elem.ownerSVGElement.createSVGPoint()
    var m = elem.parentNode.getScreenCTM()
    p.x = x
    p.y = y
    return p.matrixTransform(m.inverse())
  }
}

function Select(elem) {
  console.log(elem.pathPoints);
}

function parsePathToPoints(d, sub) {
  var darray = [];
  var pathPoints = {};
  switch (sub) {
    case 'circle':
      darray = d.replace(/M|A|Z/g, "").replace(/,/g, " ").split(" ").map(Number);
      pathPoints.p0 = {
        "x": darray[0],
        "y": darray[1]
      };
      pathPoints.p1 = {
        "x": darray[0] + darray[2],
        "y": darray[1] - darray[3]
      };
      pathPoints.p2 = {
        "x": darray[7],
        "y": darray[8]
      };
      pathPoints.p3 = {
        "x": darray[0] + darray[2],
        "y": darray[1] + darray[3]
      };
      pathPoints.p4 = {
        "x": darray[2],
        "y": darray[3]
      }; // = radius used in the circle path
      return pathPoints;
      break;
    case 'curve4':
      darray = d.replace(/M|Q/g, "").replace(/,/g, " ").split(" ").map(Number);
      for (i = 0, j = 1, o = 9; i < darray.length - 2; i += 2, j += 2, o++) {
        pathPoints["p" + (o - 9)] = {
          "x": darray[i],
          "y": darray[j]
        };
      };
      return pathPoints;
      break;
    case 'curve3':
      darray = d.replace(/M|Q/g, "").replace(/,/g, " ").split(" ").map(Number)
      for (i = 0, j = 1, o = 9; i < darray.length; i += 2, j += 2, o++) {
        pathPoints["p" + (o - 9)] = {
          "x": darray[i],
          "y": darray[j]
        };
      }
      return pathPoints;
      break;
    case 'curve2':
      darray = d.replace(/M|Q/g, "").replace(/,/g, " ").split(" ").map(Number);
      for (i = 0, j = 1, o = 9; i < darray.length; i += 2, j += 2, o++) {
        pathPoints["p" + (o - 9)] = {
          "x": darray[i],
          "y": darray[j]
        };
      }
      return pathPoints;
      break;
    case 'curve1':
      darray = d.replace(/M|Q/g, "").replace(/,/g, " ").split(" ").map(Number);
      for (i = 0, j = 1, o = 9; i < darray.length; i += 2, j += 2, o++) {
        pathPoints["p" + (o - 9)] = {
          "x": darray[i],
          "y": darray[j]
        };
      }
      return pathPoints;
      break;
    case 'cubic':
      darray = d.replace(/M|C/g, "").replace(/,/g, " ").split(" ").map(Number);
      for (i = 0, j = 1, o = 9; i < darray.length; i += 2, j += 2, o++) {
        pathPoints["p" + (o - 9)] = {
          "x": darray[i],
          "y": darray[j]
        };
      }
      return pathPoints;
      break;
  }
}

function pointsToPath(pathPoints, pathSubclass) {
  console.log("joining " + pathPoints + "for a " + pathSubclass);
}
html,
body {
  margin: 0;
  padding: 0;
  border: 0;
  overflow: hidden;
  background-color: #fff;
}
svg {
  position: fixed;
  top: 0%;
  left: 0%;
  width: 100%;
  height: 100%;
}
.inventory {
  fill: transparent;
  stroke: black;
  cursor: move;
}
.dragged {
  fill: transparent;
  stroke: green;
  cursor: move;
}
.selected {
  fill: transparent;
  stroke: red;
  cursor: move;
}
path {
  stroke-width: 3;
  stroke: #000;
  stroke-linecap: round;
}
path.fill {
  fill: #3ff;
}
#canvasBackground {
  fill: lightgrey;
}
#inventoryBackground {
  fill: grey;
}

  
    
    

    
      
      
      
      
      
      
    

    
    
  

javascript html css svg