Gstify

Jan 22, 2012

take action

When I first sat down to redesign my personal site I knew that I wanted to incorporate HTML5 Canvas somewhere in the layout. The problem was that I hadn't worked with canvas before and had to start from scratch. I went through the pain of learning every aspect of adding text, drawing shapes, importing image, etc... before I found the amazing canvas framework Processing.JS

Canvas Basics

Adding a canvas element is as simple as adding the below markup. The canvas element alone acts as a block level element with all children hidden without the use of javascript to draw text, objects, images, etc...
<canvas id="myCanvas"></canvas>
Please note that HTML5 markup and the canvas element is only supported by modern browsers such as Firefox (1.5+), Safari (1.3+), Chrome, Opera (9+), and Internet Explorer (9+).

Obviously we don't want to go adding canvas elements without some type of alternative display for browsers that do not support canvas rendering. Thankfully all graphics rendered via canvas are layered above any markup contained within the element. Here's how we degrade canvas so that browsers such as Internet Explorer 8 still display some text that informs the user that they need to stop being lazy and upgrade to a more modern browser.

First we'll add a link to an HTML5 element shiv for any user with a browser less than IE9 in the <head> portion of our document, adding the <canvas> element to our stack of recognized html markup:
<!--[if lt IE 9]>
<script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script>
<![endif]-->
Now let's update our canvas element to target non-modern browsers:
<canvas id="myCanvas">
<p>Please upgrade your browser to something newer, like <a href="http://chrome.google.com" target="_blank">Google Chrome</a></p>
</canvas>
The above markup lets anyone using a non-modern browser know that they should probably upgrade their browser. You can substitute text with an image if you want. For instance, any visitor to this site using a browser that doesn't support HTML5 Canvas is met with a standard JPEG logo as opposed to the canvas alternative.

CSS Styling Canvas

It's always good practice to style your canvas element as until drawing has been accomplished the styling will act as a kind of start screen. While we're at it, we'll also style the child within our canvas element. Styling the child elements inside of your canvas is important so that in non modern browsers we're taking up the same amount of space.
#myCanvas, #myCanvas p {
width: 460px;
height: 250px;
background-color: #f5f5f5;
color: #555;
text-align: center;
}
In modern browsers our canvas element now displays exactly as we styled it, and non-modern browsers also show a similar styling but with a note for the user to upgrade their browser. Take note that css such as text coloring and background coloring is only useful until our canvas element is initialized. Once initialized the things we draw onto our canvas can not be styled via css.
Now that we've covered the basics, how do we go about drawing to the canvas? We could use modern javascript to draw to the canvas, but in this article we're going learn how to use the javascript framework Processing.JS to handle all our drawing.

Let's get started by adding a link to processing.js in the <head> portion of our document:
<script src="processing-1.3.6.min.js"></script>
Processing.JS now adds functionality for us to reference our canvas element to a file (file-type: .pde) in which all of our processing code exists. Let's reference our code by adding the "data-processing-sources" attribute to our canvas element:
<canvas id="myCanvas" data-processing-sources="myProcessingCode.pde">
<p>Please upgrade your browser to something newer, like <a href="http://chrome.google.com" target="_blank">Google Chrome</a></p>
</canvas>
Now all we have to do is create the source file referenced, (in this case "myProcessingCode.pde") and add our Processing code.

Writing Processing Code

We're going to cover a few basic drawing methods such as shapes, text, and images, but before that I want to go over the two core functions of Processing.JS: setup, and draw.

The setup function contains all of the code we want to run when our canvas is initialized. Most importantly this is where you set such key values such as our canvas element's size and framerate. Let's go ahead and set the size, frameRate and background of our canvas element:
void setup() {
size(500, 250);
background(245);
frameRate(30);
}
In the above code we're telling Processing.JS to set the size of our canvas to a width/height of 500/250 and to set the background of our canvas to an rgb value of 245, 245, 245 (#F5F5F5) and to set our canvas frameRate (essential to looping, which we'll discuss later), all at canvas initialization.

Note that all Processing functions are designated with "void" and in this case Processing.js recognizes the setup function as the function to be ran at intialization of our canvas. Adding a custom function is simple:
void myFunction() {
// do something here
}
Our initial setup function sets values to what our css styling is for background-color and size and our canvas now mimics what we saw before adding any processing code.

Now we'll add a 50x50 pink rectangle with a 1 pixel white stroke to a random position of our canvas using by modifying our setup function.
void setup() {
size(500, 250);
background(245);
frameRate(30);

color pink = #ffb5b5;
color white = #ffffff;
fill(pink);
stroke(white);
int positionX = int(floor(random(20, 408))); // 20 pixel left and right padding
int positionY = int(floor(random(20, 158))); // 20 pixel top and bottom padding
rect(positionX, positionY, 50, 50); // x, y, width, height
}
Going over each function we see that we first declare some color variables using the following syntax:
color myColor = #hexvalue;
Always assign complicated colors to color variables so that we can link them to methods such as fill, background, and stroke. You can forego the use of hex values and instead use rgb values as so color myColor = color(255, 181 , 181);.

Next we declare our fill by using the fill() method. The color value we assign to this method will be the fill color of any shape method we then call. This also applies to our stroke() method. If you do not call fill() and stroke() before declaring the shape the shape will have a default fill color of white and a default 1 pixel stroke of black.

If you don't want to fill or stroke the next shape drawn you can do so by replacing fill() and stroke() with noFill() and/or noStroke() methods.
noFill(); // the next shape will not be filled
noStroke(); // the next shape will not be stroked
We can also declare if we want our shape to be antialiased or "smoothed" (no smoothing set by default) or change the weight of our stroke (default stroke weight is 1px) by calling the smooth() and strokeWeight() methods:
smooth(); // antialias our shape
strokeWeight(10); // set the stroke weight to 10 pixels
We now declare a positionX and positionY variable to randomize where our rectangle should appear by using the data method int, since we know our value will be an integer, and we'll make use of the random() method. The random method can and will return a floated value so we'll use the floor() method to round the number returned by random() down.
int myInt = int(floor(random(start, end))
Note that in my example I know that the size the canvas is 500x250 so to ensure that my rectangle is positioned at least 20 pixels from the border of the canvas edges my starting value is 20 and my ending value is 500 (canvas size) minus 40 (20 pixel left/right padding) minus 52 (width/height of rectangle including 1pixel stroke) for the position of X and 250(canvas size) minus ... for the Y position. You, however, can use any value you see fit or declare a non random value like so int positionX = 10;

The only thing left is to create our shape. We've chosen to create a rectangle by using the rect() method:
rect(x, y, width, height);
Remember declaring just a bare rect() without setting any fill() and/or stroke() will result in a white shape with a black 1px stroke, so don't forget to call those methods as stated earlier. If you hate rectangles you can change the shape by changing the rect() method to ellipse(), line(), point(), quad(), arc(), or triangle()
ellipse(x, y, width, height);
line(xStart, yStart, xEnd, yEnd); // doesn't auto stroke
point(x, y); // doesn't auto stroke
quad(x1, y1, x2, y2, x3, y3, x4, y4); // x,y position of each corner of a rectangle
arc(x, y, width, height, start[radian], stop[radian]); // PI radians with or without math operators eg: PI, PI/2, TWO_PI-PI, PI+TWO_PI, etc...
triangle(x1, y1, x2, y2, x3, y3); // x,y of each point of a triangle
For more 2D shape methods (including curves) check the reference section of the Processing.JS website.

Shapes via SVG

If you're familiar with SVG and are constantly working with them you'll want to know that you can define shapes via an SVG file by using PShape Datatype:
PShape mySVG; // set the PShape datatype to the mySVG variable
mySVG = loadShape("mySVGfile.svg"); // load your .svg file using loadShape();
smooth(); // antialias the shape
shape(mySVG, x, y, width, height);
Note you must always load your svg file using the loadShape() method before calling the shape() method.

Adding Text

Processing.JS provides us with the PFont Datatype and the methods loadFont(), textFont(), and text() methods.

Font loading in canvas can be a bit complex as, despite the ease of the loadFont() method, font support for canvas varies across browsers. Firefox supports canvas fonts the best, but has a pre-defined list of fonts. As of now it's best to use a surely installed font (such as Arial) or to use the PFont_list() method to check the fonts a user has available to load. For more information see the Processing.JS reference to PFont_list().

Processing.JS's draw() function is where most of your drawing should take place. The biggest thing to note is that Processing.JS automaticly loops the draw() function at whatever frameRate() you specify in your setup(). Because of this automatic looping we're given the loop() and noLoop() methods.

In this next append to our code we'll be loading images by making use of the PImage Datatype and the methods loadImage(), requestImage(), image(), and get().

Lets use the draw() function to handle our drawing from now on and let's load an image using requestImage() as opposed to loadImage() as loadImage() freezes canvas until the image is loaded while requestImage() does not. Here's a look at just the image loading code:
PImage img; // PImage for preloading
PImage part; // PImage for display
img = requestImage("yourImage.png"); // accepted formats are .jpg, .gif, and .png
part = img.get(); // get all pixels from the image
image(part, 20, 20); // display the image at x, y coordinates
Note that we're now going to initialize our PImage and PFont objects outside of our setup() and draw() functions so that they're accessible throughout our script.
PImage img;
PImage part;
PFont fontArial = loadFont("arial");

noLoop(); // Tell Processing.JS to stop looping.
}
Now we're getting down the heart of Processing.JS by utilizing it's two main features. Note that most methods, including noLoop() and loop() are accessible in other frameworks such as JQuery. You can do things like:
$('.some_div').click(function() { loop(); }
By specifying a noLoop() in our draw() function we're able to stop the looping process from occuring to prevent our rectangle from duplicating and filling up the screen.

Making Use of Looping
We're going to add some animation and some event listing for a mouse movement. We'll remove our rectangle and choose to move our image and text with our mouse. This can be done by calling the mouseMoved() function and making good use of the looping of our draw() function.

Our ability to have our image and text follow our mouse hinges on the fact that Processing.JS consistently holds the current position of our mouse in the variables mouseX and mouseY. We'll add 5 frame delay to our movement with some simple math.
PImage img;
PImage part;
PFont fontArial = loadFont("arial");

void mouseMoved() {
mX = mouseX; // set mX to our mouseX position
mY = mouseY; // set mY to our mouseY position
}
Processing.JS has other built in event listeners such as the mouseClicked() and mouseDragged functions. Check out their website for a full list of listeners, but as far as we're concerned, our canvas is now animated and interactive! For a full demo check out the live example here.

Conlusion

This article is intended to show simplified use of Processing.JS. Do not in anyway take this article and use it to judge the limits of Processing.JS or Canvas in general. The native canvas API is incredibly powerful, as is Processing.JS, this article is just a taste of what you can achieve. Thanks for reading.