Related
I would like to chain three animations with JavaScript and Transit.js. The second one must be launched after the first is complete. For this I'm trying to use callbacks.
It's important for me to separate these three functions. I don't want to nest them.
// fn 1
function one(callback) {
$(body).transition({
'opacity': '1'
}, 500, function(){
callback;
console.log('callback one');
});
}
// fn 2
function two(callback) {
$('section').transition({
'opacity':'1',
}, 500, function(){
console.log('callback two');
callback;
});
}
// fn 3
function three(callback) {
$('aside').transition({
'opacity':'1',
}, 500, function(){
console.log('callback three');
callback;
});
}
I start functions like this:
one(two(tree()));
The problem is, the second function starts before the first is completed. What is wrong with this script?
Wrap your callback function in setTimeout function
function one(callback) {
$(body).transition({
'opacity': '1'
}, 500, function(){
setTimeout(function(){
callback();
console.log('callback one');
},0);
});
}
// fn 2
function two(callback) {
$('section').transition({
'opacity':'1',
}, 500, function(){
setTimeout(function(){
callback();
console.log('callback two');
},0);
});
}
// fn 3
function three(callback) {
$('aside').transition({
'opacity':'1',
}, 500, function(){
setTimeout(function(){
callback();
console.log('callback three');
},0);
});
}
try this, it should work as you expected.
// fn 3
function three() {
$('aside').transition({
'opacity':'1',
}, 500, function(){
console.log('callback three');
});
}
// fn 2
function two() {
$('section').transition({
'opacity':'1',
}, 500, three);
}
// fn 1
function one() {
$(body).transition({
'opacity': '1'
}, 500, two);
}
one();//invoke fn1
You need to pass the reference of the function as the argument instead of passing the executed result.
// this would execute the function `three` and pass the
// returned value as the argumnet to function `two`
// after executing `two` the returned value would pass to
// function function `one` . Since any none of the function is
// returning anything the argument value would be `undefined`
one(two(tree()));
The better way to make it cyclic is adding the callback as the function reference.
function one() {
$(body).transition({
'opacity': '1'
}, 500, two);
}
// fn 2
function two() {
$('section').transition({
'opacity':'1',
}, 500, three);
}
// fn 3
function three() {
$('aside').transition({
'opacity':'1',
// if you don't want to make it cyclic then remove the
// callback argument `one`
}, 500, one);
}
UPDATE : If you wanna pass it as an argument then pass the reference as a callback function and within the callback call the function you want.
function one(callback) {
$(body).transition({
'opacity': '1'
}, 500, function(){ calback() });
// or simply
// }, 500, calback);
}
// fn 2
function two(callback) {
$('section').transition({
'opacity':'1',
}, 500, , function(){ calback() });
}
// fn 3
function three(callback) {
$('aside').transition({
'opacity':'1',
}, 500, , function(){ calback() });
}
// use nested callbacks as you want
one(function(){ two(function(){ three(function(){}); }); })
Having a few teething problems with $.deferred, $.when and $.done.
I'm calling a method which has a couple of tasks inside on a timer. I'm looking at getting a callback when everything inside this method has completed, including the stuff in timers, so started looking at $.when() and $.done() to achieve this.
The problem I am getting is the function is firing before the tasks have completed, immediately as the method is called. So, I started playing with $.deferred and resolve(), but haven't managed to get anything working. Without the timers, I can do it.
This is where I call the method:
$.when(cover.start()).done(function() {
console.log("Cover has started.");
});
This is the entire method:
return {
other: function() {},
start: function() {
var dfd = $.Deferred();
el.filter.animate({
"opacity": "0.6", "filter": "alpha(opacity=60)"
}, 2000, "easeInOutCirc", function() {
el.share.removeClass('fa-spin');
setTimeout(function() {
el.share.removeClass('fa-cog').addClass('fa-bars');
},1000);
setTimeout(function() {
el.scroll.animate({
"opacity": "1",
"bottom": "40px"
}, 1200, "easeOutBounce", function() {
var pulseOptions = { opacity: "0" };
setTimeout(function() {
el.scroll.pulse(pulseOptions, {
duration : 400,
pulses: 3,
interval: 500,
returnDelay: 800
});
}, 2000);
dfd.resolve();
});
}, 2000);
return dfd.promise();
});
}
} // end return
As you can see, after my original attempt failed, I added dfd.resolve() to where I want the callback and tried to return the promise. However, the function still fires too early. Where am I going wrong?
The problem is, you need to return promise from the start method
return {
other: function () {},
start: function () {
var dfd = $.Deferred();
el.filter.animate({
"opacity": "0.6",
"filter": "alpha(opacity=60)"
}, 2000, "easeInOutCirc", function () {
el.share.removeClass('fa-spin');
setTimeout(function () {
el.share.removeClass('fa-cog').addClass('fa-bars');
}, 1000);
setTimeout(function () {
el.scroll.animate({
"opacity": "1",
"bottom": "40px"
}, 1200, "easeOutBounce", function () {
var pulseOptions = {
opacity: "0"
};
setTimeout(function () {
el.scroll.pulse(pulseOptions, {
duration: 400,
pulses: 3,
interval: 500,
returnDelay: 800
});
}, 2000);
dfd.resolve();
});
}, 2000);
});
//need to return from start
return dfd.promise();
}
} // end return
Not fishing to steal APJ's rep' but out of interest, you could avoid callback hell by exploiting .delay() and .promise(), both of which relate to the default "fx" animation queue.
Something along the following lines would fix the problem, and would be more readable :
//animation maps
var maps = [];
maps[0] = { 'opacity':0.6, 'filter':'alpha(opacity=60)' };
maps[1] = { 'opacity':1, 'bottom':'40px' };
maps[2] = { 'opacity':0 };
maps[3] = { 'duration':400, 'pulses':3, 'interval':500, 'returnDelay':800 };
//animation functions
var f = [];
f[0] = function () {
return el.filter.animate(maps[0], 2000, "easeInOutCirc").promise();
};
f[1] = function () {
return el.share.removeClass('fa-spin').delay(1000).promise();
};
f[2] = function () {
return el.share.removeClass('fa-cog').addClass('fa-bars').delay(1000).promise();
};
f[3] = function () {
el.scroll.animate(maps[1], 1200, "easeOutBounce").promise();
}
f[4] = function () {
return el.scroll.delay(2000).promise();//delay() could be called on any element. `el.scroll` is arbitrary.
};
f[5] = function () {
el.scroll.pulse(maps[2], maps[3]);
};
return {
other: function () {},
start: function () {
//animation sequence
var p = f[0]().then(f[1]).then(f[2]).then(f[3]);
p.then(f[4]).then(f[5]);
return p;//<<<< and here's the all important return
}
}
Not sure this is 100% correct - might need some work.
It's worth noting that there are performance pros and cons with this approach :
Pros: Reusable animation maps; Reusable functions;
Cons: More liberal use of promises will cause a larger memory spike.
I have the following jQuery animate function:
$myDiv.animate({ "left": "0%" }, { duration: 1000, easing: 'easeInOutExpo' },
function () {
alert('hi');
}
);
The animation itself works. $myDiv slides with the easeInOutExpo effect, as desired. However, the callback function is never fired. To test it, I changed the callback to just alert("hi");, as you can see above. Still doesn't work.
What could I be doing wrong?
Try this
Demo: jsFiddle
$("#myDiv").animate({ "left": "0%" }, { duration: 1000, easing: 'easeInOutExpo' ,
complete:function () {
alert('hi');
}
}
);
There are a couple of things that need fixing here:
Make sure you've included jQuery UI in your code, because easeInOutExpo is not part of the standard jQuery library.
Your syntax is wrong: you're mixing up the two different options for the animate() function.
It's either
$(element).animate(properties [,duration] [,easing] [,complete]);
or
$(element).animate(properties, options)
where options is an object formatted like this:
{
duration: number,
easing: string,
complete: function,
}
You've gone with the second option, so you need to format it properly to use the complete attribute of the options object for your function:
$myDiv.animate({
"left": "0%",
}, {
duration: 1000,
easing: "easeInOutExpo",
complete: function () {
alert('hi');
},
});
Demo
Alternatively, you could use the first format option:
$("#myDiv").animate({
"left": "0%",
}, 1000, "easeInOutExpo", function () {
alert('hi');
});
Demo
One way is use of JQuery Promise
$myDiv.animate({ "left": "0%" }, { duration: 1000, easing: 'easeInOutExpo' }).promise().done(function(){
alert('hi done');
});
$("#div_id").click(function() {
$(this).effect("shake", { times:3 }, 300);
// Here I want wait that the div_id can complete its shake
alert('hello');
}
.delay I tried but it is not working.
$(this).effect("shake", { times:3 }, 300, function() {
// this will alert once the animation has completed
alert('hello');
});
If you're using jQuery UI, you should just be able to add a callback as the 4th parameter.
See: http://jqueryui.com/demos/effect/
$("#div_id").click(function() {
$(this).effect("shake", { times:3 }, 300, function(){
alert('hello');
});
}
$(this).effect("shake", { times:3 }, 300, function() {
alert('hello');
});
I have the following animations in my web page:
$(".anim-item").not(this).animate({
opacity: 0,
}, { queue: true, duration: 1000 } , function() {
// Animation complete.
});
$(this).animate({
left: 200,
}, { queue: true, duration: 1000 } , function() {
// Animation complete.
});
Currently both the animations are running simultaneously. I want the second animation to run after the first one. I tried putting the second one inside the callback function, but cannot find a way to get the $(this) reference working. Any idea how to get this working?
Thanks in advance.
Your function is wrong, if you are declaring options, then the callback goes in the options object:
$(".anim-item").animate({
opacity: 1,
}, {duration: 1000, queue: true, complete: function() {
$(this).animate({
left: 200,
}, { queue: true, duration: 1000, complete: function() {
// Animation complete.
}});
}});
Also, don't make a global variable containing the item, that's just asking for trouble, especially as jquery will maintain it for you in this instance, if you need to declare a new variable for the object in chaining, generally you are not doing it right ;)
Two ways:
cache this in a local variable before calling .animate()
use .proxy() to pass your this reference to .animate()
example 1:
var func = function(){
var self = this;
$(".anim-item").not(this).animate({
opacity: 0,
}, { queue: true, duration: 1000 } , function() {
self.animate({});
});
};
example 2:
var func = function(){
$.proxy($(".anim-item").not(this).animate({
}), this);
};
Save it under a different name, like this:
var myThis = this;
$(".anim-item").not(this).animate({
opacity: 0,
}, { queue: true, duration: 1000 } , function() {
$(myThis).animate({
left: 200,
}, { queue: true, duration: 1000 } , function() {
// Animation complete.
});
});
The closure of the inner function will make sure it's visible.
Make an alias for this via
var _this = this;
If you write a jQuery query $('.abc') and use functions like click, hover etc, this will always reference to current DOM node jQuery is processing.
Store this in a local variable.
var _this = this;
$(".anim-item").not(this).animate({
opacity: 0,
}, { queue: true, duration: 1000 } , function() {
// Animation complete. Next animation
$(_this).animate({
left: 200,
}, { queue: true, duration: 1000 } , function() {
// Animation complete.
});
}
);
In a jQuery callback function this is always set to the DOM element that the function applies to.
If you want access to this in your first callback function you'll have to create a reference to it before you animate:
var self = this;
$(".anim-item").not(this).animate({
opacity: 0,
}, { queue: true, duration: 1000 } , function() {
$(self).animate({
left: 200,
}, { queue: true, duration: 1000 } , function() {
// Animation complete.
});
});