Fabric.js V 2.0 list of breaking changes - Part 2

Selection handling

With the `Canvas` class fabric.js can handle a layer of selection / interaction.
Fabric can handle also multiple selection using a special class called `ActiveSelection`.
When a mouse interaction is performed by a user that utilize an app based on fabricJS, there are some built in selection functionalities.
What follow is a default overview of the selection process:
- Left click on an object selects it.
- Click and drag on the canvas creates a rectangular selection. All the bounding boxes intersecting with this rectangle will be selected on mouse up creating a multi selection.
- Clicking on objects with shift selects it or add it to the current selection, changing from a selected object to a multi selection of 2 objects.

How this is handled internally

An object is selected and behaves as selected when is on a canvas and is referenced on that canvas _activeObject property.
A multi selection is represented by an ActiveSelection object, that is a special class derived from the Group class, referenced in the _activeObject property of the canvas.
There is one active object at time and it is either an object or a multi selection.

Modify the selection programatically

Developers can create/destroy or change selection, outside of user mouse interactions in the following ways:
canvas.setActiveObject(object) sets the object passed as argument as the active one. The current selected object gets discarded.
canvas.discardActiveObject() Remove the current selection.
canvas.getActiveObject() Returns a reference to the current active object.
canvas.getActiveObjects() Returns an array containing a reference to the current selected objects, one or many.
None of this methods render the canvas again, so you have to call canvas.requestRenderAll after them to see the changes. canvas._setActiveObject() and canvas._discardActiveObject() are two private method used from the non private ones to make the selection job. They do not contain the event firing code, and they are not chainable. if you need to handle a selection process but you do not want to fire the side effects you inserted in the selection events, you may try to use those.

Create a multi selection

// rect1 and rect2 are 2 object on a canvas, canvas is the canvas instance
var selection = new fabric.ActiveSelection([rect1, rect2], {
  canvas: canvas
});
canvas.setActiveObject(selection);

Add an object to a multi selection

// rect1 and rect2 are 2 object on a canvas, grouped in a multi selection
// rect3 is another object on the canvas
var selection = canvas.getActiveObject();
if (selection.type === 'activeSelection') {
  selection.addWithUpdate(rect3)
}

So an object that is on a canvas can be an activeObject. Many object in a canvas can be grouped in an ActiveSelection object and behave like a multi seleciton. The objects inside the multi selection are still direct children of the canvas, and the ActiveSelection object, even if referenced in the `_activeObject` property of the canvas, it is not included in the canvas objects. canvas.getObjects() will not contain the ActiveSelection object. The active selection has a mandatory canvas property that has to be the actual canvas or it will not work.
Future non breaking updates to fabric may simplify this aspect, changing the canvas property of the multi selection implictly in some method.
A take away point is that the ActiveSelection is a service class that behaves in some predetermined way and that has little space for customization.

IMPORTANT

discardActiveObject on a multi selection triggers lot of side effects. If you are planning to do something with objects inside an active selection and reuse them, and then trash the activeSelection, first discardActiveObject, then do what you want to do with the objects, then in case select a new object.

Removed methods

The following methods are no more available since the introduction of ActiveSelection:

setActiveGroup(group)
getActiveGroup();
deactivateAll();
discardActiveGroup();
deactivateAllWithDispatch();

How to react to user selection

Regarding selections fabric provides callbacks and events.
Events fired by objects are:

selected
deselected
Events fired by canvas are:
object:selected
before:selection:cleared
selection:cleared
For each object are available 2 callbacks
onDeselect
onSelect
Those callbacks are empty and meant to be overridden, and should not used to execute much logic, but just to return a boolean to cancel the current process depending on dynamic conditions for which mutating the properties of objects like selectable, evented or others, is not comfortable or anyway brings to weird code paths.

Follows a description of the most common user selection flows, and the fabric code behind them.

Selection

An user click on an object, no other active objects on canvas
setActiveObject() gets called.
setActiveObject calls the onSelect method of the target object, passing as first argument an object with `e` property containing the reference to the event.
At this point you may have or not have assigned to onSelect some custom logic to intervene on the selection process.
If onSelect returns true, the selection process ends here and nothing gets selected. If it returns false as default, the object is set as selected, a canvas level event gets fired object:selected, and a object level one fires selected, both of them having access to the original mouse event.

Deselection

An user click on a non selectable area of the canvas, having an object selected.
discardActiveObject() gets called.
First a canvas level event fires before:selection:cleared, getting in the option argument both the actual activeObject and the event.
Then discardActiveObject calls the onDeselect method of the target object, passing as first argument an object with `e` property containing the reference to the event and eventually a reference to the next target that is replacing this, in this case undefined.
At this point you may have or not have assigned to onDeselect some custom logic to intervene on the selection process.
If onDeselect returns true, the deselection process ends here and nothing gets deselected, no additional events fired. If it returns false as default, the active object is removed from the property _activeObject, a canvas level event gets fired as selection:cleared, and a object level one fires deselected, both of them having access to the original mouse event.