HTML5 Canvas & Processing JS
Join the DZone community and get the full member experience.
Join For FreeThe content of this article was originally posted in Joey Cadle Allgaier's blog.
For those who don't quite fully grasp what HTML5 Canvas check out the W3Schools entry for the element before reading any further, but it's basically an element that defines graphics.
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 support 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 know 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 later 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 that they should probably upgrade their browser. You can put 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.
Getting Started With Processing.JS
Processing.JS is a port of the Processing Visual Programming Language developed by Ben Fry and Casey Reas designed for use on the web. You develop code using the processing language and processing.js transforms those actions into canvas elements. You can download the latest version of Processing.JS at their website: http://processingjs.org.
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 shape you can't see, 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 it
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().
Let's add some text to our canvas:
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))); int positionY = int(floor(random(20, 158))); rect(positionX, positionY, 50, 50); fill(64); // color the text #404040 rgb(64, 64, 64) PFont fontArial = loadFont("arial"); // load the Arial font textFont(fontArial, 32); // set the font-size of fontArial to 32 point text("Joey Cadle Rocks!", 110, 60); // Add the text to canvas at x, y position }
The draw() Function and Image Loading
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"); void setup() { size(500, 250); background(245); frameRate(30); img = requestImage("yourImage.png"); } void draw() { background(245); part = img.get(); image(part, 20, 20); color pink = #ffb5b5; color white = #ffffff; fill(pink); stroke(white); int positionX = int(floor(random(20, 430))); int positionY = int(floor(random(20, 180))); rect(positionX, positionY, 50, 50); fill(64); textFont(fontArial, 32); text("Joey Cadle Rocks!", 110, 60); 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
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 setup() { size(500, 250); background(245); frameRate(30); x = 20; // set initial x position y = 20; // set initial y position mX = x; // set mouseX to above x mY = y; // set mouseY to above y delay = 5; // set the frames we want to delay movement img = requestImage("yourImage.png"); } void draw() { x += (mX - x) / delay; // reset our x with current x position minus mouseX position and delay it y += (mY - y) / delay; // reset our y with current y position minus mouseY position and delay it fontX = x + 90; // add the width of our image to ensure its to the right (in the demo case: 90) fontY = y + 40; // add the height of our image to ensure its level (in the demo case: 40) background(245); part = img.get(); image(part, x, y); // draw our image at x, y based on x,y values above. fill(64); textFont(fontArial, 32); text("Joey Cadle Rocks!", fontX, fontY); // Add the text to canvas at fontX and fontY position } 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 if Processing.JS, this article is just a taste of what you
can achieve. Thanks for reading.
Short URL: http://bit.ly/z25Lvg
Source: http://joeycadle.com/blog/article/1/2012/22/01/html5-canvas-and-processing-js
Opinions expressed by DZone contributors are their own.
Comments