Fabric.js 橡皮擦问题画布

14

我想在使用Fabric.js的web应用程序中实现橡皮擦。是否有实现在Fabric.js中实现橡皮擦的方法?例如,就像在MS Paint中一样?

3个回答

18

Fabric中没有内置橡皮擦,实现起来有些困难。

Fabric的特点是一切都是基于对象的,大多数东西也是基于矢量图形。

与本地画布不同,我们不能只在全局位图上擦掉一些像素。我们在画布下面有整个对象模型,并且画布输出是所有这些对象渲染到画布上的简单循环。

我们可以通过在画布顶部放置某种覆盖层来模拟橡皮擦,然后在其上绘制“擦除”的线条,从而产生底层对象被抹去的幻觉。

但是还存在以下问题:

  • 我们如何序列化此层(JSON或SVG)?
  • 如果您擦掉以前绘制的路径的一半,然后想要处理已经擦掉的形状怎么办?形状本身需要被修改;覆盖层不起作用。
  • 橡皮擦只会影响形状还是背景颜色也会受到影响?背景图像呢?

可能还有我暂时没有考虑到的更多问题。


3

我刚刚在 Fabric 中编写了橡皮擦,希望能够回答 @kangax 提出的问题。

首先,如果您想要一个手写橡皮擦,您应该构建一个像这样的对象:

canvas.freeDrawingBrush = new fabric.PencilBrush(canvas);

然后,我稍微修改了实际的 Fabric 库 (v.2.4.3),具体如下:

createPath: function(pathData) {
      var path = new fabric.Path(pathData, {
        fill: null,
        stroke: this.color,
        strokeWidth: this.width,
        strokeLineCap: this.strokeLineCap,
        strokeMiterLimit: this.strokeMiterLimit,
        strokeLineJoin: this.strokeLineJoin,
        strokeDashArray: this.strokeDashArray,
        // ADDED BY AZ (24 Nov 2018)
        globalCompositeOperation: this.globalCompositeOperation,
        id: this.id
      });

通过使用globalCompositeOperation,您可以管理canvas.freeDrawingBrush以绘制彩色路径(您希望的颜色,我选择了红色,但您也可以选择画布的背景颜色),如下所示:

canvas.isDrawingMode = 1;
canvas.freeDrawingBrush.color = "red";
canvas.freeDrawingBrush.width = 10;
canvas.freeDrawingBrush.globalCompositeOperation = 'destination-out';
canvas.freeDrawingBrush.id = 'erasure';
ctx.beginPath(); // the context of canvas
canvas.renderAll();

当你将鼠标移动到画布上时,会出现一条红色路径。当你向上移动鼠标时,最终创建了该路径并应用了gCO,擦除了红色路径下的所有内容。
好的,如果你想保存画布,我建议使用canvas.toSVG()函数(如果你能够管理它,它非常适合视网膜屏幕)。 因此,要将画布保存为SVG,你只需要这一行代码canvas.toSVG(),然后可以将结果存储在某个地方。 当你想恢复画布时,你还需要管理“erasure”id,所以你可以使用我的恢复函数:
function restoreSketch(imageSVG) {
  fabric.loadSVGFromString(imageSVG, function (objects, options) {
    $.each(objects, function (index, value) {
      if (value.id && value.id == 'erasure') {
        value.set({
         globalCompositeOperation: 'destination-out'
        }); //set gCO for value
      }
   });                          
   var obj = fabric.util.groupSVGElements(objects, options);
   canvas.add(obj).renderAll();
});

我希望能对那些在使用Fabric.js时遇到困难的人有所帮助。
更正:根据@Benni的建议,与擦除相关的代码行可以被移动。如果您想将它们固定在画布上,您可以稍微修改代码并使用lockMovementX和lockMovementY。所以,在fabric.js库中,在

之后进行修改。
globalCompositeOperation: this.globalCompositeOperation,

添加:

lockMovementX: this.lockMovementX,
lockMovementY: this.lockMovementY,

然后,在您的代码中,在 canvas.freeDrawingBrush.id = 'erasure'; 之后添加:

canvas.freeDrawingBrush.lockMovementX = true;
canvas.freeDrawingBrush.lockMovementY = true;

那是一个很棒的想法,然而如果你使用Fabric.js中的选择功能,那么就可以选择这些透明线并将它们移动。有任何解决此问题的想法吗? - Benni
好主意,但是如果您想支持选择其他对象,则效果不佳。透明线的移动现在被锁定了(应添加lockScaling和lockRotation)。如果您移动任何非透明对象,则透明对象将停留在最后位置,这对用户来说很困惑。我也考虑过移动透明线或重新绘制对象的方法。 - Benni

0

我在这里编写了一些解决方案的代码,并解释了它的工作原理:
https://github.com/fabricjs/fabric.js/issues/1225#issuecomment-499620550

类似于@Zappescu的答案,我的答案对内部代码进行了一些调整以实现效果;可能有更好的方法,比如通过从画布中触发“path:created”事件来捕获路径,但在FabricJS团队决定在未来将其实现为功能之前,它看起来都不会很漂亮。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接