关于Fabric的事情是一切都是基于对象的,大多数事情也是基于向量的。
与原生画布不同,我们不能仅仅删除全局位图上的一些像素。我们在下面有整个对象模型,canvas输出是所有在canvas上呈现的那些对象的一个简单的循环。
一. 我们的橡皮擦想要实现的是,擦出任意的一个部分,不能直接对对象实行删除,
实现方案一: 以白色画笔为橡皮擦,在任何上面添加一个白色来覆盖当前的颜色,
1. 如果白板有背景,
2.如果白板有黑夜模式和白天模式,白色的橡皮擦线条就会被完全的展示出来
该方案失败
二. 官方提供了 Eraser Brush 方案来实现橡皮擦的 只需要将在文件下载到本地进行引用就好了
https://github.com/fabricjs/fabric.js/blob/master/src/mixins/eraser_brush.mixin.js
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.
- It creates a fabric.Group object and assigns it to the object’s clipPath.
- 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).
- It applies the existing clipPath property to the fabric.Rect from the previous step.
- 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 实现橡皮擦和数据传递接收的功能就完成了