HTML5 Canvas
HTML5, released October 28th 2014, is the latest version of HTML. It builds on top of many years of specifications and development done since when HTML was first released by Tim Berners-Lee in 1993.
This site shows off what tags were included in each version of HTML from version 1 to version 5:
http://www.martinrinehart.com/frontend-engineering/engineers/html/html-tag-history.html
HTML 1 (1993)
<a>
,<h1>
-<h6>
,<ul>
,<li>
,<p>
,<title>
HTML 2 (Nov 24 1995)
<!DOCTYPE>
,<html>
,<head>
,<body>
,<meta>
,<link>
<table>
,<thead>
,<tbody>
,<tfoot>
,<tr>
,<td>
<form>
,<input>
,<select>
,<option>
,<textarea>
<em>
,<strike>
,<strong>
,<u>
,<i>
,<img>
HTML 3 (Jan 14 1997)
<div>
,<center>
,<font>
<script>
,<style>
HTML 4 (Dec 18 1997)
<button>
,<label>
,<span>
<iframe>
HTML 5 (Oct 28 2014)
<audio>
,<video>
,<canvas>
<header>
,<footer>
,<nav>
,<section>
<summary>
,<time>
,<progress>
<output>
It looks like HTML 1 was all about creating pages with basic text. HTML 2 introducted the HTML / HEAD / BODY structure of web pages we know today, and it gave us images, forms, inputs and tables. HTML 3 added divs, fonts and, most importantly, CSS and JavaScript as we know it. HTML 4 offered buttons, labels for inputs and iframes (which I feel are rarely used much anymore today). Finally, HTML 5 came out with some new semantic improvements by giving developers the header, footer, nav, section and summary tags. Those elements don't do much to make things look different on the page but they're useful for developers to be able to add meaning to their web pages.
Perhaps the most exciting features of HTML 5 are the addition of the <audio>
, <video>
and <canvas>
tags. In the past, users relied on technologies like Flash in order to create rich interactive multimedia content on the web. The introduction of these new multimedia tags allowed developers and netizens to have native in-browser support for <audio>
<video>
experiences for the first time! Woo!
The Canvas Tag
The audio
and video
tags are alright. They allow a page to play audio and movies. What's really cool is the <canvas>
tag. The <canvas>
tag is basically a block element that the browser can programatically draw on. Think of it like a <div>
where you can use JavaScript to execute Microsoft Paint commands on. The canvas element provides an API to JavaScript where it can do things like draw lines, draw curvers, fill in shapes, and draw images to the canvas.
Note: the canvas element has limitations. The biggest limitation is that the canvas element is only showing an image at the end of the day. Although it will allow you to draw cool, unique things on the page the canvas is not good for creating text that users want to highlight, or displaying links people can click on. So: canvas is great at displaying things, but keep in mind that if you ever draw text to the canvas the text won't act like text on a website that can be highlighted and copied and pasted. The text will act like text that's inside of an image.
Drawing on the Canvas
Check out this repo to bare-bones page with a canvas we can draw on.
The canvas element provides us with something called it's context. Think of context like a repo for canvas methods. Canvas supports a variety of different contexts, primarily 2D and 3D. We'll be using our canvas for drawing simple two-dimensional images so we'll grab the 2D context off the canvas.
The canvas element is the canvas we paint on, the context is like our paintbrush. We'll use the context to paint rectangles at first. The context provides us with two ways to draw a rectangle. We can stroke or fill a rectangle. Stroke a rectangle produces an outline of a rectangle with a hollow see-through center. Filling a rectangle draws a solid rectangle. We can "dip" the context paintbrush into two seperate colors for the stroke and fill.
Let's fill a black rectangle and stroke it's border with a 5px red line.
Attach Mouse Interactions
Cool. That's some simple canvas work right there! Let's make things more interesting by making the canvas draw a rectangle wherever someone clicks.
Although the canvas element provides us the unique ability to draw things on a web page it still behaves like all other HTML elements. We can style the width, height, position and border of it. We can also attach click handlers to it.
Let's write a function that accepts an x and y parameter and draws a small rectangle at that location. Then, let's attach a click handler so whenever someone clicks on the canvas element we extract the x and y coordinates from the click event and pass the coordinates to the draw rectangle function.
Oh, the function will need access to the ctx
variable too. We'll pass that as a parameter too.
The click event object has lots of different x and y coordinate information:
clientY - y distance from the top left corner of the webpage, under the
bookmarks and URL location bar. Ignores scrolling. If the user scrolls down
1,000 pixels and clicks on the page just under their bookmarks bar the y
value will stay close to zero.
layerY - y distance from the mouse to the closest positioned element.
offsetY - y distance from the top left corner of the element that was
clicked on.
pageY - y distance from the top left corner of the page, including any
scrolling. If a user scrolls down 1,000 pixels and clicks on the page just
under their bookmarks bar the y-value will be close to 1,000.
screenY - y distance from the top left corner of the entire laptop screen,
like (0,0) would be near the top-left Apple icon on a Mac.
y - an quick-shortcut alias for the
clientY
property.
Let's use the offsetX
and offsetY
properties off the event object.
Notice that ever new rectangle is drawn on top of everything else. It's just like painting over it.
Erase Entire Canvas
There gets to be a lot of rectangles on the canvas after the user clicks a bunch. Let's add a button that will let us erase everything on the canvas. The ctx
object provides a .clearRect(x, y, width, height)
function that allows us to erase everything on the canvas within a specified rectangular region.
Graphics Loop
OK, it's cool that there's a rectangle on the page when it loads, and it's cool that we can make more rectangles when someone clicks on the page. Let's make the page even better by having a rectangle float under the user's cursor as they move their cursor around the campus.
This is as easy as attaching another event handler to the canvas. We used a click listener before. Let's make a new one that listens for mouse movement and hook it up to the same drawRectangle
function.
Hooking the canvas up to draw a rectangle every time the mouse moves ends up drawing a lot of rectangles to the page! Remember that every time something is painted to the canvas it just stays there. Everything gets painted over everything else. Let's fix this up by erasing everything on the canvas each time the mouse moves and then drawing our one new rectangle.
Every other rectangle gets deleted each time the mouse moves, but now there's just one rectangle that hovers under the user's mouse. Now our canvas looks very interactive and snappy! Very cool.
Persist Previous Drawings
OK, it's cool that we're able to constantly draw a rectangle under the users's mouse, but it's a total shame that the rectangles we click to draw are immediately erased. Nothing persists. We only see the one rectangle under the mouse.
We need to change the program so that instead of only drawing the current rectangle when the mouse moves it should draw every rectangle that's ever been added to the canvas.
Right now our program looks like this:
The problem with our program is the fact that we're erasing everything. But, we're at a catch-22. We need to erase everything so that we don't leave a trail of squares behind wherever the mouse goes.
We'll need to change our program to keep track of everywhere someone has clicked and we'll need to tell the program to draw all of the squares again each time it erases the screen.
Basically, instead of drawing rectangles immediately we'll instead add the coordinates of each rectangle to an array. Whenever we need to erase the screen we can run a for loop over the array of rectangles and immediately redraw them. We'll only add rectangles to the array that we want to stay forever. This means we can still draw rectangles that follow the mouse when it moves, but those mouse-move rectangles won't be added to the array and they won't be drawn in the future.
Our program will look like this:
How should the rectangles be stored in the array? We could use strings to have coordinates like ["0,0", "76,299"]
or we could store coordinates in small arrays like [[0,0], [76,299]]
. These options would get the job done, for sure, but they lack semantics. There's no real meaning imposed on the data when we store the data this way. Which coordinate is the x
coordinate? Is the y
coordinate in the first position or the second position? It's hard to remember, it's easy to make mistakes, and it's hard for whoever else comes to read our code later. Let's make life better.
We could store all of the rectangles as objects with x
and y
values like [{x: 0, y: 0}, {x: 76, y: 299}]
. This is definitely better because the x
and y
values have become unambigious. We can still do better!
Rectangles are things, right? Things are, like, objects, right? Let's use object-oriented programming to create a class that models what a rectangle is! A class is even better than just an object with x
and y
properties because it has a name now too. We're clearly saying "these x and y coordinates represent a Rectangle." Just the fact that the class has a name, "Rectangle", makes our code even easier to understand.
Also, creating a class gives us a great place to attach more information in the future. Now that we have a rectangle class it would be easy for us to add other properties like size, color, outline line. All of this information all in one place. Amazing.
Make a new file called rectangle.js that contains the class definition for our rectangles and remember to attach a <script>
reference to the new file in index.html.
Use the Rectangle class to create rectangles and save them to an array. Make a new function called redraw()
that accepts ctx
and an array as parameters and redraws all of the rectangles. Call the new redraw()
function just after you erase the canvas when the mouse moves, and before you draw the new rectangle that hovers under the mouse.
Careful! Notice that pressing the "clear canvas" button now erases the canvas for a moment, but all of the rectangles come back as soon as the mouse moves. This is because although the canvas was cleared of everything that was painted on it, the array still maintains memory of all the rectangles it's supposed to be drawing. We must update our "clear canvas" click handler so it resets the array of rectangles back to empty too.
Refactor Draw Method Inside Rectangle Class
Object oriented programming allows us to:
model data that represents what objects are
define functionality that defines what objects can do
Create a new function called draw
on the Rectangle class that accepts a ctx
as a parameter and has rectangles draw themselves onto the canvas with the ctx
according to their own this.x
, this.y
and this.size
values.
After doing this we can delete the drawRectangle
function in main.js and simply iterate through rectangles and tell them to draw themselves. It's nice to keep everything related to rectangles inside the one file inside the one class.
The rectangle class provides a good pattern for us to follow if ever want to add more shapes or figures to this website. We can define classes for squares, create circles, triangles or anything else.
Last updated