Reputation: 3033
Note: I have refereed SO question, but it is not useful for my case, because
1) I am trying to maintain previous border but as of now its recalculate border while scaling.
I have added below code to stop increasing border automatically while scaling the object. Now the issue is I have added a 5px border to object but when scaling the object then it is not maintaining the border which 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 what I want is to prevent increasing of border while scaling the object. Border should remain as it was earlier.
Here is 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>
1) Add Rectangle
2) Apply the border (lets say 5)
3) Scale that object
Now you can see the applied border is gone. So how to resolve that?
I have tried below option but its not working, basically i am trying to maintain 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();
};
https://github.com/kangax/fabric.js/issues/66
Unable to maintain thickness of strokeWidth while resizing in case of Groups in Fabricjs
Fabricjs How to scale object but keep the border (stroke) width fixed
Resize a fabricjs rect to maintain border size
https://github.com/kangax/fabric.js/issues/2012
But not able to found solution.
Upvotes: 17
Views: 10999
Reputation: 1
If your object is inside a group, strokeUniform property does not work. In that case you can reduce the scaling of the inner object and update its width and height property on scaling. Example:
object.on('scaling', function() {
object.item(0).set('scaleX', 1/object.scaleX);
object.item(0).set('scaleY', 1/object.scaleY);
var newobjwidth = Math.round(object.width * object.scaleX);
var newobjheight = Math.round(object.height * object.scaleY);
object.item(0).set('width', newobjwidth);
object.item(0).set('height', newobjheight);
object.item(0).setCoords();
}
If your object is blurry delete it and add it again after scaling.
Upvotes: 0
Reputation: 2862
This has become much easier as of Fabric.js version 2.7.0. There is now a strokeUniform
property that when enabled, prevents the stroke width from being affected by the object's scale values.
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>
Upvotes: 16
Reputation: 299
While working on this issue, I've noticed that after resizing an object value of width and height doesn't change, it only changes scaleX and scaleY. To maintain the stroke width, you need to reset scaleX and scalyY to 1 and also update width and height with new values.
For 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 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
});
}
Don't forget to call canvas.renderAll() later;
Upvotes: 2
Reputation: 15604
Set strokeWidth
to strokeWidthUnscaled
for first time then set @ at scalling, for caching make it false only when scalling after modified change it.
object.strokeWidth = object.strokeWidthUnscaled/((object.scaleX+object.scaleY)/2);
And for selecting 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>
Upvotes: 7
Reputation: 131
In object:scaling, I commented out the calculation based on scale. Since the unscaled was = 1 and it was divided by scaling it always resulted in the border equaling a fraction (rather than staying put).
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>
Upvotes: 1