Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Layering canvas objects in Fabric.js

Is there a way to layer objects on a Fabric.js canvas via the official API? Right now the only way I have found to do it is to manually iterate through the canvas._objects and reorder them so that they get drawn in a specific order. Is there a better way to do this that doesn't (potentially) break the object?

like image 721
Zwade Avatar asked Feb 22 '13 20:02

Zwade


4 Answers

[Edit] I've corrected my info below (my bad, I was originally thinking of the KineticJs api).

FabricJS has these API methods that change the z-index of objects:

canvas.sendBackwards(myObject)
canvas.sendToBack(myObject)
canvas.bringForward(myObject)
canvas.bringToFront(myObject)

Under the covers, FabricJs changes the z-index by removing the object from the getObjects() array and splicing it back in the desired position. It has a nice optimization that checks for intersecting objects.

bringForward: function (object) {
       var objects = this.getObjects(),
           idx = objects.indexOf(object),
           nextIntersectingIdx = idx;


       // if object is not on top of stack (last item in an array)
       if (idx !== objects.length-1) {

         // traverse up the stack looking for the nearest intersecting object
         for (var i = idx + 1, l = this._objects.length; i < l; ++i) {

           var isIntersecting = object.intersectsWithObject(objects[i]) ||
                                object.isContainedWithinObject(this._objects[i]) ||
                                this._objects[i].isContainedWithinObject(object);

           if (isIntersecting) {
             nextIntersectingIdx = i;
             break;
           }
         }
         removeFromArray(objects, object);
         objects.splice(nextIntersectingIdx, 0, object);
       }
       this.renderAll();
     },
like image 73
markE Avatar answered Nov 12 '22 18:11

markE


step 1 : You can Use preserveObjectStacking: true

step 2 : then use sendtoback,sendtofront....fabricjs options like below

Answered over here

like image 9
vijay Avatar answered Nov 12 '22 18:11

vijay


If you want to set a specific ordering for all objects, instead of moving one object forward/backward, iterative calls to bring/send to front/back are slow.

You can use the code of bringToFront as inspiration to speed this use case up: https://github.com/kangax/fabric.js/blob/586e61dd282b22c1d50e15c0361875707e526fd8/src/static_canvas.class.js#L1468

And do this:

fabric.Canvas.prototype.orderObjects = function(compare) {
    this._objects.sort(compare);
    this.renderAll();
}

Where compare defines the sorting, like:

function compare(x,y) {
    return x.getWidth() * x.getHeight() < y.getWidth() * y.getHeight();
}

some_canvas.orderObjects(compare)

If you wish to bring smaller objects to the front.

like image 3
Herbert Avatar answered Nov 12 '22 18:11

Herbert


This is how I did it. I added a zIndex property to every fabric object and then called a sort function after they were added to the canvas:

const CIRCLE_INDEX = 1
let object = new fabric.Circle({
    left: 10,
    top: 10,
    radius: 5
    ...other properties
    zIndex: CIRCLE_INDEX
});

And then call a function like this after you added your objects

const sortObjects = () => {
    canvas._objects.sort((a, b) => (a.zIndex > b.zIndex) ? 1 : -1);
    canvas.renderAll();
}
like image 3
Tom_B Avatar answered Nov 12 '22 20:11

Tom_B