A couple weeks ago, I mentioned that I was using Processing.js to animate a visualization tool for work. Today’s article showcases the class-based strategy we used to handle animation frames.
There is a simple Demo available, if you would like to see Processing.js in action.
Getting ready
To start using processing, you will need three things: the latest Processing.js code, a canvas element on an HTML page with the data-processing-sources attribute set, and a .pde file for your processing code. Here is a sample HTML:
<html>
<head>
<title>Processing.js Test App</title>
<script src="pathToProcessing/processing.min.js"></script>
</head>
<body onload="init">
<canvas data-processing-sources="pathToPde/processingTestApp.pde" width="1000" height="500"> </canvas>
</body>
</html>
How do it…
In the .pde file, define the animation base class:
function AnimationFrameClass () {}
AnimationFrameClass.prototype = {
data: null, // optional data (for internal use of children)
draw: null, // required, the function to render this animation
onend: null, // optional function to call, when animation is finished
// required function to indicate when to stop, by default after max_frames
finished: function() {
return this.max_frames < this.frame;
},
max_frames: 100, // default maximum number of frames for this animation
name: null, // the name of the animation, useful for debugging
reset: function() { // restart the frame counter
this.frame = 0;
},
onstart: null, // optional function to call, before animation begins
started: false, // used by draw function
frame: 0 // required, the frame counter
};
If you are not using a library and need an inheritance function, add the following as well:
function inherit(C, P, p) {
var F = function () {};
F.prototype = P.prototype;
C.prototype = new F();
C.uber = P.prototype;
C.uber_constructor = P;
C.prototype.constructor = C;
if (p) {
for (var prop in p) {
if (p.hasOwnProperty(prop)) {
C.prototype[prop] = p[prop];
}
}
}
return C;
}
Here is a simple example that extends the animation base class to draw the background:
color BackgroundColor = 0xFFCCCCCC;
var BackgroundAnim = inherit(function() {}, AnimationFrameClass, {
draw: function() {
background(BackgroundColor);
},
finished: function() {
return false; // never stops
},
name: 'Background Animation'
});
Globally, define a queue that AnimationFrameClass instances are appended to and process the queue in the special draw
function:
var Queue = [];
// called automatically by processing continuing the animation, draws a frame
void draw() {
var newQueue = [],
i = 0,
j = Queue.length,
animObj;
for (; i < j; i += 1) {
// clear fill and stroke that may have been set by previous animations
noFill();
noStroke();
animObj = Queue[i];
// fire started function
if (!animObj.started) {
animObj.started = true;
animObj.onstart && animObj.onstart();
}
// draw and increment the frame
animObj.draw();
animObj.frame += 1;
// fire the end function
if (animObj.finished()) {
animObj.onend && animObj.onend();
}
// this animation stays in the queue for the next frame
else {
newQueue.push(animObj);
}
}
Queue = newQueue;
}
Don’t forget to define the setup
function, which is called automatically by Processing.js on startup:
void setup() {
size(500, 500);
}
The following would then draw a gray background:
Queue.push(new BackgroundAnim());
How it works…
Processing.js searches the DOM for canvas tags with data-processing-sources attribute defined, downloads the .pde file, and applies a post processor against the code converting it to functioning JavaScript that drives the canvas element. You write your animation logic (the .pde file) in a modified Processing environment, which behaves like the Java-based Processing language, except it also has access to global JavaScript variables. For example, if you include a library like jQuery before Processing.js runs, then the code in the .pde will be able to use jQuery. That said, today’s example uses straight processing logic.
The AnimationFrameClass is a simple class that can be extended to include logic for drawing any kind of animation frame. The example above includes a Queue array that all animation instances are pushed onto and processed from. The order that animations are added to the Queue, is the order that they will be rendered during an animation frame, so they should be appended background to foreground. An animation frame is automatically handled by Processing.js when it calls the global draw
function. The draw
function of each AnimationFrameClass objects will be called each frame.
As each animation is iterated over several things happen. If an onstart
function is defined and the started
property is not set to true, then the onstart
function is called. Use onstart
to handle one-frame animations or to queue an animation that happens when the current animation first starts. Next the draw
function is called and the frame
counter incremented. Each subclass of AnimationFrameClass should define its draw
function, which is what will be rendered on each frame. If the finished
function returns true, then the onend
function is called, where you might queue an ending or series animation. If the animation is not finished, it will be appended back onto the Queue for processing again in the next frame.
Check out the Demo for a fully functioning app with lots of sample animations.
There’s more…
This article focuses mostly on my frame animation technique, and not all the awesome power of the Processing language and Processing.js. For more details, see the Processing.js documentation.