Jason Small
Jason Small

Reputation: 1054

Fabric.JS and Fabric-Brush - Can't add to lower canvas

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

Answers (2)

sjscotti
sjscotti

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

AndreaBogazzi
AndreaBogazzi

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

Related Questions