Canvas: Transforms and Transparency

on in Canvas
Last modified on

This is a Canvas programming introductory series:

Table of Contents

Transformations

Transform is a drawing technique that allows you to move the coordinate system of the Canvas. Let’s say we need to draw the same square in three places on the Canvas. This can be done by calling the fillRect() method three times, each time with different coordinates (modernizing the example from the previous article where the Canvas and context variables were set):

// Draw a 30x30 pixel square in three different places
context.rect(0, 0, 30, 30);
context.rect(50, 50, 30, 30);
context.rect(100, 100, 30, 30);
context.stroke();

The same square in three different places can be drawn using the same coordinates, but shifting the coordinate system like this:

// Draw a square with the top left corner at (0, 0)
context.rect(0, 0, 30, 30);

// Shift the coordinate system down and to the right by 50 pixels
context.translate(50, 50);
context.rect(0, 0, 30, 30);

// Shift the coordinate system down and to the right another 50 pixels
// Transformations are summed, so point (0, 0) is actually at (100, 100)
context.translate(50, 50);
context.rect(0, 0, 30, 30);
context.stroke();

Both versions of the code give the same result: they draw three squares in three different places on the Canvas.

At first glance, it may seem that the transformation makes the slightly difficult task of drawing even more difficult. For normal drawing tasks, this may be the case, but in some special situations, transformation can work wonders.

Let’s say, for example, that we have a function that draws a sequence of complex shapes that, when added together, form the image of a bird. We need to animate this image to make it look like the bird is flying across the screen. To accomplish this task without resorting to a coordinate system transformation, it would be necessary to correct each coordinate of the bird at each displacement. Applying a transformation allows you to use the same coordinates in the bird drawing code and simply shift the coordinate system for each step of the animation.

There are several types of transformations. In the previous example, we used translation to move the starting point of the coordinate system, i.e., point (0, 0) in the upper left corner of the Canvas.

In addition to translation, there are transformations such as scaling (scale transform), which allows you to draw in an enlarged or reduced scale; rotation (rotate transform), which allows you to rotate the coordinate system; and matrix transform, which allows you to stretch and bend the coordinate system just about anywhere, as long as you understand the complex matrix maths behind the required visual effects.

Transformations have the effect of accumulation (addition). In the following example, the origin (i.e., point (0, 0)) is moved to point (100, 100) by translation, after which the coordinate system is rotated several times by a certain angle around this point. Each rotation draws a new square, creating an interesting shape:

// Move point to (0,0). This is an important step because rotation is performed around this point
context.translate(100, 100);

// Draw 9 squares
let copies = 10;

for (let i = 1; i < copies; i++) {
    // Rotate the coordinate system before drawing the next square.
    // Full turn angle is 2 * Math.PI. This code rotates the system coordinates for each new square is only a fraction of this corner, performing a full rotation when drawing the last square,
    context.rotate(2 * Math.PI * 1 / (copies - 1));

    // Draw a square
    context.rect(0, 0, 60, 60);
}

context.strokeStyle = '#109bfc';
context.stroke();

With the context’s save() method, you can save the current state of the coordinate system and restore it later with the restored() method. Save the state of the context before applying the transformation so that the coordinate system can be restored in case of an error. And when drawing complex shapes that require a long sequence of steps, it’s wise to save the state as often as possible. This list of saved states acts like a history of visited web pages. Each time the restore() method is called, the system reverts to its immediately previous state.

This article is part of the JavaScript Canvas series where I post experiments, JavaScript generative art, visualizations, Canvas animations, code snippets and how-to’s.

JavaScript Canvas Logo

Transparency

So far we have dealt with pure colors. But Canvas also allows you to apply partial transparency to overlay one shape on top of another. There are two ways to use transparency. The former allows you to set a color by assigning values ​​to the fillStyle or strokeStyle property and using the rgba() function, rather than the more common rgb() function.

The rgba() function takes three parameters – the red, green, and blue color values ​​(from 0 to 255) and an optional alpha channel value (0 to 1) that specifies the transparency of the color. An alpha value of 1 is fully opaque, and a value of 0 is fully transparent. By setting the alpha value between these two limits (for example, 0.5), we get a partially transparent color, allowing you to see any content below it.

The location of the content below or above is determined by the order of the drawing operations. For example, if you first draw a circle and then a square in the same place, the square will be superimposed on the circle.

The following listing shows the code for drawing a circle and a triangle. Both shapes are filled with the same color, only the triangle has an alpha value of 0.5, making it 50% transparent:

// Set fill and outline colors
context.fillStyle = 'rgb(100, 150, 185)';
context.lineWidth = 10;
context.strokeStyle = 'red';

// Draw a circle
context.arc(110, 120, 100, 0, 2 * Math.PI);
context.fill();
context.stroke();

// Don't forget to call the beginPath() method before drawing a new shape
context.beginPath();

// Draw a triangle
context.moveTo(215, 50);
context.lineTo(15, 250);
context.lineTo(315, 250);
context.closePath();

// Fill the triangle with a transparent color
context.fillStyle = 'rgba(100, 150, 185, 0.5)';

context.fill();
context.stroke();

Another way to use transparency is to set the value of the globalAlpha context property, as shown in the following code:

context.globalAlpha = 0.5;

// Now this color is automatically assigned an alpha channel value equal to 0.5
context.fillStyle = 'rgb(100, 150, 185)';

After that, all graphics will have the same alpha channel value and the same transparency level until the globalAlpha value is set to a new value. This includes both outline colors (the strokeStyle property) and fill colors (the fillStyle property).

Which of these approaches is better? If a single transparent color is required, use the rgba() function. And if you want to draw multiple shapes with different colors but the same level of transparency, use the globalAlpha property. The globalAlpha property is also useful when you want to draw translucent images on the Canvas.

Related posts