Fabric JS resize group - html5-canvas

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/

Related

How to center crop Images in Fabricjs?

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>

Assign label/text to icon using offset -OpenLayers

I want to assign text or label using OpenLayers to Icons. I am able to assign the label to the icon but it places text on the icon .but I want to display label at offset so icon and label both display properly. I have tried using offsetY but it is not working and does not change the placement.
var styleToponimiFunction = function(feature,resolution) {
console.log(feature);
var iconName;
if (feature.get("remark")=='Daerah Istimewa') {
iconName='daerah_istimewa2.png';
}
else if (feature.get("remark")=='Kecamatan'){
iconName='kecamatan.png';
}
iconStyle = [new ol.style.Style({
image: new ol.style.Icon(({
anchor: [0.5, 46],
anchorXUnits: "fraction",
anchorYUnits: "pixel",
scale:0.03,
opacity:1,
src: "icons/" + iconName,
})),
text: new ol.style.Text({
text: feature.get('namobj'),
font: '12px Calibri,sans-serif',
fill: new ol.style.Fill({ color: 'black' }),
stroke: new ol.style.Stroke({
color: '#fff',
width: 2,
textAlign: 'center',
Baseline: 'hanging',
offsetX : 0,
offsetY : -12,
}),
declutter: true,
overflow: true
}),
})
]
return iconStyle;
}
The text options should not be inside the Stroke constructor, also Baseline should be textBaseline
stroke: new ol.style.Stroke({
color: '#fff',
width: 2,
}),
textAlign: 'center',
textBaseline: 'hanging',
offsetX : 0,
offsetY : -12,

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>

FabricJS image object clipTo shape object issue

Why this clipTo method doesn't work on the latest fabricjs version, which you could resize the object container and the image inside it. Also you able to move the container object and image object.
var imgInstance = new fabric.Image(img, {
width: instanceWidth,
height: instanceHeight,
top: (canvas.getHeight() / 2 - instanceHeight / 2),
left: (canvas.getWidth() / 2 - instanceWidth / 2),
originX: 'left',
originY: 'top'
});
canvas.add(imgInstance);
imgInstance.clipTo = function(ctx) {
/* image clipping method doesn't work on latest fabricjs version*/
ctx.save();
ctx.setTransform(1, 0, 0, 1, 0, 0);
clippingRect.render(ctx);
ctx.restore();
};
http://jsfiddle.net/efmbrm4v/2/
Or is their another approach shape object inside is the image object.
There is some caching issue with the latest version of FabricJS. To get around that, you need to set objectCaching property to false for the rectangle object.
$(document).ready(function() {
var imageLoader = document.getElementById('imageLoader');
imageLoader.addEventListener('change', handleImage, false);
});
var canvas = new fabric.Canvas('myCanvas');
var clippingRect = new fabric.Rect({
left: 100,
top: 100,
width: 100,
height: 100,
fill: 'transparent',
opacity: 1,
objectCaching: false //<-- set this
});
canvas.add(clippingRect);
function handleImage(e) {
var reader = new FileReader();
reader.onload = function(event) {
var img = new Image();
img.onload = function() {
var instanceWidth, instanceHeight;
instanceWidth = img.width;
instanceHeight = img.height;
var imgInstance = new fabric.Image(img, {
width: instanceWidth,
height: instanceHeight,
top: (canvas.getHeight() / 2 - instanceHeight / 2),
left: (canvas.getWidth() / 2 - instanceWidth / 2),
originX: 'left',
originY: 'top'
});
canvas.add(imgInstance);
imgInstance.clipTo = function(ctx) {
ctx.save();
ctx.setTransform(1, 0, 0, 1, 0, 0);
clippingRect.render(ctx);
ctx.restore();
};
canvas.renderAll();
};
img.src = event.target.result;
};
reader.readAsDataURL(e.target.files[0]);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/1.7.13/fabric.min.js"></script>
<canvas id="myCanvas" width="400" height="300" style="border: 1px solid black"></canvas>
<br />
<label>Choose a File:</label>
<br/>
<br />
<input type="file" id="imageLoader" name="imageLoader" />
<script src="//code.jquery.com/jquery-1.11.0.min.js"></script>

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" >

Resources