GeorgieP

reading and tinkering

Canvas Basics

Creating a Canvas

The canvas is a 2D element that can be painted and repainted directly on the HTML in the browser.

In HTML use the canvas tag <canvas></canvas>. It can take optional height and width attributes and is usually given an id attribute too.

          <canvas id="the canvas" height="200" width="200"></canvas>

To use and put content in the canvas, we need to use JavaScript.

          <script></script>
First we need to get a reference to the HTML canvas element, and then assign a reference to the canvas's 2d context.
          <script>
            var c = document.getElementByID("the canvas");
            var ctx = c.getContext("2d");
          </script>
Note: getContext()'s parameter is a string and yes, we can also call a 3d context.

Now you have the CanvasRenderingContext2D which provides many methods to draw and animated text and graphics on the webpage with JavaScript.

Canvas coordinate system

Note the coordinate system used by canvas has the origin at top left. Left to right on screen increases x-coordinates, top to bottom increases y-coordinates.

Loading images

To load an image we need to create a new Image Instance which allows us the (optionally set height and width):

          var myImage = new Image([width, height]);
and will require a source property to be set:
          myImage.src = "/path/file.ext";

Images in JavaScript get loaded asynchronously, which means that we will have to do work on the image once it is loaded. We can set that work to be triggered by the image's onload event.

          myImage.onload = function() {
            //do something
          };
Typically we'd want to draw the image on the canvas with the drawImage(); method provided by CanvasRenderingContext2D:
          myImage.onload = function() {
            ctx.drawImage(myImage, x, y[, width, height]);
          };
The drawImage() method requires an image object, and top-left xy coordinates. Optionally it will take width and height.

Saving out Images

This is a little bit beyond the scope of this course but a quick and dirty method is to use the canvas's .toDataURL() method and open that in new window (this method will require a python -m SimpleHTTPServer [PORT]) or python -m http.server [PORT] in Python 3x. Insert the follow code into a function assigned to button or some such...

          var imageData = c.toDataURL();
          window.open(imageData);
        

Note that the python SimpleHTTPServer is run by navigating to the directory where the HTML file is stored and then accessed at http://localhost:8000/. 8000 is the default port and can changed optionally by appending it to the python command string.

Drawing Rectangles

To draw a filled rectangle use the 2d context's .fillRect() method and to draw a stroke, use .strokeRect(). Both take starting xy coordinates, width, and height.

To delete a part or all of the canvas 'paint' you can call .clearRect() with the required coordinates, width and height. You could also do this by redrawing a filled rectangle over the area to be cleared with a solid color. To set the fill color, set the .fillStyle propety to the desired color.

A much faster and simpler way to clear the canvas is to reassign it's height of width:

          c.width = c.width;
although this would not be seen as self-documenting code.

Drawing Paths

To draw a path, a sequence of commands are required. This is similar to a turtle type program. We start the path command with .beginPath(). We then need to define a start xy coordinate for the pen with the .moveTo(x,y). Next to define an endpoint xy coordinate for a line we use .drawTo(x,y). Finally, to end the beginPath command and render the full path to the page we need to call .stroke() or .fill().

Scale, Translate and Rotate

Scale, Rotate and Translate are as they say on the tin, just remember they are operating in computer screen coordinate space and not on a cartesian plane. And that means rotations are clockwise and angles are measured in Radians too.

Note that all SRT operations shown operate on the whole 2d context, to reset the current transformation matrix to the starting condition use:

          ctx.setTransform(1, 0, 0, 1, 0, 0);

Saving and Restoring Canvas State

The context has a load of properties like fillStyle, transformation matrix, current font etc that can be added to the state stack. To save and restore a state, use the .save() and .restore() methods. For a full list of stackable states follow .save() link above.

Side note: must learn to set states as variables.

Colors!

Cam be like... b&w don't stand out. Pfff. Assign .setFillStyle and .setStrokeStyle to a color to make colors. Colors can be names like "blue" or CSS definitions like hex color code etc., for example:

          ctx.setFillStyle = "green";
And that'll do it. Just remember that the order of setting properties on the state stack is very important if you want thing to stand out.

Text!

Text is a matter of setting context properties as you see fit, including stroke and fill styles. To add text to the canvas, use either .fillText("My text here", x, y[, maxWidth]) or .strokeText(). Read all about text from diveinto5.

From Pixels to Animation

Images hold pixels. Pixels have red, green and blue channels. Some images have Alpha channels.

The ImageData() Object

ImageData contains .width and .height properties. It also contains a Uint8ClampedArray of data at .data that store the red, green, blue and aplha values for each pixel.

Uint8ClampedArray:
U = unsigned, or only positive numbers,
int8 = 8 bit, or 2^8, or 256, or 0 to 255 values,
Clamped = (not important right now),
Array = well... this is a big array of rgba values that looks like this: [r1,g1,b1,a1,r2,g2,b2,a2,r3,g3,b3,a3....rn,gn,bn,an]

Notice that the array does not group pixels together, it just lumps them all into one sequence.

Great explanations about these Arrays from Computerphile (why isn't it computerfile?).

The 3 methods we use with ImageData are:

Psuedocode for manipulating an image might look like:

          // get and store the image data array in a variable
          // count the number of values in the array (pixels*4)
          // loop through array in steps of 4
            // for each loop: i = r, i+1 =g, i+2 = b, i+3 = a
            // manipulate the values and write them back into the variable
          // set the variable as the image data
        

Other Cool Stuff

James gives a demo of how these same methods can be applied to video on a frame by frame basis to such an extent that you can apply filters to live video in real time.

James goes on to talk about requestAnimationFrame() and explains that it lets your browser render only when it can, making it way more preferrable over setInterval and setTimeout. Note that while requestAnimationFrame looks like it creates an Infinite Loop, the browser knows how to run it only when needed.

requestAnimationFrame is also perfect for controlling game engine loops.

Capturing User Input

A link to kibo.js is provided as an example useful library for grabbing user keyboard inputs.

For capturing mouse inputs, it is noted that the canvas element does capture mouse events, but that a little bit of work is required when you need to know the exact location of those click in relation to the canvas element. The following is ripped directly from the lesson text:

Mouse click events return clientX and clientY positions that are global to the browser window. Every element knows where it is positioned relative to the browsers (0,0) position (offsetLeft and offsetTop).

To get the canvas-relative of a click, you need to subtract the offsetLeft and offsetTop values from clientX and clientY. Check out the example code below.

        var c = document.querySelector("canvas");

        function handleMouseClick(evt) {
                x = evt.clientX - c.offsetLeft;
                y = evt.clientY - c.offsetTop;
                console.log("x,y:"+x+","+y);
        }
        c.addEventListener("click", handleMouseClick, false);
          

And that's it for canvas, for now.