Fabric.JS scaleToWidth not working for text items and resulting in different width then set - fabricjs

I try to set a defined width for a text object by below code (excerpt) but get a different width instead. e.g. "size to be 200" and "new size after render 202.25..." as differences. What am I missing or is this a bug?
var canvas = this.__canvas = new fabric.Canvas('c');
var size = 200;
var color = "#ff0000";
var fontScale = 1;
console.log("size to be "+size);
var opt = {
fill: color,
stroke: color,
scaleX: fontScale,
scaleY: fontScale,
fontFamily: "Times New Roman",
fontSize: 40,
fontWeight: "",
fontStyle: "",
textAlign: "center"
}
var itext = new fabric.Text("Hallo", opt);
canvas.add(itext);
itext.scaleToWidth(size);
canvas.renderAll();
console.log("new size after render "+itext.getScaledWidth());
Fiddle: http://jsfiddle.net/tomfree/drcqpwxy/4/

Related

How to align Top/Left/Bottom/Right of selected objects (FabricJS)

var canvas = window._canvas = new fabric.Canvas('c');
var red = new fabric.Rect({
top: 100,
left: 0,
width: 80,
height: 50,
fill: 'red'
});
var blue = new fabric.Rect({
top: 0,
left: 100,
width: 50,
height: 70,
fill: 'blue'
});
var green = new fabric.Rect({
top: 100,
left: 100,
width: 60,
height: 60,
fill: 'green'
});
canvas.add(red, blue, green);
const alignLeft = document.getElementById('align_left');
alignLeft.onclick = function() {
const objs = canvas.getActiveObjects();
}
<script src="https://rawgithub.com/kangax/fabric.js/master/dist/fabric.js"></script>
<canvas id="c" ></canvas>
<button id="align_left">Align Left</button>
Looking for a utility method to align edges (top,right,bottom,left) of array of objects from canvas.getActiveObjects in FabricJS v3
http://jsfiddle.net/rlightner/bzs0798x/1/
$('#align_left').click(function(){canvas.getActiveObject().set({left:0);})

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>

FabricJS. How to add dimensions displaying in controls?

How to add dimensions displaying in controls like on screenshot?
Create a text object and set it hidden. And when you are scalling object set the scale width and height to hidden text and make visible. On object modified set visible false to the text.
DEMO
var canvas = new fabric.Canvas("c");
canvas.setHeight(200);
canvas.setWidth(300);
var dimText = new fabric.Text("demo", {
fontSize: 15,
visible: false
});
canvas.add(dimText);
var circle = new fabric.Circle({
left: 15,
top: 15,
radius: 20,
fill:'',
stroke: 'red'
});
canvas.add(circle);
var text = new fabric.Text("2018", {
padding: 30,
lineHeight: 30
});
canvas.add(text);
canvas.centerObject(text);
text.setCoords();
canvas.on('object:scaling', function(option) {
var object = option.target;
var pointer = canvas.getPointer(option.e);
dimText.set({
left: pointer.x - 20,
top: pointer.y - 20,
text: parseInt(object.width * object.scaleX) + 'x' + parseInt(object.height * object.scaleY),
visible: true
})
});
canvas.on('object:modified', function(option) {
dimText.set('visible', false);
});
canvas {
border: 1px solid #dddddd;
margin-top: 10px;
border-radius: 3px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/1.6.7/fabric.min.js"></script>
<canvas id="c"></canvas>

Fabric JS set backgroundImage from fabric object

I want to create an artboard like sketch's artboard in fabric canvas elemet
like this:
let app = new Vue({
el: '#app',
computed: {
canvasSize() {
let VM = this
let el, width, height
el = VM.$refs.canvasBoxWrap
width = el.clientWidth
height = el.clientHeight
return { width, height }
}
},
data: {
dSize: ''
},
mounted() {
let VM = this
VM.dSize = VM.canvasSize
let fabricCanvasInit = () => {
let canvas = new fabric.Canvas(VM.$refs.facanvas , {
enableRetinaScaling: true
})
canvas.set({
'enableRetinaScaling': true,
'backgroundColor': '#dddddd'
})
canvas.setWidth( VM.canvasSize.width)
canvas.setHeight(VM.canvasSize.width / 16 * 9)
// canvas.set('enableRetinaScaling', true)
// canvas.set('backgroundColor' , '#dddddd')
let artBoard = new fabric.Rect({
stroke: '#000',
strokeWidth:1,
fill: 'rgba(255,255,255,1)',
width: VM.canvasSize.width - 80,
height: VM.canvasSize.width / 16 * 9 - 80
,
shadow : {
color: 'rgba(0,0,0,0.5)',
blur: 20,
offsetX: 0,
offsetY: 10,
opacity: 0.6,
fillShadow: true
}
})
canvas.add(artBoard)
canvas.artBoard = artBoard
canvas.artBoard.center()
canvas.artBoard.set({
'selectable' : false
})
canvas.renderAll()
console.log( canvas );
}
fabricCanvasInit()
}
})
but in this demo, the "artboard" was created by a fabric rect object.
When I change other object , like 'sendToBack()', I will reset the "artboard" object sendToBack()
I want add the rect with shadow like fabricCanvas.setBackgroundImage(...)
how to do that?
jsfiddle.net demo
(function() {
var canvas = this.__canvas = new fabric.Canvas('canvas');
// create a rectangle with a fill and a different color stroke
var artBoard = new fabric.Rect({
stroke: '#000',
strokeWidth:1,
fill: 'rgba(255,255,255,1)',
width: canvas.width - 40,
height: canvas.height - 40,
selectable:false,
shadow : {
color: 'rgba(0,0,0,0.5)',
blur: 20,
offsetX: 0,
offsetY: 10,
opacity: 0.6,
fillShadow: true,
}
})
canvas.centerObject(artBoard);
canvas.setBackgroundImage(artBoard);//add object as background
canvas.renderAll();
})();
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/1.7.11/fabric.min.js"></script>
<canvas id="canvas" width="400" height="400"></canvas>
You can add object as background canvas.setBackgroundImage(obj), Now this works as image and you can use sendToBack() and all . Here is your updated fiddle.

Fabric.js: How to serialize clipTo objects since ToJSON does not work for it?

Any guidance with jsfiddle example showing ClipTo serialization will be appreciated? Current ToJSON function does not work when trying to serialize clipped objects. See the ToJSON implementation at the bottom of the code.
JSFiddle Link: http://jsfiddle.net/PromInc/ZxYCP/
var img01URL = 'https://www.google.com/images/srpr/logo4w.png';
var img02URL = 'http://fabricjs.com/lib/pug.jpg';
var canvas = new fabric.Canvas('c');
// Note the use of the `originX` and `originY` properties, which we set
// to 'left' and 'top', respectively. This makes the math in the `clipTo`
// functions a little bit more straight-forward.
var clipRect1 = new fabric.Rect({
originX: 'left',
originY: 'top',
left: 180,
top: 10,
width: 200,
height: 200,
fill: '#DDD', /* use transparent for no fill */
strokeWidth: 0,
selectable: false
});
// We give these `Rect` objects a name property so the `clipTo` functions can
// find the one by which they want to be clipped.
clipRect1.set({
clipFor: 'pug'
});
canvas.add(clipRect1);
var clipRect2 = new fabric.Rect({
originX: 'left',
originY: 'top',
left: 10,
top: 10,
width: 150,
height: 150,
fill: '#DDD', /* use transparent for no fill */
strokeWidth: 0,
selectable: false
});
// We give these `Rect` objects a name property so the `clipTo` functions can
// find the one by which they want to be clipped.
clipRect2.set({
clipFor: 'logo'
});
canvas.add(clipRect2);
function findByClipName(name) {
return _(canvas.getObjects()).where({
clipFor: name
}).first()
}
// Since the `angle` property of the Image object is stored
// in degrees, we'll use this to convert it to radians.
function degToRad(degrees) {
return degrees * (Math.PI / 180);
}
var clipByName = function (ctx) {
this.setCoords();
var clipRect = findByClipName(this.clipName);
var scaleXTo1 = (1 / this.scaleX);
var scaleYTo1 = (1 / this.scaleY);
ctx.save();
var ctxLeft = -( this.width / 2 ) + clipRect.strokeWidth;
var ctxTop = -( this.height / 2 ) + clipRect.strokeWidth;
var ctxWidth = clipRect.width - clipRect.strokeWidth;
var ctxHeight = clipRect.height - clipRect.strokeWidth;
ctx.translate( ctxLeft, ctxTop );
ctx.rotate(degToRad(this.angle * -1));
ctx.scale(scaleXTo1, scaleYTo1);
ctx.beginPath();
ctx.rect(
clipRect.left - this.oCoords.tl.x,
clipRect.top - this.oCoords.tl.y,
clipRect.width,
clipRect.height
);
ctx.closePath();
ctx.restore();
}
var pugImg = new Image();
pugImg.onload = function (img) {
var pug = new fabric.Image(pugImg, {
angle: 45,
width: 500,
height: 500,
left: 230,
top: 50,
scaleX: 0.3,
scaleY: 0.3,
clipName: 'pug',
clipTo: function(ctx) {
return _.bind(clipByName, pug)(ctx)
}
});
canvas.add(pug);
};
pugImg.src = img02URL;
var logoImg = new Image();
logoImg.onload = function (img) {
var logo = new fabric.Image(logoImg, {
angle: 0,
width: 550,
height: 190,
left: 50,
top: 50,
scaleX: 0.25,
scaleY: 0.25,
clipName: 'logo',
clipTo: function(ctx) {
return _.bind(clipByName, logo)(ctx)
}
});
canvas.add(logo);
};
logoImg.src = img01URL;
//convert to json
var serialized=JSON.stringify(canvas);
canvas.clear();
canvas.loadFromDatalessJSON(serialized);
alert(serialized);
fabricjs clipTo should be included in the json representation of the canvas by default.
So if you use toJSON you will find a clipTo field in the json representation of canvas containing the clipTo's function.
Here is a demo.

Resources