HTML5 Canvas: Transformations
Jakob Jenkov |
You can apply transformations to whatever is drawn on an HTML5 canvas. Here is a list of the transformations you can apply:
- Translation (moving what is drawn)
- Rotation
- Scaling
I will cover each of these transformations in this text.
Transformation Matrix
It is possible to set a transformation matrix on the 2D Context. This matrix is multiplied onto everything drawn on the canvas. For the examples used in this tutorial I will just set it to the "identity" matrix, which is the matrix that when multiplied onto an x,y coordinate set, results in x,y. In other words, no transformation takes place.
Here is how you set the transformation matrix to the identity matrix:
context.setTransform(1, 0, 0, 1, 0, 0);
Translation
It is possible to apply translation to everything that is drawn on a canvas. Translation means relocation of what is drawn. Here is how you set translation in code:
var x = 100; var y = 50; context.translate(x, y);
This example moves all shapes drawn on the canvas by 100 on the x-axis and 50 on the y-axis.
Note: Translation is only applied to shapes drawn after the translate()
function is called.
Shapes drawn before that function call are unaffected.
Here is another example. Two rectangles are drawn at the same coordinates, but one is drawn before
the translate()
function is called, and one is drawn after.
context.fillStyle = "#ff0000"; context.fillRect(10,10, 100, 100); context.translate(50, 25); context.fillStyle = "#0000ff"; context.fillRect(10,10, 100, 100);
Here is what the result looks like when drawn on a canvas:
Rotation
You can apply automatic rotation to any shape drawn on an HTML5 canvas. This is done using the
rotate()
function on the 2D Context. Here is a simple example:
context.rotate(radians);
The angle to rotate is passed as parameter to the rotate()
function. This value
has to be in radians, not degrees.
All shapes drawn after a rotation is set will rotated around the point 0,0 on the canvas. This is the upper left corner of the canvas.
As with translation, rotation is only applied to all shapes drawn after the rotate()
function is called.
Here is an example that draws the same rectangle before and after rotation is set:
context.fillStyle = "#ff0000"; context.fillRect(10,10, 100, 100); context.rotate( (Math.PI / 180) * 25); //rotate 25 degrees. context.fillStyle = "#0000ff"; context.fillRect(10,10, 100, 100);
Here is how the rectangles look when drawn on a canvas:
Rotating Around a Different Point
As mentioned, all shapes are rotated around the upper left corner of the canvas (0,0). But, what if you wanted the shape to be rotated around a different point? For instance, rotating the shape around its own center?
To rotate a shape around its own center, you must first translate the canvas to the center of the shape, then rotate the canvas, then translate the canvas back to 0,0, and then draw the shape.
Here is a code example that rotates the blue rectangle around its own center:
var x = 10; var y = 10; var width = 100; var height = 100 var cx = x + 0.5 * width; // x of shape center var cy = y + 0.5 * height; // y of shape center context.fillStyle = "#ff0000"; context.fillRect(x, y, width, height); //draw normal shape context.translate(cx, cy); //translate to center of shape context.rotate( (Math.PI / 180) * 25); //rotate 25 degrees. context.translate(-cx, -cy); //translate center back to 0,0 context.fillStyle = "#0000ff"; context.fillRect(x, y, width, height);
And here is how it looks when drawn on a canvas:
This example first translates (moves) the center of the canvas to the center of the square (cx, cy).
Then it rotates the canvas by 25 degrees. Then it translates the canvas back to 0,0 again. The canvas
is now rotated 25 degrees around cx,cy. Everything you draw will appear as rotated around cx,cy.
Finally the rectangle is drawn as if nothing had happened, but it will now appear rotated 25 degrees around
cx, cy. This was achieved using only transformation calls. The coordinates of the rectangle are not changed.
Notice how the two calls to context.fillRect()
which draw the red and blue rectangles are identical.
It is the transformation calls in between that make them appear in different locations and rotations.
Scaling
It is possible to apply automatic scaling to all shapes drawn on an HTML5 canvas.
When scaling you scale all cordinates on the x-axis and y-axis by some factors.
You set these factors using the scale()
function, like this:
var scaleX = 2; var scaleY = 2; context.scale(scaleX, scaleY);
This example scales all coordinates on both the x-axis and y-axis by a factor of 2.
Like with translate()
and rotate()
, the scaling is only applied
to shapes drawn after the scale()
call.
Here is another code example that draws a red and a blue rectangle, where scaling is applied to the blue rectangle:
var x = 10; var y = 10; var width = 100; var height = 100 context.fillStyle = "#ff0000"; context.fillRect(x, y, width, height); context.scale(2,2); context.fillStyle = "#0000ff"; context.fillRect(x, y, width, height);
Here is the resulting grapichs on the canvas:
Notice how the blue rectangle is twice as big as the red.
Notice too, that the distance from the upper left corner of the blue rectangle to the upper left corner of the canvas (0,0) is twice as big too. All coordinates have been scaled by a factor of two, also the coordinates of the upper left corner of the rectangle. If you want to avoid the shape being moved when scaling it, you will have to combine the scaling with a translation.
Scaling as Zoom
You can use the scaling feature to achieve a zoom function. The canvas below contains 4 rectangles. Below the canvas is an input field that allows you to change the zoom level (scaling level).
Zoom Level:
If you see a text field with the zoom level value, type in zoom levels between 1 and 10, and tab out of the text field to see the result. If you see a slider, just move the slider.
Combining Translation, Rotation and Scaling
It is of course possible to combine all three transformations on the same canvas. But, just like when
combining rotation and translation, the sequence in which you make the function calls to the 2D Context
matters. The result will look different if you call scale()
before you call translate()
etc. You may have to play a bit around with the sequence of the function calls to get it right.
Tweet | |
Jakob Jenkov |