fabric.js 实现共享画板中的橡皮擦功能

2,326

关于Fabric的事情是一切都是基于对象的,大多数事情也是基于向量的。

与原生画布不同,我们不能仅仅删除全局位图上的一些像素。我们在下面有整个对象模型,canvas输出是所有在canvas上呈现的那些对象的一个简单的循环。

一. 我们的橡皮擦想要实现的是,擦出任意的一个部分,不能直接对对象实行删除,

实现方案一: 以白色画笔为橡皮擦,在任何上面添加一个白色来覆盖当前的颜色,
1. 如果白板有背景,

2.如果白板有黑夜模式和白天模式,白色的橡皮擦线条就会被完全的展示出来

该方案失败

二. 官方提供了 **Eraser Brush 方案来实现橡皮擦的 只需要将在文件下载到本地进行引用就好了

**github.com/fabricjs/fa…

import '@/libs/eraser_brush.mixin.js';  // 本地地址进行引用即可

const canvas = new fabric.Canvas('c');

// 设置画笔为橡皮擦
 canvas.freeDrawingBrush = new fabric.EraserBrush(canvas);
 canvas.freeDrawingBrush.width = 35;
 canvas.freeDrawingBrush.color = '#FFF';
 canvas.isDrawingMode = true;

这样画笔就成为橡皮擦了,指哪擦那,

这时候又产生下一个问题了,1. 如果是共享白板,你的橡皮擦是应该同步到协作者哪里的。

起初天真的以为,橡皮擦也是一个画笔 path 对象,只要把画笔对象传递给协作者,就实现了整体的橡皮擦功能, 最后同步了一堆大白线过去。

查阅官方的demo

The eraser mixin uses the clipPath property of an object the apply erasing to it.

  1. It creates a fabric.Group object and assigns it to the object’s clipPath.

  2. It creates a fabric.Rect and adds it to the group as the first object for clipping to be successful (acts as the background for destination-out global composition operation).

  3. It applies the existing clipPath property to the fabric.Rect from the previous step.

  4. After erasing is done it adds fabric.Paths to create an erasing effect on the object

整体的意思大概就是 earser 不是以path 画笔的形式去实现的,是使用clipPath对象的属性对其应用擦除。

擦除完成后,它会添加fabric.Paths 以在对象上创建擦除效果,给对象创建一个 clipPath 属性,

所以这时候需要修改同步了方案了, 在橡皮擦擦除的时候,不要同画笔同步过去,不然就会同步过去一堆白色线条

  path.clone(function (path) {
          path.globalCompositeOperation = 'destination-out';
          // http://fabricjs.com/using-transformations
          var desiredTransform = fabric.util.multiplyTransformMatrices(
            fabric.util.invertTransform(
              obj.calcTransformMatrix()
            ),
            path.calcTransformMatrix()
          );
          fabric.util.applyTransformToObject(path, desiredTransform);
          clipObject.addWithUpdate(path);
          obj.set({
            clipPath: clipObject,
            dirty: true
          });
          obj.fire('erasing:end', {
            path: path
          });
          if (obj.group && Array.isArray(_this.__subTargets)) {
            _this.__subTargets.push(obj);
          }
        });
      },

看了下eraser_brush.mixin.js 的源码 发现,橡皮擦之后是有 erasing:end 事件的。可以通过erasing:end 给协作方发送消息,这里为发送方

// 这里可以监听对象
canvas.on('erasing:end', (e) => {
  const {targets} = e;
  // targets 为受影响的所有元素,因为橡皮擦可能一次擦了几个元素
  for(let i = 0; i < targets.length; i++) {
    const item = targets[i];
    const str = item.TOJSON(['id']);
    
    sendMessage(JSON.stringify(str)) // 给协作放发送橡皮擦的更改
  } 
})

**
接收方**

fucntion updateErasing(str) {
  const canvas = new fabric.Canvas('c');
  const obj = JSON.parse(str);
  
  const item = canvas.getObjectById(obj.id);
  canvas.remove(item);   //先画板中的删除该元素
  
  fabric.util.enlivenObjects([obj], (objects) => {
     objects.forEach((o) => {
        canvas.add(o);  // 在添加经过橡皮擦之后的元素,
     })
  })
}

在接收方尝试过,修改 item 的 clipPath 对象,发现对 对象的修改,不能同步橡皮擦的功能

至此,fabric.js 实现橡皮擦和数据传递接收的功能就完成了

参考实例:fabricjs.com/erasing