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)
Related
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.
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 });
I have an animation/canvas drawing where the pixels are hard coded and not relative. How do I make it so that the canvas and the drawing/animation scales with the browser size?
I thought of making the pixel values relative to the width and height of either the browser or the canvas but I don't know if it's a good idea.
Any tips?
P.S: I didn't post the code because it is almost 1000 lines long but if required I could post part of it.
Scale to fit, fill, or stretch
Almost every device has a different display aspect ratio, and then each setup will use only part, or all of the available screen to display the window.
The only real choice you have is how to adapt to fit the available space.
As the previous answer points out you can get the available size with innerWidth, innerHeight.
So lets assume you have that as width and height.
var width = innerWidth; // or canvas width
var height = innerHeight; //
Default resolution
I am assuming you app has a fixed aspect and an optimal resolution. To make it simple to adapt your app you need to keep the optimal resolution stored in code.
var defaultWidth = ?
var defaultHeight = ?
To adapt to displays you will need to get a scale that you set at the start of the app and adjust when the display size changes. You will also need the origin. the point where coordinate 0,0 is as for some solutions that will move.
These values are all relative to the current display that app is rendering on. They will take you native coordinates and make them conform to the device pixels.
var displaySetting = {
scaleX : ?,
scaleY : ?,
originX : ?,
originY : ?,
}
Fit, fill, or stretch
The 3 most basic options.
Stretch to fit. You stretch the rendering to fit the display but lose the aspect.
Scale to fit. You scale the rendering so that you maintain the aspect and all of the display is visible. Thought depending on the device you may have empty space on the sides or top and bottom.
Scale to fill. You scale the rendering so that it fills the device screen but you may end up clipping some or the rendering on the sides or top and bottom.
.
function getDisplayTransform(type, displaySetting){
displaySetting = displaySetting || {};
// first get scales
if(type === "stretch"){
displaySetting.scaleX = width / defaultWidth;
displaySetting.scaleY = height / defaultHeight;
} else if (type === "fit") {
displaySetting.scaleX = Math.min(width / defaultWidth, height / defaultHeight);
displaySetting.scaleY = displaySetting.scaleX;
} else { // type to fill
displaySetting.scaleX = Math.max(width / defaultWidth, height / defaultHeight);
displaySetting.scaleY = displaySetting.scaleX;
}
// now that the scale is set get the location of the origin. which is
displaySetting.originX = width / 2 - defaultWidth * 0.5 * displaySetting.scaleX;
displaySetting.originY = height / 2 - defaultHeight * 0.5 * displaySetting.scaleY;
// Note that for scale to fill the origin may be of the display or canvas
return displaySetting;
}
So now you just have to set the new coordinate system
ctx.setTransform(
displaySetting.scaleX,0,
0,displaySetting.scaleY,
displaySetting.originX,displaySetting.originY
);
So to fit with space to spare use getDisplayTransform("fit",displaySetting); or to fill use getDisplayTransform("fill",displaySetting); which will have some clipping top & bottom or left & right.
Once the transform is set you render as normal. You don't have to change the coordinates in your code, Line widths, font sizes, shadow offsets all remain the same as far as you code is concerned.
Set the canvas width and height to window.innerWidth and window.innerHeight respectively. So, the code would be like (in JS):
var canvas = document.getElementById('my_canvas');
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
Above code will only change the canvas size according to window width and height. To set the drawing/animation scale, you need to set its position/size dynamically (respect to the window width/height), not hard coded.
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/
I hope you can help me :)
I want define the height of img, in relation to the 'actual' width of the image. But the width is dynamic, because it is in % of the parent object(Browser window for an wxample) .
Why do I need the height?:
Without the height it works fine, but I need it because I want that the top of alle pictures meet at top and my pictures have different proportion. (I know that this will compress the pictures... later I fix this with overflow, after I have an solution for the height problem).
How I imagine, I need something that gives returns me the px of the 'actual'(for an example, after the user changed the size of the browser window) width. And then I can multiply it by 3 and write that to the height. (for an proportion of 1to3)
This should give you all you need.
var img = document.getElementById('testImage');
// these vars are numbers representing a length in pixel
var w = img.width, h = img.height; // displayed dimensions, change on resize
var nw = img.naturalWidth, nh = img.naturalHeight; // dimensions of the original image file
var proportion = nh / nw; // proportion < 1 : 'portrait', < 1 : 'landscape'
Since width / height change on resize you have to update them in the moment you need them.