Typescript HTML5 Canvas Basics

This is going to be a tutorial on drawing lines and shapes to the HTML5 Canvas using TypeScript. I'm not going to cover drawing images to the canvas in this tutorial. That will come later in a tutorial on using texture atlases (sprite sheets). For now, I'm interested in showing you the basics of creating a game loop, and using your HTML5 Canvas element to draw shapes for use in a simple game.

Visual Studio Community

I'm using Visual Studio Community for my TypeScript Game Development. If you don't have an environment set up for TypeScript of JavaScript development already, I would highly recommend giving Visual Studio Community a try. It's free and it's a great tool. Microsoft created TypeScript so they really know what's going on from that perspective. Creating a new TypeScript project in Visual Studio will create an index.html file and an app.ts file. We'll be basically gutting the app.ts file to make our project. It starts out with a class called "Greeter" which you might want to take a quick look at if you're not familiar with TypeScript, but it's basically the equivalent of the standard "Hello World" program you usually run when starting a new programming language.

index.html File

Before we can draw to the canvas in our TypeScript app, we need to make sure we have a canvas in our HTML file. If we don't have a canvas with the appropriate id, we won't have anything to draw on. Make sure you've added a canvas tag with an id of "cnvs" a width of 1280 and a height of 720 to your index.html file.

<!DOCTYPE html>
<html lang="en">
<head>
<script src="app.js"></script>
</head>
<body>
<canvas id="cnvs" height="720" width="1280"></canvas>
</body>
</html>

Game Startup and Game Loop

We need to start out with a Canvas and a Context to work with in TypeScript.

var canvas: HTMLCanvasElement;
var ctx: CanvasRenderingContext2D;

The variable "canvas" is the canvas element that must exist in the index.html file (or whatever html file you're usign). We don't generally work with the canvas element once we have it. For the most part we use it when the game initializes to get a "context" object which we will be calling "ctx". We will then use the ctx to draw to our canvas element. We're also going to add in two functions, one to be called when the web page is finished loading, and the other is called a "Game Loop". All games have a game loop that drives the screen updates for the action within the game.

The code will look something like this:

function gameLoop() {
   requestAnimationFrame(gameLoop);
}

window.onload = () => {
   canvas = <HTMLCanvasElement>document.getElementById('cnvs');
   ctx = canvas.getContext("2d");
   gameLoop();
}

The window.onload function is going to grab the canvas, which will be in our HTML file with an id of "cnvs". Because getElementById returns an HTMLElement object and we want an HTMLCanvasElement, we need to add "<HTMLCanvasElement>" to the front of the call to "getElementById" in order to change the type to the more specific HTMLCanvasElement. Then from the canvas object we are going to get a 2d context and assign it to the ctx variable. After that, we will call the game loop.

Right now the gameLoop function is only doing one thing, and that is calling the requestAnimationFrame on itself. All this call does is make sure that gameLoop is called every time the game draws a new frame to the canvas. That's not a lot, but it's the skeleton of what needs to be done in any 2d game you make in TypeScript using the HTML5 canvas.

Clearing the HTML5 Canvas

Every time you draw a frame in your game loop, you want to get rid of everything you had drawn in the previous frame. There are several different ways to do this, but we'll be doing it by drawing a large black rectangle over everthing. This will have to be the first thing you do in your game loop (after requesting the next animation frame), so that everything you draw will be done on a clean canvas.

Here is what the new game loop will look like:

function gameLoop() {
   requestAnimationFrame(gameLoop);
   ctx.fillStyle = "black";
   ctx.fillRect(0, 0, 1280, 720);
}

Setting ctx.fillStyle = "black" sets the fill for the next thing you draw to be the color black. The next line ctx.fillRect draws a rectangle starting at point 0,0 (the top left corner) and extending 1280 pixels across and 720 pixels down the canvas. Since we are working with a canvas that is 1280x720 pixels in dimension, this rectangle will fill the entire canvas.

Draw a Circle on the Canvas

At this point if you ran your game, you'd have a web page with a large black rectangle on it (not very impressive). Let's take things just a bit further by drawing a red circle. The context doesn't have a method on it that is "fillCircle" in the way you have a fillRect that you can use to draw a rectangle to the canvas. Instead, we will need to use the "arc" method and tell it that we want to do an arc that is 2 PI radians in order to draw a full circle.

This is what the new version of the game loop will look like:

function gameLoop() {
   requestAnimationFrame(gameLoop);
   ctx.fillStyle = "black";
   ctx.fillRect(0, 0, 1280, 720);
   ctx.beginPath();
   ctx.strokeStyle = "red";
   ctx.lineWidth = 5;
   ctx.arc(400, 400, 100, 0, 2 * Math.PI);
   ctx.stroke();
}

The circle we are going to be drawing is a path, so the first thing we need to do is call "beginPath" on our context to tell the context that we are drawing a path. The next thing we're doing is telling the context what color we are drawing the stroke of that path in (red in this case). After that we are setting the "lineWidth" of the path we are drawing to be 5 pixels wide.

The call after that is to ctx.arc, which takes several parameters telling the context where it should draw the arc. The first two parameters are the x and y coordinates, which I have set to 400, 400. Because of this, the center of our circle will appear 400 pixels to the right of the top left corner of the screen, and 400 pixels down from the top right corner of the screen. The parameter after that is the radius of our arc in pixels. We have set that value to 100 pixels. The next two parameters are the starting and ending points in radians along our path. We are going from 0 to 2 PI radians, which is basically 0 to 360 degrees around the circle. If you're not familiar with radians (instead of degrees) for measuring angles the idea of radians is how far you would have to walk along a unit circle (circle with a radius of 1) to get to the angle. So if you walk PI distance along the unit cirlce, you are half way around the circle or about 180 degrees. Most games use radians instead of degrees. Basically the conversion is PI radians = 180 degrees so it's not that difficult if you're more comfortable with degrees, but you will end up using radians, so try to get used to it.

The last thing you will be doing is calling ctx.stroke. That is what will actually draw the stroke to the canvas. Everthing up to that point has just been telling the context what it will be drawing. The stroke call actually draws it. If you leave out the call to ctx.stroke, nothing is going to happen.

 

Part 2 - Classes and Interfaces