Reputation: 1054
I'm trying to use Fabric.js with Fabric Brush This issue that I'm running into is that Fabric Brush only puts the brush strokes onto the Top Canvas and not the lower canvas. (The stock brushes in fabric.js save to the bottom canvas) I think I need to convert "this.canvas.contextTop.canvas" to an object and add that object to the the lower canvas. Any ideas?
I've tried running:
this.canvas.add(this.canvas.contextTop)
in
onMouseUp: function (pointer) {this.canvas.add(this.canvas.contextTop)}
But I'm getting the error
Uncaught TypeError: obj._set is not a function
Upvotes: 0
Views: 2216
Reputation: 21
I have taken the approach suggested by AndreaBogazzi and modified the Fabric Brush so that it does the transfer from upper to lower canvas (as an image) internal to Fabric Brush. I also used some code I found which crops the image to a smaller bounding box so that is smaller than the full size of the canvas. Each of the brushes in Fabric Brush has an onMouseUp
function where the code should be placed. Using the case of the SprayBrush, the original code here was:
onMouseUp: function(pointer) {
},
And it is replaced with this code:
onMouseUp: function(pointer){
function trimbrushandcopytocanvas() {
let ctx = this.canvas.contextTop;
let pixels = ctx.getImageData(0, 0, canvas.upperCanvasEl.width, canvas.upperCanvasEl.height),
l = pixels.data.length,
bound = {
top: null,
left: null,
right: null,
bottom: null
},
x, y;
// Iterate over every pixel to find the highest
// and where it ends on every axis ()
for (let i = 0; i < l; i += 4) {
if (pixels.data[i + 3] !== 0) {
x = (i / 4) % canvas.upperCanvasEl.width;
y = ~~((i / 4) / canvas.upperCanvasEl.width);
if (bound.top === null) {
bound.top = y;
}
if (bound.left === null) {
bound.left = x;
} else if (x < bound.left) {
bound.left = x;
}
if (bound.right === null) {
bound.right = x;
} else if (bound.right < x) {
bound.right = x;
}
if (bound.bottom === null) {
bound.bottom = y;
} else if (bound.bottom < y) {
bound.bottom = y;
}
}
}
// Calculate the height and width of the content
var trimHeight = bound.bottom - bound.top,
trimWidth = bound.right - bound.left,
trimmed = ctx.getImageData(bound.left, bound.top, trimWidth, trimHeight);
// generate a second canvas
var renderer = document.createElement('canvas');
renderer.width = trimWidth;
renderer.height = trimHeight;
// render our ImageData on this canvas
renderer.getContext('2d').putImageData(trimmed, 0, 0);
var img = new fabric.Image(renderer,{
scaleY: 1./fabric.devicePixelRatio,
scaleX: 1./fabric.devicePixelRatio,
left: bound.left/fabric.devicePixelRatio,
top:bound.top/fabric.devicePixelRatio
});
this.canvas.clearContext(ctx);
canvas.add(img);
}
setTimeout(trimbrushandcopytocanvas, this._interval); // added delay because last spray was on delay and may not have finished
},
The setTimeout
function was used because Fabric Brush could still be drawing to the upper canvas after the mouseup event occurred, and there were occasions where the brush would continue painting the upper canvas after its context was cleared.
Upvotes: 0
Reputation: 14731
So the contextTop is CanvasHTMLElement context. You cannot add it. You can add to the fabricJS canvas just fabric.Object derived classes.
Look like is not possible for now. They draw as pixel effect and then they allow you to export as an image. Would be nice to extend fabricJS brush interface to create redrawable objects.
As of now with fabricJS and that particular version of fabric brush, the only thing you can do is:
var canvas = new fabric.Canvas(document.getElementById('c'))
canvas.freeDrawingBrush = new fabric.CrayonBrush(canvas, {
width: 70,
opacity: 0.6,
color: "#ff0000"
});
canvas.isDrawingMode = true
canvas.on('mouse:up', function(opt) {
if (canvas.isDrawingMode) {
var c = fabric.util.copyCanvasElement(canvas.upperCanvasEl);
var img = new fabric.Image(c);
canvas.contextTopDirty = true;
canvas.add(img);
canvas.isDrawingMode = false;
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/2.4.1/fabric.min.js"></script>
<script src="https://tennisonchan.github.io/fabric-brush/bower_components/fabric-brush/dist/fabric-brush.min.js"></script>
<button>Enter free drawing</button>
<canvas id="c" width="500" height="500" ></canvas>
That is just creating an image from the contextTop and add as an object.
Upvotes: 1