How do I run two pre-built jQuery animation sequences in parallel with one callback when both are complete?

I have some reusable animation sequence functions made with jQuery animation. I would like to run two of them in parallel, called from the same point, with one callback when both are complete. Any suggestions on how I do this?

A simple example with two animation sequences that I want to run in parallel would be:

function deleteel(element) {
    element.parentNode.removeChild(element);
}

function animation1(inputcallback){
    var div1 = document.createElement("div");
    div1.style.position = "absolute";
    div1.style.left = "0px";
    div1.style.top = "0px";
    div1.style.width = "10px";
    div1.style.height = "10px";
    div1.style.backgroundColor="red";
    document.getElementById("animationcontainer").appendChild(div1);

    $(div1).animate({top: "50"}, "slow", function() {
        $(div1).animate({left: "+50"}, "slow", function() {
            $(div1).animate({top: "20", left: "0"}, "slow", function() {
                $(div1).animate({height: "50", top: "110", left: "0"}, "slow", function() {
                    $(div1).remove();
                    inputcallback();
                });        
            });
        });
    });
    
}

function animation2(inputcallback){
    var div1 = document.createElement("div");
    div1.style.position = "absolute";
    div1.style.left = "100px";
    div1.style.top = "100px";
    div1.style.width = "15px";
    div1.style.height = "15px";
    div1.style.backgroundColor="blue";
    document.getElementById("animationcontainer").appendChild(div1);

    $(div1).animate({top: "10"}, "fast", function() {
        $(div1).animate({left: "+60"}, "slow", function() {
            $(div1).animate({top: "200", left: "100"}, "slow", function() {
                $(div1).animate({width: "50", top: "10", left: "100"}, "slow", function() {
                    $(div1).remove();
                    inputcallback();
                });        
            });
        });
    });
    
}
      

<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="animationcontainer" style="position:relative; width:500px; height:500px; background-color:grey"></div>
<button onclick="animation1(function () { alert('Finished');})">Animation 1</button>
<button onclick="animation2(function () { alert('Finished');})">Animation 2</button>
<button onclick="">Both animations</button>
      

Run codeHide result


+3


source to share


4 answers


Start by returning promises from functions instead of using callbacks

function animation1(){
    var div1 = $("<div />", {
        css : {
            position   : 'absolute',
            left       : '0px',
            top        : '0px',
            width      : '10px',
            height     : '10px',
            background : 'red'
        }
    });

    $("#animationcontainer").append(div1);

    return div1.animate({top    : "50" }, "slow")
               .animate({left   : "+50"}, "slow")
               .animate({top    : "20", left : "0" }, "slow")
               .animate({height : "50", top  : "110", left: "0"}, "slow")
               .promise()
               .done(function() {
                   $(this).remove();
               });
}

      

Then add the appropriate event handlers

$('#button1').on('click', function() {
    animation1().done(function() {
        alert('finished');
    });
});

      



and for the latter, to handle both animations, you do

$('#button3').on('click', function() {
    $.when(animation1(), animation2()).done(function() {
        alert('finished');
    });
});

      

FIDDLE

+4


source


You can use lazy jquery lazy

$.when(
    $element1.animate(...).promise(),
    $element2.animate(...).promise()
).done(function() {
    alert("Both animations are finished");
});

      



var $el1 = $('#el1'),
    $el2 = $('#el2');

$.when(
    $el1.animate({'left': 100}).promise(),
    $el2.animate({'left': 200}, 2000).promise()
).done(function() {
    alert("Both animations are finished");
});
      

#el1, #el2 { position: absolute; top: 0; left:0; width: 100px; height: 100px; background: red }
#el2 { background: blue; top: 100px }
      

<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<div id="el1"></div>
<div id="el2"></div>
      

Run codeHide result


  • here you will find an example explaining deferred
+2


source


If I can understand that you want to run the same callback after the animation finishes, but only once. The following code will do it. It's a bit old school, but it works.

We need to somehow determine that we have executed the callback. Therefore, we pass the token for the callback. It then identifies if a token is present (meaning it doesn't fire for that token) and it removes the tokens and runs the code.

var tokens = [];

function animCallback(token) {

  var i = tokens.indexOf(token);

  if (i < 0) {
    //token already removed
    return;
  }

  tokens.splice(i, 1);

  alert('finished');
}

function animateBoth() {
  tokens.push('mytoken');
  animation1(function() {
    animCallback('mytoken');
  });
  animation2(function() {
    animCallback('mytoken');
  });
}

      

function deleteel(element) {
  element.parentNode.removeChild(element);
}

var tokens = [];

function animCallback(token) {

  var i = tokens.indexOf(token);

  if (i < 0) {
    //token already removed
    return;
  }

  tokens.splice(i, 1);

  alert('finished');
}

function animateBoth() {
  tokens.push('mytoken');
  animation1(function() {
    animCallback('mytoken');
  });
  animation2(function() {
    animCallback('mytoken');
  });
}

function animation1(inputcallback) {
  var div1 = document.createElement("div");
  div1.style.position = "absolute";
  div1.style.left = "0px";
  div1.style.top = "0px";
  div1.style.width = "10px";
  div1.style.height = "10px";
  div1.style.backgroundColor = "red";
  document.getElementById("animationcontainer").appendChild(div1);

  $(div1).animate({
    top: "50"
  }, "slow", function() {
    $(div1).animate({
      left: "+50"
    }, "slow", function() {
      $(div1).animate({
        top: "20",
        left: "0"
      }, "slow", function() {
        $(div1).animate({
          height: "50",
          top: "110",
          left: "0"
        }, "slow", function() {
          $(div1).remove();
          inputcallback();
        });
      });
    });
  });

}

function animation2(inputcallback) {
  var div1 = document.createElement("div");
  div1.style.position = "absolute";
  div1.style.left = "100px";
  div1.style.top = "100px";
  div1.style.width = "15px";
  div1.style.height = "15px";
  div1.style.backgroundColor = "blue";
  document.getElementById("animationcontainer").appendChild(div1);

  $(div1).animate({
    top: "10"
  }, "fast", function() {
    $(div1).animate({
      left: "+60"
    }, "slow", function() {
      $(div1).animate({
        top: "200",
        left: "100"
      }, "slow", function() {
        $(div1).animate({
          width: "50",
          top: "10",
          left: "100"
        }, "slow", function() {
          $(div1).remove();
          inputcallback();
        });
      });
    });
  });

}
      

<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<button onclick="animateBoth()">Both animations</button>
<div id="animationcontainer" style="position:relative; width:500px; height:500px; background-color:grey"></div>
<button onclick="animation1(function () { alert('Finished');})">Animation 1</button>
<button onclick="animation2(function () { alert('Finished');})">Animation 2</button>
      

Run codeHide result


+1


source


you can also do this in pure js mode if you like, not as elegant as promises and defers, but fyi.

var calledOnce = false;

function doBoth() {
  setTimeout(doAnimation1, 1);
  setTimeout(doAnimation2, 1);
}
function doAnimation1() {
   animation1(cb);
}
function doAnimation2() {
   animation2(cb);
}

function cb() {
  if (!calledOnce) {
    calledOnce = true;
    return;
  }

  alert("one done"); // or call custom call back here.
  alert("two done");
}
      

Run codeHide result


use doBoth for onClick handler for both buttons. This will start both animations at almost the same moment. they will both call the normal handler, but only the last one to complete will execute the important logic in the callback.

0


source







All Articles