Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Adding canvas inside another canvas: obj.setCoords is not a function( fabric js)

Started using fabric.js and trying to add a canvas inside another canvas, so that the top canvas stays constant and I'll add objects to inner canvas.

Here is the snippet of adding a canvas to another canvas.

canvas  = new fabric.Canvas('artcanvas');
innerCanvas = new fabric.Canvas("innerCanvas");
canvas.add(innerCanvas);

and my html looks like this

<canvas id="artcanvas" width="500" height="500"></canvas>
<canvas id="innerCanvas" width="200" height="200" ></canvas>

Once adding these successfully, what I am going to do is , add the coordinates to the inner canvas, so that it looks like one on another to the end user.

However, ran into the below error for the tried code

    Uncaught TypeError: obj.setCoords is not a function
    at klass._onObjectAdded (fabric.js:6894)
    at klass.add (fabric.js:231)
    at main.js:60
    at fabric.js:19435
    at HTMLImageElement.fabric.util.loadImage.img.onload (fabric.js:754)
_onObjectAdded @ fabric.js:6894
add @ fabric.js:231
(anonymous) @ main.js:60
(anonymous) @ fabric.js:19435
fabric.util.loadImage.img.onload @ fabric.js:754

Looking at the error message, just went to the line of error and here is what I found in chrome console

enter image description here

Can someone point the mistake in my codes ?

like image 548
Suresh Atta Avatar asked Jun 08 '17 11:06

Suresh Atta


People also ask

How to change the position of an object on the canvas?

You can change this by setting originX= {left/center/right} Describe object's corner position in canvas element coordinates. includes padding. Used of object detection. set and refreshed with setCoords and calcCoords. When `true`, object horizontal movement is locked

How to describe object's corner position in canvas object absolute coordinates?

WARNING this is beta, this feature may change or be renamed. since 2.4.0 Describe object's corner position in canvas object absolute coordinates properties are tl,tr,bl,br and describe the four main corner. each property is an object with x, y, instance of Fabric.Point.

How do I Center a clippath on a fabric object?

1.3.4 a fabricObject that, without stroke define a clipping area with their shape. filled in black the clipPath object gets used when the object has rendered, and the context is placed in the center of the object cacheCanvas. If you want 0,0 of a clipPath to align with an object center, use clipPath.originX/Y to 'center'


2 Answers

After going through no.of discussions and internet solutions, for time being I am using Fabric Rectangle as a clipper and setting it's boundaries so user can be able to drop/play with in that particular clipper.

Dotted red(image below) is my clipper and now I can bound the dropping and below is the code to add an image with a clipper.

function addImageToCanvas(imgSrc) { 
    fabric.Object.prototype.transparentCorners = false;    
    fabric.Image.fromURL(imgSrc, function(myImg) {
        var img1 = myImg.set({
            left: 20,
            top: 20,
            width: 460,
            height: 460
        });
        img1.selectable = false;
        canvas.add(img1);

        var clipRectangle = new fabric.Rect({
            originX: 'left',
            originY: 'top',
            left: 150,
            top: 150,
            width: 200,
            height: 200,
            fill: 'transparent',
            /* use transparent for no fill */
            strokeDashArray: [10, 10],
            stroke: 'red',
            selectable: false
        });

        clipRectangle.set({
            clipFor: 'layer'
        });
        canvas.add(clipRectangle);

    });
}

enter image description here

Now while appending any image/layer to the canvas, I bind that image/layer/text to the clipper I created.

function addLayerToCanvas(laImg) {

    var height = $(laImg).height();
    var width = $(laImg).width();
    var clickedImage = new Image();
    clickedImage.onload = function(img) {

        var pug = new fabric.Image(clickedImage, {

            width: width,
            height: height,
            left: 150,
            top: 150,
            clipName: 'layer',
            clipTo: function(ctx) {
                return _.bind(clipByName, pug)(ctx)
            }
        });
        canvas.add(pug);
    };
    clickedImage.src = $(laImg).attr("src");

}

And the looks like, after restriction of bounds, enter image description here

Here is the fiddle I have created with some static image url.

https://jsfiddle.net/sureshatta/yxuoav39/

So I am staying with this solution for now and I really feel like this is hacky and dirty. Looking for some other clean solutions.

like image 130
Suresh Atta Avatar answered Sep 30 '22 19:09

Suresh Atta


As far as I know you can't add a canvas to another canvas - you're getting that error as it tries to call setCoords() on the object you've added, but in this case it's another canvas and fabric.Canvas doesn't contain that method (see docs). I think a better approach would be to have two canvases and position them relatively using CSS - see this simple fiddle

HTML

<div class="parent">
  <div class="artcanvas">
    <canvas id="artcanvas"  width="500" height="500"></canvas>  
  </div>
  <div class="innerCanvas">
    <canvas id="innerCanvas" width="200" height="200" ></canvas>  
  </div>
</div>

CSS

.parent {
  position: relative;
  background: black;
}


.artcanvas {
  position: absolute;
  left: 0;
  top: 0;
}

.innerCanvas {
  position: absolute;
  left: 150px;
  top: 150px;
}

JS

$(document).ready(function() {
    canvas  = new fabric.Canvas('artcanvas');
  innerCanvas = new fabric.Canvas("innerCanvas");
  var rect = new fabric.Rect({
    fill: 'grey',
    width: 500,
    height: 500
  });
  canvas.add(rect);
  var rect2 = new fabric.Rect({
    fill: 'green',
    width: 200,
    height: 200
  });
  innerCanvas.add(rect2);
})

To handle the object serialization, you can do something like this:

var innerObjs = innerCanvas.toObject();
console.dir(innerObjs);
var outerObjs = canvas.toObject();
innerObjs.objects.forEach(function (obj) {
    obj.left += leftOffset; // offset of inner canvas
  obj.top += topOffset;
  outerObjs.objects.push(obj);
    });
var json = JSON.stringify(outerObjs);

This will then give you the JSON for all objects on both canvases

like image 39
John M Avatar answered Sep 30 '22 18:09

John M