set Min and Max resize limits for fabric object - fabricjs

I am using fabric js for resizing object i want user to resize the object with in min&max limits. How to do this with fabric js.
I tried properties like
lockScalingX,lockScalingY,lockMomentX,lockMomentY but no luck.
Any help will be grateful.
Thanks,

There is no way to do it natively in fabric but you can hook in to the scaling event and make any modification to the object you should like. In this code I stop the scaling as well as correct fabric from shifting the top/left when I am over riding the scaling.
window.canvas = new fabric.Canvas('c');
var rect = new fabric.Rect({
left: 100,
top: 100,
width: 50,
height: 50,
fill: '#faa',
originX: 'left',
originY: 'top',
stroke: "#000",
strokeWidth: 1,
centeredRotation: true
});
canvas.add(rect);
var maxScaleX = 2;
var maxScaleY = 2;
rect.on('scaling', function() {
if(this.scaleX > maxScaleX) {
this.scaleX = maxScaleX;
this.left = this.lastGoodLeft;
this.top = this.lastGoodTop;
}
if(this.scaleY > maxScaleY) {
this.scaleY = maxScaleY;
this.left = this.lastGoodLeft;
this.top = this.lastGoodTop;
}
this.lastGoodTop = this.top;
this.lastGoodLeft = this.left;
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/1.6.4/fabric.min.js"></script>
<canvas id="c" width="600" height="600"></canvas>

Might come in handy for people searching for similar answers.
You can natively set the minimum limit for scaling by setting minScaleLimit. However, there isn't an out-of-the-box solution available for setting maximum limit.

i addapted the drag limit algorithm that i had. In my specific case, i have a canvas element with a background image and i had to limit the other objects resizing with the background image limits so i added extra margins to do that. But if you need to limit the objects resizing only with the canvas size you can set the extra margins to 0.
//Limit the draggable zone
this.canvas.on("object:scaling", function (e) {
let obj = e.target;
let canvas = obj.canvas;
let zoom = canvas.getZoom();
let pan_x = canvas.viewportTransform[4];
let pan_y = canvas.viewportTransform[5];
// width & height we are constraining to must be calculated by applying the inverse of the current viewportTransform
let canvas_height = canvas.height / zoom;
let canvas_width = canvas.width / zoom;
let totalWidth = obj.width * obj.scaleX;
let totalHeight = obj.height * obj.scaleY;
// if you need margins set them here
let top_margin = marginYTop;
let bottom_margin = marginYBottom;
let left_margin = marginXLeft;
let right_margin = marginXRight;
let top_bound = top_margin - pan_y;
let bottom_bound = canvas_height - bottom_margin - pan_y;
let left_bound = left_margin - pan_x;
let right_bound = canvas_width - right_margin - pan_x;
if (obj.top < top_bound || (obj.top + totalHeight) > bottom_bound) {
obj.scaleY = obj.canvas.lastScaleY;
obj.set("top", top_bound);
}
if (obj.left < left_bound || (obj.left + totalWidth) > right_bound) {
obj.scaleX = obj.canvas.lastScaleX;
obj.set("left", left_bound);
}
obj.canvas.lastScaleY = obj.scaleY;
obj.canvas.lastScaleX = obj.scaleX;
});

As this is top 1 topic in google:
Better answer (based on StefanHayden snipped ^^ and http://jsfiddle.net/fabricjs/58y8b/):
window.canvas = new fabric.Canvas('c');
var rect = new fabric.Rect({
left: 100,
top: 100,
width: 50,
height: 50,
fill: '#faa',
originX: 'left',
originY: 'top',
stroke: "#000",
strokeWidth: 1,
centeredRotation: true
});
//Gradient to see pattern on passing 0 in scaling
// horizontal linear gradient
rect.setGradient('fill', {
type: 'linear',
x1: -rect.width / 2,
y1: 50,
x2: rect.width / 2,
y2: 50,
colorStops: {
0: '#ffe47b',
1: 'rgb(111,154,211)'
}
});
canvas.add(rect);
var maxScaleX = 2;
var maxScaleY = 2;
//Set starting center point:
var centerPoint = rect.getCenterPoint();
//Save center point on center point changing events (moved, maybe some cases of: rotated (not center rotation), added and drop (just assuming for last two))
rect.on('moved rotated added drop', function() {
centerPoint = rect.getCenterPoint();
});
rect.on('scaling', function() {
if(this.scaleX > maxScaleX) {
this.scaleX = maxScaleX;
}
if(this.scaleY > maxScaleY) {
this.scaleY = maxScaleY;
}
rect.setPositionByOrigin(centerPoint, 'center', 'center');
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/1.6.4/fabric.min.js"></script>
<canvas id="c" width="600" height="600"></canvas>

Related

Fabricjs - selection only via border

I'm using Fabric.js to draw some rectangles on a canvas. The default behavior is that clicking inside a rectangle selects it. How can I change the behavior such that it is only selected when clicking on the border of the rectangle?
Clicking inside the rectangle but not on the border should do nothing.
You can see this behavior by drawing a rectangle on a TradingView.com chart
It there an option for this in fabric, and if not how could I go around implementing it?
This approach overrides the _checkTarget method within FabricJS to reject clicks that are more than a specified distance from the border (defined by the clickableMargin variable).
//sets the width of clickable area
var clickableMargin = 15;
var canvas = new fabric.Canvas("canvas");
canvas.add(new fabric.Rect({
width: 150,
height: 150,
left: 25,
top: 25,
fill: 'green',
strokeWidth: 0
}));
//overrides the _checkTarget method to add check if point is close to the border
fabric.Canvas.prototype._checkTarget = function(pointer, obj, globalPointer) {
if (obj &&
obj.visible &&
obj.evented &&
this.containsPoint(null, obj, pointer)){
if ((this.perPixelTargetFind || obj.perPixelTargetFind) && !obj.isEditing) {
var isTransparent = this.isTargetTransparent(obj, globalPointer.x, globalPointer.y);
if (!isTransparent) {
return true;
}
}
else {
var isInsideBorder = this.isInsideBorder(obj);
if(!isInsideBorder) {
return true;
}
}
}
}
fabric.Canvas.prototype.isInsideBorder = function(target) {
var pointerCoords = target.getLocalPointer();
if(pointerCoords.x > clickableMargin &&
pointerCoords.x < target.getScaledWidth() - clickableMargin &&
pointerCoords.y > clickableMargin &&
pointerCoords.y < target.getScaledHeight() - clickableMargin) {
return true;
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/3.6.2/fabric.min.js"></script>
<canvas id="canvas" height="300" width="400"></canvas>
Fabric.js uses Object.containsPoint() to determine whether a mouse event should target the object. This method, in turn, calculates the object's edges via Object._getImageLines() and checks how many times the projection of a mouse pointer crossed those lines.
The solution below calculates additional inner edges based on the coordinates of each corner, therefore object scale and rotation are taken care of automatically.
const canvas = new fabric.Canvas('c', {
enableRetinaScaling: true
})
const rect = new fabric.Rect({
left: 0,
top: 0,
width: 100,
height: 100,
dragBorderWidth: 15, // this is the custom attribute we've introduced
})
function innerCornerPoint(start, end, offset) {
// vector length
const l = start.distanceFrom(end)
// unit vector
const uv = new fabric.Point((end.x - start.x) / l, (end.y - start.y) / l)
// point on the vector at a given offset but no further than side length
const p = start.add(uv.multiply(Math.min(offset, l)))
// rotate point
return fabric.util.rotatePoint(p, start, fabric.util.degreesToRadians(45))
}
rect._getInnerBorderLines = function(c) {
// the actual offset from outer corner is the length of a hypotenuse of a right triangle with border widths as 2 sides
const offset = Math.sqrt(2 * (this.dragBorderWidth ** 2))
// find 4 inner corners as offsets rotated 45 degrees CW
const newCoords = {
tl: innerCornerPoint(c.tl, c.tr, offset),
tr: innerCornerPoint(c.tr, c.br, offset),
br: innerCornerPoint(c.br, c.bl, offset),
bl: innerCornerPoint(c.bl, c.tl, offset),
}
return this._getImageLines(newCoords)
}
rect.containsPoint = function(point, lines, absolute, calculate) {
const coords = calculate ? this.calcCoords(absolute) : absolute ? this.aCoords : this.oCoords
lines = lines || this._getImageLines(coords)
const innerRectPoints = this._findCrossPoints(point, lines);
const innerBorderPoints = this._findCrossPoints(point, this._getInnerBorderLines(coords))
// calculate intersections
return innerRectPoints === 1 && innerBorderPoints !== 1
}
canvas.add(rect)
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/3.6.2/fabric.min.js"></script>
<canvas id="c" width="400" height="300"></canvas>
here is my approach, when rect is clicked I am calculating where it is clicked and
if it is not clicked on border I have to set canvas.discardActiveObject , see comments on code
var canvas = new fabric.Canvas('c', {
selection: false
});
var rect = new fabric.Rect({
left: 50,
top: 50,
width: 100,
height: 100,
strokeWidth: 10,
stroke: 'red',
selectable: false,
evented: true,
hasBorders: true,
lockMovementY: true,
lockMovementX: true
})
canvas.on("mouse:move", function(e) {
if (!e.target || e.target.type != 'rect') return;
// when selected event is fired get the click position.
var pointer = canvas.getPointer(e.e);
// calculate the click distance from object to be exact
var distanceX = pointer.x - rect.left;
var distanceY = pointer.y - rect.top;
// check if click distanceX/Y are less than 10 (strokeWidth) or greater than 90 ( rect width = 100)
if ((distanceX <= rect.strokeWidth || distanceX >= (rect.width - rect.strokeWidth)) || (distanceY <= rect.strokeWidth || distanceY >= (rect.height - rect.strokeWidth))) {
rect.set({
hoverCursor: 'move',
selectable: true,
lockMovementY: false,
lockMovementX: false
});
document.getElementById('result').innerHTML = 'on border';
} else {
canvas.discardActiveObject();
document.getElementById('result').innerHTML = 'not on border';
rect.set({
hoverCursor: 'default',
selectable: false,
lockMovementY: true,
lockMovementX: true
});
}
});
canvas.add(rect);
canvas.renderAll();
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/3.6.2/fabric.min.js"></script>
<div id="result" style="width: 100%; "></div>
<canvas id="c" width="600" height="200"></canvas>
<pre>
</pre>
ps: you can also set the rect property to selectable: false and call canvas.setActiveObject(this); to make it selection inside if statement.

Fabricjs mask object with transformation

I'm trying to mask an object using Fabric.js free drawing brush. It works fine if the object is in its default position and without any transformations. But once I add transformations to the object, the mask is placed in the wrong position. I'm not sure how to solve this. Can someone take a look?
I want to be able to apply any transformations, before or after the mask, without messing up the mask.
let canvas = new fabric.Canvas("canvas", {
backgroundColor: "lightgray",
width: 1280,
height: 720,
preserveObjectStacking: true,
selection: false,
stateful: true
});
canvas.isDrawingMode = true;
canvas.freeDrawingBrush.color = "black";
canvas.freeDrawingBrush.width = 2;
canvas.on("path:created", function(options) {
clip(options.path);
});
function clip(path) {
canvas.isDrawingMode = false;
canvas.remove(path);
let mask = new fabric.Path(path.path, {
top: object.top,
left: object.left,
objectCaching: false,
strokeWidth: 0,
pathOffset: {
x: 0,
y: 0
}
});
let originalObjLeft = object.left,
originalObjTop = object.top;
object.set({
clipTo: function(ctx) {
mask.set({
left: -object.width / 2 - mask.width / 2 - originalObjLeft,
top: -object.height / 2 - mask.height / 2 - originalObjTop,
objectCaching: false
});
mask.render(ctx);
}
});
canvas.requestRenderAll();
}
// image
let image = new Image();
let object;
image.onload = function() {
object = new fabric.Image(image, {
width: 500,
height: 500,
//scaleX: 0.8,
//scaleY: 0.8,
//angle: 45,
top: 50,
left: 300
});
canvas.add(object);
};
image.src = "http://i.imgur.com/8rmMZI3.jpg";
I implement an exemple with some transformations (scaleX,scaleY,left,top).
I'm strugle to find a solution when the inital object have an angle different than 0. For the current solution I need it to divide the maskscale with the object scale and also adjust the positions.
let canvas = new fabric.Canvas("canvas", {
backgroundColor: "lightgray",
width: 1280,
height: 720,
preserveObjectStacking: true,
selection: false,
stateful: true
});
canvas.isDrawingMode = true;
canvas.freeDrawingBrush.color = "black";
canvas.freeDrawingBrush.width = 2;
canvas.on("path:created", function(options) {
clip(options.path);
});
function clip(path) {
canvas.isDrawingMode = false;
canvas.remove(path);
let mask = new fabric.Path(path.path, {
top: object.top,
left: object.left,
objectCaching: false,
strokeWidth: 0,
scaleX : 1/object.scaleX,
scaleY : 1/object.scaleY,
pathOffset: {
x: 0,
y: 0
}
});
let originalObjLeft = object.left,
originalObjTop = object.top,
originalMaskScaleX = mask.scaleX,
originalMaskScaleY = mask.scaleY,
originalObjScaleX = object.scaleX,
originalObjScaleY = object.scaleY;
object.set({
clipTo: function(ctx) {
mask.set({
left: -object.width / 2 -( mask.width / 2 * originalMaskScaleX) - originalObjLeft/originalObjScaleX ,
top: -object.height / 2 -( mask.height / 2 * originalMaskScaleY) - originalObjTop/originalObjScaleY ,
objectCaching: false
});
mask.render(ctx);
}
});
canvas.requestRenderAll();
}
// image
let image = new Image();
image.onload = function() {
object = new fabric.Image(image, {
width: 500,
height: 500,
scaleX: 0.8,
scaleY: 0.8,
// angle: 45,
top: 50,
left: 100
});
canvas.add(object);
};
image.src = "http://i.imgur.com/8rmMZI3.jpg";
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/2.3.6/fabric.js"></script>
<div class="canvas__wrapper">
<canvas id="canvas" width="1280" height="720"></canvas>
</div>
You can check here for loadFromJSON support.
The only problem remains is when the object is rotated.
Basically whenever you set an angle, your context matrix has been transformed. In order to mask properly you need to return to initial state of the Transformation Matrices. Fabricjs handles first matrix with center point of an object (calculates center of an object with or without an angle). Second matrix is rotating matrix, and third - scaling.
To display image with all options which are set to an object, you need to multiply all Matrices:
(First Matrix * Second Matrix) * Third Matrix
So the idea of clipping will be reverse engineering of rotating context and multiplications of matrices:
difference between center points of regular object without rotation and center point of the same object but with rotation. After that take result of subtractions and divide by original object scale value.
let canvas = new fabric.Canvas("canvas", {
backgroundColor: "lightgray",
width: 1280,
height: 720,
preserveObjectStacking: true,
selection: false,
stateful: true
});
const angle = 45;
let objectHasBeenRotated = false;
canvas.isDrawingMode = true;
canvas.freeDrawingBrush.color = "black";
canvas.freeDrawingBrush.width = 2;
canvas.on("path:created", function (options) {
clip(options.path);
});
function clip(path) {
canvas.isDrawingMode = false;
canvas.remove(path);
let mask = new fabric.Path(path.path, {
top: 0,
left: 0,
objectCaching: false,
strokeWidth: 0,
scaleX: 1 / object.scaleX,
scaleY: 1 / object.scaleY,
pathOffset: {
x: 0,
y: 0,
}
});
let originalObjLeft = object.left,
originalObjTop = object.top,
originalMaskScaleX = mask.scaleX,
originalMaskScaleY = mask.scaleY,
originalObjScaleX = object.scaleX,
originalObjScaleY = object.scaleY,
transformedTranslate = object.translateToGivenOrigin({
x: object.left,
y: object.top
}, object.originX, object.originY, 'center', 'center'),
originalTransformLeft = transformedTranslate.x - object.getCenterPoint().x,
originalTransformTop = transformedTranslate.y - object.getCenterPoint().y;
object.set({
clipTo: function (ctx) {
ctx.save();
ctx.rotate(-angle * Math.PI / 180);
ctx.translate(originalTransformLeft / originalObjScaleX, originalTransformTop / originalObjScaleY)
mask.set({
left: -object.width / 2 - (mask.width / 2 * originalMaskScaleX) - originalObjLeft / originalObjScaleX,
top: -object.height / 2 - (mask.height / 2 * originalMaskScaleY) - originalObjTop / originalObjScaleY,
objectCaching: false
});
mask.render(ctx);
ctx.restore();
}
});
canvas.requestRenderAll();
}
// image
let image = new Image();
image.onload = function () {
object = new fabric.Image(image, {
width: 500,
height: 500,
scaleX: 0.8,
scaleY: 0.8,
angle: angle,
top: 50,
left: 300,
id: 'pug'
});
canvas.add(object);
};
image.src = "http://i.imgur.com/8rmMZI3.jpg";
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/2.3.6/fabric.js"></script>
<div class="canvas__wrapper">
<canvas id="canvas" width="1280" height="720"></canvas>
</div>

How to keep scaling always at 1 on FabricJS [Edited]

I need in my application made in fabricJS, that when I modify an object with the scaling handlers, the scaling stays in 1, and that the size of the object takes the value of the height multiplied by scaleX. I can apply the same for the width. But I can not apply this in the images. How can I fix this?
I leave an example fiddle for a square, and an image, so that you can see the result.
The result in the image is similar to a crop, and I can not avoid this, because apparently, when I modify the container of the image, the _element property that also contains scaleX and scaleY does not change, so the image is not resized inside the container.
Here is jsFiddle.
var canvas = new fabric.Canvas('canvas');
var rect1 = new fabric.Rect({
width: 100,
height: 100,
left: 200,
top: 200,
angle: 0,
fill: 'rgba(0,0,255,1)',
originX: 'center',
originY: 'center'
});
canvas.add(rect1);
fabric.Image.fromURL('http://fabricjs.com/assets/pug_small.jpg', function(myImg) {
//i create an extra var for to change some image properties
var img1 = myImg.set({
left: 0,
top: 0,
width: 150,
height: 150
});
canvas.add(img1);
});
canvas.on('mouse:up', function(e) {
/*if(e.target != null){
//alert(e.target.width*e.target.scaleX);
}*/
//console.log(e.target);
canvas.getActiveObjects().forEach(function(obj) {
//if(obj.get('type')!='image'){
obj.set('width', obj.width * obj.scaleX, obj);
obj.set('height', obj.height * obj.scaleY, obj);
//if(obj.get('type')!='image'){
obj.scaleX = 1;
obj.scaleY = 1;
//}else{
// obj.scaleX = 1;
// obj.scaleY = 1;
// obj._element.scaleX = 1;
// obj._element.scaleY = 1;
// console.log("Width Contenedor Imagen: "+obj.width+" Width Imagen: "+obj._element.width);
// console.log("Scale Contenedor Imagen: "+obj.scaleX+" Scale Imagen: "+obj._element.scaleX);
//}
obj.setCoords();
//}
});
});
EDIT: New Discovery
Speaking of text, rect, images and polylines, it seems that I am modifying an external container, because the inner content remains intact. How can I modify the size of that let's say, inner container?

How to align object by bounding box in FabricJS?

Wondering if there is a way to align objects in FabricJs by their bounding box?
I'm using obj.getBoundingRect() function to determine objects bounders, then compare them with a bounding box (BB) coordinates of an Active one (that one which I move). If I see that something falls between some gap (let's say 10px) I assign an active object top to be the same top as a comparable element by using a .setTop() property.
The problem is that TOP is not a right attribute to use, since the top of the bounding box may differ between elements. For example, 2 elements with the same top but different angle will have different Bounding Box Top...
Hope you see my point...
https://jsfiddle.net/redlive/hwcu1p4f/
var canvas = this.__canvas = new fabric.Canvas('canvas');
//fabric.Object.prototype.transparentCorners = false;
var red = new fabric.Rect({
id: 1,
left: 100,
top: 50,
width: 100,
height: 100,
fill: 'red',
angle: 0,
padding: 10
});
canvas.add(red);
var green = new fabric.Rect({
id: 2,
left: 250,
top: 180,
width: 100,
height: 100,
fill: 'green',
angle: 45,
padding: 10
});
canvas.add(green);
canvas.renderAll();
canvas.on("object:moving", function(e){
const draggableObj = e.target;
const draggableObjBound = draggableObj.getBoundingRect();
canvas.forEachObject(function(obj) {
if (obj.id !== draggableObj.id) {
var bound = obj.getBoundingRect();
if (draggableObjBound.top > bound.top - 10 && draggableObjBound.top < bound.top + 10) {
draggableObj.setTop(obj.getTop());
}
}
});
});
canvas.forEachObject(function(obj) {
var setCoords = obj.setCoords.bind(obj);
obj.on({
moving: setCoords,
scaling: setCoords,
rotating: setCoords
});
});
canvas.on('after:render', function() {
canvas.contextContainer.strokeStyle = '#555';
canvas.forEachObject(function(obj) {
var bound = obj.getBoundingRect();
canvas.contextContainer.strokeRect(
bound.left,
bound.top,
bound.width,
bound.height
);
})
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/2.0.0-rc.3/fabric.js"></script>
<canvas id="canvas" width="800" height="500" style="border:1px solid #ccc"></canvas>
You should use the center to align them, that is not gonna change.
to align the bounding box at left 5 for example:
1) calculate bounding box.
2) set the position of the object to 5 + bb.width/2 considering center.
In this case the bounding rects get aligned.
var canvas = this.__canvas = new fabric.Canvas('canvas');
//fabric.Object.prototype.transparentCorners = false;
var red = new fabric.Rect({
id: 1,
left: 100,
top: 50,
width: 100,
height: 100,
fill: 'red',
angle: 0,
padding: 10
});
canvas.add(red);
var green = new fabric.Rect({
id: 2,
left: 250,
top: 180,
width: 100,
height: 100,
fill: 'green',
angle: 45,
padding: 10
});
canvas.add(green);
//ALIGN EVERYTHING TO 5
canvas.forEachObject(function(object) {
var bb = object.getBoundingRect();
object.setPositionByOrigin({ x: 5 + bb.width/2, y: bb.top }, 'center', 'center');
object.setCoords();
});
canvas.renderAll();
canvas.on("object:moving", function(e){
const draggableObj = e.target;
const draggableObjBound = draggableObj.getBoundingRect(true, true);
canvas.forEachObject(function(obj) {
if (obj.id !== draggableObj.id) {
var bound = obj.getBoundingRect(true, true);
if (draggableObjBound.top > bound.top - 10 && draggableObjBound.top < bound.top + 10) {
draggableObj.setPositionByOrigin({ x: draggableObj.left, y: bound.top + draggableObjBound.height/2 }, draggableObj.originX, 'center');
}
}
});
});
canvas.forEachObject(function(obj) {
var setCoords = obj.setCoords.bind(obj);
obj.on({
moving: setCoords,
scaling: setCoords,
rotating: setCoords
});
});
canvas.on('after:render', function() {
canvas.contextContainer.strokeStyle = '#555';
canvas.forEachObject(function(obj) {
var bound = obj.getBoundingRect(true, true);
canvas.contextContainer.strokeRect(
bound.left,
bound.top,
bound.width,
bound.height
);
})
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/2.0.0-rc.3/fabric.js"></script>
<canvas id="canvas" width="800" height="500" style="border:1px solid #ccc"></canvas>

How to keep border width even when object is expanded

My objective in this jsfiddle is to have a Fabric object (for example, a rect) that will "visually" keep its original border width even when it is scaled/expanded. For example, let's say that strokeWidth = 3px; usually when a rect is expanded the user will see the border wider. In my case I need the border to keep its width in screen pixels.
To achieve that, I calculate a factor factorHeight = origHeight / obj.getHeight() every time the object is scaled, and multiply strokeWidth by this factor. This works well when the object is scaled proportionally (try it in the jsfiddle).
However, when the object is not scaled proportionally, the problem is that the top/bottom border widths are different from the right/left border widths. Since there's a single strokeWidth for all these borders I cannot change this behavior. Any ideas?
Javascript:
var canvas = new fabric.Canvas('c' );
var origWidth = 40;
var origHeight = 100;
var origStrokeWidth = 3;
var rect = new fabric.Rect({
top: 30,
left: 30,
width: origWidth,
height: origHeight,
fill: 'rgba(255,255,255,0)',
strokeWidth: origStrokeWidth,
stroke: '#000000'
});
canvas.add(rect);
canvas.on('object:scaling', onObjectScaled);
function onObjectScaled(e) {
var obj = e.target;
var factorHeight = origHeight / obj.getHeight();
var factorWidth = origWidth / obj.getWidth();
var factor;
if (factorHeight < factorWidth)
factor = factorHeight;
else
factor = factorWidth;
var newStrokeWidth = origStrokeWidth * factor;
obj.setStrokeWidth(newStrokeWidth);
canvas.renderAll();
}
I had the same problem, this is the solution I found somewhere here on SO, but I cant provide you with the link to question, but here is the code that solved it:
canvas.on({
'object:scaling': function(e) {
var obj = e.target;
obj.KeepStrokeWidth = 5;
if(obj.KeepStrokeWidth){
var newStrokeWidth = obj.KeepStrokeWidth / ((obj.scaleX + obj.scaleY) / 2);
obj.set('strokeWidth',newStrokeWidth);
}
}
});
Hope it will work for you too.

Resources