Simple animation method with requestAnimationFrame code structure I'm writing a animate method to perform DOM animations. I know there are a lot of libraries out there that already does that (and I use some ideas of them) but I usually prefer writing my own code to pinpoint my problem.

The code below is working perfectly, but something with the structure is making me uncomfortable. I think it is because I use references to itself just before the objects dies, so I'm not sure if I'm reusing the animation object or creating new ones. Apparently there is no memory leak.

Can someone give it a look? Fell free to suggest code modifications even if they are unrelated with the memory leak.

window.animate = function(options) {

    var fps = options.fps || 30;
    var x = options.x || null;
    var y = options.y || null;
    var duration = Math.ceil((options.duration / 1000) * fps);
    var onProgress = options.onProgress || null;
    var onComplete = options.onComplete || null;        
    var easing = easing || 'easeInOutQuad';

    var animation = {
        fps: fps,
        x: x,
        y: y,
        duration: duration,
        onProgress: onProgress,
        onComplete: onComplete,
        easing: easing,
        _id: null,
        _iteration: 1,
        _lastFrameOccurrence: 0,
        _draw: function() {

            var $this = this;

            var now = Date.now();
            var delta = now - $this._lastFrameOccurrence;
            var interval = 1000 / $this.fps;

            if (delta &gt; interval) {

                $this._lastFrameOccurrence = now - (delta % interval);

                var response = {};
                var factor = window.w.easing[$this.easing]($this._iteration, $this.duration);
                for (var i in $this.x)
                    if ($this.x.hasOwnProperty(i))
                        response[i] = $this.x[i] + ($this.y[i] - $this.x[i]) * factor;

                if ($this.onProgress instanceof Function)
                    $this.onProgress(response);

                $this._iteration++;

                if ($this._iteration &gt; $this.duration)
                    if ($this.onComplete instanceof Function)
                        $this.onComplete();

            };

            if ($this._iteration &lt;= $this.duration) {
                $this._id = requestAnimationFrame(function() {
                    $this._draw();
                });
            } else {
                cancelAnimationFrame($this._id);
            };

        }
    };

    animation._draw();

};

                w.animate({
                    x: { opacity: 1 },
                    y: { opacity: 0 },
                    duration: 500,
                    onProgress: function(r) { console.log(r); },
                    onComplete: function() {}
                });
Firstly, the usage example at the end doesn't really make sense. You're passing 5 arguments to the function, but it seems to only take 1 options object.

Structurally, it seems overwrought to create the animation object. Everything can be handled with closures, as far as I can tell. Somewhat in the same vein, I see no reason for the $ and _ prefixes on half the stuff.

Speaking of naming, the x and y options should probably be called from and to or something. It took me a few moments to realize that's what they are - not coordinates or something similar.

Your setup also seems conflicted, or at any rate complicated. You're using frame count, FPS, and duration (time) to track things, though these may contradict each other. You can't really dictate FPS; FPS is a measured value. If the browser is busy, FPS will drop, meaning each iteration will take longer. But you're calculating animation progression based on _iteration versus duration (which has since been redefined as the target number of frames, not time).

So I can say "I want 600,000 FPS!" but I won't get that kind of performance (requestAnimationFrame aims for 30 to 60'ish FPS, usually). But I can also say I want a duration (time-wise) of 1 second, yet the animation will still run 600,000 iterations. However this will not complete in 1 second. 

It makes slightly more sense if you want low FPS, since your function just skips some onProgress calls. But low FPS is usually not considered a thing to strive for, really. In case you do want it, it might be easier to use setTimeout instead of requestAnimationFrame and ignoring updates.

Typically, animations have a set duration (in time), and FPS is whatever it is. You can't control it anyway. And so counting frames usually isn't very useful. Instead you do:

animationProgress = (now - startTime) / totalDuration


And you clamp that to 0...1. That's the input to the easing function.

Lastly, there's no need to call cancelAnimationFrame unless you've already called requestAnimationFrame and want to undo that before the next repaint.

Here's a pretty basic implementation:



function animate(options) {
  options = options || {};
  
  // defaults
  var duration = options.duration || 1000,
      ease = options.easing || function (a) { return a }, // basic linear easing
      onProgress = options.onProgress || function () {},
      onComplete = options.onComplete || function () {},
      from = options.from || {},
      to = options.to || {};
  
  // runtime variables
  var startTime = Date.now();
  
  function update() {
    var deltaTime = Date.now() - startTime,
        progress = Math.min(deltaTime / duration, 1),
        factor = ease(progress),
        values = {},
        property;
    
    for(property in from) {
      if(from.hasOwnProperty(property) &amp;&amp; to.hasOwnProperty(property)) {
        values[property] = from[property] + (to[property] - from[property]) * factor;
      }
    }
    
    onProgress(values);
    
    if(progress === 1) {
      onComplete(deltaTime);
    } else {
      requestAnimationFrame(update);
    }
  }
  
  requestAnimationFrame(update);
}

// ==============================

// Example
animate({
  from: { a: 0 },
  to:   { a: 100 },
  duration: 500,
  onProgress: function (values) {
    document.getElementsByTagName("pre")[0].innerHTML += values.a + "\n";
  },
  onComplete: function (actualDuration, averageFps) {
    document.getElementsByTagName("pre")[0].innerHTML += "Done in " + actualDuration + "ms";
  }
});
&lt;pre&gt;&lt;/pre&gt;




I've opted to pass the easing function as an option, rather than a name. Thus you can supply your own easing function on the fly, or pass in a predefined function.