Search code examples
fabricjsretina

fabricjs on retina: new object jumps to left top


I continue my work on collaborative sketch tool and trying to add retina devices support. Currently i have following behavior if user creating drawing on ipad air: small movie

Here is my code:

this.getZoomLevel = function (height) {
            if (height > 1024) {
                return 1024 / height;
            } else {
                return height / 1024;
            }
        };  

this.calculateCanvasSize = function(pHeight, pWidth) {
            var result = {
                height: 0,
                width: 0
            };

            while (result.width < pWidth - 1 && result.height < pHeight - 1) {
                result.height = result.height + 1;
                result.width = result.height * 4 / 3;
            }
            return result;
        }; 

this.initCanvas = function () {
            try {
                var parent = document.getElementsByClassName('komaso-canvas-container')[0];
                var canvasSize = this.calculateCanvasSize(parent.clientHeight, parent.clientWidth);

                var canvasHtml = "<div id='wrapper-" + this.Id + "' class='whiteboard-canvas-wrapper' data-ng-show='CurrentPage.Id==" + this.Id + "'><canvas width='" + canvasSize.width + "' height='" + canvasSize.height + "' id='whiteboard-" + this.Id + "' class='whiteboard'><p>Your brower does not support Canvas/p></canvas></div>";
                $(parent).append($compile(canvasHtml)(scope));

                this.Canvaso = document.getElementById(this.HtmlId);

                if (!this.Canvaso) {
                    console.log('Error: Cannot find the imageView canvas element!');
                    return;
                }

                if (!this.Canvaso.getContext) {
                    console.log('Error: no canvas.getContext!');
                    return;
                }

                this.FabricCanvas = new fabric.Canvas(this.HtmlId, { selectionColor: 'transparent' });

                this.FabricCanvas.setWidth(canvasSize.width);
                this.FabricCanvas.setHeight(canvasSize.height);

                fabric.Object.prototype.transparentCorners = false;

                this.FabricCanvas.on('mouse:down', this.onMouseDown);
                this.FabricCanvas.on('mouse:up', this.onMouseUp);
                this.FabricCanvas.on('mouse:move', this.onMouseMove);
                this.FabricCanvas.on('object:added', this.onObjectAdded);
                this.FabricCanvas.on('text:editing:exited', self.onTextObjectEdited);

                if (window.devicePixelRatio !== 1) {

                    var c = this.FabricCanvas.getElement(); 
                    var w = c.width, h = c.height;

                    c.setAttribute('width', w * window.devicePixelRatio);
                    c.setAttribute('height', h * window.devicePixelRatio);

                    $(c).width(canvasSize.width);
                    $(c).height(canvasSize.height);

                    c.getContext('2d').scale(window.devicePixelRatio, window.devicePixelRatio);
                }

                this.FabricCanvas.setZoom(this.getZoomLevel(this.Canvaso.height));

                this.ToggleTool(self.CurrentTool.ToolName);
                this.WhiteboardInitiated = true;
            } catch (e) {
                console.log(e);
            }
        };

getZoomLevel returns value to pass into SetZoom method of fabric js canvas object. We decided to have all clients canvas aspects are 4:3 and default dimension is 1024*768. So based on this dimensions we calculation zoom factor.

calculateCanvasSize - returns width and height for canvas according to 4:3 rule.

If you have any idea about how to fix this wrong behavior then post your comment please. Thank you in advance!


Solution

  • I would suggest you yo update to a retina enabled version of fabricjs (grab 1.6.2).

    If, for any reason you can't, i think the problem is here:

    if (window.devicePixelRatio !== 1) {
       var c = this.FabricCanvas.getElement(); 
       ...
       c.getContext('2d').scale(window.devicePixelRatio, window.devicePixelRatio);
    }
    

    getContext return a new context. This is not the context where fabric is gonna render later. If you want to have retina enabled lowerCanvas you have to scale this.FabricCanvas.contextContainer that gets created and referenced on fabric.Canvas initialization.

    I suggest you to switch to newer fabric anyway.