Skip to content

Instantly share code, notes, and snippets.

@qrush
Last active December 28, 2021 14:48
Show Gist options
  • Save qrush/4273152 to your computer and use it in GitHub Desktop.
Save qrush/4273152 to your computer and use it in GitHub Desktop.
Canvas tutorial for a high school programming class. Examples and many images based directly off Mozilla's canvas tutorial. Exercises are heavily inspired by it but very different and meant to be done side-by-side. Fork the exercise on Codepen, complete it, and move on! https://developer.mozilla.org/en-US/docs/Canvas_tutorial

Canvas

Canvas is an awesome HTML tag that we can use through Javascript to make charts, graphs, games, and almost anything you can dream of.

What can Canvas do?

Get started

Crack open http://codepen.io and sign up for an account.

Get started fast:

In HTML, drop in:

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

In JS, you'll need:

var canvas = document.getElementById('canvas1');
var ctx = canvas.getContext('2d');

Now you've got the "context" object in JavaScript, which we can use to paint on the canvas. Awesome!

Try out drawing some rectangles:

ctx.fillStyle = "rgb(200,200,0)";
ctx.fillRect(10, 10, 55, 50);
 
ctx.fillStyle = "rgba(0, 0, 200, 0.5)";
ctx.fillRect(30, 30, 55, 50);

Full example for reference if needed.

Shapes

Let's learn how to make more shapes with Canvas.

The Grid

Coordinate plane starts at the top left. Think of this like a grid from math class. The x, y axis is the same...just you're rotated a bit on your head. You can go in the negative direction too on either axis.

The Pen

We can tell the canvas how to draw stuff. The way I like to think about this is like we have a giant, stupid pen. Giant since you can really only do one thing at a time with it...and stupid since it does only what you tell it to do. :) Try thinking of it this way, and don't be shy to draw down points on paper.

Paths

Paths can be used to draw shapes with more (or less!) sides than a rectangle. This is where the "pen" comes into place. beginPath drops the pen down, and then we can tell it to moveTo around the canvas. This will continue drawing a shape to another point with lineTo.

Here's a triangle:

// Start drawing (upper left point)
ctx.beginPath();
ctx.moveTo(0, 0);

// Draw the upper right point
ctx.lineTo(150, 0);

// Draw lower point
ctx.lineTo(75, 150);

// Fill in the shape!
ctx.fill();

Arcs

Canvas is great for drawing curves. Straight lines are boring!

// ctx.arc(x, y, radius, startAngle, endAngle, antiClockwise);
ctx.arc(75, 75, 50, 0, Math.PI * 2, true);

The angles are in radians, so we can use Math.PI to help us out here. The antiClockwise parameter tells canvas which direction to draw the circle in. This can be helpful for drawing half-circles.

Text

Dropping text into your canvas is easy. Maybe you'll need a score box, show the user's name, or label a graph?

ctx.font = 'bold 40px impact';
ctx.fillStyle = 'orange';
ctx.fillText("It's Wednesday!", 25, 40);

ctx.font = 'italic 40px impact';
ctx.strokeStyle = 'pink';
ctx.strokeText("It's Wednesday!", 25, 85);

Exercises

Transforms

Transforms let us break the rules of how canvas normally works when drawing to save us time, and make painting things a lot more fun.

Translate

// ctx.translate(xPixels, yPixels);
ctx.translate(100, 100);

Translate "moves" where Canvas thinks (0, 0) is permanently. Put another way, it moves the grid on the x-axis by xPixels, and on the y-axis by yPixels.

Why do this? If we need to draw a lot of things, it's easier to do this around the origin and translate the shape later.

Save/restore

ctx.save();
// do some transformations
ctx.restore();

Saving gives us a "checkpoint" of what the Canvas' state was. If we call it before we run translate or other transformations, it will remember the original clean slate. The restore function will go back to the checkpoint created, effectively undo'ing any transform calls you may have run.

Why do this? Calling ctx.translate(100, 100) means in order to go back to the original place we were, you'd need to call ctx.translate(-100, -100). Instead of doing more math and creating more work for yourself, just save/restore it.

Rotation

// ctx.rotate(radians);
ctx.rotate(Math.PI / 4); // 45
ctx.rotate(Math.PI / 2); // 90
ctx.rotate(Math.PI);     // 180 
ctx.rotate(Math.PI * 2); // 360 

Remember radians? I hope you do. The strange thing about rotation in canvas is that it doesn't rotate the object you're trying to paint, but rather the entire canvas around the current origin. Crazy!

Scaling

// ctx.scale(xScale, yScale)
ctx.scale(2, 2);
ctx.scale(0.5, 0.3);

Scaling is stretching. Usually on the Canvas, 1 "unit" is 1 pixel on the screen. Scaling lets us bend that rule. If we scaled by 2, 2, every "unit" is now 2 pixels. You can scale independently in either axis. The default scale is 1, 1.

Exercises

Animation

Run Loop

Most games and programs that need to run forever (or at least, as long as the user is looking at them) have a "run loop" or "game loop". The basic premise is to respond and react to user input while keeping the core logic of the program running. Here's an example of what a game loop could look like:

In pseudocode:

while( user doesn't exit )
  check for user input
  run AI
  move enemies
  resolve collisions
  draw graphics
  play sounds
end while

We're going to be making a simple run loop to animate objects on our canvas. It will basically look like:

while
  render content (based on changes)
end while

This gives us a clean state every time the run loop starts a new iteration. Based on changes (time, a counter, user input, etc) we can change what is being rendered.

requestAnimFrame

There are several ways to create a game loop in Javascript. The best current way to do is with requestAnimationFrame, a function built into browsers that keeps your loop going while the current browser tab is in focus, and disables the run loop while the user is not looking at the tab.

Since this function isn't working in all browsers yet, we need to use a shim for this, which will be in your forked codepen as requestAnimFrame. Kind of lame, but oh well. Here's an example of a game loop using requestAnimFrame:

render = function() {
 // draw here
}

run = function() { 
  render();
  requestAnimFrame(run);
}

run();

All of the code we'll need to worry about here will be in render. The run function is saved as a variable, and is kicked off at the bottom of the script. Inside of run, we call render to draw our shapes, and then queue up the next run call.

Code!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment