Canvas transform not behaving as expected - javascript

Suppose I have a 400x200 canvas.
I want to work in a coordinate system where (0, 0) is in the exact middle of the canvas and positive y means up and positive x means right.
So, I set the transform as follows:
var ctx = document.getElementById("canvas").getContext("2d");
ctx.setTransform(1, 0, 0, -1, 200, 100);
ctx.fillRect(-20, -20, 40, 40);
<canvas id="canvas" style="width: 400px; height: 200px"></canvas>
So when I fill the rectangle as in the snippet above, I would expect to see a square centered in the middle of the canvas element. However, when running the above snippet (in latest Chrome) the square is demonstrably not centered. Why is this? Am I misunderstanding something about the transform matrix? If so, how can I achieve my goal?

The size for the canvas element isn't set properly which means the canvas defaults to 150 pixels in height, which is then stretched out using CSS. This gives the illusion of the object being offset.
To properly set canvas size use its attributes instead of CSS:
<canvas id="canvas" width=400 height=200></canvas>
Also be aware of that the Y-axis is now flipped upside-down so any text and images are drawn upside-down as well. These will need local transformation to be drawn correctly.

The problem seems to be that the dimensions in style are ignored and default to the standard dimensions 300×150. So set them properly with
<canvas id="canvas" width="400" height="200"></canvas>
Independent of that, it can be a good idea to not rely on hard-coded dimensions, especially if you are going to use a local coordinate system anyway.
My snippet modifications:
Use the actual canvas dimensions and scale so that the local coordinates still at least contain the square (-100,100)×(-100,100).
Add a coordinate cross before setting the transformation, that shows that even when the canvas is "wrong", the square is at the coordinate origin.
After the transformation, add a circle at positive y position to show that "up" is really up.
var cnv = document.getElementById("canvas");
var w = cnv.width, h = cnv.height;
var ctx = cnv.getContext("2d");
ctx.beginPath();
ctx.moveTo(0,h/2); ctx.lineTo(w,h/2);
ctx.moveTo(w/2,0); ctx.lineTo(w/2,h);
ctx.closePath(); ctx.stroke();
var scale = Math.min(w,h)/200.0;
ctx.setTransform(scale, 0, 0, -scale, w/2, h/2);
ctx.moveTo(0,50); ctx.arc(0,50,10,0,2*Math.PI); ctx.stroke();
ctx.fillRect(-20, -20, 40, 40);
<canvas id="canvas" width="450" height="250"></canvas>

Related

Draw rectangles in a cartesian coordinate system

I want to draw a following chart in JavaScript. Basically, the goal is to draw some rectangles in a cartesian coordinate system. Each rectangle can be represented by 4 points, whose coordinates are given.
Does anyone know how to draw it? Is there any library to do so? Ideally, i would expect some example code that I could adjust a little bit, rather than drawing everything from scratch.
You have two main options:
SVG - vector graphics that are pretty similar to HTML (you have elements with attributes etc). you can also use CSS for styling SVG.
canvas - this is basically a bitmap on which you can draw with JS.
(HTML) - of caurse you could also use simple HTML + CSS.
Here are some tutorials about those two:
http://svgtutorial.com/manipulating-svg-with-javascript/
https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API/Tutorial/Drawing_shapes
There are bunch of 3rd party libraries to help with this like:
https://d3js.org/
http://dmitrybaranovskiy.github.io/raphael/
http://fabricjs.com/
http://paperjs.org/
Here is simple example with canvas:
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
ctx.strokeRect(0, 0, 400, 200);
ctx.strokeRect(0, 200 - 20, 20, 20);
ctx.strokeRect(30, 200 - 70, 20, 70);
<canvas id="canvas" width="400" height="400"></canvas>

HTML5 canvas line width less that 1 pixel

Is there any way to draw a rectangle whose composing lines have width thinner than 1 pixel?
This code works perfectly, as expected:
// context is a HTML5 canvas 2D context
context.lineWidth = 1;
context.strokeStyle = "black";
context.rect(0, 0, 20, 20);
context.stroke();
It draws a nice rectangle.
But, if I try to draw a rectangle with thinner lines:
// See line width
context.lineWidth = 0.5;
context.strokeStyle = "black";
context.rect(0, 0, 20, 20);
context.stroke();
It still draws a rectangle whose borders have 1 pixel width.
I'm dealing with the canvas object here, and not CSS, where you have ways to "simulate" this.
Although it doesn't make much sense, you can acheive that with using a regular 1-pixel line with a 50% scaled canvas (but again it's a 1-pixel rendition, read below). See this snippet:
var canvas = document.querySelector('canvas');
var context = canvas.getContext('2d');
function scale() {
context.scale(0.5, 0.5);
draw();
}
function draw() {
context.beginPath();
context.moveTo(100, 150);
context.lineTo(450, 50);
context.stroke();
}
draw()
<canvas width="400" height="150"></canvas>
<button onclick="scale()">Scale down</button>
But again, I wonder how you expect the half-pixel line to look on your screen, antialiasing?
Right :) I suppose I was thinking on some way of drawing thinner lines, like, for example, when you use CSS styles. I've looked around and I don't think I can use alternate units.
There's no way to make something that's smaller than the smallest component unit, in our case a pixel. You can mimic the thinner look by transparency, or opacity, or even some sort of antialiasing (which again relies on transparency or the colouring of the neighbouring pixels), but not by trying to go below one pixel.
I agree, there is a sub-pixel rendering mode in browsers, for example, when you work with percentages, but in the end, the browser just renders full pixels with some of the modification I've described above.
And you know if you could render unit smaller than pixels, you'd technically have infinite resolutions on displays. I wish it was possible. :)

Canvas Rotation+Scaling

I need to rotate an image in a canvas and simultaneously resize it to make sure that the corners of the canvas does not remain empty. The solution should be something similar to what do aviary in the "Crop, Resize & Rotate" example.
I think the solution is to combine the functions of rotation and resize of canvas, but I can not find any concrete solution to the problem and I didn't find any exaustive example on the web.
Any advice would be helpful
Thanks
I have not had a look at the example you have given but I gave a detailed answer on the problem of fitting a rotated image onto a canvas so that there is no blank spaces.
There is some math involved (go figure) but it is just basic trigonometry and I provided an explanation of how its all done. There are two solutions, one that finds the min scale that will fit the canvas for any rotation and the other the will find the min scale to fit the canvas for a particular rotation.
It is assumed that the image is centered, if not there is an easy way to adapt the code provided by supplying an abstract canvas size so that the rotated image is centered on that abstract canvas.
So if your center of image is at x = 100, y = 100 and the canvas is canvasWidth = 300, canvasHeight = 300 then just use an abstract size of absCanvasWidth = (canvasWidth - x) * 2; and then the image at x = absCanvasWidth/2 do the same for height. That will fit the rotated, translated image to fill the canvas.
The answer with the code can be found for the question After rotate, draw Image at correct position
Here's some code that might help you. This shows how to rotate an image 90 degrees clockwise, then scale it to fit in the original canvas space.
window.onload = function() {
var img = document.getElementById("myImage");
var rotatedCanvas = document.getElementById("myRotatedCanvas");
var width = rotatedCanvas.offsetWidth;
var height = rotatedCanvas.offsetHeight;
// draw the original image
var ctx = document.getElementById("myCanvas").getContext("2d");
ctx.drawImage(img, 0, 0);
// draw the rotated image
ctx = rotatedCanvas.getContext("2d");
ctx.rotate(90 * Math.PI / 180);
// the last two parameters scale the image
ctx.drawImage(img, 0, -width, height, width);
};
img {
display: none;
}
canvas {
border: 1px black solid;
}
<img src="http://imgur.com/UeMOrix.gif" id="myImage"/>
<canvas id="myCanvas" width="400" height="150"></canvas>
<br>
<canvas id="myRotatedCanvas" width="400" height="150"></canvas>

lineTo on HTML 5 canvas not working properly

I'm trying to learn canvas.
I was trying to draw some lines using moveTo() and lineTo() on canvas.
The co-ordinates I give and the point rendered over canvas are not matching
I have taken a canvas of size 500px X 500px
for (0,0) it is coming fine.
for all other points it is not matching the co-ordinates
for (300, 150) it is painting at (500,500).
I'm not getting why this is happening because if I set my canvas size t0 300px X 150px it is painting correctly
here is my js
var context = document.getElementById("myCanvas").getContext("2d");
context.moveTo(0, 0);
context.lineTo(100, 100);
context.lineTo(100, 100);
context.lineTo(200, 100);
context.lineTo(300, 150);
context.stroke();
jsfiddle here
Can any one please tell me where I'm wrong
set the height and width of the canvas element directly:
<canvas id="myCanvas" width="500" height="500"> </canvas>
fiddle: http://jsfiddle.net/nLUEX/2/

Javascript Canvas bad rendered

I just experimented a bit with Javascript and HTML5 canvas and when I saw it in my browser (chrome) I realised that it's not very pretty rendered. After that I saw it in the Internet Explorer and there it looks even more crawful. I made a little Example: http://ios.xomz.de/
I just declared the canvas object in the html code
<canvas id="mycanvas" width="1000px" height="600px"/>
and rendered into it with
var canvas = document.getElementById("mycanvas");
var context = canvas.getContext("2d");
context.beginPath();
context.rect(200, 200, 600, 200);
context.lineWidth = 5;
context.strokeStyle = 'black';
context.stroke();
context.font = "40pt arial";
context.fillStyle = "black";
context.fillText("Hello World!", 220, 380);
for example.
Can you explain why the rendering isn't good ?
Do not use "px", also I'd recommend not using a self-closing tag:
<canvas id="mycanvas" width="1000" height="600"></canvas>
http://jsfiddle.net/c2KeD/
This problem is related to the way objects are drawn on a float based grid (especially vertical and horizontal lines and thus rects).
See there for an explanation and a schema : http://canop.org/blog/?p=220
Depending on the size of your objects, you need to use integer or mid-integer coordinates and sizes for your shapes, the goal being to fill complete pixels in both dimensions.
For example :
use a mid-integer for a thin line (one pixel width)
use an integer coordinate for a 2 pixels wide line
(and extend the logic for rects)
In your case, with a line width of 5, you have sharper rects by using this :
context.rect(200.5, 200.5, 600, 200);
Demonstration here : http://jsfiddle.net/dystroy/TyNBB/

Categories