is there any inbuilt functionality for eraser in fabric.js?
Check the docs.
No.
Fabric is like a vector graphics tool, although the rendering engine is Canvas. I also first thought that when using bitmap Canvas it would be logical to have an eraser. But fabric.js works in a way that to erase something, you have to remove object using something like canvas.remove(obj) or canvas.remove(canvas.getActiveObject()).
Instead of eraser, Is there any way to erase the objects using fabric js? Like, Can we erase the object using current points of x & y on mouse move and then re-render the object?
Please provide some sample script if any one knows.
Thanks in advance.
Any progress on this? Besides doing and overlay eraser like function? @kangax
there is no real eraser for this lib, since we cannot erase parts of the objects or act like a classic eraser you could expect.
You can still build one, using free drawing and composing the free drawing with your object.
But is not a fabricjs feature
Could a possible implementation be like:
On "erase" alike get the coordinates x,y on mousemove and check if in all objects are between these coordinates if so then aggregate the objects bellow with the "eraser" in blank on top on those. Creating a single object to be manageable (delete/move/resize)
What do you think this approach @asturur ?
Because I have managed to implement the "eraser" alike function but now the elements on move dosen't keep the eraser on the correct spot.
https://konvajs.github.io/docs/sandbox/Free_Drawing.html
Not as smooth looking though.
I ended up using a fabric.js revert function instead. It works really well.
mark
My solution is:
canvas.on('path:created', function (opt) {
opt.path.globalCompositeOperation = 'destination-out';
opt.path.lineWidth = 10;
//opt.path.stroke = 'black';
//opt.path.fill = 'black';
canvas.renderAll();
});
this.fCanvas.forEachObject(shape => {
shape.evented = true
shape.on('mousedown', this.onMouseDownLine) // 点击擦线
shape.on('mousemove', this.onMouseMoveLine)
})
……
onMouseDownLine(e) {
// debugger
let target = e.target
this._sendDeleteLine(target)
},
deletePath(id) {
let targetShape
this.fCanvas.forEachObject(shape => {
if (shape.id == id) {
targetShape = shape
}
})
if (targetShape) {
this.fCanvas.remove(targetShape)
}
},
I made a new Brush (extending PencilBrush makes life easier) to simulate an eraser brush, and I think it is good enough to be used for a general use case. This class supports erasing objects without erasing the background, and you can even customize it to filter out certain objects not to be erased.
The downside is, to optimize performance (especially when there's lots of objects on canvas), I had to merge all the objects impacted by the eraser into a single Image, so certain properties might be lost. It is possible to not merge them in a group, but be prepared for a visible lag when there's a lot of objects.
I think it would be nice to add something similar to the library in case people (like me) need it, especially since having to manually edit a private method could spell a lot of troubles on future versions.
/*
* Note: Might not work with versions other than 3.1.0
*
* Made it so that the bound is calculated on the original only
*/
const ErasedGroup = fabric.util.createClass(fabric.Group, {
original: null,
erasedPath: null,
initialize: function (original, erasedPath, options, isAlreadyGrouped) {
this.original = original;
this.erasedPath = erasedPath;
this.callSuper('initialize', [this.original, this.erasedPath], options, isAlreadyGrouped);
},
_calcBounds: function (onlyWidthHeight) {
const aX = [],
aY = [],
props = ['tr', 'br', 'bl', 'tl'],
jLen = props.length,
ignoreZoom = true;
let o = this.original;
o.setCoords(ignoreZoom);
for (let j = 0; j < jLen; j++) {
prop = props[j];
aX.push(o.oCoords[prop].x);
aY.push(o.oCoords[prop].y);
}
this._getBounds(aX, aY, onlyWidthHeight);
},
});
/*
* Note1: Might not work with versions other than 3.1.0
*
* Made it so that the path will be 'merged' with other objects
* into a customized group and has a 'destination-out' composition
*/
const EraserBrush = fabric.util.createClass(fabric.PencilBrush, {
/**
* On mouseup after drawing the path on contextTop canvas
* we use the points captured to create an new fabric path object
* and add it to the fabric canvas.
*/
_finalizeAndAddPath: function () {
var ctx = this.canvas.contextTop;
ctx.closePath();
if (this.decimate) {
this._points = this.decimatePoints(this._points, this.decimate);
}
var pathData = this.convertPointsToSVGPath(this._points).join('');
if (pathData === 'M 0 0 Q 0 0 0 0 L 0 0') {
// do not create 0 width/height paths, as they are
// rendered inconsistently across browsers
// Firefox 4, for example, renders a dot,
// whereas Chrome 10 renders nothing
this.canvas.requestRenderAll();
return;
}
// use globalCompositeOperation to 'fake' eraser
var path = this.createPath(pathData);
path.globalCompositeOperation = 'destination-out';
path.selectable = false;
path.evented = false;
path.absolutePositioned = true;
// grab all the objects that intersects with the path
const objects = this.canvas.getObjects().filter((obj) => {
// if (obj instanceof fabric.Textbox) return false;
// if (obj instanceof fabric.IText) return false;
if (!obj.intersectsWithObject(path)) return false;
return true;
});
if (objects.length > 0) {
// merge those objects into a group
const mergedGroup = new fabric.Group(objects);
// This will perform the actual 'erasing'
// NOTE: you can do this for each object, instead of doing it with a merged group
// however, there will be a visible lag when there's many objects affected by this
const newPath = new ErasedGroup(mergedGroup, path);
const left = newPath.left;
const top = newPath.top;
// convert it into a dataURL, then back to a fabric image
const newData = newPath.toDataURL({
withoutTransform: true
});
fabric.Image.fromURL(newData, (fabricImage) => {
fabricImage.set({
left: left,
top: top,
});
// remove the old objects then add the new image
this.canvas.remove(...objects);
this.canvas.add(fabricImage);
});
}
this.canvas.clearContext(this.canvas.contextTop);
this.canvas.renderAll();
this._resetShadow();
},
});
// to use it, just set the brush
const eraserBrush = new EraserBrush(canvas);
eraserBrush.width = 10;
eraserBrush.color = "#ffffff";
canvas.freeDrawingBrush = eraserBrush;
canvas.isDrawingMode = true;
Codepen example: https://codepen.io/anon/pen/byPeNQ
Hi @Madaoism, I'm going through this issue since so long.

Here i'm erasing the whole square and it results like this.

Some part of an element is still there after erasing. can you please provide some solution for this ?
https://konvajs.github.io/docs/sandbox/Free_Drawing.html
Not as smooth looking though.
I ended up using a fabric.js revert function instead. It works really well.
I want to know how to save canvas data about image for konvajs
My solution is:
canvas.on('path:created', function (opt) { opt.path.globalCompositeOperation = 'destination-out'; opt.path.lineWidth = 10; //opt.path.stroke = 'black'; //opt.path.fill = 'black'; canvas.renderAll(); });
it not works. can show you complete code?