简体   繁体   中英

Maintaining object size while zooming in fabric js

Is there any solution for maintaining object size even if the user zoom in or out? I want to achieve like on the google maps behavior. So if I have an object (group of object) with a height and width of 20 even if I zoom into it, it should still be 20 pixels by default. Right now the behavior I have is that when the user zoom in or out the image gets bigger / smaller. I also did looping all the objects that I have on the canvas then set the scaleX and scaleY for it but the result was so laggy for me.

 var canvas = new fabric.Canvas('c') ; var circle = new fabric.Circle({ radius: 20, fill: 'red', left: 100, top: 100 }); var triangle = new fabric.Triangle({ fill: 'red', left: 200, top: 200 }); canvas.add(circle, triangle) ; //canvas.zoomToPoint(new fabric.Point(canvas.getCenter().left, canvas.getCenter().top,1)) ; $(function(){ $('#zoomIn').click(function(){ canvas.setZoom(canvas.getZoom() * 1.1 ) ; }) ; $('#zoomOut').click(function(){ canvas.setZoom(canvas.getZoom() / 1.1 ) ; }) ; }) ; 
 #canvasContainer {border: dotted gray thin ;} 
 <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/1.7.22/fabric.min.js"></script> <button id="zoomIn">+</button> <button id="zoomOut">-</button> <div id="canvasContainer"> <canvas id="c" width="400" height="400"></canvas> </div> 

This is not a built in feature of fabricjs and you have to solve it with subclassing or modification of the standard code of the fabric.Object class.

I will make an example then you can use it to find the solution that better suits your project.

So for the whole canvas there is a zoom stage, and then every object has its own transformation. This happen in fabric.Object.prototype.transform . At that point the zoom is done and you can't really avoid it. What you can do is zoom in the opposite direction before drawing.

So flag the objects you need to do not zoom with a new property ignoreZoom default to false.

Modify the fabric.Object.prototype.transform in this way:

transform: function(ctx, fromLeft) {
      if (this.group && !this.group._transformDone && this.group === this.canvas._activeGroup) {
        this.group.transform(ctx);
      }
      // ADDED CODE FOR THE ANSWER
      if (this.ignoreZoom && !this.group && this.canvas) {
        var zoom = 1 / this.canvas.getZoom();
        ctx.scale(zoom, zoom);
      }
      // END OF ADDED CODE FOR THE ANSWER
      var center = fromLeft ? this._getLeftTopCoords() : this.getCenterPoint();
      ctx.translate(center.x, center.y);
      this.angle && ctx.rotate(degreesToRadians(this.angle));
      ctx.scale(
        this.scaleX * (this.flipX ? -1 : 1),
        this.scaleY * (this.flipY ? -1 : 1)
      );
      this.skewX && ctx.transform(1, 0, Math.tan(degreesToRadians(this.skewX)), 1, 0, 0);
      this.skewY && ctx.transform(1, Math.tan(degreesToRadians(this.skewY)), 0, 1, 0, 0);
    },

This solves rendering, but does not solve controls and caching. Caching is harder to understand, so i suggest you just disable caching for the objects that do not need to zoom.

For controls i ll be back later, is not super simple.

 fabric.Object.prototype.ignoreZoom = false; fabric.Object.prototype.transform = function(ctx, fromLeft) { if (this.group && !this.group._transformDone && this.group === this.canvas._activeGroup) { this.group.transform(ctx); } // ADDED CODE FOR THE ANSWER if (this.ignoreZoom && !this.group && this.canvas) { var zoom = 1 / this.canvas.getZoom(); ctx.scale(zoom, zoom); } // END OF ADDED CODE FOR THE ANSWER var center = fromLeft ? this._getLeftTopCoords() : this.getCenterPoint(); ctx.translate(center.x, center.y); this.angle && ctx.rotate(degreesToRadians(this.angle)); ctx.scale( this.scaleX * (this.flipX ? -1 : 1), this.scaleY * (this.flipY ? -1 : 1) ); this.skewX && ctx.transform(1, 0, Math.tan(degreesToRadians(this.skewX)), 1, 0, 0); this.skewY && ctx.transform(1, Math.tan(degreesToRadians(this.skewY)), 0, 1, 0, 0); }; var canvas = new fabric.Canvas('c') ; var circle = new fabric.Circle({ radius: 20, fill: 'red', left: 100, top: 100, ignoreZoom: true }); var triangle = new fabric.Triangle({ fill: 'red', left: 200, top: 200 }); canvas.add(circle, triangle) ; //canvas.zoomToPoint(new fabric.Point(canvas.getCenter().left, canvas.getCenter().top,1)) ; $(function(){ $('#zoomIn').click(function(){ canvas.setZoom(canvas.getZoom() * 1.1 ) ; }) ; $('#zoomOut').click(function(){ canvas.setZoom(canvas.getZoom() / 1.1 ) ; }) ; }) ; 
 #canvasContainer {border: dotted gray thin ;} 
 <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/1.7.22/fabric.min.js"></script> <button id="zoomIn">+</button> <button id="zoomOut">-</button> <div id="canvasContainer"> <canvas id="c" width="400" height="400"></canvas> </div> 

Here are the following steps on how we solved this problem:

1.GET the scale - (1 / zoomLevel). I set it to "1" by default.

Loop all the group objects and set the "scaleX" and "scaleY" into this kind of calculation.

objects.forEach(object => {
  object.scaleX = (scale || 1) * 2.5;
  object.scaleY = (scale || 1) * 2.5;
});

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM