lundi 9 janvier 2017

small SVG app - javascript-to-HTML architecture?

I am on my path learning javascript & 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;
}
<body>
  <svg id="svg" height="480" width="480" viewbox="0 0 480 580" preserveAspectRatio="xMinYMax meet" xmlns="http://ift.tt/nvqhV5" xmlns:xlink="http://ift.tt/PGV9lw">
    <rect id="canvasBackground" width="480" height="480" x="0" y="0" pointer-events="all" />
    <rect id="inventoryBackground" width="480" height="100" x="0" y="480" pointer-events="all" />

    <g id="inventory">
      <path class="inventory" subclass="circle" d="M30,530 A35,35 1 1,1 100,530 A35,35 1 1,1 30,530" />
      <path class="inventory" subclass="curve4" d="M125,500 Q155,490 185,500 Q195,530 185,560 Q155,570 125,560 Q115,530 125,500" />
      <path class="inventory" subclass="curve3" d="M245,495 Q275,520 280,560 Q245,570 210,560 Q215,520 245,495" />
      <path class="inventory" subclass="curve2" d="M305,515 Q345,475 385,515 Q345,555 305,515" />
      <path class="inventory" subclass="curve1" d="M305,550 Q345,580 385,550" />
      <path class="inventory" subclass="cubic" d="M420,495 C470,530 380,530 425,565" />
    </g>

    <g id="canvas">
    </g>
  </svg>
</body>



Aucun commentaire:

Enregistrer un commentaire