How to center crop Images in Fabricjs? - image

I'm able to add Images to the canvas using Fabric.js.
However, I have a fixed width & height for the image box.
The images will be added dynamically and their width and height would vary.
To make it consistent, I want to center crop the Image to fit the width and height of the image box.
How do I do that using Fabric.js?
Sample Images
Here is my code:
let newObj = {
"version": "4.6.0",
"objects": [{
type: 'image',
version: '4.6.0',
originX: 'left',
originY: 'top',
left: 24,
top: 24,
width: 250,
height: 250,
fill: 'rgb(0,0,0)',
scaleX: 0.49,
scaleY: 0.49,
visible: true,
backgroundColor: '',
fillRule: 'nonzero',
paintFirst: 'fill',
globalCompositeOperation: 'source-over',
src: 'https://logo.clearbit.com/mircrosoft.com?size=400',
crossOrigin: null,
filters: [],
scaleToWidth: 250,
scaleToHeight: 250
}],
"background": "#000000"
}
var canvas1 = new fabric.StaticCanvas("a", { width: 600, height: 400 });
canvas1.loadFromJSON(newObj, function() {
canvas1.renderAll();
});
<script type='text/javascript' src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/521/fabric.min.js"></script>
<canvas id="a"></canvas>

I would recommend you that instead of cropping you should scale the original to fit the size you need, that way it works for big and small images, and it's a simple calculation.
In your sample you have hardcoded scaleX: 0.49 not sure why that, If the original image is quite large resolution that scale won't do much and center crop will get just a fraction of the image.
On my example below I'm assuming that your image box is a square, but if not you can use:
http://fabricjs.com/docs/fabric.Image.html#scaleX
http://fabricjs.com/docs/fabric.Image.html#scaleY
const canvas1 = new fabric.StaticCanvas("a", {width: 300, height: 200});
const image_url = 'https://logo.clearbit.com/mircrosoft.com?size=400'
new fabric.Image.fromURL(image_url, img => {
const size = 100
img.top = img.left = 20
img.scale(size/img.width);
canvas1.add(img);
canvas1.renderAll();
});
canvas {
border: solid 1px red
}
<script type='text/javascript' src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/521/fabric.min.js"></script>
<canvas id="a"></canvas>

Related

How to increase touch area control points on mobile device ( not increase display size of control points ) in fabricjs?

How to increase touch area of control points on mobile device with FabricJS? Because on mobile they are too small for smooth interactive with. But not change size of control points ( for nice view ).
Here is my code:
How to increase interactive of control points = view control points size x 2 ?
var canvasObject = document.getElementById("editorCanvas");
// set canvas equal size with div
$(canvasObject).width($("#canvasContainer").width());
$(canvasObject).height($("#canvasContainer").height());
var canvas = new fabric.Canvas('editorCanvas', {
backgroundColor: 'white',
selectionLineWidth: 2,
width: $("#canvasContainer").width(),
height: $("#canvasContainer").height()
});
// test customize control points
var rect = new fabric.Rect({
left: 30,
top: 30,
fill: 'red',
width: 150,
height: 150
});
canvas.add(rect);
rect.set({
transparentCorners: false,
cornerColor: 'white',
cornerStrokeColor: 'rgba(14,19,24,.15)',
borderColor: 'rgba(41,198,203,1)',
selectionLineWidth: 8,
cornerSize: 12,
cornerStyle: 'circle',
});
#canvasContainer {
width: 100%;
height: 100vh;
background-color: gray;
}
<script src="https://code.jquery.com/jquery-3.3.1.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/3.3.2/fabric.js"></script>
<div id="canvasContainer">
<canvas id="editorCanvas"></canvas>
</div>
Please show me the way to resolve this problem.
By the way: ( on mobile device ) is there any way to allow drag object only object is selected before?
Thank you very much!
You need to override _setCornerCoords method of object and change the value of cornerHypotenuse.
DEMO
var canvasObject = document.getElementById("editorCanvas");
// set canvas equal size with div
$(canvasObject).width($("#canvasContainer").width());
$(canvasObject).height($("#canvasContainer").height());
var canvas = new fabric.Canvas('editorCanvas', {
backgroundColor: 'white',
selectionLineWidth: 2,
width: $("#canvasContainer").width(),
height: $("#canvasContainer").height()
});
// test customize control points
var rect = new fabric.Rect({
left: 30,
top: 30,
fill: 'red',
width: 150,
height: 150
});
canvas.add(rect);
rect.set({
transparentCorners: false,
cornerColor: 'white',
cornerStrokeColor: 'rgba(14,19,24,.15)',
borderColor: 'rgba(41,198,203,1)',
selectionLineWidth: 8,
cornerSize: 5,
cornerStyle: 'circle'
});
rect._setCornerCoords = function() {
var coords = this.oCoords,
newTheta = fabric.util.degreesToRadians(45 - this.angle),
cornerHypotenuse = this.cornerSize * 2 * 0.707106,
cosHalfOffset = cornerHypotenuse * fabric.util.cos(newTheta),
sinHalfOffset = cornerHypotenuse * fabric.util.sin(newTheta),
x, y;
for (var point in coords) {
x = coords[point].x;
y = coords[point].y;
coords[point].corner = {
tl: {
x: x - sinHalfOffset,
y: y - cosHalfOffset
},
tr: {
x: x + cosHalfOffset,
y: y - sinHalfOffset
},
bl: {
x: x - cosHalfOffset,
y: y + sinHalfOffset
},
br: {
x: x + sinHalfOffset,
y: y + cosHalfOffset
}
};
}
}
#canvasContainer {
width: 100%;
height: 100vh;
background-color: gray;
}
<script src="https://code.jquery.com/jquery-3.3.1.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/3.3.2/fabric.js"></script>
<div id="canvasContainer">
<canvas id="editorCanvas"></canvas>
</div>

How to mapping "letter-spacing" value from text node of SVG to "charSpacing" property of fabric.TextBox in FabricJS?

I have problem with how to re-presentation of node in SVG with "letter-spacing" in SVG document to fabric.TextBox.
In file SVG text node is:
<text transform="matrix(1 0 0 1 51.5211 22.2889)" style="fill:#3C2415; font-size:11px; letter-spacing:3;">MINH TUẤN</text>
And my fabric.TextBox is:
var textbox_0_1 = new fabric.Textbox("MINH TUẤN", {
width: 200,
fontSize: 11,
fill: "#3C2415",
editable: true,
textAlign: "center",
charSpacing: 3,
});
Here is display on SVG & FabricJS:
How to find correct charSpacing which is respectively with letter-spacing attribute in SVG's Text node ?
Notes:
In fabric.TextBox document write:
charSpacing :Number
additional space between characters expressed in thousands of em unit
And in document for SVG write:
(if no unit identifier is provided) values in user space — for example, "15"
And Here is my code:
var canvasObject = document.getElementById("editorCanvas");
// set canvas equal size with div
$(canvasObject).width($("#canvasContainer").width());
$(canvasObject).height($("#canvasContainer").height());
var canvas = new fabric.Canvas('editorCanvas', {
backgroundColor: 'white',
selectionLineWidth: 2,
width: $("#canvasContainer").width(),
height: $("#canvasContainer").height()
});
var textbox_0_1 = new fabric.Textbox("MINH TUẤN", {
top: 20,
left: 20,
width: 200,
fontSize: 11,
fill: "#000000",
editable: true,
textAlign: "center",
charSpacing: 3,
});
canvas.add(textbox_0_1);
setObjectCoords();
canvas.renderAll();
function setObjectCoords() {
canvas.forEachObject(function(object) {
object.setCoords();
});
}
#canvasContainer {
width: 100%;
height: 100vh;
background-color: gray;
}
<script src="https://code.jquery.com/jquery-3.3.1.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/2.4.3/fabric.js"></script>
<div id="canvasContainer">
<canvas id="editorCanvas"></canvas>
</div>
Thank you,
You need to parse from pixel to em.
DEMO
var canvas = new fabric.Canvas('editorCanvas', {
backgroundColor: 'white',
width: 300,
height: 100
});
var letterSpacing = 5, fontSize = 30;
var textbox = new fabric.Text("MINH TUẤN", {
top: 20,
fontSize: fontSize,
fill: "#000000",
fontFamily : "Verdana"
});
canvas.add(textbox);
var parsedSpacing = fabric.util.parseUnit(letterSpacing, fontSize) / fontSize * 1000;
textbox.set({charSpacing : parsedSpacing});
canvas.requestRenderAll();
console.log(parsedSpacing)
canvas{
border:1px solid;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/2.4.3/fabric.js"></script>
<svg width="300" height="100" viewBox="0 0 300 100">
<text font-size="30" letter-spacing="5" font-family="Verdana" y="40">MINH TUẤN</text>
</svg>
<canvas id="editorCanvas"></canvas>

How to create an Editable Text which is curved along a Path in Fabricjs?

I want to make Editable Text which is curved along a fabric.Path as bellow expected image.
Here is my code:
I created a curved fabric.Path & a fabric.TextBox, but I don't know how to curve text along that Path. Please help me resolve this problem.
var canvasObject = document.getElementById("editorCanvas");
// set canvas equal size with div
$(canvasObject).width($("#canvasContainer").width());
$(canvasObject).height($("#canvasContainer").height());
var canvas = new fabric.Canvas('editorCanvas', {
selectionLineWidth: 2,
width: $("#canvasContainer").width(),
height: $("#canvasContainer").height()
});
var Path_0_1 = new fabric.Path('M127.91,99.29c32.16,10.73,53.41,7.33,67.81,0 c34.07-17.34,41.99-62.82,60.98-60.98c8.19,0.79,14.11,9.98,17.91,17.91', {
fill : null,
stroke: '#000000',
});
canvas.add(Path_0_1);
var curvedText = new fabric.IText("Anh Minh", {
left: 50,
top: 50,
width: 200,
fontSize: 24,
fontFamily: 'Times New Roman',
fontWeight: 'normal',
textAlign: 'center',
lineHeight: 1,
fill: "#404041",
});
canvas.add(curvedText);
#canvasContainer {
width: 100%;
height: 500px;
}
<script src="https://code.jquery.com/jquery-3.3.1.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/2.4.3/fabric.js"></script>
<div id="canvasContainer">
<canvas id="editorCanvas"></canvas>
</div>
Addition information:
After few hours google search, I see an interesting Konva.TextPath, I wonder If can we make same Object like that Konva.TextPath in Fabric.js?
Thank you!

Fabric toDataUrl with multiplier not working as expected

In my code I created method to clip images using fabric shapes. I have used stackoverflow answer for achieving this. Somehow after using this method I cannot render the canvas using default fabric canvas render method.
var img01URL = 'https://www.google.com/images/srpr/logo4w.png';
var img02URL = 'http://fabricjs.com/lib/pug.jpg';
var canvas = new fabric.Canvas('c');
document.getElementById('download').addEventListener('click', function() {
canvas.renderAll();
this.href = canvas.toDataURL({
format: 'png',
multiplier: 2
});
this.download = "test.png";
}, false);
var clipRect1 = new fabric.Rect({
originX: 'left',
originY: 'top',
angle: 90,
left: 195,
top: 0,
width: 200,
height: 200,
fill: '#DDD', /* use transparent for no fill */
strokeWidth: 1,
lockMovementX: true,
lockMovementY: true,
angle: 5,
stroke: 'red'
});
var pugImg = new Image();
pugImg.onload = function (img) {
var pug = new fabric.Image(pugImg, {
angle: 0,
width: 500,
height: 500,
left: 245,
top: 35,
scaleX: 0.3,
scaleY: 0.3,
clipName: 'pug',
clipTo: function(ctx) {
ctx.save();
ctx.setTransform(1, 0, 0, 1, 0, 0);
clipRect1.render(ctx);
ctx.restore();
}
});
canvas.add(pug);
};
pugImg.src = img02URL;
pugImg.setAttribute('crossOrigin', 'anonymous');
#c {
border:0px solid #ccc;
}
<script src="//cdn.bootcss.com/fabric.js/1.5.0/fabric.js"></script>
<canvas id="c" width="500" height="400"></canvas>
<a id="download">Download as image</a>
Your problem is the multiplier.
When using that kind of clipTo functions, you are setting the transform of canvas to plain. When rendering with canvas with multiplier, the base canvas transformation is scaled by the multiplier.
Your rect will be drawn without this transform ( because of setTransform(1,0,0,1,0,0) and will clip out the image.
Instead of setting the transform to [1,0,0,1,0,0] you should set to the invers of the current transformation of the object you are clipping.
var img01URL = 'https://www.google.com/images/srpr/logo4w.png';
var img02URL = 'http://fabricjs.com/lib/pug.jpg';
var canvas = new fabric.Canvas('c');
document.getElementById('download').addEventListener('click', function() {
var data = canvas.toDataURL({
format: 'png',
multiplier: 2
});
console.log(data);
//var img = document.getElementById('export');
//img.src = data;
}, false);
var clipRect1 = new fabric.Rect({
originX: 'left',
originY: 'top',
angle: 90,
left: 195,
top: 0,
width: 200,
height: 200,
fill: '#DDD', /* use transparent for no fill */
strokeWidth: 1,
lockMovementX: true,
lockMovementY: true,
stroke: 'red',
angle: 5
});
var pugImg = new Image();
pugImg.onload = function (img) {
var pug = new fabric.Image(pugImg, {
angle: 0,
width: 500,
height: 500,
left: 245,
top: 35,
scaleX: 0.3,
scaleY: 0.3,
clipName: 'pug',
clipTo: function(ctx) {
ctx.save();
var myMatrix = this.calcTransformMatrix();
myMatrix = fabric.util.invertTransform(myMatrix);
ctx.transform.apply(ctx, myMatrix);
clipRect1.render(ctx);
ctx.restore();
}
});
canvas.add(pug);
};
pugImg.src = img02URL;
#c {
border:0px solid #ccc;
}
<script src="//www.deltalink.it/andreab/fabric/fabric.js"></script>
<canvas id="c" width="500" height="400"></canvas>
<a id="download">Download as image</a>
<img id="export" >

Fabric JS resize group

I have Canvas with a few group. In group are two text and Line, i wanna resize selected Line with using Input Text. Now i have input text, and when i select a group in input text is actually height, but now i change value to text field and resize Group/Object but i have problems "border" this element which allows me resize and rotate Object. When i set SelectedObject.set({height: 300}); only border is resizing, scaledObject.item(0).set({height: 300}); only line without border, when i put this together everything is resized but border is not around object, how i can resize Object correctly with using Input Text?
I have this solution for a group that has a figure and text, I did this to maintain the text size and border width as well.
var canvas = new fabric.Canvas("c1");
reinit()
canvas.on({
'object:scaling': function(e) {
var obj = e.target,
w = obj.width * obj.scaleX,
h = obj.height * obj.scaleY,
s = obj.strokeWidth;
console.log(obj.width, obj.scaleX, h,w,s)
obj._objects[0].set({
'height' : obj.height,
'width' : obj.width,
'scaleX' : 1,
'scaleY' : 1,
h: h,
w: w
//top: 1,
//left: 1,
});
/*e.target.set({
'height' : h,
'width' : w,
'scaleX' : 1,
'scaleY' : 1
});*/
}
});
canvas.on({
'object:modified': function(e) {
console.log(e)
//e.target.set({scaleX:1, scaleY:1})
group = e.target
rect = e.target._objects[0]
rect.set({height:rect.h, width: rect.w})
console.log('r',rect.width, group.width)
text = group._objects[1]
canvas.remove(group)
canvas.add(new fabric.Group([rect,text], {
top: group.top,
left: group.left
}))
}
});
function reinit(){
var el = new fabric.Rect({
originX: "left",
originY: "top",
stroke: "rgb(0,0,0)",
strokeWidth: 1,
fill: 'transparent',
opacity: 1,
width: 200,
height: 200,
cornerSize: 6
});
var text = new fabric.IText('test', { fontSize: 16});
var group = new fabric.Group([ el, text ], {
width: 200,
height: 200,
left: 5,
top: 5,
});
canvas.add(group);
canvas.renderAll();
}
<script src="http://fabricjs.com/lib/fabric.js"></script>
<canvas id="c1" width="600" height="500" style="border:1px solid #ccc"></canvas>
here the fiddle http://jsfiddle.net/davidtorroija/qs0ywh8k/1/
and if this is not useful to you also you have other ways in this answers:
how to display size on shape after scaled using fabric js?
Rect with stroke, the stroke line is mis-transformed when scaled
EDIT:
Here you have another perfect example!
also uses ellipses and triangles etc
http://jsfiddle.net/GrandThriftAuto/6CDFr/15/

Resources