I have a number of jQuery animation effects applied to certain elements on the page:
jQuery("#bgd_balance").animate({
backgroundPositionY: "0px",
backgroundPositionX: "0px",
'background-size':'100%'
},800,"swing");
jQuery(".balance_left_box").delay(2000).slideDown(200,"easeInCirc");
jQuery(".balance_left_box p.first-line").delay(2400).slideDown(600,"easeInCirc");
jQuery(".balance_left_box").delay(1000).animate({
height:"270px",
top:"64px"
},100,"easeInCirc");
The problem I'm facing is that when I'm tweaking delay of a certain element, I have to go through everything and adjust all other delays accordingly.
Is it possible to have something like this instead (pseudocode):
queue.add(
delay(2000),
jQuery(".balance_left_box").slideDown(200,"easeInCirc"),
delay(2000),
jQuery(".balance_left_box p.first-line")X.slideDown(600,"easeInCirc");
delay(1000),
jQuery(".balance_left_box").animate({
height:"270px",
top:"64px"
},100,"easeInCirc");
).run();
I know I can achieve this "queuing" by adding callback function to animate()
call but then resulting code will be really bulky and hard to read, in my opinion.
3 Answers 3
The way I see it, you have 2 options; either use Deferreds, or store your delay in a variable:
var delay = 0,
$left_box = $(".balance_left_box");
$("#bgd_balance").animate({
backgroundPositionY: "0px",
backgroundPositionX: "0px",
'background-size':'100%'
}, 800, "swing");
$left_box.delay(delay += 2000).slideDown(200, "easeInCirc");
$left_box.find("p.first-line").delay(delay += 2400).slideDown(600, "easeInCirc");
$left_box.delay(delay += 1000).animate({
height:"270px",
top:"64px"
}, 100, "easeInCirc");
You could create your own queue by creating a temporary element and adding methods to it's queue using promise objects
var queueEl = $("<div></div>");
queueEl.delay(2000).queue(function(next){
jQuery(".balance_left_box").slideDown(200,"easeInCirc").promise().done(next);
}).delay(2000).queue(function(next){
jQuery(".balance_left_box p.first-line").slideDown(600,"easeInCirc").promise().done(next);
}).delay(1000).queue(function(next){
jQuery(".balance_left_box").animate({
height:"270px",
top:"64px"
},100,"easeInCirc").promise().done(next);
});
Though it might as well just be nested callbacks.
I created this function a little while ago:
function queue(start) {
var rest = [].splice.call(arguments, 1),
promise = $.Deferred();
if (start) {
$.when(start()).then(function () {
queue.apply(window, rest);
});
} else {
promise.resolve();
}
return promise;
}
I think you could do exactly what you are doing like this:
queue(function () {
return jQuery(".balance_left_box").delay(2000).slideDown(200,"easeInCirc");
}, function () {
return jQuery(".balance_left_box p.first-line").delay(2000).slideDown(600,"easeInCirc");
}, function () {
return jQuery(".balance_left_box").delay(1000).animate({
height:"270px",
top:"64px"
},100,"easeInCirc");
});
While that doesn't look so great (I think you should be naming your animations), with an added delay utility function:
function delay(msec) {
return function () {
var promise = $.Deferred();
window.setTimeout(function () { promise.resolve(); }, msec);
return promise;
};
}
you could write it like this:
(function ($) {
function showMessageBox($message) {
return function () {
return $message.slideDown(200,"easeInCirc");
};
}
function showMessageTitle($title) {
return function () {
return $title.slideDown(600,"easeInCirc");
};
}
function resizeMessage($message) {
return function () {
return $message.animate({
height:"270px",
top:"64px"
},100,"easeInCirc");
};
}
//and then, it is just one more animation ...
function showMessage($message, $title) {
return function () {
return queue(
delay(2000),
showMessageBox($message),
delay(2000),
showMessageTitle($title),
delay(1000),
resizeMessage($message)
);
};
}
$(function () {
var $message = $(".balance_left_box"),
$title = $message.find("p.first-line"),
animation = showMessage($message, $title);
animation();
//you could have saved some code by not returning a function
//but I think it is better to follow a convention wherein
//all animations are functions that you must call to have them execute
//because then reusing them is easier later
});
}(jQuery));