I'm having a problem with setInterval and jquery animate. Here is my code:
function slides1() {
...
$("table#agah1").animate({
"left": first1
}, "slow");
$("table#agah2").animate({
"left": first2
}, "slow");
}
$(function () {
cyc = setInterval("slides1()", 3000);
});
When switch to another browser tab, and return after a time, the animation keep doing it without delay, for the time I've been away from the tab, and then act correct. I've added these also without any luck:
$(window).focus(function () {
jQuery.fx.off = false;
cyc = setInterval("slides1()", 3000);
});
$(window).blur(function () {
jQuery.fx.off = true;
window.clearInterval(cyc);
});
Newer versions of jQuery use requestAnimationFrame callbacks to handle effects, and browsers don't process those on hidden tabs.
In the meantime, your setInterval events are still happening, causing more animations to get queued up.
Rather than use setInterval to schedule the animations, use the "completion callback" of the last animation to trigger the next cycle, with a setTimeout if necessary.
function slides1() {
...
$("table#agah1").animate({
"left": first1
}, "slow");
$("table#agah2").animate({
"left": first2
}, "slow", function() {
setTimeout(slides1, 2000); // start again 2s after this finishes
});
}
$(function () {
setTimeout(slides1, 3000); // nb: not "slides1()"
});
This will ensure that there's a tight coupling between the interanimation delay and the animations themselves, and avoid any issues with setTimeout getting out of sync with the animations.
Related
I thought it would be simple but I still can't get it to work. By clicking one button, I want several animations to happen - one after the other - but now all the animations are happening at once. Here's my code - can someone please tell me where I'm going wrong?:
$(".button").click(function(){
$("#header").animate({top: "-50"}, "slow")
$("#something").animate({height: "hide"}, "slow")
$("ul#menu").animate({top: "20", left: "0"}, "slow")
$(".trigger").animate({height: "show", top: "110", left: "0"}, "slow");
});
Queue only works if your animating the same element. Lord knows why the above got voted up but it will not work.
You will need to use the animation callback. You can pass in a function as the last param to the animate function and it will get called after the animation has completed. However if you have multiple nested animations with callbacks the script will get pretty unreadable.
I suggest the following plugin which re-writes the native jQuery animate function and allows you to specify a queue name. All animations that you add with the same queue name will be run sequentially as demonstrated here.
Example script
$("#1").animate({marginTop: "100px"}, {duration: 100, queue: "global"});
$("#2").animate({marginTop: "100px"}, {duration: 100, queue: "global"});
$("#3").animate({marginTop: "100px"}, {duration: 100, queue: "global"});
I know this is an old question, but it should be updated with an answer for newer jQuery versions (1.5 and up):
Using the $.when function you can write this helper:
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;
}
Then you can call it like this:
queue(function () {
return $("#header").animate({top: "-50"}, "slow");
}, function () {
return $("#something").animate({height: "hide"}, "slow");
}, function () {
return $("ul#menu").animate({top: "20", left: "0"}, "slow");
}, function () {
return $(".trigger").animate({height: "show", top: "110", left: "0"}, "slow");
});
You could do a bunch of callbacks.
$(".button").click(function(){
$("#header").animate({top: "-50"}, "slow", function() {
$("#something").animate({height: "hide"}, "slow", function() {
$("ul#menu").animate({top: "20", left: "0"}, "slow", function() {
$(".trigger").animate({height: "show", top: "110", left: "0"}, "slow");
});
});
});
});
A slight improvement on #schmunk's answer is to use a plain object jQuery object's queue in order to avoid conflicting with other unrelated animations:
$({})
.queue(function (next) {
elm1.fadeOut('fast', next);
})
.queue(function (next) {
elm2.fadeIn('fast', next);
})
// ...
One thing to keep in mind is that, although I have never run into problems doing this, according to the docs using the queue methods on a plain object wrapper is not officially supported.
Working With Plain Objects
At present, the only operations supported on plain JavaScript objects wrapped in jQuery
are: .data(),.prop(),.bind(), .unbind(), .trigger() and .triggerHandler().
You can also put your effects into the same queue, i.e. the queue of the BODY element.
$('.images IMG').ready(
function(){
$('BODY').queue(
function(){
$('.images').fadeTo('normal',1,function(){$('BODY').dequeue()});
}
);
}
);
Make sure you call dequeue() within the last effect callback.
Extending on jammus' answer, this is perhaps a bit more practical for long sequences of animations. Send a list, animate each in turn, recursively calling animate again with a reduced list. Execute a callback when all finished.
The list here is of selected elements, but it could be a list of more complex objects holding different animation parameters per animation.
Here is a fiddle
$(document).ready(function () {
animate([$('#one'), $('#two'), $('#three')], finished);
});
function finished() {
console.log('Finished');
}
function animate(list, callback) {
if (list.length === 0) {
callback();
return;
}
$el = list.shift();
$el.animate({left: '+=200'}, 1000, function () {
animate(list, callback);
});
}
Animate Multiple Tags Sequentially
You can leverage jQuery's built-in animation queueing, if you just select a tag like body to do global queueing:
// Convenience object to ease global animation queueing
$.globalQueue = {
queue: function(anim) {
$('body')
.queue(function(dequeue) {
anim()
.queue(function(innerDequeue) {
dequeue();
innerDequeue();
});
});
return this;
}
};
// Animation that coordinates multiple tags
$(".button").click(function() {
$.globalQueue
.queue(function() {
return $("#header").animate({top: "-50"}, "slow");
}).queue(function() {
return $("#something").animate({height: "hide"}, "slow");
}).queue(function() {
return $("ul#menu").animate({top: "20", left: "0"}, "slow");
}).queue(function() {
return $(".trigger").animate({height: "show", top: "110", left: "0"}, "slow");
});
});
http://jsfiddle.net/b9chris/wjpL31o0/
So, here's why this works, and what it's doing:
The call to $.globalQueue.queue() is just queueing a call to your tag's animation, but it queues it on the body tag.
When jQuery hits your tag animation in the body queue, your tag's animation starts, on the queue for your tag - but the way the jQuery animation framework works, any custom animation callback causes a tag's animation queue (the body's in this case) to halt, until the custom animation calls the passed-in dequeue() function. So, even though the queues for your animated tag and body are separate, the body tag's queue is now waiting for its dequeue() to be called. http://api.jquery.com/queue/#queue-queueName-callback
We just make the last queued item on the tag's queue a call to continue the global queue by calling its dequeue() function - that's what ties the queues together.
For convenience the globalQueue.queue method returns a this reference for easy chaining.
setInterval
For the sake of completeness, it's easy to land here just seeking an alternative to setInterval - that is you're not so much looking to make separate animations coordinate, as just fire them over time without the strange surge ahead in your animation caused by the way newer browsers will postpone animation queues and timers to save CPU.
You can replace a call to setInterval like this:
setInterval(doAthing, 8000);
With this:
/**
* Alternative to window.setInterval(), that plays nicely with modern animation and CPU suspends
*/
$.setInterval = function (fn, interval) {
var body = $('body');
var queueInterval = function () {
body
.delay(interval)
.queue(function(dequeue) {
fn();
queueInterval();
dequeue(); // Required for the jQuery animation queue to work (tells it to continue animating)
});
};
queueInterval();
};
$.setInterval(doAthing, 8000);
http://jsfiddle.net/b9chris/h156wgg6/
And avoid those awkward blasts of animation when a background tab has its animations re-enabled by the browser.
This has already been answered well (I think jammus's answer is the best) but I thought I'd provide another option based on how I do this on my website, using the delay() function...
$(".button").click(function(){
$("#header").animate({top: "-50"}, 1000)
$("#something").delay(1000).animate({height: "hide"}, 1000)
$("ul#menu").delay(2000).animate({top: "20", left: "0"}, 1000)
$(".trigger").delay(3000).animate({height: "show", top: "110", left: "0"}, "slow");
});
(replace 1000 with your desired animation speed. the idea is your delay function delays by that amount and accumulates the delay in each element's animation, so if your animations were each 500 miliseconds your delay values would be 500, 1000, 1500)
edit: FYI jquery's 'slow' speed is also 600miliseconds. so if you wanted to use 'slow' still in your animations just use these values in each subsequent call to the delay function - 600, 1200, 1800
I was thinking about a backtracking solution.
Maybe, you can define that every object here has the same class, for example .transparent
Then you can make a function, say startShowing, that looks for the first element which has the .transparent class, animate it, remove .transparent and then call itself.
I can't assure the sequence but usually follows the order in which the document was written.
This is a function I did to try it out
function startShowing(){
$('.pattern-board.transparent:first').animate(
{ opacity: 1},
1000,
function(){
$(this).removeClass('transparent');
startShowing();
}
);
}
Use the queue option:
$(".button").click(function(){
$("#header").animate({top: "-50"}, { queue: true, duration: "slow" })
$("#something").animate({height: "hide"}, { queue: true, duration: "slow" })
$("ul#menu").animate({top: "20", left: "0"}, { queue: true, duration: "slow" })
$(".trigger").animate({height: "show", top: "110", left: "0"}, { queue: true, duration: "slow" });
});
How do I make my .right-menu DIV to fadein only after a couple of moments the mouse is hovering its parent .right-menu-background ? The thing is that when you move the cursor quickly in and out, .right-menu DIV is reappearing a lot of times after.
How do I delay animation for few ms?
Here's the code:
$(function(){
$(".right-menu-background").hover(function(){
$(this).find(".right-menu").fadeIn();
}
,function(){
$(this).find(".right-menu").fadeOut();
}
);
});
a easy fix is to use .stop()
$(function () {
$(".right-menu-background").hover(function () {
$(this).find(".right-menu").stop(true, true).fadeIn();
}, function () {
$(this).find(".right-menu").stop(true, true).fadeOut();
});
});
using timer
$(function () {
$(".right-menu-background").hover(function () {
var el = $(this).find(".right-menu");
var timer = setTimeout(function(){
el.stop(true, true).fadeIn();
}, 500);
el.data('hovertimer', timer);
}, function () {
var el = $(this).find(".right-menu");
clearTimeout(el.data('hovertimer'))
el.stop(true, true).fadeOut();
});
});
Use the stop() function in front of fading calls ...stop(true, true)
With those two parameters set to true, the animation queue is cleared and the last animation is played this will get ride of the weird effect
$(this).find(".right-menu").stop(true, true).fadeIn();
Use .delay() function.
Here is the code:
$(function(){
$(".right-menu-background").hover(function(){
$(this).find(".right-menu").delay(800).fadeIn(400);
},function(){
$(this).find(".right-menu").fadeOut(400);
});
});
Check the demo here: http://jsfiddle.net/Mju7X/
I have an image on my site that has a jquery hover action assigned to it. But it's easy to accidentally mouse over that area, and if you do it more than once, the hover keeps appearing, disappearing, appearing, etc, until it's shown and disappeared once for every time you moused over it. Is there a way to make it so the action doesn't fire unless you hover for a few seconds? I don't want to just delay the action, because it would still happen for every mouseover, I want to see if there's a way the mouseover doesn't count unless you're on the image for a few seconds.
Script so far:
$("img.badge").hover(
function() {
$("h3.better").animate({"left": "125px"}, 1200);
},
function() {
$("h3.better").animate({"left": "-500px"}, 800);
});
You could use setTimeout to launch the action and bind a function calling clearTimeout on the mouseout event :
$('img.badge').hover(function(){
window.mytimeout = setTimeout(function(){
$("h3.better").animate({"left": "125px"}, 1200);
}, 2000);
}, function(){
clearTimeout(window.mytimeout);
});
Or you could use a plugin for that, like hoverintent.
Use a .stop() before animate, to cancel the previous animation. I believe this is what you are looking for, and will solve your current problem.
$("img.badge").hover(
function() {
$("h3.better").stop().animate({"left": "125px"}, 1200);
},
function() {
$("h3.better").stop().animate({"left": "-500px"}, 800);
});
You can use a timer to not fire the hover action until you've been hovered a certain amount of time like this and then, if the hover leaves before the timer fires, you clear the timer so nothing happens if you're only hovered a short period of time:
$("img.badge").hover(function() {
var timer = $(this).data("hover");
// if no timer set, set one otherwise if timer is already set, do nothing
if (!timer) {
// set timer that will fire the hover action after 2 seconds
timer = setTimeout(function() {
$("h3.better").stop(true).animate({"left": "125px"}, 1200);
$(this).data("hover", null);
}, 2000);
// save timer
$(this).data("hover", timer);
}
}, function() {
var timer = $(this).data("hover");
if (timer) {
clearTimeout(timer);
$(this).data("hover", null);
} else {
// probably would be better to make this an absolute position rather
// than a relative position
$("h3.better").stop(true).animate({"left": "-500px"}, 800);
}
});
Note: I also added .stop(true) to your animation so animations will never pile up.
I have an animation every one second and I have a button Stop, when I click to the button the animation is stopped but it continues after. There is not another way to remove the animation ?
This is my code.
$(document).ready(function() {
setInterval(function() {
$("#myImg").animate({top: '50%',left: '50%',width: '174px',height: '174px',padding: '10px'}, 500);
$("#myImg").animate({ marginTop: '0',marginLeft: '0',top: '0',left: '0',width: '70px',height: '70px',padding: '5px'}, 600);
},1000);
});
$("#stop").click(function(){
$("#myImg").stop();
});
Thank you
jsFiddle demo
-You don't need setInterval, you can use the .animate() callback to loop your anim function.
-Nest your animate functions
-Use the .stop() method on your first .animate() iteration just to prevent animation buildups and clear some animation memory ;)
$(document).ready(function() {
function anim(){
$("#myImg").stop().animate({top: '50%',left: '50%',width: '174px',height: '174px',padding: '10px'}, 500, function(){
$(this).animate({ marginTop: '0',marginLeft: '0',top: '0',left: '0',width: '70px',height: '70px',padding: '5px'}, 600, anim); // <-- the 'anim' callback
}); // SCROLL THERE --> TO FIND THE CALLBACK :D -->
}
anim(); // run!!
$("#stop").click(function(){
$("#myImg").stop();
});
});
The setInterval() call is continuously adding in new calls to .animate() to your elements. That means that you succeed in stopping the animation, then the setInterval() calls it again, making the animation run again.
Why do you have this on setInterval()?
In any case, you'll have to cancel the schedule as well. You can do that by doing something similar to the following:
var schedule = setInterval(fn, 1000);
clearInterval(schedule);
I am creating I am creating a drop down which I want to delay about 250 ms so that it's not triggered when someone quickly scrolls across the button.
Here's my current code. I tried using the delay() method but it's not going well.
$(".deltaDrop").hover(function(){
$('.deltaDrop ul').stop(false,true).slideDown(250);
$('.delta').css('background-position','-61px -70px');
},function(){
$('.deltaDrop ul').stop(false,true).slideUp(450);
$('.delta').css('background-position','-61px 0');
});
Thanks
var timer;
timer = setTimeout(function () {
-- Your code goes here!
}, 250);
Then you can use the clearTimeout() function like this.
clearTimeout(timer);
This should work.
$(".deltaDrop").hover(function(){
$('.deltaDrop ul').stop(false,true).hide(1).delay(250).slideDown();
$('.delta').css('background-position','-61px -70px');
},function(){
$('.deltaDrop ul').stop(false,true).show(1).delay(450).slideUp();
$('.delta').css('background-position','-61px 0');
});
.delay only works when you're dealing with the animation queue. .hide() and .show() without arguments don't interact with the animation queue. By adding the .hide(1) and .show(1) before the .delay() makes the slide animations wait on the queue.
setTimeout(function() {
$('.deltaDrop ul').slideDown()
}, 5000);
Untested, unrefactored:
$(".deltaDrop")
.hover(
function()
{
var timeout = $(this).data('deltadrop-timeout');
if(!timeout)
{
timeout =
setTimeout(
function()
{
$('.deltaDrop ul').stop(false,true).slideDown(250);
$('.delta').css('background-position','-61px -70px');
$('.deltaDrop').data('deltadrop-timeout', false);
},
250
);
$(this).data('deltadrop-timeout', timeout);
}
},
function()
{
var timeout = $(this).data('deltadrop-timeout');
if(!!timeout)
{
clearTimeout(timeout);
$('.deltaDrop').data('deltadrop-timeout', false);
}
else
{
$('.deltaDrop ul').stop(false,true).slideUp(450);
$('.delta').css('background-position','-61px 0');
}
}
);