Grid drawn using a <canvas> element looking stretched - javascript

I'm trying to draw a grid on a <canvas> element with the ultimate goal of making a Go board.
For some reason the grid is looking stretched, with the lines being thicker than 1 pixel and the spacing being completely wrong. It doesn't even start in the (10,10) position..
It would be great if someone could take a look at tell me what I'm doing wrong.
http://jsfiddle.net/h2yJn/

I've found the problem. I was setting the dimensions of the <canvas> using CSS, when you actually have to set the width and height attributes. This was causing it to be stretched/skewed.
var canvas = $('<canvas/>').attr({width: cw, height: ch}).appendTo('body');
http://jsfiddle.net/h2yJn/66/

Please try it outside jsfiddle, maybe jsfiddle is applying some linear transformation.
Also please make sure that you add 0.5 everywhere to both x and y coordinates. Alternatively, you can apply translate(0.5, 0.5) to shift all coordinates by half a pixel.

Related

Apply zoom in center of the canvas

I need to apply zoom to the javascript canvas which I have badly accomplished by using the following line of code:
ctx.scale(2,2) //doubles everything's size
Instead of zooming, its obviously doubling the size of the canvas and all of its elements. I'd be okay with this if I got it working like the image below shows:
Any ideas on how I could accomplish what is depicted in the picture above? I'm not using any external libraries hence making this so difficult. Thanks.
You can translate the context by half the canvas size using ctx.translate()
EDIT :
var zoomfactor = 2; //set whatever you want as zoom factor
ctx.transform(zoomfactor,0,0,zoomfactor,-(zoomfactor-1)*canvas.width/2,-(zoomfactor-1)*canvas.height/2)

Getting CSS left and top when div is rotated

I'm trying to get the style.left and style.top of a rectangular div, after it has been rotated using style.transform=rotate(90deg).
I understand how the div is being rotated, with it being rotated around a 'transform point'. And I also understand that a div could be rotated by 45 degrees, so giving the new top/left of that would be awkward (In effect giving the bounding box left/top).
But back to the original question, rotating the rectangular div by 90 degrees, is there a way to get the 'new' left/top?
The reason I need this, is for a project im working on to upload images, allow the user to zoom, rotate etc, but currently having to do it with PHP to keep all the dimensions correct for the final image (Which is obviously bad, because I'm having to keep loading a new image once PHP has done the rotating/zooming etc)
I've also made a little jsfiddle showing that the top/left position doesn't change when it is rotated
Okay, thanks to the comment left above, I managed to throw together an answer.
Basically using:
newleft = parseInt(div.style.top) + Math.cos(90) * parseInt(div.style.height);
newtop = parseInt(div.style.left) + Math.sin(90) * parseInt(div.style.height);
after the div had been rotated.
I've updated my jsfiddle aswell, because the one in the comment above uses jQuery, but this way uses only javascript.

Scaling a canvas to full screen really decreases the resolution

I'm trying to make a full screen game to help me learn JS. I made a canvas item and streched it to the size of the screen using CSS. Then I draw a circle. The issue I have is that the circle looks terrible!
_draw.arc(20, 20, 10, 0, 2 * Math.PI);
Here is a jsfiddle example. http://jsfiddle.net/H5dHD/152/
I've tried using different scale factors (so _draw.scale) but it dosent seem to matter...
What am I doing wrong?
P.S. I know the coordinates are off. I didn't include that code for the example.
The problem is that you resized the canvas using the CSS-style, and do not change the actual width and height. When you use CSS styling to change the size, the canvas will be stretched, but the internal drawing resolution stays the same. The result is that the canvas blurs.
To change the internal resolution, change the width and height attributes of the canvas HTML element itself.
document.getElementById('iDraw').height = screen.availHeight;
document.getElementById('iDraw').width = screen.availWidth;
Here is your updated fiddle:
http://jsfiddle.net/H5dHD/154/

Why do images lose quality after the context has been rotated?

I'm making a top-down shooter game that relies on the avatar always being rotated pointing to the mouse cursor. I achieve rotation like this:
//Rendering.
context.save(); //Save the context state, we're about to change it a lot.
context.translate(position[0] + picture.width/2, position[1] + picture.height/2); //Translate the context to the center of the image.
context.rotate(phi); //Rotate the context by the object's phi.
context.drawImage(picture.image, -picture.width/2, -picture.height/2); //Draw the image at the appropriate position (center of the image = [0, 0]).
context.restore(); //Get the state back.
When the phi is zero, the image is rendered in its normal quality, with sharp edges and detectable pixels. But, when I set the phi to a nonzero value (actually, when it's not 0, Pi/2, Pi, Pi+Pi/2 or 2Pi), the image looses it's sharpness and the individual pixels can't be seen anymore, because they are blurred out.
Here's a screenshot (sorry about the general bad quality of the screenshot, but I think that the difference is more than noticeable):
This is, well, a bit unacceptable. I can't have the images always blurred out! Why is this happening and can I solve it?
You could try
context.imageSmoothingEnabled = false;
See docs:
context.imageSmoothingEnabled [ = value ]
Returns whether pattern fills and the drawImage() method will attempt to smooth images if they have to rescale them (as opposed to just rendering the images with "big pixels").
Can be set, to change whether images are smoothed (true) or not (false).
If you want a true pixel-art retro style effect, you'd need to manually create rotated sprite images for several angles, look up the appropriate sprite for the current value of phi, and draw it without rotation. This obviously requires a fair amount of art work!
IF you are rotating images around their center point, make sure the image itself has an even number of pixels. Once you end up on odd coordinates the image data needs to be interpolated for the target canvas. Apple has some nice documentation on translating and rotating the canvas.
So for any image, as suggested above use rounding to snap to full pixels.
context.translate(Math.floor(img.width/2), Math.floor(img.height/2));
This way every source pixel of your image will always be drawn exactly into a pixel inside the canvas and blurring does not occur. This however is only true for multiples of 90 degrees.
It seems that all browsers do, to some extend, antialiasing in image drawing so you will probably have to provide rotated images as sprites.
According to this Chromium bug report you might be lucky there if they haven't fixed it yet. Read through and you'll learn that Ian Hickson likely opposed making antialiased image drawing optional.
(picture.width/2, picture.height/2) point won't always work.
(Math.floor(picture.width/2) + 0.5, Math.floor(picture.height/2) + 0.5) should help.
Well, actually it is something you cannot get around
If you rotate an image by a multiple of 90 degrees, your library should smart enough so that no interpolation is applied.
But as soon as you rotate an image by an angle different from a multiple of 90 degrees, you need to interpolate. As a consequence, you get that smoothing. If you are interested in the theory, you may look for a book on computer graphics or image processing.
For the concrete case of image rotation you may have a look at this paper,
http://bigwww.epfl.ch/publications/unser9502.html

Drawing on the canvas with a "pencil"

I made a script that draws a series of lines on a canvas that makes it look like a sketch. There are two issues with the script. One, why is the y value twice as much as it should be? And two, why is the line several pixels wide and faded out?
I've tried it in both Google Chrome and Firefox and I get the same incorrect results. I realize that I can divide the y value by two to fix the first problem but that part of my question is why do I need to do that. I shouldn't have to.
I think you have two issues:
You need to be more careful in how you calculate the offset of where to draw. I have some code below that demonstrates how to handle this properly.
You aren't setting the width and height on the <canvas> element itself, which means it will scale your lines in funny ways depending how what you've set in your css.
An Example
I built a simple collaborative drawing app using <canvas> and socket.io that lets you draw to the screen like a pencil. You can check it out here:
http://xjamundx.no.de/
The source is also on github if that might help:
https://github.com/xjamundx/CollabPaintJS/ (main repo)
https://github.com/xjamundx/CollabPaintJS/blob/master/public/collabpaint.js (canvas drawing code)
In particular I do something like this to figure out where to draw things:
x = e.clientX + window.scrollX
y = e.clientY + window.scrollY
x -= $game.offsetLeft
y -= $game.offsetTop
Give a width and a height to your canvas; always !
http://jsfiddle.net/mz6hK/7/
fixed

Categories

Resources