Tag Archives: Requestanimationframe

JavaScript animation timing made easy (requestAnimationFrame, cancelRequestAnimationFrame)

I often find myself writing a lot of the same code from project to project. One of those pieces of code is around using requestAnimationFrame and cancelRequestAnimationFrame. I wrote a quick and dirty animation object that allows me to register all of my rendering functions, start the animation and cancel it. Here’s an example of how to use this object:

function render () {
   // Do your rendering work here
}
Animation.add(render);
Animation.start();

It can also be used to add several animations before and even after Animation has started, you can add new rendering routines on the fly. This is useful in a number of instances where objects have their own render routines and you want to add rendering routines at different times.

I put in a few workarounds. The typical hack to allow setTimeout/cancelTimeout fallbacks are in there. There is also a work around to deal with the missing mozCancelRequestAnimationFrame and the fact that mozRequestAnimationFrame doesn’t return an integer per spec.

So here’s the code for the Animation object:

/*!
 * jsSimple v0.0.1
 * www.jssimple.com
 *
 * Copyright (c) Tobin Titus
 * Available under the BSD and MIT licenses: www.jssimple.com/license/
 */
var Animation = (function() {
  "use strict";
  var interval, moz, renderers = [];

// PRIVATE
  function __mozFix (val) {
    /* Mozilla fails to return interger per specification */
    if (!val) { 
      moz = true;
      val = Math.ceil(Math.random() * 10000);
    } 
    return val;
  }

  function __renderAll() {
    var r, length;

    // execute all renderers
    length = renderers.length;
    for (r = 0; r < length; ++r) {
       renderers[r]();
     }
     if (interval) {
       interval = __mozFix(window.requestAnimationFrame(__renderAll));
     }
   }
   function __registerAnimationEventHandlers() {
     var pre, prefixes = ['webkit', 'moz', 'o', 'ms'];
     // check vendor prefixes
     for (pre = prefixes.length - 1; 
          pre >= 0 && !window.requestAnimationFrame; --pre) {
      window.requestAnimationFrame = window[prefixes[pre] 
            + 'RequestAnimationFrame'];
      window.cancelAnimationFrame = window[prefixes[pre] 
            + 'CancelAnimationFrame'] 
            || window[prefixes[pre] + 'CancelRequestAnimationFrame'];
    }

    // downlevel support via setTimeout / clearTimeout
    if (!window.requestAnimationFrame) {
      window.requestAnimationFrame = function(callback) {
        // could likely be an adaptive set timeout
        return window.setTimeout(callback, 16.7);
      };
    }

    if (!window.cancelAnimationFrame) {
      window.cancelAnimationFrame = function(id) {
        clearTimeout(id);
      };
    }
  }
  __registerAnimationEventHandlers(); 

// PUBLIC
  function add(renderer) {
    if (renderer) {
      renderers.push(renderer);
    }
  }

  function isRunning() {
    return !!interval;
  }

  function start() {
    interval = __mozFix(window.requestAnimationFrame(__renderAll));
  }

  function stop() {
    if (!moz) {
      window.cancelAnimationFrame(interval);
    }
    interval = null;
  }

// OBJECT DEFINITION
  return {
    "add": add,
    "isRunning": isRunning,
    "start": start,
    "stop": stop
  };
}());

I’ve put this code on jsFiddle to test out.

I’ve also put this on github as jsSimple. I’ll likely add a few other bits of code to this as I move along.