May 24, 2011

Things That Spin + Move

Integration

A couple of years ago at ZendCon, I saw a twitter visualizer called Visible Tweets. It was being used to feature attendee comments during the keynote.

My first thought was, wow that's mesmerizing! My second thought was that this would be a great way to feature Perforce check-ins. Tweets have an author and a body of text. Perforce changes also have an author and a body of text. This should be easy right?

Wrong. Unfortunately the Visible Tweets visualizer only knows how to pull from Twitter and - because it's compiled Flash - there is no way to modify it without the original source. I contacted the author, but he wasn't keen on sharing the project files. I can understand that... no animated check-ins for me.

Fast forward to 2011 and CSS transforms have arrived. Webkit has actually had them for a couple of years, but with the release of Firefox 4 and IE 9 (yes, even IE) they are now present in all major browsers. I decided to see if I could recreate the Visible Tweets animation in pure JavaScript and HTML/CSS. If you're not interested in the technical details, skip ahead to see how I made out.

Nitty Gritty

I didn't think it would be easy, but it turned out to be harder than I had expected (~500 lines of code). The end result is not quite as fluid as the Flash version and the message positioning logic is different, but I think it's a decent knock-off. It also shows the value of using CSS transitions with JavaScript. Early on I tried using 100% JavaScript (e.g. dojo.anim, jQuery.animate), but the performance just wasn't there. With CSS transitions driven by JavaScript, the browser does the heavy lifting and it's much more efficient about it. The result is higher frame-rates and way smoother animation.

For anyone else interested in driving CSS animations with JavaScript, I want to share a couple of things that I learned. The first thing that tripped me up is that you can't set a transition and a transform at the same time (transitions govern the duration and easing of an animation - transforms dictate what to change - e.g. scale, rotate move). If you do, the transform happens immediately instead of animating over a period of time. The fix is to set your transition rules first and then set your transforms separately via window.setTimeout(). I found that using a timeout of 0 was enough. This just gives the browser a chance to evaluate the transition rules before you tell it what you want to animate. Also note, transitions aren't limited to transforms. You can animate other CSS properties too (like color, opacity, borders, etc.).

The other thing I found is that changing direction is hard. The Visible Tweets animation does a lot of zooming in and out. This amounts to two separate transforms in CSS (one to scale-up and another to scale-down). The problem is that it's not possible to ease across two transforms. So, the animation halts abruptly between the two movements. The solution (and it's not perfect) is to provide custom "cubic-bezier" easing functions that really slow down the animation at the end of the first transform and the beginning of the second. This helps the two animations "flow together".

Visible Feeds

I decided to call my Visible Tweets knock-off "Visible Feeds", as it is designed to work with any sort of message feed. Out of the box it supports JSON and RSS formats. Personally I find JSON a lot easier to work with than RSS (XML), but P4Web will serve up an RSS feed of changelists, so you might find that the most convenient input if you want to show Perforce submits.

If you don't have P4Web installed, but still want to animate submits, check out an earlier post of mine Rest Easy! A P4 Web Service in 50 LOC. One of the example response functions returns a list of changes in JSON format.

I chose to write Visible Feeds as a Dojo Toolkit dijit because there are so many great ways to extend them. If a dijit doesn't do exactly what you want, you can sub-class it via dojo.declare or monkey patch it in-place with dojo/method scripts. The latter technique is particularly handy for massaging feed data so that it displays just right.

A 'Bio' Feed

With the 2011 Perforce User Conference fast approaching (and shaping up to be our best ever!), I thought it would be fun to animate the presenter bio's. The bio's also give me an opportunity to show off monkey-patching via dojo/method (more on that below).

Here's the HTML to make it happen:

<html>
  <head>
    <script type="text/javascript"
            djConfig="parseOnLoad: true"
            src="http://ajax.googleapis.com/ajax/libs/dojo/1.5/dojo/dojo.xd.js"></script>
    <script type="text/javascript"
            src="http://ajax.googleapis.com/ajax/libs/dojo/1.5/dijit/dijit.js"></script>
    <script type="text/javascript"
            src="visibleFeeds.js"></script>
    <link rel="stylesheet" href="visibleFeeds.css" type="text/css">
  </head>
  <body>
    <div dojoType="visibleFeeds"
         format="json"
         url="uconf-2011-bios.json"
         fields="{'body': 'about', 'author': 'name'}">
      <script type="dojo/method" event="getTime" args="data">
        return this.getValue('company', data);
      </script>
      <script type="dojo/method" event="loadHandler" args="response">
        this.fetchCount = this.fetchCount ? ++this.fetchCount : 1;
        var offset      = this.maxMessages * (this.fetchCount - 1);
        if (offset > response.length) {
          this.fetchCount = 1;
          offset          = 0;
        }
        this.data = response.slice(offset, offset + this.maxMessages);
        this.loadMessage();
      </script>
    </div>
  </body>
</html>

A bit of a breakdown on what's happening here. The head section pulls in all of the required bits: dojo and dijit support from Google's CDN (content delivery network) and the Visible Feeds JavaScript and CSS from our public depot.

The body instantiates the Visible Feeds dijit with a div using dojoType=visibleFeeds (this is how you use dijits declaratively). I have set the format to JSON and the url to the location of my presenter bio's file. The fields attribute tells Visible Feeds what keys to use when looking up the message body and author in the feed.

The two inline script tags are the monkey-patching I was referring to above. Since the bio's don't have an associated time, I patched getTime() to show the company name instead (normally it would say something like "2 minutes ago") and since the set of bio's is fixed (instead of continually rolling in like submits), I patched the loadHandler() to paginate through them and loop back around when it reaches the end.

Demo

Remember when I said CSS transforms have arrived? I'm afraid I wasn't being entirely truthful. It turns out that IE doesn't actually support CSS transitions yet. And, performance in Firefox 4 (at least on my computer) leaves much to be desired. This means that in the two most popular browsers, the animation is either non-existant, or of the bad stop-motion variety!

So, before you check out the demo, make sure you're running a respectable version of Safari or Chrome (any predictions on when it will run in IE?) I hope you enjoy Visible Feeds. If you put it to good use, or have any questions about how it works, please drop me a note in the comments below.

Launch Demo