Fabric.js:如何通过鼠标绘制多边形

时间:2014-10-06 15:09:04

标签: fabricjs

我想在Fabric.js中使用鼠标交互绘制fabric.Polygon。 我做了一个小jsfiddle来显示我的实际状态:http://jsfiddle.net/Kienz/ujefxh7w/

按下ESC键后,"交互式绘图模式"取消并完成多边形。但现在多边形的位置是错误的(控件是正确的)。

有人有想法吗?

4 个答案:

答案 0 :(得分:1)

问题定义

当多边形作为静态对象添加时(从某种意义上说,这些点不会被修改),它可以根据提供的点计算left, right, minX, minY, width, height和中心点。

但是当想要动态创建多边形时,就会出现问题。 在调用setCoords()后,它会计算移动框的位置,但它会根据有关宽度和高度的错误信息来计算。

记住,创建1磅多边形时,宽度和高度等于0,左上角等于该点。

解决方案

首先,更正尺寸

_calcDimensions()计算多边形的widthheightminXminYminXminY是所有要点中的最低点。

这告诉我们一些点放置在旧中心上方的左侧和顶部。我们应该使用这些信息将旧左移动到正确的位置。新的左上角是由minXminY信息翻译的旧中心点。

var oldC = polygon.getCenterPoint();
polygon._calcDimensions();
polygon.set({
  left: oldC.x + polygon.get('minX'),
  top: oldC.y + polygon.get('minY')
});

修复点

中心移动了某个向量v后(更改leftrightwidthheight属性的结果)。我们需要以v的反向更新所有点。

var newC = polygon.getCenterPoint();
var moveX = -(newC.x-oldC.x);
var moveY = -(newC.y-oldC.y)
var adjPoints = polygon.get("points").map(function(p) {
  return {
    x: p.x + moveX,
    y: p.y + moveY
  };
});

检查以下小提琴,获取完整示例:http://jsfiddle.net/orian/dyxjkmes/5/



/**
 * fabric.js template for bug reports
 *
 * Please update the name of the jsfiddle (see Fiddle Options).
 * This templates uses latest dev verison of fabric.js (https://rawgithub.com/kangax/fabric.js/master/dist/all.js).
 */

// initialize fabric canvas and assign to global windows object for debug
var canvas = window._canvas = new fabric.Canvas('c');

// Do some initializing stuff
fabric.Object.prototype.set({
  transparentCorners: false,
  cornerColor: 'rgba(102,153,255,0.5)',
  cornerSize: 12,
  padding: 7
});

// ADD YOUR CODE HERE
var mode = "add",
  currentShape;

canvas.observe("mouse:move", function(event) {
  var pos = canvas.getPointer(event.e);
  if (mode === "edit" && currentShape) {
    var points = currentShape.get("points");
    points[points.length - 1].x = pos.x - currentShape.get("left");
    points[points.length - 1].y = pos.y - currentShape.get("top");
    currentShape.set({
      points: points
    });
    canvas.renderAll();
  }
});

canvas.observe("mouse:down", function(event) {
  var pos = canvas.getPointer(event.e);

  if (mode === "add") {
    var polygon = new fabric.Polygon([{
      x: pos.x,
      y: pos.y
    }, {
      x: pos.x + 0.5,
      y: pos.y + 0.5
    }], {
      fill: 'blue',
      opacity: 0.5,
      selectable: false
    });
    currentShape = polygon;
    canvas.add(currentShape);
    mode = "edit";
  } else if (mode === "edit" && currentShape && currentShape.type === "polygon") {
    var points = currentShape.get("points");
    points.push({
      x: pos.x - currentShape.get("left"),
      y: pos.y - currentShape.get("top")
    });
    currentShape.set({
      points: points
    });
    canvas.renderAll();
  }
});

    fabric.util.addListener(window, 'keyup', function(e) {
      if (e.keyCode === 27) {
        if (mode === 'edit' || mode === 'add') {
          mode = 'normal';

          var points = currentShape.get("points");
          points.pop();
          currentShape.set({
            points: points
          });

          var oldC = currentShape.getCenterPoint();
          currentShape._calcDimensions();

          var xx = currentShape.get("minX");
          var yy = currentShape.get("minY");
          currentShape.set({
            left: currentShape.get('left') + xx,
            top: currentShape.get('top') + yy
          });

          var pCenter = currentShape.getCenterPoint();
          var adjPoints = currentShape.get("points").map(function(p) {
            return {
              x: p.x - pCenter.x + oldC.x,
              y: p.y - pCenter.y + oldC.y
            };
          });
          currentShape.set({
            points: adjPoints,
            selectable: true
          });

          canvas.setActiveObject(currentShape);
          currentShape.setCoords();
          canvas.renderAll();
        } else {
          mode = 'add';
        }
        currentShape = null;
      }
    })

canvas {
  border: 1px solid #999;
}

img {
  display: none;
}

<script src="https://rawgit.com/kangax/fabric.js/master/dist/fabric.js"></script>
<canvas id="c" width="600" height="600"></canvas>
&#13;
&#13;
&#13;

复制和粘贴

function fixPoly(poly) {
  var oldC = poly.getCenterPoint();
  poly._calcDimensions();
  poly.set({
    left: poly.get('left') + poly.get("minX"),
    top: poly.get('top') + poly.get("minY")
  });

  var pCenter = poly.getCenterPoint();
  poly.get("points").forEach((p) => {
    p.x -= pCenter.x - oldC.x;
    p.y -= pCenter.y - oldC.y
  });
  poly.setCoords();
};

答案 1 :(得分:1)

这个问题有两个解决方案,非常简单:

解决方案#1

只需从画布中删除多边形并在每个添加的点上重新创建它(使用新的fabric.Polygon(...))!

优点&amp;缺点:是的,你会得到稍差的表现,因为画布会被重新渲染两次,但你每次都会省去重新计算坐标的麻烦。

您可以在下面的代码段或this forked fiddle中查看此解决方案。

&#13;
&#13;
/**
 * fabric.js template for bug reports
 *
 * Please update the name of the jsfiddle (see Fiddle Options).
 * This templates uses latest dev verison of fabric.js (https://rawgithub.com/kangax/fabric.js/master/dist/all.js).
 */

// initialize fabric canvas and assign to global windows object for debug
var canvas = window._canvas = new fabric.Canvas('c');

// Do some initializing stuff
fabric.Object.prototype.set({
  transparentCorners: false,
  cornerColor: 'rgba(102,153,255,0.5)',
  cornerSize: 12,
  padding: 7
});

// ADD YOUR CODE HERE
var mode = "add",
  currentShape;

canvas.observe("mouse:move", function(event) {
  var pos = canvas.getPointer(event.e);
  if (mode === "edit" && currentShape) {
    var points = currentShape.get("points");
    points[points.length - 1].x = pos.x;
    points[points.length - 1].y = pos.y;
    currentShape.set({
      points: points
    });
    canvas.renderAll();
  }
});

canvas.observe("mouse:down", function(event) {
  var pos = canvas.getPointer(event.e);

  if (mode === "add") {
    var polygon = new fabric.Polygon([{
      x: pos.x,
      y: pos.y
    }, {
      x: pos.x + 0.5,
      y: pos.y + 0.5
    }], {
      fill: 'blue',
      opacity: 0.5,
      selectable: false
    });
    currentShape = polygon;
    canvas.add(currentShape);
    mode = "edit";
  } else if (mode === "edit" && currentShape && currentShape.type === "polygon") {
    var points = currentShape.get("points");
    points.push({
      x: pos.x,
      y: pos.y
    });
    canvas.remove(currentShape);
    currentShape = new fabric.Polygon(points, {
      fill: 'blue',
      opacity: 0.5,
      selectable: false
    });
    canvas.add(currentShape);
  }
});

fabric.util.addListener(window, 'keyup', function(e) {
  if (e.keyCode === 27) {
    if (mode === 'edit' || mode === 'add') {
      mode = 'normal';
      var points = currentShape.get('points');
      canvas.remove(currentShape);
      currentShape = new fabric.Polygon(points, {
        fill: 'blue',
        opacity: 0.5,
        selectable: true
      });
      canvas.add(currentShape);
    } else {
      mode = 'add';
    }
    currentShape = null;
  }
})
&#13;
canvas {
  border: 1px solid #999;
}
img {
  display: none;
}
&#13;
<script src="https://rawgithub.com/kangax/fabric.js/master/dist/fabric.js"></script>
<canvas id="c" width="600" height="600"></canvas>
&#13;
&#13;
&#13;

解决方案#2

每次重新计算多边形尺寸,就像在Polygon类的构造函数中完成一样。摘自代码:

currentShape.set({
    points: points
});
currentShape._calcDimensions();
currentShape.set({
    left: currentShape.minX,
    top: currentShape.minY,
    pathOffset: {
        x: currentShape.minX + currentShape.width / 2,
        y: currentShape.minY + currentShape.height / 2
    }
});
currentShape.setCoords();
canvas.renderAll();

优点&amp;缺点:更好的性能(在负载很重的画布上可能会很明显),但是你必须有更多的代码才能将它们添加到两个处理程序中。

您可以在下面的代码段或this forked fiddle中查看。

&#13;
&#13;
/**
 * fabric.js template for bug reports
 *
 * Please update the name of the jsfiddle (see Fiddle Options).
 * This templates uses latest dev verison of fabric.js (https://rawgithub.com/kangax/fabric.js/master/dist/all.js).
 */

// initialize fabric canvas and assign to global windows object for debug
var canvas = window._canvas = new fabric.Canvas('c');

// Do some initializing stuff
fabric.Object.prototype.set({
  transparentCorners: false,
  cornerColor: 'rgba(102,153,255,0.5)',
  cornerSize: 12,
  padding: 7
});

// ADD YOUR CODE HERE
var mode = "add",
  currentShape;

canvas.observe("mouse:move", function(event) {
  var pos = canvas.getPointer(event.e);
  if (mode === "edit" && currentShape) {
    var points = currentShape.get("points");
    points[points.length - 1].x = pos.x;
    points[points.length - 1].y = pos.y;
    currentShape.set({
      points: points
    });
    canvas.renderAll();
  }
});

canvas.observe("mouse:down", function(event) {
  var pos = canvas.getPointer(event.e);

  if (mode === "add") {
    var polygon = new fabric.Polygon([{
      x: pos.x,
      y: pos.y
    }, {
      x: pos.x + 0.5,
      y: pos.y + 0.5
    }], {
      fill: 'blue',
      opacity: 0.5,
      selectable: false
    });
    currentShape = polygon;
    canvas.add(currentShape);
    mode = "edit";
  } else if (mode === "edit" && currentShape && currentShape.type === "polygon") {
    var points = currentShape.get("points");
    points.push({
      x: pos.x,
      y: pos.y
    });
    currentShape.set({
      points: points
    });
    currentShape._calcDimensions();
    currentShape.set({
      left: currentShape.minX,
      top: currentShape.minY,
      pathOffset: {
        x: currentShape.minX + currentShape.width / 2,
        y: currentShape.minY + currentShape.height / 2
      }
    });
    currentShape.setCoords();
    canvas.renderAll();
  }
});

fabric.util.addListener(window, 'keyup', function(e) {
  if (e.keyCode === 27) {
    if (mode === 'edit' || mode === 'add') {
      mode = 'normal';
      currentShape.set({
        selectable: true
      });
      currentShape._calcDimensions();
      currentShape.set({
        left: currentShape.minX,
        top: currentShape.minY,
        pathOffset: {
          x: currentShape.minX + currentShape.width / 2,
          y: currentShape.minY + currentShape.height / 2
        }
      });
      currentShape.setCoords();
      canvas.renderAll();
    } else {
      mode = 'add';
    }
    currentShape = null;
  }
})
&#13;
canvas {
  border: 1px solid #999;
}
img {
  display: none;
}
&#13;
<script src="https://rawgithub.com/kangax/fabric.js/master/dist/fabric.js"></script>
<canvas id="c" width="600" height="600"></canvas>
&#13;
&#13;
&#13;

答案 2 :(得分:0)

对于那些寻求使用fabricjs 3的最新答案的人。

FabricJS进行了很多更改,其中之一是对_calcDimmensions方法的更改。它不再在对象上存储值minX和minY。而是返回具有以下内容的对象:

  {
    left: minX,
    top: minY,
    width: width,
    height: height
  }

因此,该解决方案将更新为以下内容:

currentShape.set({
points: points
});
var calcDim = currentShape._calcDimensions();
currentShape.width = calcDim.width;
currentShape.height = calcDim.height;
currentShape.set({
    left: calcDim.left,
    top: calcdim.top,
    pathOffset: {
        x: calcDim.left + currentShape.width / 2,
        y: calcDim.top + currentShape.height / 2
    }
});
currentShape.setCoords();
canvas.renderAll();

答案 3 :(得分:-1)

你可以解决“跳跃”问题。通过在初始化时设置originX和originY点来实现多边形。

        var polygon = new fabric.Polygon([{
        x: pos.x,
        y: pos.y
    }, {
        x: pos.x + 0.5,
        y: pos.y + 0.5
    }], {
        fill: 'blue',
        opacity: 0.5,
        selectable: false,
        originX: pos.x,
        originY: pos.y
    });

见这里: http://jsfiddle.net/ujefxh7w/48/

我不知道选择框移动的原因。

编辑:发现重新加载画布对象修复了选择框

序列化并加载序列化对象可以解决问题:

var json = JSON.stringify(canvas);
canvas.loadFromJSON(json, function() {
    canvas.renderAll();
});

http://jsfiddle.net/ujefxh7w/50/