My mission should I choose to accept it: to revamp the Newicon website’s front page graphic by adding some animation. The graphic as it stood was a static image of a fantastical device, in the vein of a Rube Goldberg machine or one of Wallace and Gromit’s inventions, depicting our company’s customer journey. The wonderful idiosyncratic components of this machine absolutely needed to be animated, and after an initial setup, I had a look at the newicon.net page source and started my research.
Handily, the graphic was already an Illustrator-compatible SVG file. Newicon’s director Steve O’Brien had recommended me a few frameworks to work with SVGs, including D3 and Snap.svg, a modern successor (IE9+) to the popular Raphael framework (IE6+). I chose the latter and got to work.
Figuring out the other parts of Snap’s API took a little time, and the documentation on their site doesn’t provide any examples of the code it describes in use at all; not necessarily a problem for old hands but a stumbling block for a newbie like me. Raphael’s documentation, however, was more comprehensive and did provide examples; as mentioned, the two frameworks are related (both written by Dmitry Baranovskiy), and some of their methods and arguments are almost identical. I’d recommend anybody having trouble with the Snap.svg docs to check out Raphael for more in depth examples. With a firmer understanding of the framework, animating all of the different parts of our machine was easy enough, and I concentrated on manipulating the quartet of attributes that tended to be quick in browsers (scaling, opacity, translation and rotation) http://www.html5rocks.com/en/tutorials/speed/high-performance-animations/ for a nice smooth result. The only part of the code that required a little more thought was the generation of smoke particles. I ending up generating them dynamically and pushing them into an array at runtime.
The result looked great, but when I checked Chrome’s timeline in the dev tools to get a closer look at browser performance I was shocked. Chrome is a quick browser, I was running a 3GHz Core i3 mac, and I was barely managing 30fps – this whilst animating not more than 20 elements at a time.
I went a little deeper into the frame breakdown in Chrome. Both scripting and rendering were taking longer than was needed for a clean 60fps framerate. requestAnimationFrame() (the new-ish and widely supported browser method that allows browsers, and not a timer function, to call animation-related code on page redraws) was being used by Snap as it should have been, but I found a ton of timers firing as each animation loop stacked atop the other. In addition to this, rendering time was also significant, the result of animating multiple, detailed SVG groups and paths on a large Snap canvas.
A quick google (I say quick, but Snap is a newish framework and there’s not a huge amount of content out there) and it transpired that a few other Snap users had run into this problem when animating multiple elements (Google group posts, Stack exchange post).
The solution for Snap user Mike Heavers was to avoid the animation API and manipulate the elements manually in a loop, using their .attr() methods to change attributes for each frame. I tried this method, using a draw function called from a requestAnimationFrame() loop. With the code rewritten, the animation was significantly faster – just under 60fps in Chrome with a larger number of manipulated elements onscreen.
A few changes to make the animated elements less detailed – not a significant compromise, as their movement made many nuances less visible anyway – and the render time was also reduced, leaving Newicon’s wonderful machine chugging and whirring smoothly. You can see the result on the front page.
So I learned a few things along the way that are worth sharing here. The first is that Snap.svg does a great job of making working with SVG as painless as possible. The second is that its animate() method, like jQuery’s, is easy to use and powerful, but might not be the quickest choice when animating scores of elements simultaneously. There are other options, and HTML5 canvas was next on my list if performance had continued to be an issue. In this case, though, manually looping with requestAnimationFrame() was enough to keep animation at 60fps on our target machines. For smaller animation tasks, including detailed and interesting UI animations that scale well for responsive design and degrade gracefully on older machines, I’d reach for Snap and SVG without a second thought.
If you’re interested in working with Newicon on your next digital project, get in touch now.