Maintain strokeWidth while scaling in js fabric

Note. I refer to the SO question , but this is not useful for my case, because

1) I'm trying to save the previous border, but at the moment its recalculated border when scaling.

I added the code below to automatically increase the border when scaling an object. Now the problem is that I added a 5px border to the object, but when scaling the object it does not support the border that I added earlier.

canvas.on('object:scaling', (e) => { var o = e.target; if (!o.strokeWidthUnscaled && o.strokeWidth) { o.strokeWidthUnscaled = o.strokeWidth; } if (o.strokeWidthUnscaled) { o.strokeWidth = o.strokeWidthUnscaled / o.scaleX; } }); 

Now I want to prevent the border from increasing when scaling an object. The border should remain as it was before.

Here is a snippet / Codepen

 var canvas = new fabric.Canvas('canvas1'); $('.add_shape').click(function() { var cur_value = $(this).attr('data-rel'); if (cur_value != '') { switch (cur_value) { case 'rectangle': var rect = new fabric.Rect({ left: 50, top: 50, fill: '#aaa', width: 50, height: 50, opacity: 1, stroke: '#000', strokeWidth: 1 }); canvas.add(rect); canvas.setActiveObject(rect); break; case 'circle': var circle = new fabric.Circle({ left: 50, top: 50, fill: '#aaa', radius: 50, opacity: 1, stroke: '#000', strokeWidth: 1 }); canvas.add(circle); canvas.setActiveObject(circle); break; } } }); canvas.on('object:scaling', (e) => { var o = e.target; if (!o.strokeWidthUnscaled && o.strokeWidth) { o.strokeWidthUnscaled = o.strokeWidth; } if (o.strokeWidthUnscaled) { o.strokeWidth = o.strokeWidthUnscaled / o.scaleX; } }); /* Control the border */ $('#control_border').change(function() { var cur_value = parseInt($(this).val()); var activeObj = canvas.getActiveObject(); if (activeObj == undefined) { alert('Please select the Object'); return false; } activeObj.set({ strokeWidth: cur_value }); canvas.renderAll(); }); 
 button { max-resolution: 10px; height: 30px; } div { margin: 10px } 
 <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/2.0.0-beta.7/fabric.js"></script> <div> <button class="add_shape" data-rel="circle">Add Circle</button> <button class="add_shape" data-rel="rectangle">Add Rectangle</button> <label class="control-label">Border</label> <input id="control_border" type="range" min="0" max="10" step="1" value="0" /> </div> <canvas id="canvas1" width="600" height="300" style="border:1px solid #000000;"></canvas> 

Actions

1) Add a rectangle
2) Apply border (say 5)
3) Scale this object

Now you can see that the application boundary is gone. So how to solve this?

Update

I tried the option below, but it doesnโ€™t work, basically I try to support strokeWidth / Border for objects like rectangle, circle, triangle, line, polygon

What I have tried so far:

 //1st try canvas.on('object:scaling', (e) => { var o = e.target; o.strokeWidth = o.strokeWidth / ((o.scaleX + o.scaleY) / 2); var activeObject = canvas.getActiveObject(); activeObject.set('strokeWidth',o.strokeWidth); }); //2nd try canvas.on('object:scaling', (e) => { if (!o.strokeWidthUnscaled && o.strokeWidth) { o.strokeWidthUnscaled = o.strokeWidth; } if (o.strokeWidthUnscaled) { o.strokeWidth = o.strokeWidthUnscaled / o.scaleX; } }); //3rd try fabric.Object.prototype._renderStroke = function(ctx) { if (!this.stroke || this.strokeWidth === 0) { return; } if (this.shadow && !this.shadow.affectStroke) { this._removeShadow(ctx); } ctx.save(); ctx.scale(1 / this.scaleX, 1 / this.scaleY); this._setLineDash(ctx, this.strokeDashArray, this._renderDashedStroke); this._applyPatternGradientTransform(ctx, this.stroke); ctx.stroke(); ctx.restore(); }; 

The questions I refer to are:

https://github.com/kangax/fabric.js/issues/66

Cannot maintain strokeWidth thickness when resizing in case of groups in Fabricjs

Fabricjs How to scale an object, but fix the width of the frame (stroke)

Resize fabricjs rectangle to keep border size

https://github.com/kangax/fabric.js/issues/2012

But could not find a solution.

+8
javascript canvas fabricjs fabricjs2
source share
4 answers

Set strokeWidth to strokeWidthUnscaled for the first time, and then set @ to scalling, since caching only makes it false when scalling modifies it after the change.

 object.strokeWidth = object.strokeWidthUnscaled/((object.scaleX+object.scaleY)/2); 

And to select a new stroke width do this

 var newStrokeWidth = cur_value / ((activeObj.scaleX + activeObj.scaleY) / 2) activeObj.set({ strokeWidth: newStrokeWidth, strokeWidthUnscaled : cur_value }); 

Example

 var canvas = new fabric.Canvas('canvas1'); var globalStrokeWidth = 1; $('.add_shape').click(function() { var cur_value = $(this).attr('data-rel'); if (cur_value != '') { switch (cur_value) { case 'rectangle': var rect = new fabric.Rect({ left: 50, top: 50, fill: '#aaa', width: 50, height: 50, opacity: 1, stroke: '#000', strokeWidth: globalStrokeWidth }); canvas.add(rect); canvas.setActiveObject(rect); break; case 'circle': var circle = new fabric.Circle({ left: 50, top: 50, fill: '#aaa', radius: 50, opacity: 1, stroke: '#000', strokeWidth: globalStrokeWidth }); canvas.add(circle); canvas.setActiveObject(circle); break; } } }); canvas.on('object:scaling', (e) => { var o = e.target; if (!o.strokeWidthUnscaled && o.strokeWidth) { o.strokeWidthUnscaled = o.strokeWidth; } if (o.strokeWidthUnscaled) { if(o.objectCaching) o.objectCaching = false; o.strokeWidth = o.strokeWidthUnscaled / ((o.scaleX + o.scaleY) / 2); } }); canvas.on('object:modified', (e) => { var o = e.target; if(!o.objectCaching) o.objectCaching = true; canvas.renderAll(); }); /* Control the border */ $('#control_border').change(function() { var cur_value = parseInt($(this).val()); globalStrokeWidth = cur_value; var activeObj = canvas.getActiveObject(); if (activeObj == undefined) { alert('Please select the Object'); return false; } var newStrokeWidth = cur_value / ((activeObj.scaleX + activeObj.scaleY) / 2) activeObj.set({ strokeWidth: newStrokeWidth, strokeWidthUnscaled : cur_value }); activeObj.setCoords(); canvas.renderAll(); }); 
 button { max-resolution: 10px; height: 30px; } div { margin: 10px } 
 <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/2.0.0-beta.7/fabric.js"></script> <div> <button class="add_shape" data-rel="circle">Add Circle</button> <button class="add_shape" data-rel="rectangle">Add Rectangle</button> <label class="control-label">Border</label> <input id="control_border" type="range" min="0" max="10" step="1" value="0" /> </div> <canvas id="canvas1" width="600" height="300" style="border:1px solid #000000;"></canvas> 
+4
source share

In the object: scaling, I commented on the calculation on a scale. Since unscaled was = 1 and it was separated by scaling, it always led to the fact that the border was equal to the fraction (and not left).

 var canvas = new fabric.Canvas('canvas1'); $('.add_shape').click(function() { var cur_value = $(this).attr('data-rel'); if (cur_value != '') { switch (cur_value) { case 'rectangle': var rect = new fabric.Rect({ left: 50, top: 50, fill: '#aaa', width: 50, height: 50, opacity: 1, stroke: '#000', strokeWidth: 1 }); canvas.add(rect); canvas.setActiveObject(rect); break; case 'circle': var circle = new fabric.Circle({ left: 50, top: 50, fill: '#aaa', radius: 50, opacity: 1, stroke: '#000', strokeWidth: 1 }); canvas.add(circle); canvas.setActiveObject(circle); break; } } }); canvas.on('object:scaling', (e) => { var o = e.target; if (!o.strokeWidthUnscaled && o.strokeWidth) { o.strokeWidthUnscaled = o.strokeWidth; } if (o.strokeWidthUnscaled) { //o.strokeWidth = o.strokeWidthUnscaled / o.scaleX; } }); /* Control the border */ $('#control_border').change(function() { var cur_value = parseInt($(this).val()); var activeObj = canvas.getActiveObject(); if (activeObj == undefined) { alert('Please select the Object'); return false; } activeObj.set({ strokeWidth: cur_value }); canvas.renderAll(); }); 
 button { max-resolution: 10px; height: 30px; } div { margin: 10px } 
 <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/2.0.0-beta.7/fabric.js"></script> <div> <button class="add_shape" data-rel="circle">Add Circle</button> <button class="add_shape" data-rel="rectangle">Add Rectangle</button> <label class="control-label">Border</label> <input id="control_border" type="range" min="0" max="10" step="1" value="0" /> </div> <canvas id="canvas1" width="600" height="300" style="border:1px solid #000000;"></canvas> 
+1
source share

While working on this problem, I noticed that after resizing the object, the width and height do not change, it only changes scaleX and scaleY. To maintain the stroke width, you need to reset scaleX and scalyY to 1, as well as update the width and height with the new values.

For a rectangle:

 var currObj = canvas.getActiveObject(); if (currObj.type === 'rect') { var newWidth = currObj.width * currObj.scaleX, newHeight = currObj.height * currObj.scaleY; currObj.set({ 'width': newWidth, 'height': newHeight, scaleX: 1, scaleY: 1 }); } 

For an ellipse:

 var currObj = canvas.getActiveObject(); if (currObj.type === 'ellipse') { var newRX = currObj.rx * currObj.scaleX, newRY = currObj.ry * currObj.scaleY; currObj.set({ 'rx': newRX, 'ry': newRY, scaleX: 1, scaleY: 1 }); } 

Remember to call canvas.renderAll () later;

http://jsfiddle.net/eLoamgrq/5/

+1
source share

This has become much easier than in Fabric.js version 2.7.0. Now there is a strokeUniform property which, when enabled, prevents the stroke width from affecting the scale value of the object.

 obj.set('strokeUniform', true); 

https://github.com/fabricjs/fabric.js/pull/5546

 var canvas = new fabric.Canvas('canvas1'); $('.add_shape').click(function() { var cur_value = $(this).attr('data-rel'); if (cur_value != '') { switch (cur_value) { case 'rectangle': var rect = new fabric.Rect({ left: 50, top: 50, fill: '#aaa', width: 50, height: 50, opacity: 1, stroke: '#000', strokeWidth: 1, noScaleCache: false, strokeUniform: true, }); canvas.add(rect); canvas.setActiveObject(rect); break; case 'circle': var circle = new fabric.Circle({ left: 50, top: 50, fill: '#aaa', radius: 50, opacity: 1, stroke: '#000', strokeWidth: 1, noScaleCache: false, strokeUniform: true }); canvas.add(circle); canvas.setActiveObject(circle); break; } } }); /* Control the border */ $('#control_border').change(function() { var cur_value = parseInt($(this).val()); var activeObj = canvas.getActiveObject(); if (activeObj == undefined) { alert('Please select the Object'); return false; } activeObj.set({ strokeWidth: cur_value }); canvas.renderAll(); }); 
 button { max-resolution: 10px; height: 30px; } div { margin: 10px } 
 <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/2.7.0/fabric.min.js"></script> <div> <button class="add_shape" data-rel="circle">Add Circle</button> <button class="add_shape" data-rel="rectangle">Add Rectangle</button> <label class="control-label">Border</label> <input id="control_border" type="range" min="0" max="10" step="1" value="0" /> </div> <canvas id="canvas1" width="600" height="300" style="border:1px solid #000000;"></canvas> 
0
source share

All Articles