Resize image and maintain overlay positions - javascript

I am resizing an image in Javascript to have the image fill the parent container, with its correct aspect ratio. On top of that image i have items that i need to overlay on the image with specific coordinates.
Original image size is: 654px X 418px
On top of that image i overlay a few other canvas items at exact coordinates, 56px X 82px
I resize the image, keeping correct aspect ratio of 1.56
Image container to scale too: 1672px X 829px
New image size with correct aspect ratio: 1297px X 829px
How do i calculate where the new canvas overlay coordinates are??

You can use the properties of the image element to determine the scaling factor to apply to the overlay items.
The intrinsic (unscaled) dimensions of an image are:
const unscaledWidth = img.naturalWidth,
unscaledHeight = img.naturalHeight;
The dimensions of the image as drawn are:
const scaledWidth = img.offsetWidth,
scaledHeight = img.offsetHeight;
The scaling factors are thus:
const scaleX = scaledWidth / unscaledWidth,
scaleY = scaledHeight / unscaledHeight;
Ref:
HTMLImageElement.naturalWidth
HTMLImageElement.naturalHeight
HTMLElement.offsetWidth
HTMLElement.offsetHeight

Related

Resizing canvas to fit within container

Having some trouble trying to get my Canvas/Stage to resize and fit correctly within the parent container. I've found other similar posts and while the answers did help me with obtaining the new screen size it still doesn't want to fit within the container and instead goes right to the edges of the screen (which is expected from the examples), going over the parent container with the left/right content.
I've tried many different ways but still cannot find the correct way where it, doesn't go outside the parent container and the scale X/Y doesn't throw off the position of the shape within the canvas. The most obvious way for me was to set a constraint to the actual canvas/stage via CSS but that ends up throwing off the scaling making the shape appear outside the canvas.
In the code link, I included my hacky approach that I was trying to do and while it looks correct in some cases with smaller device screens, the canvas width will still go outside the parent container. I'm not sure if I'm overthinking this a lot or if there is a much simpler way to approach my issue, any help or tips would be great.
Live CodeSandbox: https://codesandbox.io/s/white-surf-yc2vh
Desired Result Image: https://imgur.com/a/w0oXxG0
I think what you want / need is to resize the canvas so that it fits into the container, while keeping the aspect ratio which you have specified.
You can use this function to resize some rectangle (rect) so that it fits into some other rectangle (container):
// fit a rect into some container, keeping the aspect ratio of the rect
export const fitRectIntoContainer = (rectWidth, rectHeight, containerWidth, containerHeight) => {
const widthRatio = containerWidth / rectWidth; // ration container width to rect width
const heightRatio = containerHeight / rectHeight; // ration container height to rect height
const ratio = Math.min( widthRatio, heightRatio ); // take the smaller ratio
// new rect width and height, scaled by the same ratio
return {
width: rectWidth * ratio,
height: rectHeight * ratio,
};
};
Then you can resize the canvas so that it fits into the container:
const canvasSize = fitRectIntoContainer( 700, 600, size.width, size.height );
const canvasWidth = canvasSize.width;
const canvasHeight = canvasSize.height;
Then I guess you don't need any scaling anymore (so scaleX and scaleY can be both 1, or undefined)

Why are there 2 diffrent Sizes of a HTML Canvas? [duplicate]

This question already has answers here:
Size of HTML5 Canvas via CSS versus element attributes
(3 answers)
Closed 3 years ago.
im currently working on a HTML/Javascript Project where i am using a HTML Canvas and the Context2D for drawing.
More or less i'm drawing a part of a 2d world with no fixed tile size.
let width = canvas.width;
let height = canvas.height;
let cellHeight = height/rows * viewSizeMultiplier.y,cellWidth = width/columns * viewSizeMultiplier.x;
The viewSizeMultiplier is like 1/8 for 8 Tiles on the Map. I've struggeld alot by getting a specific Tile when clicking on the Canvas because the canvas.size does not adjust itself by resizing the window.
.canvas {
width: 60%;
height: 80%;
left:5%;
top:10%;
}
That's the way i implemented my canvas in css. For getting the current Tile on my screen i had to calculate the aspect ratio of the diffrent sizes like that:
function getMousePos(canvas, evt) {
var rect = canvas.getBoundingClientRect(),
scaleX = canvas.width / rect.width,
scaleY = canvas.height / rect.height;
return {
x: (evt.clientX - rect.left) * scaleX,
y: (evt.clientY - rect.top) * scaleY
}
}
So my question is why are there 2 diffrent Sizes of the Canvas? If it uses the canvas.size Size does it adjusts its resolution?
Added Snippet :
let canvas = document.getElementsByClassName('canvas')[0];
const canvasWidth= canvas.width;
const actualWidth =canvas.getBoundingClientRect().width;
console.log(canvasWidth,actualWidth);//300 , 522
The CSS styling won't change the pixel dimensions of the canvas after it's first been created. You need to specifically set canvas.width and canvas.height to new dimensions to change the pixel size of the canvas, otherwise you'll end up with nothing more than the original width * height pixels scaled to different sizes.
You'll need to listen for resize events to know when to change the dimensions of the canvas.
Well, the canvas' inner dimensions can be different from the canvas DOM element's dimensions.
canvas.width gives you the canvas inner width, while rect.width only gives you the canvas DOM element's outer width, excluding any portion of the canvas that is drawn beyond the outer width, and that needs scrolling to reach. The same applies for height.
So, in short, whenever you need to scroll to see all your content in a canvas, this implies that your canvas' inner dimensions is larger than its outer dimensions.

Fabric js image quality is not getting good after set image height and width dynamically

I have added image into canvas and dynamically I want to change image height and width according to the rectangle height and width. I have done calculation of image height and width according to rectangle height and width and original image height and width.
Using below code I have changed image height and width but image quality is getting very low
var image_height = 200; //For now here image height is dummy
var image_width = 150; //For now here image width is dummy
var obj = canvas.getActiveObject();
obj.setHeight(image_height);
obj.setWidth(image_width);
obj.setCoords();
canvas.renderAll();
I ran into the same problem a few days ago. The solution I found is to not try to set widthand height but rather ask your image to scale up to the canvas dimensions.
Here is how I did it :
var scaleWidth = canvas.getWidth() / img.width;
var scaleHeight = canvas.getHeight() / img.height;
var scale = Math.min(scaleWidth, scaleHeight);
bgImage = new fabric.Image(img, {
[...]
scaleX: scale,
scaleY: scale,
[...]
});
canvas.add(bgImage);
canvas.sendToBack(bgImage); // useful in case you want a background but not **needed**
canvas.renderAll();
In the code above I compute width and height ratios, pick the lower one, and then I apply it on the picture using scaleX and scaleY properties.
If it still doesn't work for you, please let me know in the comments.
EDIT : I completed my linked code for more clarity.
2nd EDIT : It appears a method called scaleexists and could be used on your image. Looks cleaner if you want to keep your ratio (aka scaleXand scaleY properties would have the same value).
When you export canvas to image, You can add multiplier. It will increase resolution as well as size of image.
canvas.toDataURL({ multiplier: 8 });

How can i get high resolution image from smaller canvas?

I am using one canvas in my web app and it's actual height and width are 500px. I am showing this canvas on screen as 500px square but i want image exported from this canvas as 1600px square. I have tried below code with no luck.
canvas.width = 1600;
canvas.style.width = 500;
Any help will be appreciated.
You can have the canvas display at 500px while still having a resolution of 1600px. Display size and resolution are independent. For resolution you set the canvas width and height properties. For display size you set the canvas style width and height properties.
// create a canvas or get it from the page
var canvas = document.createElement("canvas");
// set the resolution (number of pixels)
canvas.width = canvas.height = 1600;
// set the display size
canvas.style.width = canvas.style.height = "500px";
// get the rendering context
var ctx = canvas.getContext("2d");
To get the rendering to match the display size you need to scale up all rendering. You can do this by setting the transform scale to the canvas resolution divided by the display size
var scale = 1600 / 500; // get the scale that matches display size
ctx.setTransform(scale,0,0,scale,0,0);
Now when you render to the canvas you use the screen size coordinates.
ctx.fillRect(0,0,500,500); // fill all of the canvas.
ctx.fillStyle = "red"; // draw a red circle 100 display pixels in size.
ctx.beginPath();
ctx.arc(250,250,100,0,Math.PI * 2);
ctx.fill();
When you then save the canvas, what ever method you use as long as it is not screen capture the saved canvas will be 1600 by 1600 and all the rendering will be correctly positions and proportional
HTML
<canvas width="1600px" height="1600px" > </canvas>
CSS
canvas{
position :absolute;
transform:scale(0.3125);
left:-500px; //adjust
top:-350px; //adjust
}
Use transform:scale() to adjust size of your canvas
Now 1600 * 1600 will be the actual size of your canvas, so you can directly export images from your canvas
But in view it show as 500px * 500px beacuse it's scaled down, it dose not affect the image quality while exporting
Honest answer: you can't.
If you did, then you'd have found a way to losslessly compress data with less than 1/9th of the original size, and without any encoding, which is unarguably impossible.
What you can do is scale it up in a way that it at least doesn't get blurry. To do that, you need the final image to be an integer multiple of the previous canvas, so the browser won't apply anti-aliasing. Or if you want to use your own copying formula with putImageData that would get rid of anti-aliasing, you'll still get various incongruences and it would be very slow
In your case, the closest you could get is 1500x1500 ( 3*500x3*500 ). If your point was to process an image, you're not in luck, but if you just want to display something good enough, you can resort to various other tricks such as centering the canvas and using properties like box-shadow to make it clear that it's separate from the rest of the screen

Scale div into div like background image

I'm working on a web app and now facing a rather tricky task.
I have an image that I have to add overlay for.
Let's say I have an image of a car. I need to add values, like outside and inside temperature, on top of the car image.
It would be easy to add fixed pixel offsets for those temperature labels, but the image need to be scalable to every screen height and width-wise. I can't think of easy way to scale div into div exactly as "background-size:contain;" does for images.
Could someone point me to right tracks before I write complex javascript scaling logic?
ww = window width
wh = window height
ow = Original Width of your image
oh = Original Height of your image
nw = New Width of your image
nh = New Height of your image
mt = Margin top to adjust image to the screen vertically
ox = overlay location x coordinate according to original size of the image
oy = overlay location y coordinate according to original size of the image
nox = New ox
noy = new oy
sf = scaling factor
based on screen size you'll have a scaling factor for your image.
sf = ww / ow -> we find our scaling factor
nh = oh * sf -> then calculate our new image height
mt = (oh - nh) / 2 -> amount we need to shift image up to center screen
nox = ox * sf -> new overlay x coordinate
noy = (oy * sf) - mt -> new overlay y coordinate
edit
if image is too wide then this logic needs to be adjusted to shift image horizontally not vertically
edit 2
If you're targeting modern browsers only, then you can use CSS transform: scale(sx[, sy]); . This will re-size your DIV and all it's content proportionally Use transform: scale() with transform-origin: 0 0; to re-size from top left corner.
If you are using a SVG image this would be easy:
http://css-tricks.com/svg-text-typographic-designs/
Here is two examples with CSS:
http://codepen.io/chriscoyier/pen/zxqdev
http://maketea.co.uk/2013/12/16/smooth-text-overlays-with-css-transforms.html
I'm never tested CSS3 object-fit , but I think it's very useful for your solution:
https://developer.mozilla.org/en-US/docs/Web/CSS/object-fit
https://dev.opera.com/articles/css3-object-fit-object-position/

Categories