fabric.js lockUniScaling on fabric.Image.fromURL - javascript

I load an image with fabric.js (http://fabricjs.com), but I can't get the 'lockUniScaling' working on my element.
This works:
canvas.add(new fabric.Rect({ width: 50, height: 50, fill: '#77f', top: 100, left: 100 }));
canvas.item(0).lockUniScaling = true;
How can I get the 'lockUniScale' to this element?
fabric.Image.fromURL('http://www.domain.com/image.png', function(obj) {
canvas.add(obj.scale(1).rotate(0).set({ left: 100, top: 100 }));
canvas.renderAll();
});
If I add some other elements I can grab them by using item(1), item(2),... but I can't get the image-element.
Any hints?

Related

Offset issues images when inside group (scaleToWidth)

I'm experiencing an issue when scaling an image inside a group the offset of the image changes. The group consists of a background (Rect) and an image. When scaling the group the offset if the image becomes different. Expected is that the offset remains the same as the background.
I'm using Fabric 4.0.0-beta.6
const canvas = new fabric.Canvas('canvas');
canvas.setWidth(document.body.clientWidth);
canvas.setHeight(document.body.clientHeight);
const bg = new fabric.Rect({
width: 100,
height: 100,
fill: 'red',
});
fabric.Image.fromURL("https://dummyimage.com/300x150/000/fff", function(img) {
img.scaleToWidth(bg.width)
const post = new fabric.Group([bg, img], {
left: 100,
top: 100,
});
post.scaleToWidth(400);
canvas.add(post);
canvas.renderAll();
});
https://jsbin.com/migogekoxu/1/edit?html,css,js,output
Setting the strokeWidth property of the background element to 0 fixed the offset for me.
const canvas = new fabric.Canvas('canvas');
canvas.setWidth(document.body.clientWidth);
canvas.setHeight(document.body.clientHeight);
const bg = new fabric.Rect({
width: 100,
height: 100,
fill: 'red',
strokeWidth: 0, //<---
});
fabric.Image.fromURL("https://dummyimage.com/300x150/000/fff", function(img) {
img.scaleToWidth(bg.width)
const post = new fabric.Group([bg, img], {
left: 100,
top: 100,
});
post.scaleToWidth(400);
canvas.add(post);
canvas.renderAll();
});
<canvas id="canvas"></canvas>
<script src="https://cdn.jsdelivr.net/npm/fabric#4.0.0-beta.6-browser/dist/fabric.min.js"></script>
Obviously that's a bug with fabricJS. If you don't want to fix it on your own in it's source code you can workaround it however.
You can manually correct the position by adding an offset of 0.5 to the image's top and left properties.
For example:
const canvas = new fabric.Canvas('canvas');
const bg = new fabric.Rect({
width: 100,
height: 100,
fill: 'red'
});
fabric.Image.fromURL("https://dummyimage.com/300x150/000/fff", function(img) {
img.scaleToWidth(bg.width);
img.left += 0.5;
img.top += 0.5;
const post = new fabric.Group([bg, img], {
left: 100,
top: 100
});
post.scaleToWidth(400);
canvas.add(post);
canvas.renderAll();
});
<script src="https://cdn.jsdelivr.net/npm/fabric#4.0.0-beta.6-browser/dist/fabric.min.js"></script>
<canvas id="canvas" width="600" height="400"></canvas>

Fabricjs - How to detect total width/height of canvas where objects exist

So I want to save the canvas based on scale 1 and I want to include all existing/visible objects.
So right now, my canvas is 600x400 and if I were to save it, it would only save what's inside that 600x400, and it respects zoom level (a higher zoom will see less things, a lower zoom will see more things but smaller).
I've gotten around this by running this code:
let zoom = this.canvas.getZoom();
this.canvas.setZoom(1);
let url = this.canvas.toDataURL({width: 1000, height: 700, multiplier: 1});
this.canvas.setZoom(zoom);
window.open(
url,
'_blank' // <- This is what makes it open in a new window.
);
What this does is save the image as 1000x700, so stuff that were past 600px but under 1000 still gets saved.
However, this is hard coded. So I was wondering if there was an existing function or a clean/simple way to detect where all the objects are in and returns the full size (width and height).
fiddle: http://jsfiddle.net/1pxceaLj/3/
Update 1:
var gr = new this.fabric.Group([], {
left: 0,
top: 0
});
this.canvas.forEachObject( (o) => {
gr.add(o);
});
this.canvas.add(gr);
this.canvas.renderAll();
console.log(gr.height, gr.width); // 0 0
Solution
Using group was the best idea. Best example: http://jsfiddle.net/softvar/mRA8Z/
function selectAllCanvasObjects(){
var objs = canvas.getObjects().map(function(o) {
return o.set('active', true);
});
var group = new fabric.Group(objs, {
originX: 'center',
originY: 'center'
});
canvas._activeObject = null;
canvas.setActiveGroup(group.setCoords()).renderAll();
console.log(canvas.getActiveGroup().height);
}
One possibility would be to create a kind of Container using a fabricjs group and adding all created objects to this container.
I updated your fiddle here: http://jsfiddle.net/1pxceaLj/4/
Thus, you could just use group.width and group.height, perhaps adding a little offset or minimum values, and there you are having dynamical value to pass into toDataUrl even when having a smaller canvas.
code:
var canvas = new fabric.Canvas('c');
var shadow = {
color: 'rgba(0,0,0,0.6)',
blur: 20,
offsetX: 10,
offsetY: 10,
opacity: 0.6,
fillShadow: true,
strokeShadow: true
}
var rect = new fabric.Rect({
left: 100,
top: 100,
fill: "#FF0000",
stroke: "#000",
width: 100,
height: 100,
strokeWidth: 10,
opacity: .8
});
var rect2 = new fabric.Rect({
left: 800,
top: 100,
fill: "#FF0000",
stroke: "#000",
width: 100,
height: 100,
strokeWidth: 10,
opacity: .8
});
rect.setShadow(shadow);
//canvas.add(rect);
//canvas.add(rect2);
var gr = new fabric.Group([ rect, rect2 ], {
left: 0,
top: 0
});
canvas.add(gr);
function save()
{
alert(gr.width);
alert(gr.height);
let zoom = canvas.getZoom();
var minheight = 700;
canvas.setZoom(1);
let url = this.canvas.toDataURL({width: gr.width, height: gr.height > minheight ? gr.height : minheight, multiplier: 1});
canvas.setZoom(zoom);
window.open(
url,
'_blank'
);
}

How to blur part of an image

we know that fabric.Rect we can mask image.But Providing masking method as 'blur' instead of current gray color overlay is this possible or Is anyone has idea about this
$("#context2Add").click(function () {
var mask = new fabric.Rect({
left: 100,
top: 100,
fill: 'grey',
width: 100,
height: 100
});
fabricCanvas2.add(mask);
});

Removing the item being clicked

I created a canvas and I can add objects. How can I remove the item clicked?
var canvas = new fabric.Canvas('c');
var rect = new fabric.Rect({
left: 50,
top: 50,
fill: 'green',
width: 40,
height: 80
});
var circle = new fabric.Circle({
radius: 20,
fill: 'red',
left: 100,
top: 100
});
canvas.add(rect);
canvas.add(circle);
Fabric.js provides object:selected event on canvas. You can listen to this event and then remove the item it occurred on. Here's the sample code:
canvas.on('object:selected',function(ev){
canvas.remove(ev.target);
});
You can read the documentation and look at the jsfiddle i created here: http://jsfiddle.net/yrL4eLsn/1/
Please refer fabrics readme : fabricjs.com/fabric-intro-part-2/
canvas.on('mouse:down', function(options) {if (options.target){console.log('an object was clicked!',options.target.type);canvas.remove(options.target);}});
Posting code in single line as I am using a cellphone

Deselecting mouse event 'mouse:down' after initial event has been rendered

I am very new to fabric.js, but I am learning quickly.
I have a few items that, when clicked, will fill with the color red.
My problem lies with clicking the image again to reset only that image back to default (black).
Can someone explain how to achieve this?
canvas.on('mouse:down', function(e) {
e.target.setFill('red');
e.target.lockMovementX = e.target.lockMovementY = true;
e.target.lockScalingX = e.target.lockScalingY = true;
e.target.lockUniScaling = true;
canvas.deactivateAll().renderAll();
});
Please see the JSFIDDLE for the full example code
Look at the if statement below. It detects the current color and switches it:
(function(){
var canvas = new fabric.Canvas('c4');
canvas.hoverCursor = 'default';
canvas.on('mouse:down', function(e) {
var color = e.target.fill;
if(color == '#000')
{
e.target.setFill('red');
} else {
e.target.setFill('#000');
}
e.target.lockMovementX = e.target.lockMovementY = true;
e.target.lockScalingX = e.target.lockScalingY = true;
e.target.lockUniScaling = true;
canvas.deactivateAll().renderAll();
});
canvas.add(new fabric.Circle({ radius: 10, fill: '#000', top: 100, left: 200 }));
canvas.add(new fabric.Circle({ radius: 10, fill: '#000', top: 100, left: 150 }));
canvas.add(new fabric.Circle({ radius: 10, fill: '#000', top: 100, left: 100 }));
})();
Look at this Fiddle : http://jsfiddle.net/hpyrk05w/3/
it is so simple :
you have to check on mouse down if current fill of object is what?
if current fill is red than fill it with black color and if not, than fill it with red color
(function(){
var canvas = new fabric.Canvas('c4');
canvas.hoverCursor = 'default';
canvas.on('mouse:down', function(e) {
if(e.target.getFill()=="red")
e.target.setFill('black');
else
e.target.setFill('red');
e.target.lockMovementX = e.target.lockMovementY = true;
e.target.lockScalingX = e.target.lockScalingY = true;
e.target.lockUniScaling = true;
canvas.deactivateAll().renderAll();
});
canvas.add(new fabric.Circle({ radius: 10, fill: '#000', top: 100, left: 200 }));
canvas.add(new fabric.Circle({ radius: 10, fill: '#000', top: 100, left: 150 }));
canvas.add(new fabric.Circle({ radius: 10, fill: '#000', top: 100, left: 100 }));
})();
pre { margin-left: 15px !important }
canvas{border: 1px solid red; }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<script src="http://fabricjs.com/lib/fabric.js"></script>
<canvas id="c4" width="450" height="500"></canvas>

Categories

Resources