title: FabricJS - When moving a path the 'path' object doesn't update
body: <p>Previously I was using the <a href="http://fabricjs.com/docs/fabric.Line.html" rel="noreferrer">Line</a> class in <a href="http://fabricjs.com/" rel="noreferrer">FabricJS</a> to allow users to draw lines on a canvas. I now need to implement curved lines, but as Fabric's Line class doesn't support quadratic curves I have re-written my code to use the <a href="http://fabricjs.com/docs/fabric.Path.html" rel="noreferrer">Path</a> class instead.</p>
  
  <p>Previously when drawing a Line the x1, y1, x2, and y2 values would update automatically if the line was moved on the canvas which made it easy to update the position of the start and end anchor points that I had created (just rectangles on the canvas which can be manipulated in the same way you would manipulate anchor points in Adobe Illustrator).</p>
  
  <p>The Path class doesn't use x1, y1, x2 or y2, instead you pass a string which is converted into an array e.g. <code>M 100 100, Q 200 200 500 500</code>. These values are then available as part of a <code>path</code> object (with 100 100 being the x and y of the start point of the path, 200 200 being the curve and 500 500 being the end) which means I can get the start and end points in a similar way that I did with the Line class.</p>
  
  <p>The issue is that the path object doesn't update when the line itself is moved. The <code>left</code> and <code>top</code> values update but I'm finding it difficult to manually update the path values based on this. I feel like I may be missing something obvious (e.g. the ability to auto-update the path object or get a delta/difference that I can use to manually reposition the anchors on the object:moving function).</p>
  
  <p>The snippet below is taken from the <a href="http://fabricjs.com/quadratic-curve" rel="noreferrer">quadratic curve demo</a> on Fabric's website. If you move drag the actual line (not the anchor points) you can see that the anchor points remain where they are.</p>
  
  <p><div class="snippet" data-lang="js" data-hide="false" data-console="false" data-babel="false">
  <div class="snippet-code">
  <pre class="snippet-code-js lang-js prettyprint-override"><code>(function() {
    var canvas = this.__canvas = new fabric.Canvas('c', {
      height: 563,
      width: 1000,
    });
    fabric.Object.prototype.originX = fabric.Object.prototype.originY = 'center';
  
    canvas.on({
      'object:selected': onObjectSelected,
      'object:moving': onObjectMoving,
      'before:selection:cleared': onBeforeSelectionCleared
    });
  
    (function drawQuadratic() {
  
      var line = new fabric.Path('M 100 100, Q 200 200 200 200', { fill: '', stroke: 'black', objectCaching: false });
      
      console.log(line);
      
      console.log(line);
  
      line.selectable = true;
      canvas.add(line);
  
      var p1 = makeCurvePoint(200, 200, null, line, null)
      p1.name = "p1";
      canvas.add(p1);
  
      var p0 = makeCurveCircle(100, 100, line, p1, null);
      p0.name = "p0";
      canvas.add(p0);
  
      var p2 = makeCurveCircle(300, 100, null, p1, line);
      p2.name = "p2";
      canvas.add(p2);
  
    })();
  
    function makeCurveCircle(left, top, line1, line2, line3) {
      var c = new fabric.Circle({
        left: left,
        top: top,
        strokeWidth: 5,
        radius: 12,
        fill: '#fff',
        stroke: '#666'
      });
  
      c.hasBorders = c.hasControls = false;
  
      c.line1 = line1;
      c.line2 = line2;
      c.line3 = line3;
  
      return c;
    }
  
    function makeCurvePoint(left, top, line1, line2, line3) {
      var c = new fabric.Circle({
        left: left,
        top: top,
        strokeWidth: 8,
        radius: 14,
        fill: '#fff',
        stroke: '#666'
      });
  
      c.hasBorders = c.hasControls = false;
  
      c.line1 = line1;
      c.line2 = line2;
      c.line3 = line3;
  
      return c;
    }
  
    function onObjectSelected(e) {
      var activeObject = e.target;
  
      if (activeObject.name == "p0" || activeObject.name == "p2") {
        activeObject.line2.animate('opacity', '1', {
          duration: 200,
          onChange: canvas.renderAll.bind(canvas),
        });
        activeObject.line2.selectable = true;
      }
    }
  
    function onBeforeSelectionCleared(e) {
      var activeObject = e.target;
      if (activeObject.name == "p0" || activeObject.name == "p2") {
        activeObject.line2.animate('opacity', '0', {
          duration: 200,
          onChange: canvas.renderAll.bind(canvas),
        });
        activeObject.line2.selectable = false;
      }
      else if (activeObject.name == "p1") {
        activeObject.animate('opacity', '0', {
          duration: 200,
          onChange: canvas.renderAll.bind(canvas),
        });
        activeObject.selectable = false;
      }
    }
  
    function onObjectMoving(e) {
      if (e.target.name == "p0" || e.target.name == "p2") {
        var p = e.target;
  
        if (p.line1) {
          p.line1.path[0][1] = p.left;
          p.line1.path[0][2] = p.top;
        }
        else if (p.line3) {
          p.line3.path[1][3] = p.left;
          p.line3.path[1][4] = p.top;
        }
      }
      else if (e.target.name == "p1") {
        var p = e.target;
  
        if (p.line2) {
          p.line2.path[1][1] = p.left;
          p.line2.path[1][2] = p.top;
        }
      }
      else if (e.target.name == "p0" || e.target.name == "p2") {
        var p = e.target;
  
        p.line1 &amp;&amp; p.line1.set({ 'x2': p.left, 'y2': p.top });
        p.line2 &amp;&amp; p.line2.set({ 'x1': p.left, 'y1': p.top });
        p.line3 &amp;&amp; p.line3.set({ 'x1': p.left, 'y1': p.top });
        p.line4 &amp;&amp; p.line4.set({ 'x1': p.left, 'y1': p.top });
      }
    }
  })();</code></pre>
  <pre class="snippet-code-html lang-html prettyprint-override"><code>&lt;script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/1.7.21/fabric.min.js"&gt;&lt;/script&gt;
  &lt;canvas id="c"&gt;&lt;/canvas&gt;</code></pre>
  </div>
  </div>
  </p>