Search code examples
jqueryarraysanimationfadecycle

jQuery - cycle through multiple animations on single function / create array of animations so every animation is different and then it starts over


I need to have multiple animations cycling, or randomly changing, every time animation occurs on single function.

Currently there's a fade, what I want is to cycle through for example fade, toggle, animate, etc

Here is my code i need it to be used in:

var items = $(".items").on("click", "div", function() {
    var not = $.map($(".items div").not(this), function(el) {
      return $(el).index(".items div")
    });
    var next = not[Math.floor(Math.random() * not.length)];
    var index = $(this).index(".items div"); 
    var elems = $(">div", items).toArray();
    [elems[next], elems[index]] = [elems[index], elems[next]];
  $(this).add(items.find("div").eq(next)).fadeTo(600, 0, function() {
    items.html(elems).find("div").fadeTo(600, 1)
  });
});

I cant find any information about this anywhere but I saw it being used in some scripts, thanks a lot for ideas


Solution

  • Initial version was simpler, but slightly bugged as the effect callbacks for the swap get called twice. New version at the bottom:

    JSFiddle: https://jsfiddle.net/TrueBlueAussie/w8cmvu1m/5/

    The transitions are queued up in this example using .then()

    $(function() {
    
      // Swap the html of two elements
      var swap = function($elements) {
        var val = $elements.eq(0).html();
        $elements.eq(0).html($elements.eq(1).html());
        $elements.eq(1).html(val);
      };
    
      // Array of effects functions - each takes 2 jQuery elements and swaps the values while not visible
      // Each returns a promise that resolves once the animation has completed
      var effects = [
        // fade in/out
        function($elements) {
          return $elements.fadeOut(function() {
            swap($elements);
          }).fadeIn().promise();
        },
        // Animate
        function($elements) {
          return $elements.slideUp(function() {
            swap($elements);
          }).slideDown().promise();
        }
      ];
    
      // Start with a resolved promise
      var promise = $.when();
    
      // Delegated handler for click on items
      $(document).on("click", ".items div", function() {
        // All items except this one
        var $item = $(this);
        var $notThis = $(".items div").not($item);
    
        // Randomly choose another item
        var $other = $notThis.eq(Math.floor(Math.random() * $notThis.length));
    
        // Randomly choose an effect
        var effect = Math.floor(Math.random() * effects.length);
        promise = promise.then(effects[effect]($item.add($other)));
      });
      var interval = setInterval(function() {
        var $items = $('.items div');
        $items.eq(Math.floor(Math.random() * $items.length)).click()
      }, 3000);
    });
    

    Updated: https://jsfiddle.net/TrueBlueAussie/w8cmvu1m/6/

    I corrected the double-callback issue using deferreds and promises, but not happy with the solution:

      var effects = [
        // fade in/out
        function($elements) {
            var def = $.Deferred();
          $elements.fadeOut(500).promise().always(function() {
            swap($elements);
            $elements.fadeIn(500).promise().always(function(){
                def.resolve();
            });
          });
          return def.promise();
        },
        // Animate
        function($elements) {
            var def = $.Deferred();
          $elements.slideUp(500).promise().always(function() {
            swap($elements);
            $elements.slideDown(500).promise().always(function(){
                def.resolve();
            });
          });
          return def.promise();
        }
      ];