JS Tip of the Day: Canvas for Bitmaps

Canvas for Bitmaps
Level: Beginner

The HTML canvas is an image-like element in HTML that lets you dynamically render a bitmap image to the screen. Canvas bitmaps support both 2D and 3D graphics (3D through WebGL), though for our purposes here, we’re only going to cover the basics of the 2D capabilities through the standard 2D canvas API.

To get started with canvas, the first thing you’ll need is a canvas element. Canvas elements can be added in HTML or created in JavaScript dynamically with document.createElement(). In either case, the canvas should be given an explicit width and height. This is separate from the width and height dimensions you might set with CSS. These width and height attributes/properties of a canvas determine how many pixels are used in the bitmap it renders.

<canvas id="canvas" width="300" height="200"></canvas>

Or in JavaScript:

let canvas = document.createElement('canvas');
canvas.width = 300;
canvas.height = 200;
// (don't forget to add to DOM)

In order to start drawing into the canvas, you would use a canvas context. It’s the context that houses all the methods and properties used for drawing, not the canvas element. For the standard 2D APIs, you would get a context using:

let context = canvas.getContext('2d');

From the context, which here is a CanvasRenderingContext2D object, you have a number of options for drawing including (but not necessarily limited to):

  • drawing lines and shapes
  • drawing text
  • drawing images

How content is drawn into the canvas depends on the current state of the context. When drawing a shape, for example, you would define the state of the canvas to have a certain fill collor and a certain path to draw, then follow that with a draw command to do the actual drawing.

// define state
context.fillStyle = 'red';
context.moveTo(10,100);
context.lineTo(55,10);
context.lineTo(100,100);

// perform draw
context.fill();

Text is treated like a shape where the font and text define the shapes being drawn. When drawn, text is drawn from the baseline (bottom of the text) so you’ll need to be sure to offset the text by its height or font size if you are positioning it by it’s top-left.

context.font = '20px serif';
context.fillText('Hello, canvas!', 0, 20); // 20 px height offset

When drawing images, one thing you’ll need to be sure to do is make sure the image is loaded before you attempt to draw it in the canvas. If you don’t, the image won’t get rendered because the canvas doesn’t wait for images to load on its own.

let img = new Image();
img.onload = event => {
    // image loaded and can be drawn
    context.drawImage(img, 0 , 0);
}
img.src = 'http://placekitten.com/g/300/200';
// here, image not yet loaded and may not draw
// (though it can work if the image is cached)

Canvases are rendered a lot like HTML where the content rendered first is rendered below any content that follows it. This is important to consider expecially with images given that you need to wait for an image to load. If you rendered other content before an image loads, the image, when it finally did load, could draw over top of, and cover it. Anything that needs to be drawn on top of the image would also need to wait for that image to load and get drawn after.

The following is a more complete example combining different kinds of drawing on a canvas. Because everything is drawn on top of the image, all drawing commands are handled within the image’s onload event.

<canvas id="canvas" width="300" height="200"></canvas>

<script>
let canvas = document.getElementById('canvas');
let context = canvas.getContext('2d');

let img = new Image();
img.onload = event => {

    // kitten image
    context.drawImage(img, 0 , 0);

    // inspiring text
    let text = 'deep in thought';
    context.font = '20px sans-serif';
    context.fillStyle = 'lightblue';
    context.strokeStyle = 'black';
    context.lineWidth = 6;
    context.strokeText(text, 10, 25);
    context.fillText(text, 10, 25);

    // whisker lines
    context.strokeStyle = 'orange';
    context.lineWidth = 3;
    context.moveTo(170, 100);
    context.lineTo(130, 90);
    context.moveTo(170, 100);
    context.lineTo(130, 100);
    context.moveTo(170, 100);
    context.lineTo(130, 110);
    context.moveTo(180, 100);
    context.lineTo(220, 90);
    context.moveTo(180, 100);
    context.lineTo(220, 100);
    context.moveTo(180, 100);
    context.lineTo(220, 110);
    context.stroke();
}
img.src = 'http://placekitten.com/g/300/200';
</script>

See the CanvasRenderingContext2D documentation on MDN (link below) for more information about the different drawing commands available.

More info: