Valentino Ru
Valentino Ru

Reputation: 5052

Fabric.js: Group-Selection transforms certain objects' center position to canvas origin

In an angular application, I implemented a simple whiteboard with Fabric.js, which lets the user draw some basic shapes and freehand drawing. The shapes are botch empty and filled circle and rectangle, and a basic UML diagram template, which are two single rects on top of each other.

When I select a single or multiple objects, the position of all instances of rects and circles are automatically transformed to the canvas' origin, only leaving the selection marker at its original position. Oddly, this not happens with the UML template, which in the end also is composed of two simple rects like the transformer one, except with another initial size. Freehand drawings from the built-in function are not concerned too.

When this elements are selected individually, they remain "in position". I did not overwrite any selection methods.

Worth mentioning is that the whiteboard is located in a widget inside the app, meaning that you can drag the whiteboard around the display, hence the values adjustedX and adjustedY. However, the objects are always transformed into the origin, and other objects work normally, so I tend to exclude that.

single selection

enter image description here

group selection: all objects selected

enter image description here

group selection: only rect selected

enter image description here

This is the code which creates my canvas and the two shapes

        const component = this;
        this.canvas = new fabric.Canvas('myCanvas', {
            selectionColor: 'rgba(0, 0, 255, 0.1)',
            selectionLineWidth: 2,
            selection: false,
            preserveObjectStacking: true,

        });
        this.canvas.isDrawingMode = true;
        this.canvas.freeDrawingBrush.width = 5;
        this.canvas.setHeight(window.innerHeight);
        this.canvas.setWidth(window.innerWidth);
        $(window).on('resize', function(){
            component.canvas.setHeight(window.innerHeight);
            component.canvas.setWidth(window.innerWidth);
        });
        this.setObjectsSelectable(false);


        if(this.canvas){
            // omitted variable declarations

            $(".upper-canvas")
                .on('mousedown touchstart',function(e){
                    isDown = true;
                    // ...

                    switch(component.drawingMode){
                        case DrawingMode.UML:
                            var rectTop = new fabric.Rect({
                                width: 1,
                                height: 1,
                                left: anchorX,
                                top: anchorY,
                                stroke: component.color,
                                strokeWidth: component.stroke,
                                fill: '',
                                selectable: false
                            });
                            var rectBottom = new fabric.Rect({
                                width: 1,
                                height: 1,
                                left: anchorX,
                                top: anchorY,
                                stroke: component.color,
                                strokeWidth: component.stroke,
                                fill: '',
                                selectable: false
                            });
                            component.canvas.add(rectTop);
                            component.canvas.add(rectBottom);
                            umlObjectTop = rectTop;
                            umlObjectBottom = rectBottom;
                            break;
                        case DrawingMode.SHAPE:
                            var rect = new fabric.Rect({
                                width: 5,
                                height: 5,
                                left: anchorX,
                                top: anchorY,
                                stroke: component.color,
                                strokeWidth: component.stroke,
                                fill: component.fill,
                                selectable: false
                            });
                            component.canvas.add(rect);
                            drawingObject = rect;
                            break;
                        case DrawingMode.CIRCLE:
                            // ...
                            break;
                    }
                })
                .on('mousemove touchmove', function(e){
                    // ...

                    switch(component.drawingMode){

                        case DrawingMode.UML:

                            if(!isDown) return;

                            // ...

                            if(heightTop > 100){
                                heightTop = 100;
                                heightBottom = component.abs(anchorY - adjustedY) - 160;
                            }
                            if(anchorX > adjustedX){
                                umlObjectTop.set({ left: component.abs(adjustedX) });
                                umlObjectBottom.set({ left: component.abs(adjustedX) });
                            }
                            if(anchorY > adjustedY){
                                umlObjectTop.set({ top: component.abs(adjustedY) });
                                umlObjectBottom.set({ top: component.abs(adjustedY)+heightTop });
                            }

                            umlObjectTop.set({ width: widthT });
                            umlObjectTop.set({ height: heightTop });
                            umlObjectBottom.set({ width: widthT });
                            umlObjectBottom.set({ height: heightBottom });
                            component.canvas.renderAll();
                            break;
                        case DrawingMode.SHAPE:
                            if(!isDown) return;
                            if(anchorX > adjustedX){
                                drawingObject.set({ left: component.abs(adjustedX) });
                            }
                            if(anchorY > adjustedY){
                                drawingObject.set({ top: component.abs(adjustedY) });
                            }

                            drawingObject.set({ width: component.abs(anchorX - adjustedX) });
                            drawingObject.set({ height: component.abs(anchorY - adjustedY) });
                            component.canvas.renderAll();
                            break;

                        case DrawingMode.CIRCLE:
                            // ...
                            break;
                    }
                })
                .on('mouseup touchend', function(e){
                    isDown = false;

                    switch(component.drawingMode){
                        case DrawingMode.SHAPE:
                        case DrawingMode.CIRCLE:
                            component.canvas.add(drawingObject);
                            break;
                    }
                });
        }

Note: selection = false gets activated when changing into "selection mode" in the application.

Upvotes: 1

Views: 1395

Answers (1)

Valentino Ru
Valentino Ru

Reputation: 5052

So I wasn't really able to find the cause of this behaviour, but found a work-around.

When dropping the object, I just clone this element, remove the original one and drop the cloned one, and tadaaa, the objects do not get transformed anymore.

dropObject(drawingObject: any, id: number, awtype: string) {
        const component = this;
        drawingObject.clone(function (clone) {
            clone.set('selectable', false);
            component.addLongClickListener(clone);
            clone.toObject = (function (toObject) {
                return function () {
                    return fabric.util.object.extend(toObject.call(this), {
                        id: this.id,
                        awtype: this.awtype
                    });
                };
            })(clone.toObject);
            clone.id = id;
            clone.awtype = awtype;
            component.canvas.add(clone);
            component.canvas.renderAll();
        });
        this.canvas.remove(drawingObject);
    }

Upvotes: 1

Related Questions