Why are my drawings sized relatively to the canvas size? - javascript

I have a canvas defined as
<canvas id="field"></canvas>
style.css:
canvas#field {
width: 500px;
height: 250px;
border: 3px solid black; /* for now */
}
Whenever I try drawing stuff on it, like
var ctx = field.getContext("2d")
// circle
ctx.beginPath();
ctx.arc(100, 75, 50, 0, 2 * Math.PI);
ctx.stroke();
It always ends up being sized relatively to the canvas dimensions. If I make the canvas twice as big, the circle is also twice as big, even though my radius is always 50px. If I make my canvas a square, the circle becomes elongated downwards, and vice versa if I stretch the canvas out sideways.
How do I make the canvas treat the numbers I give it as absolute values?

You should set canvas width and height as HTML attributes instead of CSS rules:
<canvas id="field" width="500" height="250"></canvas>
Quoting MDN:
The <canvas> element has only two attributes, width and height. These are both optional and can also be set using DOM properties. When no width and height attributes are specified, the canvas will initially be 300 pixels wide and 150 pixels high. The element can be sized arbitrarily by CSS, but during rendering the image is scaled to fit its layout size: if the CSS sizing doesn't respect the ratio of the initial canvas, it will appear distorted.
You can also set width and height dynamically using JavaScript:
const canvas = document.querySelector('canvas');
canvas.width = '500';
canvas.height = '250';

Related

Dimensions of a canvas

I found this pretty awesome blog post that explains how to resize a canvas to fit any screen http://www.html5rocks.com/en/tutorials/casestudies/gopherwoord-studios-resizing-html5-games/ I implemented it and it is sizing it flawlessly but I get this problem that my objects appear off screen when the canvas is too little.
For example for my game I define a world with dimensions width 480 and height 1024, if I put an element on x axis at about 400 but the display that is currently used is 360px wide then the canvas would have correct ratio of width to height 0.46875 but the object will not be visible.
To solve this problem I suppose I need to define a ratio between the absolute world dimensions and the screen dimensions and multiply by that when I'm calculating the object's position, however my question is - is there a way for the canvas to do that automatically?
Both the canvas itself and the canvas element have a width and height. If they're not the same, the content of the canvas is automatically scaled to fit the element. So yes, you could define your canvas to be the size you want, and define the element such that it scales. Naturally, though, everything scales, and if the aspect ratio is different, your shapes get distorted.
The width and height of the canvas within the canvas element are controlled by the width and height attributes of the canvas element. (They default to 300 and 150, respectively.)
The width and height of the canvas element are controlled by CSS; they default to the width and height of the canvas, but you can override that with CSS.
For example: Here's a canvas that's 300x300 (thanks to the CSS), within a canvas element that's only 300x150 (because those are the default width and height of the canvas element). You can see the effect of scaling when I draw a circle in it — it comes out oval instead:
// Get the canvas element
const canvas = document.getElementById("clock");
// Get the 2D rendering context for it
const context = canvas.getContext("2d");
// Show the canvas's dimensions (NOT the dimensions of the element)
document.getElementById("dimensions").innerHTML = canvas.width + "x" + canvas.height;
// Draw a circle -- it comes out oval because of scaling
const path = new Path2D();
path.arc(75, 75, 50, 0, Math.PI * 2, true);
context.fillStyle = context.strokeStyle = "blue";
context.fill(path);
canvas#clock {
width: 300px;
height: 300px;
background: #1E1E1E;
}
<canvas id="clock"></canvas>
<div id="dimensions"></div>
Here's an example where the canvas and its container are the same width and height, so there's no scaling; the code is identical, it's just I've added width="300" height="300" to the canvas HTML:
// Get the canvas element
const canvas = document.getElementById("clock");
// Get the 2D rendering context for it
const context = canvas.getContext("2d");
// Show the canvas's dimensions (NOT the dimensions of the element)
document.getElementById("dimensions").innerHTML = canvas.width + "x" + canvas.height;
// Draw a circle
const path = new Path2D();
path.arc(75, 75, 50, 0, Math.PI * 2, true);
context.fillStyle = context.strokeStyle = "blue";
context.fill(path);
canvas#clock {
/* We could comment out the width and height properties
since they'll default to the values from the attributes
*/
width: 300px;
height: 300px;
background: #1E1E1E;
}
<canvas id="clock" width="300" height="300"></canvas>
<div id="dimensions"></div>
The automatic scaling is handy provided you ensure the aspect ratio of the element and its canvas are the same. If you don't directly set the size of the canvas element, on most browsers it will default to the aspect ratio of the canvas thanks to the width and height attributes. From MDN's coverage of aspect-ratio:
Browsers have added an internal aspect-ratio property that applies to replaced elements and other related elements that accept width and height attributes. This appears in the browser's internal UA stylesheet.
In Firefox, the internal stylesheet rule looks like this:
img, input[type="image"], video, embed, iframe, marquee, object, table {
aspect-ratio: attr(width) / attr(height);
}

javascript: how does canvas.drawImage work

I'm trying to draw an image onto a canvas, like this:
var c=document.getElementById("myCanvas");
var ctx=c.getContext("2d");
var img=document.getElementById("scream");
ctx.drawImage(img,0,0,100,100,0,0,200,200);
The canvas is 200px by 200px and the image is much bigger (but styled at 200px by 200px too)
As you can see in the jsfiddle the canvas doesn't show the image (I was expecting a part of the image).
My understanding of drawImage (as is described here) goes like this:
"0,0,100,100" defines a rectangle on the image which is the part that is drawn onto the canvas. 0,0 defines the top/left corner and 100,100 are the widths of the sides.
This rectangle is drawn onto the canvas inside the rectangle defined by 0,0,200,200
Any suggestions what goes wrong here ?
You image is actually 585 x 585 so what you are doing is to clip a corner from it (which is blank) and draw it onto canvas which of course won't show anything.
Scaling the image with CSS doesn't actually change its size. It only scales it for display.
So what you need to do is to use the original size of the image as basis. If you simply want to scale it down to fit in canvas you can do:
ctx.drawImage(img, 0, 0, 200, 200);
The same goes for canvas. Don't scale canvas using CSS but set the width and height properties/attributes or else the canvas will default to 300x150 (which is then scaled by your css):
<canvas width=200 height=200 ...>
Modified fiddle
Set the width and height on the canvas and draw the image at the same dimensions. Updated your fiddle - http://jsfiddle.net/FQhGg/2/
var c=document.getElementById("myCanvas"),
ctx=c.getContext("2d"),
img=document.getElementById("scream"),
width = img.width,
height = img.height;
c.width = width;
c.height = height;
ctx.drawImage(img,0,0,width,height);

Canvas doesn't follow up resize of image?

I've got the following piece of code:
HTML
<img src="http://hollywoodteenonline.com/wp-content/uploads/2011/12/justin_bieber_someday_fragrance_dree_hemingway.jpg" id="imagem"/>
<canvas id="mycanvas">
CSS
#mycanvas{
height:200px;
width: 200px;
border: solid;
color: black;
}
#imagem{
height:200px;
width: 200px;
}
Javascript
var canvas = document.getElementById("mycanvas"),
ctx = canvas.getContext("2d"),
img = document.getElementById("imagem");
ctx.drawImage(img,0,0);
As you can see, the canvas doens't follow up the resize of the original image. As the code is written it "should" show all of the source but resized to de designated canvas. Is there any way to go around this?
Issues
First issue is that you don't set a size for your canvas element by using its properties. This is the only way you can affect the content of the canvas and not settings it means it will default to 300 x 150 pixel no matter what you set as CSS rule for it.
Second issue is that you are using CSS to the set size. This will affect the element itself, not the content of the canvas. Technically this isn't wrong in case you want to scale the canvas as an image, but it won't do anything for the canvas and the result is that you just scale the 300x150 pixels around.
The third issue, if the image is of different size than 300x150 it won't fit the canvas (too small or get cropped if too big).
Solutions
One solution is to set the size of the canvas to the size of the image and paint the image in:
canvas.width = img.width;
canvas.height = img.height;
ctx.drawImage(img, 0, 0);
Now you can resize the canvas (as an image using CSS) if you want.
Or you can scale the image using the size of the canvas (remove the CSS rule; and you still need to set a size for canvas):
ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
LIVE DEMO HERE
You also need to take care in how you invoke your script. For images to work with canvas they need to be loaded. As they load asynchronous you need to handle load events one way or another (ie. inside a window.onload in this case).

Relatively sizing HTML Canvas

The HTML5 <canvas> element does not accept relative sizes (percent) for its width and height properties.
What I'm trying to accomplish is to have my canvas sized relative to the window. This is what I've come up with so far, but I'm wondering if there is a better way that is:
Simpler
Does not require wrapping the <canvas> in a <div>.
Not dependent on jQuery (I use it to get the width/height of the parent div)
Ideally, doesn't redraw on browser resize (but I think that might be a requirement)
See below for the code, which draws a circle in the middle of the screen, 40% wide up to a maximum of 400px.
Live demo: http://jsbin.com/elosil/2
Code:
<!DOCTYPE html>
<html>
<head>
<title>Canvas of relative width</title>
<style>
body { margin: 0; padding: 0; background-color: #ccc; }
#relative { width: 40%; margin: 100px auto; height: 400px; border: solid 4px #999; background-color: White; }
</style>
<script language="javascript" type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.6.2/jquery.min.js"></script>
<script>
function draw() {
// draw a circle in the center of the canvas
var canvas = document.getElementById('canvas');
var relative = document.getElementById('relative');
canvas.width = $(relative).width();
canvas.height = $(relative).height();
var w = canvas.width;
var h = canvas.height;
var size = (w > h) ? h : w; // set the radius of the circle to be the lesser of the width or height;
var ctx = canvas.getContext('2d');
ctx.beginPath();
ctx.arc(w / 2, h / 2, size/2, 0, Math.PI * 2, false);
ctx.closePath();
ctx.fill();
}
$(function () {
$(window).resize(draw);
});
</script>
</head>
<body onload="draw()">
<div id="relative">
<canvas id="canvas"></canvas>
</div>
</body>
</html>
The canvas width and height attributes are separate from the same canvas's width and height styles. The width and height attributes are the size of the canvas's rendering surface, in pixels, whereas its styles choose a location in the document where the browser should draw the content from the rendering surface. It just so happens that the default value for the width and height styles, if they're not specified, is the rendering surface's width and height. So you're right about #1: there's no reason to wrap it in a div. You can set percentage values for all of the styles on your canvas element, just like any other element.
For #3, it's pretty easy (and cross-browser) to get the size of things with clientWidth and clientHeight, as long as you're not using padding on your canvas element.
I coded up the slightly simplified version here.
For #4, you're right about being out of luck. It's possible to check before setting width and height and leave the canvas alone if it doesn't need resizing, which would eliminate some of the redraws, but you can't get rid of all of them.
EDIT: Portman pointed out I messed up the centering style. Updated version here.
Like said by sethobrien a canvas element has TWO pairs width/height of attributes.
canvas.width / canvas.height are about the size in pixel of the buffer that will contains the result of drawing commands.
canvas.style.width / canvas.style.height are about the size used to show the canvas object in the browser window and they can be in any of the units supported by css.
You can indeed set canvas.width and canvas.height just once, do the drawing in the canvas, setting the style size parameters in percentage and then forget about redrawing the canvas content. Of course this means that the browser will just do the scaling itself like for a regular image loaded from the network so the visible result will show pixel scaling artifacts.
You need to redraw the canvas content after the resize of the canvas element only if you want pixel-perfect results.
Alright. Here is the technique that i ve used to implement the same.
Suppose you have the canvas height=400, for the window's height=480, and you want to change the height of it relatively if the window's height changes to 640.
canvas = document.getElementById("canvas");
canvas.height=window.innerHeight*400/480;
p.s: do not initialize the height of the canvas inside the html tag.
Make use of 'window.innerHeight' (which returns the height of the browser's window.. similarly 'window.innerWidth') any where you want to calculate the relative positions on the window.
Hope you got what you needed.

Rectangle on HTML5 Canvas is stretched

I am working on a website and I try to get rectangles on a HTML5 canvas with javascript. Normally this is no problem but now when I make a rectangle with the width and height of 10. It seems that it makes a rectangle with the width of 10 and the height of 20.
I'm making the Rectangle like this:
var canvas = $("#canvas");
var context = canvas.get(0).getContext("2d");
context.fillRect(0, 0, 10, 10);
The div canvas is set with a width of 100% but i tried to give it a fixed width and that didn't help either.
You need to set a width and height on your canvas element, otherwise this can be the result in some browsers.
<canvas id="canvas" width="400" height="300" />
And you can not set the canvas size with CSS, this will stretch the canvas.
The default size of the canvas is 300 x 150.
You are probably using these defaults, which will make the rectangle look not square....

Categories