Deny js fabric objects to scale from canvas border

I am trying to save an object (built in js fabric over the canvas) inside borders at all times. This was achieved by moving and rotating. I got help from Relocate an object within the canvas border to achieve this. But when I start to scale the object, it just keeps going out of the border. I do not understand what needs to be done to keep it inside the border, even when scaling. Please help me with the code to prevent this behavior. It would be great if you could also add a demo.

    <html>
<head>
    <title>Basic usage</title>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/1.7.3/fabric.min.js"></script>

</head>
<body>
<canvas id="canvas" style= "border: 1px solid black" height= 480 width = 360></canvas>
<script>
 var canvas = new fabric.Canvas('canvas');
  canvas.add(new fabric.Circle({ radius: 30, fill: '#f55', top: 100, left: 100 }));

  canvas.item(0).set({
    borderColor: 'gray',
    cornerColor: 'black',
    cornerSize: 12,
    transparentCorners: true
  });
  canvas.setActiveObject(canvas.item(0));
  canvas.renderAll();


  canvas.on('object:moving', function (e) {
        var obj = e.target;
         // if object is too big ignore
        if(obj.currentHeight > obj.canvas.height || obj.currentWidth > obj.canvas.width){
            return;
        }        
        obj.setCoords();        
        // top-left  corner
        if(obj.getBoundingRect().top < 0 || obj.getBoundingRect().left < 0){
            obj.top = Math.max(obj.top, obj.top-obj.getBoundingRect().top);
            obj.left = Math.max(obj.left, obj.left-obj.getBoundingRect().left);
        }
        // bot-right corner
        if(obj.getBoundingRect().top+obj.getBoundingRect().height  > obj.canvas.height || obj.getBoundingRect().left+obj.getBoundingRect().width  > obj.canvas.width){
            obj.top = Math.min(obj.top, obj.canvas.height-obj.getBoundingRect().height+obj.top-obj.getBoundingRect().top);
            obj.left = Math.min(obj.left, obj.canvas.width-obj.getBoundingRect().width+obj.left-obj.getBoundingRect().left);
        }
});

</script>
</body>
</html>

My demo is attached here .: https://jsfiddle.net/3v0cLaLk/

+2
4

, object:scaling, object:modified .

1) :

this.canvas.on('object:scaling', (e) => this._handleScaling(e));

2) , :

_handleScaling(e) {
  var obj = e.target;
  var brOld = obj.getBoundingRect();
  obj.setCoords();
  var brNew = obj.getBoundingRect();

3) , , :

  // left border
  // 1. compute the scale that sets obj.left equal 0
  // 2. compute height if the same scale is applied to Y (we do not allow non-uniform scaling)
  // 3. compute obj.top based on new height
  if(brOld.left >= 0 && brNew.left < 0) {
    let scale = (brOld.width + brOld.left) / obj.width;
    let height = obj.height * scale;
    let top = ((brNew.top - brOld.top) / (brNew.height - brOld.height) *
      (height - brOld.height)) + brOld.top;
    this._setScalingProperties(0, top, scale);
  } 

4) :

  // top border
  if(brOld.top >= 0 && brNew.top < 0) {
    let scale = (brOld.height + brOld.top) / obj.height;
    let width = obj.width * scale;
    let left = ((brNew.left - brOld.left) / (brNew.width - brOld.width) * 
      (width - brOld.width)) + brOld.left;
    this._setScalingProperties(left, 0, scale);
  }
  // right border
  if(brOld.left + brOld.width <= obj.canvas.width 
  && brNew.left + brNew.width > obj.canvas.width) {
    let scale = (obj.canvas.width - brOld.left) / obj.width;
    let height = obj.height * scale;
    let top = ((brNew.top - brOld.top) / (brNew.height - brOld.height) * 
      (height - brOld.height)) + brOld.top;
    this._setScalingProperties(brNew.left, top, scale);
  }
  // bottom border
  if(brOld.top + brOld.height <= obj.canvas.height 
  && brNew.top + brNew.height > obj.canvas.height) {
    let scale = (obj.canvas.height - brOld.top) / obj.height;
    let width = obj.width * scale;
    let left = ((brNew.left - brOld.left) / (brNew.width - brOld.width) * 
      (width - brOld.width)) + brOld.left;
    this._setScalingProperties(left, brNew.top, scale);
  }

5) BoundingRect , :

  if(brNew.left < 0
  || brNew.top < 0
  || brNew.left + brNew.width > obj.canvas.width
  || brNew.top + brNew.height > obj.canvas.height) {
    obj.left = this.scalingProperties['left'];
    obj.top = this.scalingProperties['top'];
    obj.scaleX = this.scalingProperties['scale'];
    obj.scaleY = this.scalingProperties['scale'];
    obj.setCoords();
  } else {
    this.scalingProperties = null;
  }
}

6) , , :

_setScalingProperties(left, top, scale) {
  if(this.scalingProperties == null 
  || this.scalingProperties['scale'] > scale) {
    this.scalingProperties = {
      'left': left,
      'top': top,
      'scale': scale
    };
  }
}
+3

, . , .

this.canvas.on('object:modified', function (options: any) {
    let obj = options.target;
    let boundingRect = obj.getBoundingRect(true);
    if (boundingRect.left < 0
        || boundingRect.top < 0
        || boundingRect.left + boundingRect.width > scope.canvas.getWidth()
        || boundingRect.top + boundingRect.height > scope.canvas.getHeight()) {
        obj.top = obj._stateProperties.top;
        obj.left = obj._stateProperties.left;
        obj.angle = obj._stateProperties.angle;
        obj.scaleX = obj._stateProperties.scaleX;
        obj.scaleY = obj._stateProperties.scaleY;
        obj.setCoords();
        obj.saveState();
    }
});
+7

:

var canvas = new fabric.Canvas('canvas');
  canvas.add(new fabric.Circle({ radius: 30, fill: '#f55', top: 100, left: 100 }));

  canvas.item(0).set({
    borderColor: 'gray',
    cornerColor: 'black',
    cornerSize: 12,
    transparentCorners: true
  });
  canvas.setActiveObject(canvas.item(0));
  canvas.renderAll();


  canvas.on('object:moving', function (e) {
        var obj = e.target;
         // if object is too big ignore
        if(obj.currentHeight > obj.canvas.height || obj.currentWidth > obj.canvas.width){
            return;
        }        
        obj.setCoords();        
        // top-left  corner
        if(obj.getBoundingRect().top < 0 || obj.getBoundingRect().left < 0){
            obj.top = Math.max(obj.top, obj.top-obj.getBoundingRect().top);
            obj.left = Math.max(obj.left, obj.left-obj.getBoundingRect().left);
        }
        // bot-right corner
        if(obj.getBoundingRect().top+obj.getBoundingRect().height  > obj.canvas.height || obj.getBoundingRect().left+obj.getBoundingRect().width  > obj.canvas.width){
            obj.top = Math.min(obj.top, obj.canvas.height-obj.getBoundingRect().height+obj.top-obj.getBoundingRect().top);
            obj.left = Math.min(obj.left, obj.canvas.width-obj.getBoundingRect().width+obj.left-obj.getBoundingRect().left);
        }
});

    var left1 = 0;
    var top1 = 0 ;
    var scale1x = 0 ;    
    var scale1y = 0 ;    
    var width1 = 0 ;    
    var height1 = 0 ;
  canvas.on('object:scaling', function (e){
    var obj = e.target;
    obj.setCoords();
    var brNew = obj.getBoundingRect();
    
    if (((brNew.width+brNew.left)>=obj.canvas.width) || ((brNew.height+brNew.top)>=obj.canvas.height) || ((brNew.left<0) || (brNew.top<0))) {
    obj.left = left1;
    obj.top=top1;
    obj.scaleX=scale1x;
    obj.scaleY=scale1y;
    obj.width=width1;
    obj.height=height1;
  }
    else{    
      left1 =obj.left;
      top1 =obj.top;
      scale1x = obj.scaleX;
      scale1y=obj.scaleY;
      width1=obj.width;
      height1=obj.height;
    }
 });
<html>
<head>
    <title>Basic usage</title>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/1.7.3/fabric.min.js"></script>

</head>
<body>
<canvas id="canvas" style= "border: 1px solid black" height= 480 width = 360></canvas>
</body>
</html>
Hide result

+4

canvas.on('object:modified', function (data) {
var currentObject = data.target;
var tempObject = angular.copy(data.target);
var canvasMaxWidth = canvas.width - 20,
    canvasMaxHeight = canvas.height - 20;
    var actualWidth = currentObject.getBoundingRect().width,
    actualHeight = currentObject.getBoundingRect().height;
if (actualHeight > canvasMaxHeight) {
    currentObject.scaleToHeight(canvasMaxHeight);
    currentObject.setCoords();
    canvas.renderAll();
    if (tempObject.scaleX < currentObject.scaleX) {
        currentObject.scaleX = tempObject.scaleX;
        currentObject.setCoords();
        canvas.renderAll();
    }
    if (tempObject.scaleY < currentObject.scaleY) {
        currentObject.scaleY = tempObject.scaleY;
        currentObject.setCoords();
        canvas.renderAll();
    }
        if (currentObject.getBoundingRectHeight() < canvasMaxHeight - 50) {
            currentObject.scaleX = (currentObject.scaleX * canvasMaxHeight) / (currentObject.scaleX * currentObject.width);
            currentObject.setCoords();
            canvas.renderAll();
        }

}
if (actualWidth > canvasMaxWidth) {
    currentObject.scaleToWidth(canvasMaxWidth);
    obj.setCoords();
    canvas.renderAll();
    if (tempObject.scaleX < currentObject.scaleX) {
        currentObject.scaleX = tempObject.scaleX;
        currentObject.setCoords();
        canvas.renderAll();
    }
    if (tempObject.scaleY < currentObject.scaleY) {
        currentObject.scaleY = tempObject.scaleY;
        currentObject.setCoords();
        canvas.renderAll();
    }
}
obj.setCoords();
canvas.renderAll();
});
0

Source: https://habr.com/ru/post/1665660/


All Articles