DevLost

A developer lost in the mountains

Fabric: extend Image class

Recently I came up with the need to extend Image class of Fabric js lib. I had to keep synchronized two different Fabric Canvases, with different scale.

In the code snippet below, fabric.TavImage extends fabric.Image. AddToCanvases function adds an instance of TavImage to one canvas, then clones the object and adds it, scaled, to another canvas.

// 
//  TavImage
//
fabric.TavImage = fabric.util.createClass(fabric.Image, {

    type: 'tavImage',

    initialize: function (element, options) {
        options || (options = {});

        this.callSuper('initialize', element, options);
        this.set('scaleFrontCanvas', options.scaleFrontCanvas || '');
        this.set('scaleHiddenCanvas', options.scaleHiddenCanvas || '');
        this.set('id', options.id || '');
        this.set('objFrontCloned', options.objFrontCloned || '');
        this.set('trasfParams', options.trasfParams || '');
        this.set('isBaseImage', options.isBaseImage || false);
        this.set('scaleFactor', options.scaleFactor || '');
    },

    toObject: function () {
        return fabric.util.object.extend(this.callSuper('toObject'), {
            scaleFrontCanvas: this.get('scaleFrontCanvas'),
            scaleHiddenCanvas: this.get('scaleHiddenCanvas'),
            id: this.get('id'),
            objFrontCloned: this.get('objFrontCloned'),
            trasfParams: this.get('trasfParams'),
            isBaseImage: this.get('isBaseImage'),
            scaleFactor: this.get('scaleFactor')
        });
    },

    _render: function (ctx) {
        this.callSuper('_render', ctx);
    },

   
    addToCanvases: function (hiddenCanvas, frontCanvas) {

        hiddenCanvas.hiddenObjectsCount += 1;

        // check if it is not already in canvs
        var objects = hiddenCanvas.getObjects();
        for (var i in objects) {
            if (objects[i].get('id') == this.id)
                return;
        }


        // add to frontCanvas
        this.addToFrontCanvas(frontCanvas);
        
        // add to hidden Canvas
        this.addToHiddenCanvas(hiddenCanvas);
        
    },

    addToFrontCanvas: function (frontCanvas) {

        // clone object
        this.objFrontCloned = fabric.util.object.clone(this);
        this.objFrontCloned.scaleX = this.scaleFrontCanvas || this.scaleX;
        this.objFrontCloned.scaleY = this.scaleFrontCanvas || this.scaleY;

        frontCanvas.add(this.objFrontCloned);

        // center base image layer
        if (this.isBaseImage == true) {
            this.objFrontCloned.center();
        }

        this.objFrontCloned.setCoords();
    },

    addToHiddenCanvas: function (hiddenCanvas) {
        // scale to hiddenCanvas
        this.scaleX = this.scaleHiddenCanvas || this.scaleX;
        this.scaleY = this.scaleHiddenCanvas || this.scaleY;

        hiddenCanvas.add(this);


        // center base image layer
        if (this.isBaseImage == true) {
            //this.objFrontCloned.center();
            //this.objFrontCloned.setCoords();
        }
        else {
            this.updateCoordsForHiddenCanvas();
            //this.updateScaleForHiddenCanvas();
        }

    },

    updateCoordsForHiddenCanvas: function () {

        this.left = Math.round(this.trasfParams.s + 1/this.scaleFrontCanvas * (this.objFrontCloned.left - this.trasfParams.s1));
        this.top = Math.round(this.trasfParams.t + 1/this.scaleFrontCanvas * (this.objFrontCloned.top - this.trasfParams.t1));

        this.setCoords();
    },

    updateScaleForHiddenCanvas: function () {
        if(this.scaleFactor == '')
            this.scaleFactor = this.objFrontCloned.scaleX;
        this.scaleX = this.objFrontCloned.scaleX / this.scaleFactor;
        this.scaleY = this.objFrontCloned.scaleY / this.scaleFactor;
        
        this.setCoords();
    }
   
});

/**
   * Creates an instance of fabric.Image from an URL string
   * @static
   * @param {String} url URL to create an image from
   * @param {Function} [callback] Callback to invoke when image is created (newly created image is passed as a first argument)
   * @param {Object} [imgOptions] Options object
   */
fabric.TavImage.fromURL = function (url, callback, imgOptions) {
    fabric.util.loadImage(url, function (img) {
        callback(new fabric.TavImage(img, imgOptions));
    }, null, imgOptions && imgOptions.crossOrigin);
};