13

我想使用 Fabric.js 在我的网络应用程序中实现橡皮擦。有没有办法在 Fabric.js 中实现橡皮擦?例如,例如在 MS Paint 中?

4

3 回答 3

17

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

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

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

我们可以模拟橡皮擦的一种方法可能是在画布上进行某种覆盖。并在其上画出“擦除”的线条,给人一种底层物体被抹去的错觉。

但这仍然存在并发症:

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

可能还有更多我当时没有想到的问题。

于 2013-10-22T17:23:44.280 回答
3

我刚刚用 Fabric 写了我的橡皮擦,我希望也能回答@kangax 提出的问题。

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

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

然后我在这里稍微破解了实际的织物库(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(),您可以将结果存储在某处。当您想恢复画布时,您还应该管理“擦除”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;
于 2018-11-25T18:00:14.383 回答
0

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

与@Zappescu 的回答类似,我对内部代码进行了一些调整以达到效果;可能有更好的方法,比如当路径被画布上的 'path:created' 事件触发时捕获路径,但在 FabricJS 团队决定在未来将其作为一项功能实现之前,它看起来也不好看。

于 2019-06-06T19:33:05.650 回答