Better way to run a finite cycle of animations - javascript

First of all, this code, as ugly as it is, works, for all intents and purposes. However, it really makes me both sick and mad to look at.
For the lazy, the main portion of the code that sucks is this:
setTimeout(function() {
toggle(); //off
setTimeout(function() {
toggle(); //on
setTimeout(function() {
toggle(); //off
setTimeout(function() {
toggle(); //on, stays for stallTime
setTimeout(function() {
toggle(); //off
callback();
}, stallTime);
}, 300);
}, 300);
}, 300);
}, 300);
I would, of course, love to control my animations just by changing the CSS class on an element. However, transitionend and webkitTransitionEnd and the like are not reliable in the least. Am I forced to resorting to this kind of hideous code? Ideally I'd like to be able to set a variable number of "flashes", but this code is too stodgy for that.
Please rescue me from my filth.

var count = 5;
var i = setInterval(function() {
toggle();
count--;
if(count == 0) {
clearInterval(i);
callback();
}
}, 300);

To avoid setInterval, I wrote a recursive function:
// link.addClass('hover'); //on - no longer needed
flash = function(count) {
if (count > 1) {
setTimeout(function() {
toggle();
flash(count - 1);
}, 300);
} else {
setTimeout(function() {
toggle(); //off
callback();
}, stallTime);
}
}
flash(6); // 2 * the number of flashes.

Related

Increasing/decreasing audio on a video element, triggered via jQuery Waypoints

I have a page with a series of (HTML 5) videos whose audio needs to fade in or out depending on your position on the page (and it may be different for each video). I'm using jQuery Waypoints with the InView plugin to detect when an element is in the viewport. I'm not sure how to do it consistently, so that you don't cause unexpected behavior when you trip a waypoint while the volume is still decreasing or increasing.
var waypoint = new Waypoint.Inview({
element: $('#element')[0],
enter: function() {
$('#element')[0].volume = 0;
$('#ielement')[0].play();
incVol($('#element')[0]);
},
entered: function() {},
exit: function() {},
exited: function() {
decVol($('#element')[0]);
}
});
function incVol(e) {
setTimeout(function() {
if ((e.volume + .05) < 1) {
e.volume += .05;
incVol(e);
} else {
e.vol = 1;
}
}, 100)
}
function decVol(e) {
setTimeout(function() {
if ((e.volume - .05) > 0) {
e.volume -= .05;
decVol(e);
} else {
e.volume = 0;
e.pause();
}
}, 100)
}
This is an inconsistent attempt, if you trigger 'enter' while 'decVol' is still running you lose volume completely and have to trigger an 'exit', wait, and then trigger 'enter' again.
I've also tried something with jQuery's animate on volume. But that doesn't seem consistent either.
var waypoint = new Waypoint.Inview({
element: $('#element')[0],
enter: function() {
$('#element')[0].volume = 0;
$('#element')[0].play();
$('#element').animate({
volume: 1
}, 1000);
},
entered: function() {},
exit: function() {},
exited: function() {
$('#element').animate({
volume: 0
}, 1000, function() {
$('#element')[0].pause();
});
}
});
If I scroll up and down too fast, especially if I have multiple waypoints of this type in the page, then the queue of events becomes extensive and I have fade ins/outs happening far after the events have triggered (though, I prefer this implementation for the moment).
Any suggestions on how to achieve what I want a little better?
Prefacing the animate() function with stop() is the answer.
Something like:
$('#element').stop(true, false).animate({
volume: 1
}, 1000);

Successive setTimout() calls all execute at once

setTimeout(thisFunc, 500);
setTimeout(thatFunc, 500);
setTimeout(otherFunc, 500);
finalFunc();
What I expect is thisFunc(), followed by a half second pause, then thatFunc() followed by a half second pause, then otherFunc(), followed by a pause. Then finalFunc().
What actually happens, is the page pauses for 1 1/2 seconds then all four functions seem to execute at once.
How can I achieve the pause I am after? It's being done purely for aesthetical purposes in the UI.
You can use the callbacks too if you need to use 500 ms as a variable for example:
var timeout = 500;
setTimeout(function() {
thisFunc();
setTimeout(function() {
thatFunc();
setTimeout(function() {
otherFunc();
}, timeout);
}, timeout);
}, timeout);
An alternate way would be:
setTimeout(thisFunc, 500);
function thisFunc () {
/* ...whatever... */
setTimeout(thatFunc, 500);
}
function thatFunc () {
/* ...whatever... */
setTimeout(otherFunc, 500);
}
function otherFunc () {
/* ...whatever... */
setTimeout(finalFunc, 500);
}
function finalFunc () {
/* ...whatever... */
}
Try to give the delay properly,
setTimeout(thisFunc, 500);
setTimeout(thatFunc, 1000);
setTimeout(otherFunc, 1500);
setTimeout(finalFunc, 2000);
If you give 500 ms for all the setTimeouts, then every thing would be fired at a same time.
And the best approach would be,
var func = function(func){ func(); }
function setContinuos() {
var funcs = Array.from(arguments);
var delay = funcs.pop();
funcs.forEach(function(itm, index) {
setTimeout(function() {
func(itm);
}, (index + 1) * delay)
});
}
setContinuos(func1,func2,func3,func4,500);
DEMO

Javascript blinking with setTimeouts

Helllo i am getting problem with blinking every 0.5 seconds. I start my code with #blink display= "inline" but it always stays inline. When starting my code, i call startBlinking.
function startBlinking() {
setInterval(function () {
blink();
}, 1000);
}
function blink() {
setTimeout(function () {
document.getElementById('blink').style.display = "none";
}, 500);
setTimeout(function () {
document.getElementById('blink').style.display = "inline";
}, 500);
}
Can you help me? Thanks.
The problem with your attemt is that you're setting the same timeout length for both hiding and showing the element, meaning it will hide and show again at the same time! Remove one and it should work:
function startBlinking() {
setInterval(function () {
blink();
}, 1000);
}
function blink() {
// note no timeout for the hiding part
document.getElementById('blink').style.display = "none";
setTimeout(function () {
document.getElementById('blink').style.display = "inline";
}, 500);
}
startBlinking();
<div id="blink">blink</div>
<p>some text following</p>
As you can tell, this causes the following content to jump. We are also fetching the element every time we're hiding or showing it. Better to swap to using visibility and make the function a bit more flexible:
function blink(element, time) {
// using visibility: hidden; instead of display: none;
// allows the element to keep its rendering space
element.style.visibility = "hidden";
setTimeout(function () {
element.style.visibility = "visible";
}, time);
setTimeout(function () {
blink(element, time); // recurse
}, time * 2);
}
// query the DOM for element once instead of every iteration
blink(document.getElementById("blink"), 500);
<div id="blink">blink</div>
<p>following content stays put</p>
You might also want to be able to stop the blinking at some point
function blink(element, time) {
function loop(){
element.style.visibility = "hidden";
setTimeout(function () {
element.style.visibility = "visible";
}, time);
timer = setTimeout(function () {
loop();
}, time * 2);
cleared = false;
}
var timer, cleared = true;
// expose methods
return {
start: function() {
if (cleared) loop();
},
stop: function() {
clearTimeout(timer);
cleared = true;
}
};
}
var blinking = blink(document.getElementById("blink"), 500);
document.getElementById("start").addEventListener("click", function(){
blinking.start();
});
document.getElementById("stop").addEventListener("click", function(){
blinking.stop();
});
<div id="blink">blink div</div>
<button id="start">start blinking</button><br />
<button id="stop">stop blinking</button>
you can make it simple by toggling a class
.hide{
display:none;
}
setInterval(function () {
$('.blink').toggleClass('hide')
}, 500);
JS Fiddle
Just call it once, setTimeout() is ripping it out of the thread so it gets set right back to inline.
function blink() {
setTimeout(function () {
if (document.getElementById('blink').style.display === 'inline') {
document.getElementById('blink').style.display = "none";
} else {
document.getElementById('blink').style.display = "inline";
}
}, 500);
}
you have messed up the event que of javascript, why dont you try this
function startBlinking() {
initial = 'inline';
index = 0;
setInterval(function () {
if(index==2){
initial = (initial=='none')?'block':'none';
document.getElementById('blink').style.display = initial;
index=0;
}
index++;
}, 1000);
}
For the jQuery fans:
you can use the 'pulsate' effect
http://jqueryui.com/effect/
This may or may not achieve the exact blinking you need but it's a lot simpler!

Jquery loop and run the (click) functions each X seconds

I have this (simplified for here) code which change the background position on click,
but i need to put them in a auto loop too
<script type="text/javascript">
$(window).load(function(){
$(".sle1").click(function() {
$(".slimgs").animate({backgroundPosition: '-908px 0px'});
});
$(".sle2").click(function() {
$(".slimgs").animate({backgroundPosition: '-681px 0px'});
});
$(".sle3").click(function() {
$(".slimgs").animate({backgroundPosition: '-454px 0px'});
});
});
</script>
I mean after 5 seconds of page load this first function runs
$(".sle1").click(function() {
$(".slimgs").animate({backgroundPosition: '-908px 0px'});
});
then after 5 second the .sle2 and when it reachs the .sle3 (last function) after 5 seconds it should go back and run the first function again (a loop)
i tried putting ", 5000)" after each function but it didn't work
any help is appreciated
Use window.setInterval to execute a function every 5 seconds.
To cycle through those three functions, you could store all of them in an array and set i every time to the function that should be called next.
var i = 0;
var functions = [
function() {
// instead of copying that code, you could also do
// $(".sle1").click() - or you can just use functions[0]
// as argument when assigning the click listener.
$(".slimgs").animate({backgroundPosition: '-908px 0px'});
i = 1;
},
function() {
// second animation here
i = 2
},
function() {
// third animation here
i = 0
}
];
window.setInterval(function () {
functions[i]();
}, 5000);
[Edit]: no more ring-counter as that wouldn't work with the clicking.
For future reference: If you don't need the clicking to interfere with the automatic switching and want to archive something similar with only automatic cycling, get rid of th i= statements in the functions and instead insert i++; i%= functions.length after functions[i]();.
This should work, altough there are more ellegant ways to do it
$(window).load(function(){
$(".sle1").click(function() {
$(".slimgs").animate({backgroundPosition: '-908px 0px'});
window.setTimeout(function() {
$(".sle2").click();
},5000);
});
$(".sle2").click(function() {
$(".slimgs").animate({backgroundPosition: '-681px 0px'});
window.setTimeout(function() {
$(".sle3").click();
},5000);
});
$(".sle3").click(function() {
$(".slimgs").animate({backgroundPosition: '-454px 0px'});
window.setTimeout(function() {
$(".sle1").click();
},5000);
});
window.setTimeout(function() {
$(".sle1").click();
},5000);
});
setTimeout(
function() {
$(".sle1").trigger('click');
setInterval(
function() {
$(".sle1").trigger('click');
},
15000
);
},
5000
);
setTimeout(
function() {
$(".sle2").trigger('click');
setInterval(
function() {
$(".sle2").trigger('click');
},
15000
);
},
10000
);
setTimeout(
function() {
$(".sle3").trigger('click');
setInterval(
function() {
$(".sle3").trigger('click');
},
15000
);
},
15000
);
In case you're willing to improve your code, making it more concise, you could try the following, using window.setInterval:
function changeBackground(interval, frames) {
var int = 1;
function changer() {
document.body.id = "b" + int;
int++;
if (int === frames) {
int = 1;
}
}
var swap = window.setInterval(changer, interval);
}
changeBackground(2000, 10); //milliseconds, frames
Example online
Talking about your example, it's harder to tell as it's not very clear.
Try adding .stop() and duration for your .animate() effects:
$(window).load(function(){
$(".sle1").click(function() {
$(".slimgs").stop().animate({backgroundPosition: '-908px 0px'}, 5000);
});
$(".sle2").click(function() {
$(".slimgs").stop().animate({backgroundPosition: '-681px 0px'}, 5000;
});
$(".sle3").click(function() {
$(".slimgs").stop().animate({backgroundPosition: '-454px 0px'}, 5000);
});
});
.stop() - The jQuery .stop() method is used to stop animations or effects before it is finished.

Detecting if browser window is active and start event after window is active again [JavaScript]

The idea is simple: I have buttons which refer to another website. Whenever the user has clicked more than two links I want to refresh some content (through Ajax). For that to work I need to detect if my window is active or not, since I only want the event to start when the user is BACK on my page.
Without further ado this is my code:
$(document).on("click", "a", function() {
numberOfClicks += 1;
if (numberOfClicks >= 2)
{
userOnWebsiteOrNot();
numberOfClicks = 0;
}
});
function userOnWebsiteOrNot()
{
if (focusedOrNot == 0)
{
$('#resultaat').hide().fadeIn(5000);
}
}
window.addEventListener('focus', function() {
document.title = 'focused';
focusedOrNot = 0;
});
window.addEventListener('blur', function() {
document.title = 'not focused';
focusedOrNot = 1;
});
It DOES detect whenever the user is on the page or not, but somehow the fade always happens.
Could anybody explain me what I'm doing wrong or give me any ideas?
Thanks
Yenthe
ANSWER:
I needed a setTimeOut on three functions because they would otherwise check too fast. Thank you for that help Romo! ;) All credit goes to Romo to be honest.
$(document).on("click", "a", function() {
numberOfClicks += 1;
if (numberOfClicks >= 2)
{
haallinks();
setTimeout(function() {
userOnWebsiteOrNot();
}, 2000);
numberOfClicks = 0;
}
});
function userOnWebsiteOrNot()
{
if (focusedOrNot === 0)
{
$('#resultaat').hide().fadeIn(5000);
}
else
{
controlerenActiefOfNiet();
}
}
function controlerenActiefOfNiet()
{
setTimeout(function() {
userOnWebsiteOrNot();
}, 2000);
}
window.addEventListener('focus', function() {
setTimeout(function() {
focusedOrNot = 0;
}, 0);
});
window.addEventListener('blur', function() {
setTimeout(function() {
focusedOrNot = 1;
}, 1000);
});
I think the problem is that when you click a link the second time the window will always be focused. JS runs pretty fast. To overcome this, I think you should do a setTimeout() and delay it 200ms or so to give the window time to "lose" focus
setTimeout(function() {userOnWebsiteOrNot(); },2000);
http://jsfiddle.net/xVHgE/
Edit: Adding delay to event listener. I don't think you can "delay" an event though, just the function it runs.
window.addEventListener('focus', function() {
setTimeout( function() {
$('#test').html('focus');
focusedOrNot = 0;
} , 5000);
});

Categories

Resources