Spicy Yoghurt | Last updated: 16 May 2019 | HTML5 game tutorial

Draw images and sprite animations

Draw your own images on the canvas and learn how to stretch and scale them. Use clipping on sprites to create sprite animations. By the end of this tutorial you can draw your own images and animations on the canvas.


In this tutorial you'll continue to build your game, but first there will be a general explanation about how to draw images to the canvas and how to display sprite animations.
This tutorial requires the game project you've created in the previous tutorials. If you skipped those steps, you can download the game project up to now here.

Get a reference to an HTML image element

Before you can start to actually draw an image on the canvas, you'll need a reference to the image you want to draw somehow. There are multiple ways to do this, but in this first example you're going to do it the easy way and reference an image element on a web page.

An image element is a HTML container for images. You can create one by using the <img> tag on a web page. You can specify a source for the image by using the src attribute. The browser will load the source and display the image inside the element when loaded. It's what you would normally use to display an image on a web page. You can use the image displayed in the image element, reference it and draw it on a canvas.

Here's an example implementation of an <img> tag. It's an image form the potion bottle drawing tutorial. The HTML element of the image will be referenced in this tutorial later on, to demonstrate the image drawing operations.

An example image for drawing images on the canvas

To create your own image element, start by adding an <img> tag to your page. Set the src and id attributes.

                    
                        <img id="myImage" src="/img/my-image.png">
                    
                    
The id is just a tag to help you identify the element, it doesn't really do anything on its own. You can reference the id in your JavaScript code. Use getElementById() and pass the id as argument to get the element holding the image.

            var img = document.getElementById("myImage");
        
The img variable now holds a valid reference to an image element. You can use this variable to draw the image on the canvas.

Draw an image on the canvas

Drawing images on the canvas is pretty straight forward. You can simply use the drawImage() function on the 2dContext.

Setting up a canvas and getting a reference to the context is covered in one of the previous tutorials of this series. You can read all about it at the setting-up a canvas tutorial
The function needs a reference to an image and a position. The method header looks like this: context.drawImage(img,x,y); You've just stored your reference, so all you need is an x- and y position. You can use it like this:

            context.drawImage(img, 10, 30);
        
The img variable, filled with the reference to your image element, will be drawn on the canvas. In this example it will be drawn on (10, 30), like this:

Why isn't the image visible?

Did you implement every step until now, but you're not seeing an image getting drawn? Or the image is drawn, but only sometimes? Good chance its because you're referencing an image element on the page that isn't fully loaded yet.

It takes some time for the image element to have loaded its source. If you reference the element while the source isn't loaded, there is nothing to draw yet. The code on the page is executed before all resources had a chance to fully load.

You can fix this by using the window.onLoad event to wait for the page and all its resources to load completely, before referencing and drawing an image element to the canvas. This way you can be sure the image element and the image itself are both present on the page, ready for you to reference for drawing.

You can implement the onLoad event by setting a function to serve as event handler. When the event fires, the function will be triggered. Here's an example implementation:

            window.onload = function (){
                //The page is completely loaded now
                //You can reference the image element
                var img = document.getElementById("myImage");
                context.drawImage(img, 10, 30);
            }
        

Load an image from an url

You've just learned how to reference images inside image elements, present on the page. For a game you can't expect this to work the same. You would be able to see all images and sprites used in the game on the webpage.

You probably want to load images via an url, without them ever having been on a web page. You can load an image from an url with JavaScript by defining a new Image object and setting its src property. Use the onload event on the image to start drawing when the image source has loaded.

Here's a simple implementation that draws yourimage.png on (10, 10) on the canvas as soon as it's loaded:


            var img = new Image();
            img.onload = function() {
               context.drawImage(img, 10, 10);
            };
            img.src = 'https://www.linktoyourimage.com/yourimage.png';

        

Resize an image

With the drawImage() method you can easily stretch and scale images. Just add width and height to the existing arguments of the function call, like so: context.drawImage(img,x,y,width,height);

Here's an example that stretches the image from before to a size of 100x200.

            context.drawImage(img, 10, 30, 100, 200);
        
As you can see, the ratio of the image is changed. The image looks deformed now.

Scale while preserving the image ratio

To resize the image but keep the aspect ratio of the image the same, you can use the width and height properties of the image.

In the next example the image is made twice as small as the original:

            context.drawImage(img, 10, 30, img.width / 2, img.height / 2);
        
Here's the result, with the original image displayed next to it. The image is scaled, but no longer deformed. The aspect ratio is maintained and is the same as that of the original image. Just keep in mind you need to scale width and height by the same amount if you want to preserve the aspect ratio of an image.

Draw only a part of an image

Sometimes you only want to draw a small part of the original image. Leaving out a part of the source image is called clipping. The drawImage() function can be extended to support clipping. All you need to do is add a few extra arguments.

First you define the source rectangle of the image, then you define the destination rectangle. The method header looks like this context.drawImage(img,sx,sy,swidth,sheight,x,y,width,height);. Here's an example implementation:

            context.drawImage(img, 100, 0, 200, 50, 10, 30, 200, 50);
        
In the example a fragment of the image is picked from (100, 0), with a width of 200 and height of 50. The fragment is drawn to (10, 30), with the same width and height as the source. The result will look like this: A small part of the source image is drawn. The rest of the image is clipped.

Clip and resize simultaneously

If you would select a source rectangle for the drawImage() function that is smaller or larger than the destination, the image is scaled or stretched.

In the next example the destination rectangle is twice the size of the source rectangle. The size of the clipped source is 200x50, while the size of the destination is 400x100. The image is clipped and scaled at the same time.

            context.drawImage(img, 100, 0, 200, 50, 10, 30, 400, 100);
        
As you can see the image is now clipped and drawn twice the size of the previous example.

Use clipping with sprites

You can use the technique of clipping to display images from sprites. Sprites are collections of images, all merged together on the same source image. It is a technique, most commonly used in games, to store animations or a set of assets in a single image file.

Here's an example sprite image, containing 10 frames of an animation:
Example sprite animation of potion bottles It's one image, but it consists of many smaller images merged together. Every sub image can be seen as a frame. In the example, you see the potion bottle change color a little bit more, every frame. Multiple animation frames can be packed together in one single image this way.

Let's say you want to display the third image from the example sprite. You'll need to know it's coordinates first. With sprite animations, it is most common to use the same width an height for every frame inside the sprite. So, to display a single image simply keep track of the column and row and multiply it by the width or height of the frame.

Here's an example implementation of how to display the ninth frame from inside the sprite (that's the fourth column, second row):

            //Define the size of a frame
            var frameWidth = 50;
            var frameHeight = 61;

            //Rows and columns start from 0
            var row = 1;
            var column = 3;

            context.drawImage(sprite, column*frameWidth, row*frameHeight, frameWidth, frameHeight, 10, 30, frameWidth, frameHeight);
        
It will look like this:

Create a sprite animation

In the previous example you've drawn only one image to the canvas. But if you want to create an animation from the sprite, you'll need to display a lot more frames, at a high interval.

Basically, you keep changing the coordinates of the source rectangle of the sprite image. Draw new images fast enough, and you'll have an animation.

Here's an example implementation using an interval to draw a new frame 10 times per second. It's a quick example using setInterval() to skip the hassle of having to explain the time stamp again, but of course you would normally merge this code in your requestAnimationFrame() game loop and work with the time that has passed between each frame to decide when to pick a new sprite image. Didn't set-up a game loop yet? Learn how to create a proper game loop here.

            //Define the number of columns and rows in the sprite
            var numColumns = 5;
            var numRows = 2;

            //Define the size of a frame
            var frameWidth = sprite.width / numColumns;;
            var frameHeight = sprite.height / numRows;;

            //Rows and columns start from 0
            var row = 0;
            var column = 0;

            setInterval(function(){

                //Pick a new frame
                column++;
                if (column > numColumns - 1){
                    column = 0;
                    row++;
                    if (row > numRows - 1){
                        row = 0;
                        column = 0;
                    }
                }

                //Clear and draw
                context.clearRect(0, 0, canvas.width, canvas.height);
                context.drawImage(sprite, column*frameWidth, row*frameHeight, frameWidth, frameHeight, 10, 30, frameWidth, frameHeight);

            //Wait for next step in the loop
            }, 100);
        
The result is a simple animation of the potion bottle changing color. Of course, an animation with actual motion would be even prettier to watch, but this is just a quick example to get the general idea. Multiple frames of a sprite are drawn fast after each other to create the illusion of an animation.

Leave a comment