<h1>Manage Async Animation by using <a href="https://github.com/Takazudo/jQuery.DeferredPipeline">jQuery.DeferredPipeline</a></h1>
<ul class="desc">
  <li>I think it is difficult to animate some targets Asynchronous.</li>
  <li><a href="https://github.com/Takazudo/jQuery.DeferredPipeline">jQuery.DeferredPipeline</a> make it more simple.</li>
  <li>This is sample. Circle1 move to right then Circle2 move to right then Circle1 move to bottom then Circle2 move to bottom ....</li>
  <li>I really appreciate to <a href="https://github.com/Takazudo">@Takazudo.</a></li>
</ul>
<button class="start-button">start animation</button>
<section class="animation-outer">
  <div class="animation-inner">
    <h2>Circle 1</h2>
    <div class="circle1"></div>
  </div>
  <div class="animation-inner">
    <h2>Circle 2</h2>
    <div class="circle2"></div>
  </div>
</section>
@import "compass/css3";

html,body{
  min-width: 100%;
  min-height: 100%;
}
body{
  padding: 30px;
}

/* ----------------------------------------
 outer / inner
---------------------------------------- */
.animation-outer{
  display: -webkit-box;
  display: -moz-box;
}
.animation-inner{
  width: 400px;
}

/* ----------------------------------------
 circle
---------------------------------------- */
.circle{
  width: 100px;
  height: 100px;
  position: relative;
  display: inline-block;
  border-radius: 50px;
}

.circle1{
  @extend .circle;
  background-color: red;
}
.circle2{
  @extend .circle;
  background-color: blue;
}
View Compiled
/*! EveEve (https://github.com/Takazudo/EveEve)
 * lastupdate: 2013-06-13
 * version: 0.1.0
 * author: 'Takazudo' Takeshi Takatsudo <takazudo@gmail.com>
 * License: MIT */
(function(){var t,i=[].slice;t=window,t.EveEve=function(){function t(){}return t.prototype.on=function(t,i){var l,n,s,r,e;for(null==this._callbacks&&(this._callbacks={}),l=t.split(" "),r=0,e=l.length;e>r;r++)n=l[r],(s=this._callbacks)[n]||(s[n]=[]),this._callbacks[n].push(i);return this},t.prototype.once=function(t,i){return this.on(t,function(){return this.off(t,arguments.callee),i.apply(this,arguments)}),this},t.prototype.trigger=function(){var t,l,n,s,r,e,c;if(t=arguments.length>=1?i.call(arguments,0):[],n=t.shift(),s=null!=(c=this._callbacks)?c[n]:void 0){for(r=0,e=s.length;e>r&&(l=s[r],l.apply(this,t)!==!1);r++);return this}},t.prototype.off=function(t,i){var l,n,s,r,e,c;if(!t)return this._callbacks={},this;if(s=null!=(c=this._callbacks)?c[t]:void 0,!s)return this;if(!i)return delete this._callbacks[t],this;for(n=r=0,e=s.length;e>r;n=++r)if(l=s[n],l===i){s=s.slice(),s.splice(n,1),this._callbacks[t]=s;break}return this},t}()}).call(this);

/*! jQuery.deferredPipeline (https://github.com/Takazudo/jQuery.deferredPipeline)
 * lastupdate: 2014-03-27
 * version: 0.1.0
 * author: 'Takazudo' Takeshi Takatsudo <takazudo@gmail.com>
 * License: MIT */
(function(){var a={}.hasOwnProperty,b=function(b,c){function d(){this.constructor=b}for(var e in c)a.call(c,e)&&(b[e]=c[e]);return d.prototype=c.prototype,b.prototype=new d,b.__super__=c.prototype,b};!function(a){var c,d,e;return c=window.EveEve,d={},d.util={},e=function(b){return a.Deferred(function(a){return setTimeout(function(){return a.resolve()},b)})},d.MESSAGE={error_no_deferred:"Error: registered function should return a deferred"},d.util.isPromise=function(a){return null!=a&&null!=a.then&&null!=a.done?!0:!1},d.Item=function(c){function e(b,c){this.options=a.extend({},d.Item.defaults,c),this.started=!1,this.running=!1,this.stopped=!1,this._fn=b}return b(e,c),e.defaults={done:null,fail:null,complete:null},e.prototype._attachNoDeferredMessage=function(a){return a.msg=d.MESSAGE.error_no_deferred},e.prototype._triggerSuccess=function(a){var b;return this.stopped=!0,"function"==typeof(b=this.options).done&&b.done(a),this.trigger("success",a),this._triggerComplete(a,!0)},e.prototype._triggerFail=function(a,b,c){var d,e;return null==b&&(b=!1),null==c&&(c=!1),this.stopped=!0,d={},d.aborted=b,c&&this._attachNoDeferredMessage(d),"function"==typeof(e=this.options).fail&&e.fail(a,d),this.trigger("fail",a,d),this._triggerComplete(a,!1,b,c)},e.prototype._triggerComplete=function(a,b,c,d){var e,f;return null==c&&(c=!1),null==d&&(d=!1),e={successed:b,aborted:c},d&&this._attachNoDeferredMessage(e),"function"==typeof(f=this.options).complete&&f.complete(a,e),this.trigger("complete",a,e)},e.prototype.destroy=function(){return this.off(),this._fn=null},e.prototype.stop=function(){var a;if(!this.stopped)return this.stopped=!0,null!=(a=this._completeStats)&&(a.aborted=!0),this._triggerFail([],!0)},e.prototype.run=function(){var a;return this._completeStats={aborted:!1},this.started=!0,!this.stopped||this.running?(this.running=!0,a=this._fn(this._completeStats),d.util.isPromise(a)?(this.trigger("run"),a.then(function(a){return function(){return a.running=!1,a.stopped?void 0:a._triggerSuccess(arguments)}}(this),function(a){return function(){return a.running=!1,a._triggerFail(arguments,!1)}}(this))):(this.running=!1,void this._triggerFail([],!1,!1,!0))):void 0},e}(c),d.Pipeline=function(c){function f(b){this.completeCount=0,this.options=a.extend({},d.Pipeline.defaults,b),this._items=[]}return b(f,c),f.defaults={pipeSize:3},f.prototype._beforeStoppingItems=function(){return this._stoppingItemsInProgress=!0,this.trigger("startStoppingItems")},f.prototype._afterStoppingItems=function(){return this._stoppingItemsInProgress=!1,this.trigger("endStoppingItems"),this.trigger("stop")},f.prototype._handleTheLastItemCompletion=function(){return this.running=!1,e(0).done(function(a){return function(){return a.trigger("allComplete")}}(this))},f.prototype._handleNextRun=function(){return this._stoppingItemsInProgress?this.once("endStoppingItems",function(a){return function(){return a._tryToRunNextItem()}}(this)):this._tryToRunNextItem()},f.prototype._findNextPendingItem=function(){var a,b,c,d;for(d=this._items,b=0,c=d.length;c>b;b++)if(a=d[b],a.started===!1)return a;return null},f.prototype._removeItem=function(a){var b,c,d,e,f;for(c=[],f=this._items,d=0,e=f.length;e>d;d++)b=f[d],b!==a&&c.push(b);return this._items=c,a.destroy()},f.prototype._tryToRunNextItem=function(){var a,b,c,d,e;for(b=this.options.pipeSize,d=this.countRunningItems(),a=!1,c=function(b){return function(){var c;return c=b._findNextPendingItem(),null!=c?c.run():a=!0,d=b.countRunningItems()}}(this),e=[];b>d&&!a;)e.push(c());return e},f.prototype.destroy=function(){var a,b,c,d;for(this.stopAll(),this.off(),d=this._items,b=0,c=d.length;c>b;b++)a=d[b],a.destroy();return this._items=null},f.prototype.add=function(a,b){var c;return c=new d.Item(a,b),c.on("run",function(a){return function(b){return a.trigger("itemRun",b)}}(this)),c.on("success",function(a){return function(b){return a.trigger("itemSuccess",b)}}(this)),c.on("fail",function(a){return function(b,c){return a.trigger("itemFail",b,c)}}(this)),c.on("complete",function(a){return function(b,d){var e;return e=1===a._items.length,a._removeItem(c),a.completeCount+=1,a.trigger("itemComplete",b,d),e?a._handleTheLastItemCompletion():a._handleNextRun()}}(this)),this._items.push(c)},f.prototype.countRunningItems=function(){var a,b,c,d,e;for(b=0,e=this._items,c=0,d=e.length;d>c;c++)a=e[c],a.running&&(b+=1);return b},f.prototype.size=function(){return this._items.length},f.prototype.run=function(){return!this.running&&this._items.length?(this.running=!0,this.trigger("run"),this._tryToRunNextItem()):void 0},f.prototype.stopAll=function(){var a,b,c,d;for(this._beforeStoppingItems(),d=this._items,b=0,c=d.length;c>b;b++)a=d[b],a.stop();return this._afterStoppingItems()},f.prototype.stopAllWithoutTheLast=function(){var a,b,c,d,e;for(this._beforeStoppingItems(),b=this._items[this._items.length-1],e=this._items,c=0,d=e.length;d>c;c++)a=e[c],a!==b&&a.stop();return this._afterStoppingItems()},f}(c),a.DeferredPipelineNs=d,a.DeferredPipeline=d.Pipeline}(jQuery)}).call(this);


(function(){
  'use strict';
  var $startButton = $('.start-button'),
      $circle1 = $('.circle1'),
      $circle2 = $('.circle2'),
      dfd1,dfd2;
  
  $startButton.on('click', function(){
    var pipeline = new $.DeferredPipeline({ pipeSize: 1 });
    dfd1 = {
      right : doAnimation($circle1, {right : '-=300px'}), // animation to right
      bottom: doAnimation($circle1, {bottom: '-=300px'}), // animation to bottom
      left  : doAnimation($circle1, {right : '+=300px'}), // animation to left
      top   : doAnimation($circle1, {bottom: '+=300px'})  // animation to top
    },
    dfd2 = {
      right : doAnimation($circle2, {right : '-=300px'}), // animation to right
      bottom: doAnimation($circle2, {bottom: '-=300px'}), // animation to bottom
      left  : doAnimation($circle2, {right : '+=300px'}), // animation to left
      top   : doAnimation($circle2, {bottom: '+=300px'})  // animation to top
    };
    pipeline.add(dfd1.right);
    pipeline.add(dfd2.right);
    pipeline.add(dfd1.bottom);
    pipeline.add(dfd2.bottom);
    pipeline.add(dfd1.left);
    pipeline.add(dfd2.left);
    pipeline.add(dfd1.top);
    pipeline.add(dfd2.top);
    pipeline.run();
  });
  
  // common animation function
  function doAnimation($elm, prop){
    return function(){
      var d = $.Deferred();
      $elm.animate(prop, {
        complete: d.resolve
      });
      return d;
    }
  }
})();

External CSS

  1. //cdnjs.cloudflare.com/ajax/libs/skeleton/1.2/base.min.css
  2. //cdnjs.cloudflare.com/ajax/libs/skeleton/1.2/skeleton.css
  3. //cdnjs.cloudflare.com/ajax/libs/skeleton/1.2/layout.css

External JavaScript

  1. //cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.min.js