CS559 Spring 2021 Sample Solution - Workbook 2
Written by CS559 course staff
Now that we understand that Canvas is an immediate mode graphics API, we can look more at what commands it gives us for drawing.
Canvas provides a rich set of options for creating shapes and coloring and styling them. We won’t talk about all of them here, and only cover the basic options. Beyond that, you are encouraged to go out onto the web to read and try things on your own.
We recommend reading this page first and then going on to other web resources, some of which are listed at the end of this page. We’ll explain some of the concepts, and then point you to things that give details.
This page, rather than using embedded HTML files, puts all of the canvas right onto the page. All of the drawing code is contained in the
for_students directory of this workbook - from the filenames, you should be able to figure out what pieces of code go with each box on this page.
You might wonder why we don’t use “live code boxes” where the actual code that gets run appears on the page itself and you can read and tinker with it in place. Most of the better tutorials on the web do this. And its great since the code is right there in the text, and it is really easy to tinker with. For CS559, however, we very quickly get to more complex programs where you will want to use “real tools”. So you should get used to that with the simple examples and see from the very start how we structure programs. You should also be able to use the debugger.
But this means that (1) you will actually have to go looking at the source code files, and (2) when we do put a snippet of code on a web page, it will be “marked up” as HTML and not interactive.
Review the Basics
On Page 1 (Web Graphics APIs) we saw a few simple examples. The simplest one was this:
Understanding this is quite important. Be sure to find the code in 02-02-01.js. This is the actual drawing code (without the comments):
let canvas1 = document.getElementById("canvas1")); let context1 = canvas1.getContext("2d"); context1.fillStyle = "#F00"; context1.fillRect(30,30,30,30);
Line by line:
- Line 1 gets the
canvaselement from the DOM. The element is just like any other HTML element.
- Line 2 gets the context object out of the Canvas element. The context object stores all of the state that we need for drawing - things like where we will draw, what the current color to draw is, partially finished objects, etc. At a practical level, most of the “drawing commands” are methods of this object.
- Line 3 sets the color for filling objects. Canvas is a stateful drawing model. For example, we pick a color and then we draw with that color. In contrast, a stateless drawing model would pass everything we need to know to draw to the actual drawing command.
- Line 4 actually draws the rectangle. It uses the “current state” of the context (such as the color we set on the previous line). Conceptually, the square is drawn immediately - changing the pixels on the screen. After the function completes, there is no memory that these pixels should be associated with a rectangle. The only representation of the rectangle is in the code. In practice, the drawing may happen asynchronously - we might not see the changes in the pixels until the system gets around to it.
You can look at the Mozilla (Official) Canvas Tutorial: Getting Started (recommended - but optional) to get more details. You can see a few differences - these are worth pointing out:
- They are concerned with “fallback content” - if you’re a real web developer, you need to be worried about what happens if someone uses an old browser. For class, we assume that everyone who looks at your program will have a modern, compatible web browser.
- They put the drawing code into an
onloadevent handler for the Canvas, whereas we put the handler into the
onloadevent for the entire page. Either way, we cannot draw into the canvas element until it has been created. For us, we wait until the entire page (including the canvas element) has been created.
Insides and Outsides
You may have noticed that when we drew the rectangle, we “filled” its inside with red color (
In Canvas, we can apply styles (like colors) to both the insides and outsides of shapes (like rectangles). The inside is the “fill” and the outside is the “stroke” (as in, you make a stroke with a pen to draw the outline or boundary of a shape).
You can look at the Mozilla (Official) Canvas Tutorial: Styles to see more about what styles are available (including non-constant fills).
There’s another key concept hidden in that example. When you set a drawing state, it stays for the next object. So, for example, when square 3 in the previous example set dashed lines, the next square also got it (whether it wanted it or not). In the simple example, we can see what is going on - but in a more complex example, the state might get set by some code somewhere very different.
Since the green square didn’t know what happened before it, if it wants something specific, it has to reset all of the different pieces of the state!
Instead, a better idea is to have drawing an object “clean up” after itself. If we set some state to draw an object, we set it back before going on to the next one. Unless we’re sure it wants our changes. To simplify this convention, stated APIs (like Canvas) allow us to “save” and “restore” the state. Observe (and look at the source code! 02-02-02b.js):
Saving and restoring works like a stack: if you save twice, it makes a stack of the two saves. Each restore takes something else off of the stack.
Notice (in 02-02-02c.js) how this leads to nesting, or “hierarchy”. We will use this concept (save/restore with a stack) for many things in graphics.
Drawing Order and Transparency
If rectangles (or any shape, for that matter) overlap, the shape that is drawn last covers over anything that was drawn before it. This is like painting with thick paint - we see the last thing drawn. Here is an example (in 02-02-03a.js):
Of course, if we don’t fill, then we don’t cover over things inside the rectangle.
Drawing order even effects commands that draw the same rectangle (remember, we draw the stroke and the fill separately). The stroke and fill share some of the same area. Since the stroke is centered on the line around the filled area, the fill covers half the stroke. Here is an example where we switched the order of stroke and fill (02-02-03b.js):
All of our colors so far have been opaque - they cover over what is behind them. We can also make semi-transparent colors. That is, the color lets through some of what’s behind it. To do this, we extend our colors with an extra number: the opacity (or alpha). In addition to an amount of red, green, and blue, we add a fourth number that is the amount that it covers what is behind it. By default, this is 100% (or 255/255), so things are opaque. But most places where we specify 3 numbers, we can specify a fourth - so to make red that only blocks 50% of what it covers, we can say
7F is 127, or about half of 255).
Remember that drawing order matters - the transparent thing covers what was there before it. Things drawn afterwards will cover the transparent thing. Also, the transparent things will let the background (white) through if that is what they cover.
Here is a simple example (02-02-03c.js):
We are using the simple math for transparency (it’s called alpha-blending). There are more ways to combine the colors. Canvas supports many of them, but we might not get to learn about all of them in class.
Note that in the code, I specified colors as
rgba(255,0,0,.5) rather than
Also, notice that the stroke can be transparent, and we can have transparent dark and light colors.
Shapes Besides Rectangles (Paths)
Canvas has 2 kinds of shapes: rectangles, and everything else. There are also images and text, which can be considered shapes, too.
To make a path based shape, you define a “path” - which is basically the outline of the shape, and then you can stroke and fill that. Just as the rectangle had
strokeRect paths have
fill. The biggest difference is that rather than telling stroke and fill what the shape is, it uses the current path. That is, the current path is part of the context, the same way things like fill color and line width are.
The official documentation is the best place to look Mozilla (Official) Canvas Tutorial: Drawing Shapes. You can skip the section on “Bezier and quadratic curves” for now - we’ll learn about them in a few weeks. The parts about
SVG paths you can skip as well.
Make sure you understand how to make paths, and use them to draw with Canvas. And note that to make a circle, you need to draw an arc that has
2*Math.PI radians of arc to it.
Now we’ve seen the basics of drawing with Canvas. On the Page 4 (Canvas Drawing: Your Turn), you’ll get to try it out.
From this page, make sure you understand the concepts (like drawing state).
You will want to learn more about Canvas drawing, so you can make more interesting pictures. Some of the things (like curves and transformations) we’ll introduce in the coming weeks as we introduce the graphics concepts in class. For now, focus on making shapes and giving them styles.
Here are some resources to look at. You don’t have to read them all, but do read some beyond just this tutorial.
- Mozilla (Official) Canvas API Documentation This is the “official” documentation. Everything is in here, somewhere. It is actually quite well written and well organized.
- Mozilla (Official) Canvas Tutorial (top level) This is part of #1. It very quickly gets beyond the basics. We mainly need the basics. Many of the pages are very useful such as Mozilla (Official) Canvas Tutorial: Drawing Shapes and Mozilla (Official) Canvas Tutorial: Styles.
- Canvas Cheat Sheet: A concise page that reminds you of the different things you can do with Canvas.
- HTML Canvas Deep Dive: This is a “book length” tutorial on web graphics program (it even gets to 3D stuff). The first chapter covers a lot of the basic stuff.
Ok, now move on to Next: Where did I draw
This page (2) has no points.