HTML5 Canvas Set Top/Left - javascript

I can easily set the canvas.width and canvas.height properties. I cannot find the right JavaScript syntax to set the offset of the canvas using top and left. I have tried setting these properties on the canvas directly and on the bounding rectangle.
I would like to be able to set the top and left offset so that I can reposition the canvas when a user clicks on a point.
If canvas.width = 600 works just fine; why doesn't canvas.top = 80 work? I am confused. Thx for your help.

In order to set the top position of the canvas, you need to wrap the canvas inside of another div with absolute positioning. Then set the position of the canvas to be relative to its wrapping div. Finally, you can set the style of the canvas.
Make sure you provide units e.g. px, em, rem, %, etc...
var panel = document.getElementById('panel');
panel.width = 600;
panel.height = 200;
panel.style.top = '80px'; // Must specify unit.
.container {
position: absolute;
background: #0FF;
}
#panel {
position: relative;
background: #F00;
}
<div class="container">
<canvas id="panel"></canvas>
</div>
The docs state:
The effect of top depends on how the element is positioned (i.e., the value of the position property):
When position is set to absolute or fixed, the top property specifies the distance between the element's top edge and the top edge of its containing block. (Containing block needs to have property position: relative)
When position is set to relative, the top property specifies the distance the element's top edge is moved below its normal position.
When position is set to sticky, the top property behaves like its position is relative when the element is inside the viewport, and like its position is fixed when it is outside.
When position is set to static, the top property has no effect.

Please try canvas.style.top = "80px";

Make sure you have set position to relative or absolute. Have Look at the example below.
<!DOCTYPE html>
<html>
<body>
<canvas id="myCanvas" width="200" height="100" style="border:1px solid #d3d3d3;top: 80px; position: absolute;">
Your browser does not support the HTML5 canvas tag.</canvas>
<script>
var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d");
ctx.moveTo(0, 0);
ctx.lineTo(200, 100);
ctx.stroke();
</script>
</body>
</html>

In short, width and height are html attributes and can therefore be set using element.width and element.height. However, top is a css proberty, not an html attribute - therefore it can only be set using element.style.top.
Also, as already pointed out in the other answers, the css position proberty has to be either fixed or absolute in order for top and left to work.

Related

Why canvas objects are re-scaled when using CSS to style width and dimensions? [duplicate]

I have 2 canvases, one uses HTML attributes width and height to size it, the other uses CSS:
<canvas id="compteur1" width="300" height="300" onmousedown="compteurClick(this.id);"></canvas>
<canvas id="compteur2" style="width: 300px; height: 300px;" onmousedown="compteurClick(this.id);"></canvas>
Compteur1 displays like it should, but not compteur2. The content is drawn using JavaScript on a 300x300 canvas.
Why is there a display difference?
It seems that the width and height attributes determine the width or height of the canvas’s coordinate system, whereas the CSS properties just determine the size of the box in which it will be shown.
This is explained in the HTML specification:
The canvas element has two attributes to control the size of the element’s bitmap: width and height. These attributes, when specified, must have values that are valid non-negative integers. The rules for parsing non-negative integers must be used to obtain their numeric values. If an attribute is missing, or if parsing its value returns an error, then the default value must be used instead. The width attribute defaults to 300, and the height attribute defaults to 150.
To set the width and height on a canvas, you may use:
canvasObject.setAttribute('width', '150');
canvasObject.setAttribute('height', '300');
For <canvas> elements, the CSS rules for width and height set the actual size of the canvas element that will be drawn to the page. On the other hand, the HTML attributes of width and height set the size of the coordinate system or 'grid' that the canvas API will use.
For example, consider this (jsfiddle):
var ctx = document.getElementById('canvas1').getContext('2d');
ctx.fillStyle = "red";
ctx.fillRect(10, 10, 30, 30);
var ctx2 = document.getElementById('canvas2').getContext('2d');
ctx2.fillStyle = "red";
ctx2.fillRect(10, 10, 30, 30);
canvas {
border: 1px solid black;
}
<canvas id="canvas1" style="width: 50px; height: 100px;" height="50" width="100"></canvas>
<canvas id="canvas2" style="width: 100px; height: 100px;" height="50" width="100"></canvas>
Both have had the same thing drawn on them relative to the internal coordinates of the canvas element. But in the second canvas, the red rectangle will be twice as wide because the canvas as a whole is being stretched across a bigger area by the CSS rules.
Note: If the CSS rules for width and/or height aren't specified then the browser will use the HTML attributes to size the element such that 1 unit of these values equals 1px on the page. If these attributes aren't specified then they will default to a width of 300 and a height of 150.
The canvas will be stretched if you set the width and height in your CSS. If you want to dynamically manipulate the dimension of the canvas you have to use JavaScript like so:
canvas = document.getElementById('canv');
canvas.setAttribute('width', '438');
canvas.setAttribute('height', '462');
The browser uses the css width and height, but the canvas element scales based on the canvas width and height. In javascript, read the css width and height and set the canvas width and height to that.
var myCanvas = $('#TheMainCanvas');
myCanvas[0].width = myCanvas.width();
myCanvas[0].height = myCanvas.height();
Shannimal correction
var el = $('#mycanvas');
el.attr('width', parseInt(el.css('width')))
el.attr('height', parseInt(el.css('height')))
Canvas renders image by buffer, so when you specify the width and height HTML attributes the buffer size and length changes, but when you use CSS, the buffer's size is unchanged. Making the image stretched.
Using HTML sizing.
Size of canvas is changed -> buffer size is changed -> rendered
Using CSS sizing
Size of canvas is changed -> rendered
Since the buffer length is kept unchanged, when the context renders the image,
the image is displayed in resized canvas (but rendered in unchanged buffer).
CSS sets the width and height of the canvas element so it affects the coordinate space leaving everything drawn skewed
Here's my way on how to set the width and height with Vanilla JavaScript
canvas.width = numberForWidth
canvas.height = numberForHeight
I believe CSS has much better machinery for specifying the size of the canvas and CSS must decide styling, not JavaScript or HTML. Having said that, setting width and height in HTML is important for working around the issue with canvas.
CSS has !important rule that allows to override other styling rules for the property, including those in HTML. Usually, its usage is frowned upon but here the use is a legitimate hack.
In Rust module for WebAssembly you can do the following:
fn update_buffer(canvas: &HtmlCanvasElement) {
canvas.set_width(canvas.client_width() as u32);
canvas.set_height(canvas.client_height() as u32);
}
//..
#[wasm_bindgen(start)]
pub fn start() -> Result<(), JsValue> {
// ...
let canvas: Rc<_> = document
.query_selector("canvas")
.unwrap()
.unwrap()
.dyn_into::<HtmlCanvasElement>()
.unwrap()
.into();
update_buffer(&canvas);
// ...
// create resizing handler for window
{
let on_resize = Closure::<dyn FnMut(_)>::new(move |_event: Event| {
let canvas = canvas.clone();
// ...
update_buffer(&canvas);
// ...
window.add_event_listener_with_callback("resize", on_resize.as_ref().unchecked_ref())?;
on_resize.forget();
}
}
There we update the canvas buffer once the WASM module is loaded and then whenever the window is resized. We do it by manually specifying width and height of canvas as values of clientWidth and clientHeight. Maybe there are better ways to update the buffer but I believe this solution is better than those suggested by #SamB, #CoderNaveed, #Anthony Gedeon, #Bluerain, #Ben Jackson, #Manolo, #XaviGuardia, #Russel Harkins, and #fermar because
The element is styled by CSS, not HTML.
Unlike elem.style.width & elem.style.height trick used by #Manolo or its JQuery equivalent used by #XaviGuardia, it will work for canvas whose size is specified by usage as flex or grid item.
Unlike the solution by #Russel Harkings, this also handles resizing. Though I like his answer because it is really clean and easy.
WASM is the future! Haha :D
P.S. there's a ton of .unwrap() because Rust explicitly handles possible failures.
P.P.S.
{
let on_resize = Closure::<dyn FnMut(_)>::new(move |_event: Event| {
let canvas = canvas.clone();
// ...
update_buffer(&canvas);
// ...
window.add_event_listener_with_callback("resize", on_resize.as_ref().unchecked_ref())?;
on_resize.forget();
}
can be done much cleaner with better libraries. E.g.
add_resize_handler(&window, move |e: ResizeEvent| {
let canvas = canvas.clone();
// ...
update_buffer(&canvas);
})
If you want a dynamic behaviour based on, e.g. CSS media queries, don't use canvas width and height attributes. Use CSS rules and then, before getting the canvas rendering context, assign to width and height attributes the CSS width and height styles:
var elem = document.getElementById("mycanvas");
elem.width = elem.style.width;
elem.height = elem.style.height;
var ctx1 = elem.getContext("2d");
...

In Javascript, why does getBoundingClientRect() sometimes return floating point values?

I'm trying to work with an HTML5 canvas element. One thing i'm trying to do is set up a mousemove event to track the mouse on the canvas for drawing and other purposes. I have not been able to find a definitive answer on how to get the exact coordinates of the pixel in the canvas the mouse is over. I found a tutorial on the web that had this html (I added the background-color to the canvas to make it obvious on the rendered page):
<!DOCTYPE HTML>
<html>
<head>
<style>
body {
margin: 0px;
padding: 0px;
}
#myCanvas {
background-color: cornflowerblue;
}
</style>
</head>
<body>
<canvas id="myCanvas" width="578" height="200"></canvas>
<script>
function writeMessage(canvas, message) {
var context = canvas.getContext('2d');
context.clearRect(0, 0, canvas.width, canvas.height);
context.font = '18pt Calibri';
context.fillStyle = 'black';
context.fillText(message, 10, 25);
}
function getMousePos(canvas, evt) {
var rect = canvas.getBoundingClientRect();
return {
x: evt.clientX - rect.left,
y: evt.clientY - rect.top
};
}
var canvas = document.getElementById('myCanvas');
var context = canvas.getContext('2d');
canvas.addEventListener('mousemove', function(evt) {
var mousePos = getMousePos(canvas, evt);
var message = 'Mouse position: ' + mousePos.x + ',' + mousePos.y;
writeMessage(canvas, message);
}, false);
</script>
</body>
</html>
When the page is loaded, you see the canvas as a blueish rectangle in the upper left corner of the page. Move the mouse over it and the text in the canvas changes to show the mouse position as two ints, one each for X and Y.
I then modified the canvas style by adding a top and left margin, and left the rest of the page unchanged.
#myCanvas {
background-color: cornflowerblue;
margin-left: 30px;
margin-top: 30px;
}
When I rendered the page now, the blue rectangle of the canvas was offset from the top and left of the page as I expected. But passing my mouse over the canvas now had the X and Y mouse coordinates that were being displayed in the canvas coming up as floats with many decimal places. Tracing through the code a bit, it seems that getBoundingClientRect() is returning a rect where the values top and left are floats.
I assume I could do something like truncate or round the values being returned by getBoundingClientRect(), but that feels like the wrong way to go about it to me.
Am i somehow using getBoundingClientRect() incorrectly, or is it expected that it should return float values?
And is there a clear cut way to get the exact X and Y coordinates of the mouse over the canvas when listening for various mouse events?
tldr; you did zoom/unzoom in your browser.
The problem
margin-left: 30px;
In this jsfiddle thats mimics your problem it does not works as you said on my computer when navigator zoom is set to 100% (normal). But if you zoom inside your browser you will notice such behavior.
margin-left: 11%;
Instead if you use % margin like in this jsfiddle you will notice it does returns floating mouse position wether zoom is on or not.
The answer
The thing is mouse position is computed as it appears on the screen : it may only have entire position coordinates since it is pixel based.
However getBoundingClientRect returns what browser computes to be "Bounding Client Rect of the element" after applying margins, zoom and others modifiers but before telling the GPU to render it. In short it returns the real position of the element which is later approximated by the GPU to be rendered within a matrix of pixels. If you use pixel margins/sizes/paddings/etc then elements positions remains integer based, but if you zoom or use em/% positioning values then it may result floating positions.
The solutions
round bounds
assume it is indeed a floating position and it's just the GPU that needs to round it in order to make it fit on the screen
Edit : The forgotten solutions

Javascript, CSS, jQuery - my canvas moves to where it shouldnt be

Problem is in title. It used to do it on the odd occasion. Then i added the jQuery rotate.js plugin to spin my heading 360 degrees and now it is constantly placed to the right of where it should be. It may be interesting to note that i got the offset of the canvas with
var rect = gameController.canvas.getBoundingClientRect();
var offset = {
x: rect.left,
y: rect.top
};
And that still has coordinates of 0,0 in the top left corner of where it should be ( which is correct and how it should be when the canvas has not moved).
#canvas {
z-index: 1;
position: absolute;
margin-top: 52px;
margin-left: 44px;
background-image:url(images/background.jpg);
}
If the position is absolute, it is removed from the document and nothing will make it move. Strange.
getBoundingClientRect() returns the sum of the canvas and its css border-boxes.
Maybe check the canvas with getClientRects() to see exactly where it is.
I'm not sure why it moved, i probably have a css error somewhere..but i had my canvas as position absolute, relative to a div that it was inside. I placed my canvas right at the top of the body tag so it was absolute, relative to the whole page, then changed the placement of it with top and bottom to get it correct. Now nothing will move it. It works fine with the rotate.js plugin. Thanks for having a look at my question.

position canvas on top of Scrollbar

I am simulation a projectile motion path. I've one div with css overflow property and a curve drawn using canvas. I want the motion path to appear at top of everything. But curve gets cut at the position where the scroll bar is. If i change the canvas z-index to maximum or something similar to make canvas to appear at top then the scrollbar doesnt work... here is a jsfiddle demo of my problem JSFIDDLE Demo
Following is my javascript code:
var canvas = document.getElementById('canvasTron');
var context = canvas.getContext('2d');
context.beginPath();
context.moveTo(100, 150);
context.lineTo(350, 50);
context.stroke();
Is there any way to do that ???
If you have no situation, but to go with what you have mentioned, here is the solution.
Addition in CSS:
#canvasTron{position:absolute; clip: rect(48px, 351px, 151px, 99px);}
With canvas, you cannot get the scroll to work effectively. As it will occupy the area of that div below it and will not make the scroll work.
The provided solution has to be purely applied in case of WORST CASE SCENARIO, where you are left with no option and have to do it under the existing circumstances. Otherwise, it is NOT POSSIBLE.
Instead of using canvas, You can do this :
<div id="canvasTron"></div>
canvasTron
{
position:absolute;
width:0px;
height:200px;
border:2px solid black;
transform:rotate(50deg);
-webkit-transform:rotate(50deg);
-ms-transform:rotate(50deg);
-o-transform:rotate(50deg);
-moz-transform:rotate(50deg);
left:180px;
}
Remains light weight and scrolling is also working fine.

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.

Categories