FabricJS - 移动路径时, 'path' 对象不会更新

标签 fabricjs

以前我使用的是 Line类(class) FabricJS允许用户在 Canvas 上绘制线条。我现在需要实现曲线,但由于 Fabric 的 Line 类不支持二次曲线,我重新编写了代码以使用 Path类代替。

以前在绘制一条线时,如果线在 Canvas 上移动,x1、y1、x2 和 y2 值将自动更新,这使得更新我创建的起点和终点 anchor 的位置变得容易(只是矩形上的可以像在 Adob​​e Illustrator 中操作 anchor 一样操作 Canvas )。

Path 类不使用 x1、y1、x2 或 y2,而是传递一个转换为数组的字符串,例如M 100 100, Q 200 200 500 500 .然后这些值作为 path 的一部分可用。对象(100 100 是路径起点的 x 和 y,200 200 是曲线,500 500 是终点),这意味着我可以以与使用 Line 类似的方式获得起点和终点类(class)。

问题是当线本身移动时,路径对象不会更新。 lefttop值更新,但我发现很难基于此手动更新路径值。我觉得我可能遗漏了一些明显的东西(例如,自动更新路径对象或获取增量/差异的能力,我可以用它来手动重新定位对象上的 anchor :移动功能)。

下面的片段取自 quadratic curve demo在 Fabric 的网站上。如果您移动拖动实际线(而不是 anchor ),您可以看到 anchor 保持在原来的位置。

(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 && p.line1.set({ 'x2': p.left, 'y2': p.top });
      p.line2 && p.line2.set({ 'x1': p.left, 'y1': p.top });
      p.line3 && p.line3.set({ 'x1': p.left, 'y1': p.top });
      p.line4 && p.line4.set({ 'x1': p.left, 'y1': p.top });
    }
  }
})();
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/1.7.21/fabric.min.js"></script>
<canvas id="c"></canvas>

最佳答案

这可能不是一个完美的解决方案。您需要从路径对象计算路径的终点。然后将其设置为所有三个圆圈。并且暂时无法设置路径对象的路径,因此您需要使用计算出的点路径创建一个新的路径对象。

演示

(function() {
  var line;
  var canvas = this.__canvas = new fabric.Canvas('c', {
    selection: false,
    perPixelTargetFind: true,
    targetFindTolerance: 10
  });
  fabric.Object.prototype.originX = fabric.Object.prototype.originY = 'center';

  canvas.on({
    'object:moving': onObjectMoving,
    'object:modified': reinitpath
  });

  line = new fabric.Path('M 100 100 Q 200, 200, 300, 100', {
    fill: '',
    stroke: 'black',
    name: 'line',
    hasControls: false,
    hasBorders: false,
    objectCaching:false

  });
  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;
    setLineToCircle(c, line1, line2, line3)

    return c;
  }


  function onObjectMoving(e) {
    var p = e.target;
    if (p.name == "p0" || p.name == "p2") {
      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 (p.name == "p1") {
      if (p.line2) {
        p.line2.path[1][1] = p.left;
        p.line2.path[1][2] = p.top;
      }
    } else if (p.name == "line") {

      var transformedPoints = getTransformedPoint(p);
      p0.left = transformedPoints[0].x;
      p0.top = transformedPoints[0].y;
      p2.left = transformedPoints[1].x;
      p2.top = transformedPoints[1].y;
      p1.left = transformedPoints[2].x;
      p1.top = transformedPoints[2].y;

    }
  }

  function reinitpath(e) {
    p0.setCoords();
    p1.setCoords();
    p2.setCoords();
    canvas.remove(line);
    var path = line.path;
    if (e.target.name == 'line') {
      var transformedPoints = getTransformedPoint(line);
      path = [
        [],
        []
      ];
      path[0][0] = 'M';
      path[0][1] = transformedPoints[0].x;
      path[0][2] = transformedPoints[0].y;
      path[1][0] = 'Q';
      path[1][1] = transformedPoints[2].x;
      path[1][2] = transformedPoints[2].y;
      path[1][3] = transformedPoints[1].x;
      path[1][4] = transformedPoints[1].y;

    };
    line = new fabric.Path(path, {
      fill: '',
      stroke: 'black',
      name: 'line',
      hasControls: false,
      hasBorders: false,
      objectCaching:false
    });
    canvas.add(line);
    setLineToCircle(p1, null, line, null)
    setLineToCircle(p0, line, p1, null);
    setLineToCircle(p2, null, p1, line);

  }

  function getTransformedPoint(p) {
    var points = [];
    var path = p.path;
    points.push(new fabric.Point(path[0][1], path[0][2]));
    points.push(new fabric.Point(path[1][3], path[1][4]));
    points.push(new fabric.Point(path[1][1], path[1][2]));
    var matrix = line.calcTransformMatrix();
    return points.map(function(p) {
        return new fabric.Point(p.x - line.minX - line.width / 2, p.y - line.minY - line.height / 2);
      })
      .map(function(p) {
        return fabric.util.transformPoint(p, matrix);
      });
  }

  function setLineToCircle(circle, line1, line2, line3) {
    circle.line1 = line1;
    circle.line2 = line2;
    circle.line3 = line3;
  }
})();
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/1.7.22/fabric.js"></script>

<canvas id="c" width="600" height="600"></canvas>

关于FabricJS - 移动路径时, 'path' 对象不会更新,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51406351/

相关文章:

image - 当 Openseadragon 图像旋转时 Fabric Canvas 改变位置

javascript - FabricJs 多个 Canvas 多次加载同一图像

javascript - 在将 Canvas 转换为 html5 中的图像后,当前上传的图像未设置在确切位置

javascript - FabricJS 没有给我在 IE 中平移图像的 MovementX 和 MovementY,但它在其他浏览器中工作正常

JavaScript - 动态创建 SVG 并为光标修改

javascript - 旋转后移动 Canvas 图像对象

javascript - Fabricjs - 以编程方式选择对象以立即移动/拖动

javascript - 无法使用现有 Canvas 元素添加新 Canvas 元素

canvas - 使用 Fabricjs 在 Canvas 上渲染一条跟随鼠标的线最终会变慢

html - Canvas fabric.js 中的图层