Reputation: 71
So, I tried to do an animated banner (containing two images- #baner1 and #baner2. When one fades out the second fades in). It works properly most of the times, but sometimes these images change rapidly (like one second after each other, despite the timer is 12000 miliseconds). Any idea what causes it?
My code:
setInterval(()=>{
$('#baner1').fadeOut(2000);
$('#baner2').fadeIn(2000);
},12000)
setInterval(()=>{
$('#baner1').fadeIn(2000);
$('#baner2').fadeOut(2000);
},24000)
Also, it's (as far as I know) my only code affecting these images. Thanks in advance for help.
Upvotes: 0
Views: 80
Reputation: 1146
Elaborating on Stratuba's solution and my detailed explanation of why it failed and the benefits of centralizing,
we can go further in code mutualization:
let nBanners = 2;
let displayedBanner = 1; // Banner 1 is initially displayed.
setInterval(()=>{
let next = displayedBanner + 1;
if(next > nBanners) next = 1; // With banner nums starting at 0 this could become a simple modulo.
$('#baner'+displayedBanner).fadeOut(2000);
$('#baner'+next ).fadeIn(2000);
displayedBanner = next;
}, 12000);
The benefits are:
if
s as there are bannersIn another story you could even have your JS automatically determine how many banners it has to roll over (by dumping all your banners as HTML blocks with a specific class; even without an ID, because you could then refer to each banner by its position in getElementsByClassName()
's return).
Upvotes: 0
Reputation: 1146
The error lies in how you transcripted your logic to setInterval
s.
You wanted to show #banner2
at 12 s, then #banner1
at 24 s, #banner2
at 36, and so on.
Banner by banner it gives: #banner1
at 24, 48, 72, …, and #banner2
at 12, 36, 60, …: each banner reappears 24 seconds after its last appearance, but #banner2
with a 12 s initial shift.
Not knowing how to introduce this shift, you put the 12 s shift in a setInterval(), which, instead of the intended timeline (with parenthesis for the initial state):
(1)-1-1-1
(-)2-2-2-
Gave:
(1)-1-1-1
(-)222222
And, as pointed by Stratubas' answer, when 1 and 2 occurs simultaneously, you can only "hope" that the one you first declared passes first, because setInterval() does not guaranty that your callback will be called exactly 12 s after you invoked it, but "at the first free slot after 12 s have passed". So if a slot is free but only 11.9999 s elapsed, your callback does not get called and will wait for the next chance.
Nanoseconds introduced by the browser to handle your calls, or in the fadeIn and fadeOut, and its liberty to reorder simultaneously stacked calls, will trigger this undeterministic lag and reordering.
So, what you wanted could have been:
setTimeout(()=>{setInterval(()=>{
$('#baner1').fadeOut(2000);
$('#baner2').fadeIn(2000);
},24000);},12000); // Start the 24 s interval only after the first 12 s have elapsed.
setInterval(()=>{
$('#baner1').fadeIn(2000);
$('#baner2').fadeOut(2000);
},24000);
BUT as your two callbacks are related, it will be better to centralize the code,
and as such you nevertheless would have ended up following Stratubas' answer.
Upvotes: 0
Reputation: 3067
Your callbacks are overlapped.
They go like a
, a+b
, a
, a+b
...
The a+b
is sometimes executed as b+a
(because two setInterval
s aren't synchronized perfectly) and I think this is what's causing you trouble.
What you meant to do is a
, b
, a
, b
...
You can try changing you logic to use a single setInterval
, and use a variable to know if it's the a
"turn" or the b
"turn". Something like this:
let nextTurn = 'a';
setInterval(()=>{
if (nextTurn === 'a') {
$('#baner1').fadeOut(2000);
$('#baner2').fadeIn(2000);
nextTurn = 'b';
} else {
$('#baner1').fadeIn(2000);
$('#baner2').fadeOut(2000);
nextTurn = 'a';
}
},12000)
Upvotes: 2