Make fabric.js objects render in a specific layer order

I am using fabric.js along with react.js to add certain images to my canvas.

I want my canvas to have three layers – components, silhouettes, and patterns. I am adding fabric.Image() objects to each of the categories separately like this –

Components (I want this to be the top layer) –

const addComponent = () => {
comp.src = currentComp;
comp_ = new fabric.Image(comp);

canvas.add(comp_); 

Silhouettes (I want this to be the middle layer) –

const addSilhouette = () => {
main.src = currentSilhouette;
main_ = new fabric.Image(main);

canvas.add(main_);

Patterns (I want this to be the bottom layer) –

const addPattern = () => {
patternImg.src = currentPatternComp;
patternImg_ = new fabric.Image(patternImg);

canvas.add(patternImg_);

The problem is that the top layer (the components layer) can have a variable n number of objects on it, and a user can change the image of any layer at any time.

I want that whenever any layer is edited, the image in the pattern layer stays on the bottom, the one in the silhouettes layer stays in the middle and all the images of the components layer stay on top.

I haven’t been able to do this, so any possible workarounds are much appreciated.

Answer

Instead of using canvas.add, try using canvas.insertAt and add a custom data field to each object to track which “layer” it’s located in.

const layers = {
  BOTTOM: 0,
  MIDDLE: 1,
  TOP: 2
};

function addImage(url, layer) {
  const objects = canvas.getObjects();
  let targetIndex = objects.length;
  for (let i = 0; i < objects.length; i++) {
    if (layer < objects[i].customLayer) {
      targetIndex = i;
      break;
    }
  }
  fabric.Image.fromURL(url, function (img) {
    img.set({customLayer: layer})
    canvas.insertAt(img, targetIndex);
  });
}

This ensures that added images will be added to the top of their respective “layer”, but still be behind all other layers on top of those. If you need to remove images, you can still do canvas.remove like normal, it’s just the addition where things work a little differently.

JSFiddle: https://jsfiddle.net/hs0o3nq6/