Today we’ll look at the basics of transformations on a canvas. The way transformations work is you call one of several functions that changes the current transformation matrix on the context of the canvas. This does not change anything that has already been drawn. It only effects future drawing actions.
Canvas uses a basic 2D transformation matrix exactly the same as Flash does. There are two functions that allow you to change the matrix: transform() and setTransform(). These both take six params: a, b, c, d, tx, ty. The tx and ty are easiest to understand as they merely translate the coordinate system on the x, y axes. The a, b, c, d control rotation and scaling, shearing, or any combination of the above, depending on what values you set. I am not going to go into a lengthy discussion on transformation matrices, but I will say that if you want to use it to scale something, a and d control scale x and scale y, and if you want to rotate something by a particular angle A (in radians of course), use cos A, -sin A, sin A, cos A for a, b, c, d.
The difference between transform() and setTransform() is that the former transforms the current matrix, and the latter, setTransform, kind of resets the matrix back to original and then applies the params you set.
But if matrix transformations are not your cup of tea, you’ll be glad to know that there are a few more basic functions that we mere humans can comprehend easily. These are translate(), scale(), and rotate(). The first two take two params, one for each of the x, y axes, and rotate takes a single parameter of an angle in radians. These all operate like transform, in that they “add” to the current transform, transforming it even more.
Here’s a rather boring example, which at least demonstrates all of these in action:
[php lang=”JavaScript”]context.transform(1, 0, 0, 1, 100, 0);
drawRect(“#000000”);
context.translate(0, 55);
drawRect(“#ff0000”);
context.translate(100, 0);
context.rotate(Math.PI / 4);
drawRect(“00ff00”);
context.translate(100, 0);
context.scale(2, 2);
drawRect(“#0000ff”);
context.setTransform(1, 0, 0, 1, 0, 0);
drawRect(“#ffff00”);
function drawRect(color) {
context.strokeStyle = color;
context.strokeRect(0.5, 0.5, 50, 50);
}[/php]
This gives us something like this:
Again, this assumes you’ve set up a canvas and context. We’ve defined a function drawRect at the end. This takes a color string, and draws a 50 x 50 rectangle starting at 0.5, 0.5. This, by the way is another trick of getting smooth straight lines in Canvas. Put your coordinates on .5 pixels and they will generally appear more uniform. Putting them on whole values winds up antialiasing them in some cases, exactly the opposite of what Flash does. Sigh.
Anyway, each time we call this function, it will draw the same rectangle, theoretically in the same location. However, we’ll mess with the transformation matrix each time and see what happens.
First we transform(1, 0, 0, 1, 100, 0). Remember a and d control scale, so the scale is 1.0 or no change in scale. The last two are tx and ty, so we are moving the matrix 100 pixels on the x axis. Then we draw a black rectangle.
Then we translate down 55 pixels and draw a red rectangle.
Then translate 100 to the right, rotate 45 degrees and draw a green one.
Translate 100 pixels more and scale by 2 on each axis and draw blue. Note that we are still rotated 45 degrees. And the 100 pixels we moved on the x axis is also rotated, so from a global viewpoint we moved down and to the right. We never un-rotated, so this makes sense.
Finally, we use setTransform(1, 0, 0, 1, 0, 0) which is the identity matrix. This sets everything back to the way it started out. You can always call this to get back to square one. We draw a yellow rect here to prove it.
Finally, we can do something technical and artsy by combining a bunch of this at once.
[php lang=”JavaScript”]$(function () {
var canvas, context, i, angle = 0;
canvas = $(“#canvas”)[0];
context = canvas.getContext(“2d”);
context.lineWidth = 0.25;
for(i = 0; i < 500; i += 1) {
context.setTransform(Math.cos(angle), -Math.sin(angle), Math.sin(angle), Math.cos(angle), i + 50, 100);
context.strokeRect(-25, -25, 50, 50);
angle += 0.05;
}
});[/php]
this gives you something like this:
Click to see full size.
Here we are just drawing 500 squares in a for loop. Each one is translated and rotated using setTransform, and then scaled up a bit separately using scale. There are multiple ways of achieving this same effect with the different functions, just as there are multiple ways you can roll a pair of dice to come up with, say, an 11. [OK, Todd Anderson has just pointed out that there are only two ways to roll an 11 with a pair of dice. So let’s change that to an 8. Anyway, you get the idea.] Play around with this one a bit, changing parameters, maybe adding some more interesting colors, etc.
And with that, I leave you for today. See you tomorrow with another installment, assuming I can keep dreaming up things to write about.