Top-Left coordinates not updated on rotating image using fabricjs - fabricjs

When I rotate an image in fabricjs, the top-left corner's coordinates are not updated after rotated. Instead, the top-left corner of the image still refers to the old point. I believe that it should recalculate the top-left corner based on the image's new position. Is there a way to achieve this? Any help is appreciated!
Below is the code for image rotation:
function rotate(){
activeCanvas.forEachObject(function(obj){
if(obj instanceof fabric.Image){
curAngle = obj.getAngle();
obj.setAngle(curAngle-90);
}
});
activeCanvas.renderAll();
}
Now, after the rotation, I want top-left coordinates of the new rotated image but it still returns top and left from the old image state.
For example, let say the image's top-left corner was originally at (100,200) and the image's dimensions are 500x600. Now, if I rotate the image in 90 degrees, the new dimensions are 600x500 and the top-left corner changes as well, as the image is rotated related to its center. But the fabricjs image still refers to the old top-left corner. Is there any method just like setCoords() to get the new upper left corner point as its top left?

As you can see from the snippet below, if you only rotate your object, only the bounding box will be updated, you have to move your object to have the position of your object updated.
var canvas = this.__canvas = new fabric.Canvas('c');
var rect = new fabric.Rect({
left: 120,
top: 30,
width: 100,
height: 100,
fill: 'green',
angle: 20
});
canvas.on("object:rotating", function() {
var ao = canvas.getActiveObject();
if(ao){
console.log('top and left are the same after rotation');
console.log('top:' + ao.top);
console.log('left:' + ao.left);
console.log('but not the bounding box');
var bound = ao.getBoundingRect();
console.log('bounding box - top:' + bound.top);
console.log('bounding box - left:' + bound.left);
}
})
canvas.add(rect);
canvas.renderAll();
<script src="https://rawgit.com/kangax/fabric.js/master/dist/fabric.js"></script>
<script src="https://seikichi.github.io/tmp/PDFJS.0.8.715/pdf.min.js"></script>
<canvas id="c" style="border:1px solid black"></canvas>

I guess you are looking for something like this:
canvas.on('object:rotating', function(options) {
options.target.setCoords();
var left = options.target.oCoords.tl.x,
top = options.target.oCoords.tl.y;
console.log(left, top);
});

Related

Set coords for Bounding Rect of a Path after moving it in fabricjs

There are dozen of topics on Stackoverflow on this problem, none of them shows a solution. So the problem is painful for many.
The problem: when I move a path, it's BoundingRect is not moving.
Possible solutions:
the creators of the library recommend _setPath method, but there are no examples, I can't understand how to use it;
Somehow to set Left, Top, Width and Height separately to the bounding rect (pathLine.getBoundingRect()). I don't know how, it's not working with Set().
To set Left, Top, Width and Height to the path. If I do so, the bounding rect is correct, but pathline is also shifting. I can't make it work.
delete and add path from canvas every time it moves. Bad solution :(
Remarkable that _calcDimensions() works correct with path coords.
Which means there are methods that work with path, we just need to find a proper one.
var pathLine = new fabric.Path('M 10 10 L 10 100 L 100 100', {
fill: '',
stroke: 'black',
objectCaching: false
})
var rect = new fabric.Rect({
left: 100,
top: 100,
fill: 'grey',
width: 50,
height: 50,
});
function createCanvas(id){
canvas = new fabric.Canvas(id);
canvas.add(pathLine);
canvas.add(rect);
pathLine.perPixelTargetFind = true;
canvas.on('object:moving', function(e) {
rect.setCoords();
const { width, height, left, top } = pathLine._calcDimensions();
var bound = pathLine.getBoundingRect();
pathLine.path[2][1] = rect.left ;
pathLine.path[2][2] = rect.top ;
canvas.renderAll();
});
return canvas;
}
Any ideas?

How to repeat Paths in Free Drawing?

Please look at
https://stackblitz.com/edit/js-wxidql
Here is my code.
import { fabric } from "fabric";
const canvas = new fabric.Canvas("c", { isDrawingMode: true });
canvas.setBackgroundColor("rgb(255,73,64)", canvas.renderAll.bind(canvas));
canvas.on("path:created", e => {
let mousePath = e.path;
let offsetPath = new fabric.Path();
let offsetLeft = mousePath.left + 60;
let offsetTop = mousePath.top + 60;
offsetPath.setOptions({
path: mousePath.path,
left: offsetLeft,
top: offsetTop,
width: mousePath.width,
height: mousePath.height,
fill: '',
stroke: 'black'
});
canvas.add(offsetPath);
canvas.requestRenderAll();
});
Here is an resulting image of my drawing session.
I drew the happy face in the top left corner of the canvas with my mouse.
The offset image was added by my code.
How can I change my code to make the offset drawing look like the one drawn with my mouse?
Edit: from https://github.com/fabricjs/fabric.js/wiki/When-to-call-setCoords
I tried using
offsetDraw.setCoords();
but I was unable to find a way to make it work.
Edit: What I have presented here is a minimized example. I am working on an larger project where I save each path drawn by the user. Later I recreate those paths in an animation like sequence.
Edit: I made some changes to my code in a effort to understand fabricjs.
const canvas = new fabric.Canvas("c", { isDrawingMode: true });
canvas.setBackgroundColor("rgb(255,73,64)", canvas.renderAll.bind(canvas));
canvas.on("path:created", e => {
let mousePath = e.path;
let offsetPath = new fabric.Path();
let offsetLeft = mousePath.left + 60;
let offsetTop = mousePath.top + 60;
offsetPath.setOptions({
path: mousePath.path,
//left: offsetLeft,
//top: offsetTop,
width: mousePath.width,
height: mousePath.height,
fill: '',
stroke: 'black'
});
canvas.add(offsetPath);
canvas.requestRenderAll();
console.log("mousePath left " + mousePath.left + " top " + mousePath.top);
console.log("offsetPath left " + offsetPath.left + " top " + offsetPath.top);
});
In that code, I commented out the setting of the left and top properties of the offsetPath and added console.log lines. I drew a circle in the top left corner of the canvas with my mouse. The resulting image was the following.
The following was printed in the console.
mousePath left 7.488148148148148 top 10.5
offsetPath left -0.5 top -0.5
I don't understand the results. Why was the offset circle rendered in that position?
Edit: I drew another test with my mouse.
It seems that the code repeats the paths of concentric circles rather well. But, the vertical lines are moved out of their correct position. Their displacements increase as the distance from the center increases. Why?
I found a solution for my own question. But, if someone has a better one, then please post it. Look at
https://stackblitz.com/edit/js-ncxvpg
The following is the code.
const canvas = new fabric.Canvas("c", { isDrawingMode: true });
canvas.setBackgroundColor("rgb(255,73,64)", canvas.renderAll.bind(canvas));
canvas.on("path:created", e => {
let mousePath = e.path;
mousePath.clone(clone => {
clone.setOptions({
left: mousePath.left + 100,
top: mousePath.top + 100
});
canvas.add(clone);
});
});
If someone has an explaination as to why my original code didn't work, then please post it.
thanks for sharing your comments on my question. I am facing a similar issue, just that if I click anywhere on the Canvas the first time. Then the offset issue vanishes. This happens for any object that I programmatically insert into the canvas.
In my case, I suspect that the canvas.pointer is having some offset of the current mouse position. However, when I click and add one very small path of 1pixel, then the pointer offset gets corrected. After this, all subsequent paths or objects created programatically are shown at the correct positions.

Fabric.js: Text position at right half of canvas (horizontal center)

"centerH()" positions the text at the horizontal center of the complete canvas size. I want to center my text at the right half (as if 50% of the left canvas was cropped). I could implement a while loop which always checks the distance to the right and left until it is equal but maybe there is an easier way to do this?
You can set left of text as half of canvas width.
text.set({
left:canvas.width/2
});
var canvas = new fabric.Canvas('canvas');
var text = new fabric.Text('FabricJS',{
left:10,top:20
});
canvas.add(text);
function setToCenter(){
text.set({
left:canvas.width/2
});
text.setCoords();
canvas.requestRenderAll();
}
canvas {
border: 1px solid #999;
}
<script src="https://rawgit.com/kangax/fabric.js/master/dist/fabric.js"></script>
<button onclick='setToCenter()'>Center</button>
<canvas id="canvas" width="400" height="400"></canvas>

FabricJS 2 - Image isn't stretching to the given width

I have just upgraded from fabric 1.7 to 2 and now the image object is behaving differently.See the screenshot, the image where the arrow is is completely ignoring the fact that i set a width on it, it looks like it's actually scaling it based on the given height to keep the image ratio. I don't want this to happen, the image needs to stretch to the size i tell it to.
Anyone have any idea to stop it doing this? I mean if i set a width in the options for the image object i expect it to respect those dimensions. It should be stretching to fill where the red box is.
This is happening when loading the image initially as a square and setting {width:1000,height:400} for example, but instead it looks like it's taking the height and scaling the width down to keep it square.
You need to set scaleX for width and scaleY for height. It's a breaking change for v2.
DEMO
var canvas = new fabric.Canvas('c');
var index = 0,
json;
var url = '//fabricjs.com/assets/pug.jpg';
fabric.Image.fromURL(url, function(img) {
var elWidth = img.naturalWidth || img.width;
var elHeight = img.naturalHeight || img.height;
img.set({
scaleX:200/elWidth,
scaleY:200/elHeight
})
canvas.add(img);
})
canvas{
border:2px solid #000;
}
<script type="text/javascript" src="
https://rawgit.com/kangax/fabric.js/master/dist/fabric.js"></script>
<canvas id="c" width="400" height="400"></canvas>

how to stretch image size to match fabric.Image object

I am using fabricjs to implement image editing and I try to use a fabric.Image object as the background image of canvas to store the data. And the following is the code:
var canvas = new fabric.Canvas('canvasId');
var imageObject = new fabric.Image($originImage);
canvas.add(imageObject);
but I found the $originImage's size is much larger than canvas' size and also imageObject's size, so the canvas can only show part of the image. I want to know how to stretch the $originImage to adapt the canvas then canvas can display all of the $originImage?
Here what I have done
$canvas.width = $originImage.clientWidth;
$canvas.height = $originImage.clientHeight;
var fabricCanvas = new fabric.Canvas('canvasId');
// canvas.setFabricCanvas(fabricCanvas);
var imageObject = new fabric.Image($originImage);
// fabricCanvas.add(imageObject);
// fabricCanvas.isDrawingMode = true;
fabricCanvas.setBackgroundImage(imageObject, fabricCanvas.renderAll.bind(fabricCanvas), {
scaleX: fabricCanvas.width / $originImage.naturalWidth,
scaleY: fabricCanvas.height / $originImage.naturalHeight
});
the upper is my related code and below is the display:
it is solved, and the previous question is because I resize the $originImage first, so when I input the image src as setBackgroundImage's parameter, it can display normally.
For version 2.0 of fabricjs, they have changed the way to handle width/height compared to previous version. If you apply width/height, then it basically crop the image to that particular size compared to the original image size. In order to resize it to a proper size, you have to switch to use scaleX/scaleY like other people have suggested.
For example, I use image.fromUrl to load an image and set it to be the background:
fabric.Image.fromURL(imgUrl, function(img) {
img.set({
scaleX: currentWidth / img.width,
scaleY: currentHeight / img.height,
top: topPosition,
left: leftPosition
originX: 'left', originY: 'top'
});
canvas.setBackgroundImage(img, canvas.renderAll.bind(canvas));
});
where currentWidth/currentHeight can be your canvas size if you want full background or they can be a specific width/height that you want (in my project, I want to do letter-boxed image instead so I set the width and height based on my letter-boxing algorithm), and top/left is the location that you want to place your image in the canvas (leave it none if you want to be full background image). Do not set any width and height since that will crop the scaled image rather than setting the image to be the exact size.
If this does not work, check the backgroundImage object from the canvas and see if its dimension and scale properties are different compared to a manual calculation. If you take a look, you will see the width and height properties are your natural image width and height, but it will have scaleX and scaleY less than 1 if your canvas size is less than the image size or more than 1 if it is bigger.
Also I see you are loading image directly into an image object. That might be different compared to load the data and set it directly using setBackgroundImage.
canvas.setBackgroundImage(img, canvas.renderAll.bind(canvas), {
scaleX: canvas.width / $originalImg.naturalWidth,
scaleY: canvas.width / $originalImg.naturalHeight
});
use scaleX,scaleY to resize.
(function() {
var $originalImg = $('#originalImage')[0];
console.dir($originalImg)
var canvas = this.__canvas = new fabric.Canvas('c');
var img = new fabric.Image($originalImg);
canvas.setBackgroundImage(img,canvas.renderAll.bind(canvas),{
scaleX:canvas.width/$originalImg.naturalWidth,
scaleY:canvas.width/$originalImg.naturalHeight
});
})();
canvas{
border-width: 1pz;
border-style: solid;
border-color: #000;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/2.0.0-rc.3/fabric.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<img id='originalImage'src='http://fabricjs.com/assets/pug_small.jpg'>
<canvas id='c' width=200 height=200></canvas>
I am using the following script to ensure the image is no larger than the canvas width:
var aspect = image.width / image.height; //Aspect ratio of image
new fabric.Image(image, {
scaleX: canvas.width / image.width,
scaleY: canvas.width / (image.height * aspect)
});

Resources