Monday, December 12, 2011

animation nation part 2: introduction to raphael.js

So the other day I was talking about making an animation for my company's registration process.

I quickly put together an animation proof-of-concept in ProcessingJS. I didn't go too far with it in part because IE is still in our target browsers, and ProcessingJS relies on the canvas object, so we couldn't use the result on the site.

My thoughts turned to RaphaĆ«l in part because I knew it would work with IE. Superficially, Raphael is very similar to canvas-based APIs (in fact I thought it used a "shim" to let older versions of IE do canvas things) but really it's profoundly different... rather than a canvas of pixels, Raphael deals with SVG (Scalable Vector Graphics) vector objects. These objects are great because they deal with twisty lines and curves rather than squares, and you can scale them to any size without them getting pixel-y.

Vector graphics require a different mindset than "regular". Specifically, Raphael is object oriented and once you've created your "paper" you add shapes and lines to it. Those shapes and lines then have attributes, similar to CSS attributes, that you can modify. The examples on the Raphael homepage are impressive, and it's easy to make juicy effects, especially in terms of bouncy scaling and rotation.

I wasn't crazy about the homepage's boilerplate for two reasons: one is I prefer something I can copy and paste into a blank document and hit the ground running. Here's some code that let me do that (once I pulled down a local copy of raphael-min.js)
<head>
<script src="raphael-min.js"></script>
<script>
window.onload = function(){
  var paper = Raphael("thepaper", 320, 200);
  var circle = paper.circle(50, 40, 10);
  circle.attr("fill", "#f00");
  circle.attr("stroke", "#fff");
}
</script>
<body>
<div id="thepaper"></div>
</body>

The second improvement I made was to use a name div as a canvas, rather than ask Raphael to provide the div and position it absolutely... I think it's more common that people will be embedding a bit of Raphael in a larger webpage, so an id'd div seems the way to go for that. However, that meant I couldn't let Raphael just run as commands directly in the script tags, I had to wrap it as a window.onload function (obviously, $(document).ready(); is even a better bet if you're already using jQuery, I just wanted to remove the dependency.)

The Raphael documentation is a reference, not a tutorial, so you have to poke around... it probably helps if you know some of the basics of SVG. 

In processing, I had made up a sine wave as a serious of short line segments. In Raphael, I figure I'd want a Path: a path is a series of line and curve segments. Entertainingly, you define a path via a string, and a series of text commands. It reminded me of drawing with the language Logo and its turtle graphics in my youth. 

So to see a Path in action, you can add this line to the boilerplate, which will draw a rotated chunky path.
var p = paper.path("M50,50 L100,50 L100,100 L150,100 L150,50").rotate(45,50,50);
Some things to note:
  • Raphael uses the same kind of command chaining as jQuery, so I added the rotate command to the end. 
  • You can use commas or spaces as delimiters between commands and the arguments.
  • I used capital letters, so all the coordinates were absolute... if I had used lowercase, they would be relative to where we last drew.
  • By default angles are in degrees (i.e. 360 = circle). 
  • If I didn't specify the coordinates to rotate around, the Path would have rotated around its own center.
  • Raphael uses "painter order" rather than Z-indexing... the last thing you add to the Paper is what appears at front.
Once I was here, it still wasn't clear how to go forward in terms of making the animation I wanted. There's a pretty extensive set of animation features, but I couldn't figure out if there was a way to make the minor adjustments to the Path. (In fact, it turns out I can't even rotate a Path as an animation! Hmm.) So while I thought it would be "more Raphaelish" to make the objects once and then animate their attributes, it turned out I was going to have to do it similarly to the Processing version, and constantly create and destroy my path objects each tick.

To cut to the chase, here's what I ended up with, click kick to see it with some inertia, or just spin for a constant rotation:
You can go to the webpage to view the source though it's a bit hacky and ugly...
So comparing that animation to the original graphic:
It's not 100%, but with the tweaking I did it's not bad. Our art guy was very pleased with the result.
As the animation works, you can see where I had to add in additional "crossbeams" - the 2D original didn't have to worry about that. Also I couldn't quite figure out how to duplicate the "over under" cheat in the original, where they put a break in the underlying squiggle, so working with our art guy we decided to color one of the squiggles a bit darker.

The thing is the animation flickered a bit on IE, and even on Firefox the constant motion was pushing the CPU to around 7-10%. I decided to punt-- I added a step button to move the animation one frame at a time, and then I laboriously constructed an animated GIF:

Neat! Not quite as 3D as the Processing version, alas. (And since I was "cheating" and making a GIF anyway, I could have stuck with Processing.... in fact I might go and try that, since it's more powerful, and easier to push out a series of TIFs (rather than relying on screenshots like I did))

Finally, we were looking for the animation to run for a bit, then stop, then restart. I made the GIF animate once, and then used jQuery like this when we wanted to see it spin:

//first set up an offscreen image with the animated GIF:
this.dnaImg =  new Image();
this.dnaImg.src = '/components/onboarding/images/DNAIconAnimate.gif';
//later when we want it to animate, (re)set the 
//src of the onscreen image to the offscreen one:
$('.dnago').attr('src',this.dnaImg.src);

Easy-peasy, and it added a lot of visual pizazz to that part of the site.

No comments:

Post a Comment