Transformations: Translate, Rotate, and Scale

Understand the transformation matrix and how to use it to translate, rotate, and scale your canvas drawings.


Understanding the Transformation Matrix in Canvas

The HTML5 Canvas provides powerful tools for drawing graphics, and a core concept behind many canvas operations is the transformation matrix. This matrix allows you to manipulate the canvas context, applying translations, rotations, scaling, and even more complex transformations to the shapes and images you draw.

What is the Transformation Matrix?

The transformation matrix is a 3x3 matrix that defines how coordinates in the canvas are transformed before rendering. It's a mathematical representation of the current transformation state. The canvas context has a current transformation matrix that is applied to all subsequent drawing operations. Think of it as a filter that shapes go through before being drawn on the screen.

While you don't directly manipulate the entire 3x3 matrix using code (unless you want to), you interact with it implicitly through functions like translate(), rotate(), and scale(). These functions *modify* the underlying transformation matrix.

In the canvas context, the transformation matrix is usually represented using 6 values, and can be envisioned conceptually like this (although internally it's still a 3x3 matrix):

  [ a  c  e ]
[ b  d  f ]
[ 0  0  1 ]  

Where:

  • a: Horizontal scaling.
  • b: Vertical skewing.
  • c: Horizontal skewing.
  • d: Vertical scaling.
  • e: Horizontal translation (movement).
  • f: Vertical translation (movement).

Initially, the transformation matrix is the identity matrix:

  [ 1  0  0 ]
[ 0  1  0 ]
[ 0  0  1 ]  

This means that initially, no transformations are applied; coordinates are drawn as they are.

How translate(), rotate(), and scale() Modify the Matrix

These methods *multiply* the current transformation matrix with a new matrix that represents the specific transformation. Understanding this multiplication is crucial to predicting the final rendering outcome, especially when chaining transformations.

translate(x, y)

The translate() method shifts the origin of the canvas context. It effectively moves all subsequent drawing operations by x pixels horizontally and y pixels vertically. This alters the 'e' and 'f' components of the transformation matrix.

Behind the scenes, translate(x, y) multiplies the current matrix with the following translation matrix:

  [ 1  0  x ]
[ 0  1  y ]
[ 0  0  1 ]  

Example:

  const canvas = document.getElementById('myCanvas');
const ctx = canvas.getContext('2d');

ctx.translate(50, 50); // Move the origin 50px right, 50px down
ctx.fillRect(0, 0, 100, 100); // Draws a rectangle at the *translated* origin  

rotate(angle)

The rotate() method rotates the canvas context around the current origin (initially the top-left corner). The angle is specified in radians. This alters 'a', 'b', 'c', and 'd' components of the transformation matrix.

rotate(angle) multiplies the current matrix with the following rotation matrix:

  [ cos(angle)  -sin(angle)  0 ]
[ sin(angle)   cos(angle)  0 ]
[ 0           0           1 ]  

Example:

  const canvas = document.getElementById('myCanvas');
const ctx = canvas.getContext('2d');

ctx.rotate(Math.PI / 4); // Rotate 45 degrees (PI/4 radians) clockwise
ctx.fillRect(0, 0, 100, 100); // Draws a rotated rectangle  

scale(x, y)

The scale() method scales the canvas context. It multiplies the size of subsequent drawing operations by x horizontally and y vertically. This alters the 'a' and 'd' components of the transformation matrix.

scale(x, y) multiplies the current matrix with the following scaling matrix:

  [ x  0  0 ]
[ 0  y  0 ]
[ 0  0  1 ]  

Example:

  const canvas = document.getElementById('myCanvas');
const ctx = canvas.getContext('2d');

ctx.scale(2, 0.5); // Double the width, halve the height
ctx.fillRect(0, 0, 100, 100); // Draws a scaled rectangle  

Saving and Restoring the Transformation Matrix

The canvas provides save() and restore() methods to manage the transformation matrix stack.

  • ctx.save(): Saves the current state of the canvas context, including the transformation matrix.
  • ctx.restore(): Restores the most recently saved state, effectively undoing any transformations applied since the last save().

This is incredibly important for isolating transformations. Without save() and restore(), transformations would accumulate indefinitely, often leading to unexpected results.

Example:

  const canvas = document.getElementById('myCanvas');
const ctx = canvas.getContext('2d');

ctx.save(); // Save the initial state

ctx.translate(50, 50);
ctx.rotate(Math.PI / 4);
ctx.fillRect(0, 0, 50, 50); // Transformed rectangle

ctx.restore(); // Restore the initial state

ctx.fillRect(0, 0, 50, 50); // Rectangle at the original position  

The setTransform() Method

For direct control over the transformation matrix, you can use the setTransform(a, b, c, d, e, f) method. This method *completely replaces* the current transformation matrix with a new matrix defined by the provided parameters. This can be useful for resetting the transformation or for applying complex transformations directly.

Example:

  const canvas = document.getElementById('myCanvas');
const ctx = canvas.getContext('2d');

ctx.setTransform(1, 0, 0, 1, 100, 50); // Set a custom transformation
ctx.fillRect(0, 0, 50, 50); // Draws a rectangle with the specified transformation  

The transform() Method

The `transform(a, b, c, d, e, f)` method multiplies the current transformation matrix with the matrix described by the arguments. This allows incremental modifications to the transformation matrix using raw values. It's functionally similar to a series of `scale()`, `rotate()`, `translate()` calls, but performed in a single operation.

Chaining Transformations

Canvas transformations are cumulative. The order in which you apply them matters. For example, rotating *after* translating will result in a different outcome than translating *after* rotating.save() and restore() judiciously to control the scope of your transformations and avoid unwanted side effects.

Conclusion

Understanding the transformation matrix is key to mastering canvas graphics. By understanding how translate(), rotate(), and scale() modify the matrix, and by using save() and restore() effectively, you can create complex and dynamic visual effects in your canvas applications.