How can you validate to select only objects and not a group of objects in FabricJS with Canvas?

Viewed 50

I have a method that selects the objects on the canvas by clicking a button. But, how to validate that when I select it? It skips the group (which doesn't select the group) and only the individual objects;

here I generate a group of figures

const group = new fabric.Group([
      new fabric.Rect({ width: 100, height: 100, fill: 'red' }),
      new fabric.Rect({ width: 100, height: 100, fill: 'yellow', left: 100 }),
      new fabric.Rect({ width: 100, height: 100, fill: 'blue', top: 100 }),
      new fabric.Rect({
        width: 100,
        height: 100,
        fill: 'green',
        left: 100,
        top: 100
      })
    ])
    this.canvas.add(group)

I create an individual figure

let rect1 = new fabric.Rect( {
      width: 100,
      height: 100,
      fill: 'transparent',
      stroke: 'blue',
      left: 140
    });
    this.canvas.add(rect1)

I create another individual figure

let rect2 = new fabric.Rect( {
      width: 100,
      height: 100,
      fill: 'transparent',
      stroke: 'blue',
      left: 240
    });
   this.canvas.add(rect2)

Here my select method, but I select both the group and the individual objects. How can I validate so that it only selects the objects and not the group?

multiseleccion(){
    this.canvas?.discardActiveObject();
    
      this.seleccionar = new fabric.ActiveSelection(this.canvas?.getObjects(), {
      canvas: this.canvas
      });
      this.canvas?.setActiveObject(this.seleccionar);
      this.canvas?.requestRenderAll();
2 Answers

Fabricjs' getObjects() method returns an array of objects. Unfortunately it does not deliver any method to distinguish a group from a plain shape. We can work around that though. A group has an unique property a shape does not have called _objects.

So we just need to loop over the returned array, look for objects containing this property and simply remove those. Afterwards we can send the cleaned up array to the setActiveObject() method.

Here's an example:

var canvas = new fabric.Canvas('c');
const group = new fabric.Group([
    new fabric.Rect({
        width: 100,
        height: 100,
        fill: 'red'
    }),
    new fabric.Rect({
        width: 100,
        height: 100,
        fill: 'yellow',
        left: 100
    }),
    new fabric.Rect({
        width: 100,
        height: 100,
        fill: 'blue',
        top: 100
    }),
    new fabric.Rect({
        width: 100,
        height: 100,
        fill: 'green',
        left: 100,
        top: 100
    })
])
canvas.add(group);

let rect1 = new fabric.Rect({
    width: 100,
    height: 100,
    fill: 'transparent',
    stroke: 'blue',
    left: 140
});
canvas.add(rect1);

let rect2 = new fabric.Rect({
    width: 100,
    height: 100,
    fill: 'transparent',
    stroke: 'blue',
    left: 240
});
canvas.add(rect2);

function multiseleccion() {
    canvas?.discardActiveObject();
    let activeObjects = this.canvas?.getObjects();
    for (let a = activeObjects.length - 1; a >= 0; a--) {
        if (activeObjects[a]._objects) {
            activeObjects.splice(a, 1);
        }
    }

    let seleccionar = new fabric.ActiveSelection(activeObjects, {
        canvas: canvas
    });
    canvas?.setActiveObject(seleccionar);
    canvas?.requestRenderAll();
}
multiseleccion();
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/5.2.4/fabric.min.js" integrity="sha512-HkRNCiaZYxQAkHpLFYI90ObSzL0vaIXL8Xe3bM51vhdYI79RDFMLTAsmVH1xVPREmTlUWexgrQMk+c3RBTsLGw==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<canvas id="c" width="400" height="300" style="border:1px solid #ccc"></canvas>

Every Object (predefined) in fabric has a type property that can be used to identify a certain object. in your case you can filter out objects of type 'group', this should be the cleanest implementation

I have modified your multiseleccion method to group all the objects on canvas except the object of type group

multiseleccion() {
  this.canvas?.discardActiveObject();

  const objectsWithoutGroups = this.canvas?.getObjects().filter(fabricObject => fabricObject.type !== 'group') || []
  if(objectsWithoutGroups.length) {
    this.seleccionar = new fabric.ActiveSelection(objectsWithoutGroups, {
      canvas: this.canvas
    });
    this.canvas?.setActiveObject(this.seleccionar);
    this.canvas?.requestRenderAll();
  }
}
Related