Control z-index in Fabric.js
Asked Answered
B

6

27

In fabricjs, I want to create a scene in which the object under the mouse rises to the top of the scene in z-index, then once the mouse leaves that object, it goes back to the z-index where it came from. One cannot set object.zindex (which would be nice). Instead, I'm using a placeholder object which is put into the object list at the old position, and then the old object is put back in the position where it was in the list using canvas.insertAt. However this is not working.

See http://jsfiddle.net/rFSEV/ for the status of this.

var canvasS = new fabric.Canvas('canvasS', { renderOnAddition: false, hoverCursor: 'pointer', selection: false });
var bars = {}; //storage for bars (bar number indexed by group object)
var selectedBar = null; //selected bar (group object)
var placeholder = new fabric.Text("XXXXX", { fontSize: 12 });

//pass null or a bar
function selectBar(bar) {
    if (selectedBar) {
        //remove the old topmost bar and put it back in the right zindex
        //PROBLEM: It doesn't go back; it stays at the same zindex
        selectedBar.remove();
        canvasS.insertAt(selectedBar, selectedBar.XZIndex, true);
        selectedBar = null;
        }
    if (bar) {
        //put a placeholder object ("XXX" for now) in the position
        //where the bar was, and put the bar in the top position
        //so it shows topmost
        selectedBar = bar;
        canvasS.insertAt(placeholder, selectedBar.XZIndex, true);
        canvasS.add(bar);
        canvasS.renderAll();
        }
    }

canvasS.on({
     'mouse:move': function(e) {
        //hook up dynamic zorder
        if (!e.target) return;
        if (bars[e.target])
            selectBar(e.target);
        else
            selectBar(null);
        },
    });

var objcount = canvasS.getObjects().length;

//create bars
for (var i = 0; i < 20; ++i) {
    var rect = new fabric.Rect({
      left: 0,
      top: 0,
      rx: 3,
      ry: 3,
      stroke: 'red',
      width: 200,
      height: 25
    });
    rect.setGradientFill({
      x1: 0,
      y1: 0,
      x2: 0,
      y2: rect.height,
      colorStops: {
        0: '#080',
        1: '#fff'
      }
    });    
    var text = new fabric.Text("Bar number " + (i+1), {
        fontSize: 12
    });
    var group = new fabric.Group([ rect, text ], {
      left: i + 101,
      top: i * 4 + 26
    });
    group.hasControls = group.hasBorders = false;

    //our properties (not part of fabric)
    group.XBar = rect;
    group.XZIndex = objcount++;

    canvasS.add(group);
    bars[group] = i;
    }
canvasS.renderAll();
Barrybarrymore answered 3/1, 2013 at 0:5 Comment(3)
There's 4 methods in Fabric to control z-index: bringForward (1 level up), bringToFront (all the way up), sendBackwards (1 level down), and sendToBack (all the way down). They can all be called on object directly or on canvas (passing desired object).Rhiannonrhianon
That's good information, but are you saying that insertAt does not have any effect on z-index? (What's the purpose of insertAt then?). Also if those 4 methods are the only option, we would have to call sendBackwards N times: say, to move it from z-index 105 to 55, we'd have to call it 50 times in a loop, which would apparently redraw each time. That is crazily complex.Barrybarrymore
insertAt should work as well. It's meant exactly to insert object at correct location. However, it's used for adding new objects onto canvas. To change z-index of existing objects, you can either use those 4 methods or modify canvas._objects array directly. z-index is simply the same as the index of objects in that array (0 rendered first, then 1, then 2, and so on)Rhiannonrhianon
P
38

Since fabric.js version 1.1.4 a new method for zIndex manipulation is available:

canvas.moveTo(object, index);
object.moveTo(index);

I think this is helpful for your use case. I've updated your jsfiddle - i hope this is what you want:
jsfiddle

Phonetics answered 29/4, 2013 at 9:24 Comment(3)
Also see github.com/kangax/fabric.js/issues/135 for getting the current zIndex.Pompey
Also: bringToFront, sendToBack, bringForward, sendBackwardsArdyth
Does not appear to work correctly on canvas Group() objectsBedizen
T
10

Also make sure you change z-index AFTER adding object to canvas.

So code will looks like:

canvas.add(object);
canvas.moveTo(object, index);

Otherwise fabricjs don`t care about z-indexes you setup.

Trematode answered 29/10, 2014 at 9:25 Comment(0)
G
4

After I added a line object, I was make the line appear under the object using:

canvas.add(line);
canvas.sendToBack(line);

Other options are

  • canvas.sendBackwards
  • canvas.sendToBack
  • canvas.bringForward
  • canvas.bringToFront

see: https://github.com/fabricjs/fabric.js/issues/135

Gehring answered 30/5, 2019 at 19:8 Comment(0)
P
0

You can modify your _chooseObjectsToRender method to have the following change at the end of it, and you'll be able to achieve css-style zIndexing.

objsToRender = objsToRender.sort(function(a, b) {
  var sortValue = 0, az = a.zIndex || 0, bz = b.zIndex || 0;

  if (az < bz) {
    sortValue = -1;
  }
  else if (az > bz) {
    sortValue = 1;
  }

  return sortValue;
});

https://github.com/fabricjs/fabric.js/pull/5088/files

Pallbearer answered 5/7, 2018 at 21:44 Comment(0)
N
0

You can use these two functions to get z-index of a fabric object and modify an object's z-index, since there is not specific method to modify z-index by object index :

fabric.Object.prototype.getZIndex = function() {
    return this.canvas.getObjects().indexOf(this);
}

fabric.Canvas.prototype.moveToLayer = function(object,position) {
    while(object.getZIndex() > position) {
        this.sendBackwards(object);
    }
}
Noland answered 14/5, 2020 at 16:33 Comment(0)
H
0

For me, sendToBack() did not work in 'object:moving' event handler. So I had to use:

canvas.preserveObjectStacking = true;

via https://github.com/fabricjs/fabric.js/wiki/FAQ#how-can-i-change-the-order-of-objects-on-the-canvas

Hardy answered 21/6, 2024 at 21:8 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.