I have been trying to keep an object (constructed in fabric js over a canvas) inside the boundaries at all the times. It has been achieved at moving and rotating it. I took help from Move object within canvas boundary limit for achieving this. But when I start to scale the object, it simply keeps on going out of boundary. I do not understand what has to be done to keep it inside the boundary only, even while scaling. Please help me with a code to prevent this behavior. It would be great if you can attach a demo too.
<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/
Advertisement
Answer
If you want to perform a real time prevention, you should use object:scaling
event, as object:modified
is only triggered at the end of the transformation.
1) Add event handler to canvas:
this.canvas.on('object:scaling', (e) => this._handleScaling(e));
2) In the handler function, get the old and the new object’s bounding rect:
_handleScaling(e) { var obj = e.target; var brOld = obj.getBoundingRect(); obj.setCoords(); var brNew = obj.getBoundingRect();
3) For each border, check if object has scaled beyond the canvas boundaries and compute its left, top and scale properties:
// 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) Similar code for the other borders:
// 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) If object’s BoundingRect has crossed canvas boundaries, fix its position and scale:
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) Finally, when setting the scaling properties, we have to stick with the smallest scale in case the object has crossed more than one border:
_setScalingProperties(left, top, scale) { if(this.scalingProperties == null || this.scalingProperties['scale'] > scale) { this.scalingProperties = { 'left': left, 'top': top, 'scale': scale }; } }