Javascript how to end a function - javascript

There are 4 horses on my track. https://i.imgur.com/xKFTZ8a.png
I press start and the horses are supposed to complete a lap. I have managed to make them go around the first corner and make them stop at the second corner like so https://i.imgur.com/4uwrBiN.png
Heres my code for the white horse, my problem is that the functions are calling each other constantly and due to this I am unable to go around the second corner due to
positionTop >= window.innerHeight * 0.84 - 50
from horse1 function running and preventing the horse from going left.
var interval = 0;
function startRace() {
var raceTimer = setInterval(100);
var raceActive = true;
loop();
}
function loop() {
interval = setInterval(horse1, 10);
interval = setInterval(horse2, 10);
interval = setInterval(horse3, 10);
interval = setInterval(horse4, 10);
}
function horse1() {
var horse1 = document.getElementById('horse1');
var positionLeft = horse1.offsetLeft;
var positionTop = horse1.offsetTop
horse1.className = 'horse runRight'
if (positionLeft >= window.innerWidth * 0.84 - 50) {
horse1down();
} else {
horse1.style.left = positionLeft + 1 + 'px';
}
}
function horse1down() {
var horse1 = document.getElementById('horse1');
var positionTop = horse1.offsetTop;
horse1.className = 'horse runDown'
if (positionTop >= window.innerHeight * 0.85 - 50) {
horse1left()
} else {
horse1.style.top = positionTop + 1 + 'px';
}
}
function horse1left() {
var horse1 = document.getElementById('horse1');
var positionLeft = horse1.offsetLeft
horse1.style.left = positionLeft - 1 + 'px';
horse1.className = 'horse runLeft'
}
function myLoadFunction() {
var startButton = document.getElementById('start');
startButton.addEventListener('click', startRace);
}
document.addEventListener('DOMContentLoaded', myLoadFunction);
What I have tried:
I have tried clear intervals but I might be doing them wrong.
function horse1down() {
var horse1 = document.getElementById('horse1');
var positionTop = horse1.offsetTop;
horse1.className = 'horse runDown'
if (positionTop >= window.innerHeight * 0.85 - 50) {
horse1left()
clearInterval(interval);
interval = setInterval(horse1, 10);
} else {
horse1.style.top = positionTop + 1 + 'px';
}
}

You are overwriting variable interval.Create an array with 4 different intervals and you will be able to clear them separately.
In general its better to use setTimeout insead of setInterval. You can easily rewrite setInterval function to safer setTimeout. setInterval doesn't care whether the callback is still running or not.
In some cases, the function might need longer than the interval time to finish execution. What would happen is that you'll end up with a bunch of queued requests that may not necessarily return in order.

Related

Reduce setTimeout time relative to time left

I'm making a random "spinner" that loops through 8 divs and add a class active like this:
https://jsfiddle.net/9q1tf51g/
//create random setTimeout time from 3sec to 5sec
var time = Math.floor(Math.random() * (5000 - 3000 + 1)) + 3000;
var exit = false;
function repeat(){
//my code
if(!exit){
setTimeout(repeat, 50);
}
}
My problem is, I want the function repeat to end slowly, to create more suspense. I think I can do this by raising the 50 from the timeout but how can I do this accordingly to the time left?
Thanks in advance!
You can try this.
$('button').on('click', function(){
var time = Math.floor(Math.random() * (5000 - 3000 + 1)) + 3000;
var anCounter = 1;
var anState = "positive";
var exit = false;
//var time1 = 50000;
setInterval(function(){time = time-1000;}, 1000);
function repeat(){
if(anCounter>7 && anState=="positive"){ anState="negative"}
if(anCounter<2 && anState=="negative"){ anState="positive"}
$('div[data-id="'+anCounter+'"]').addClass('active');
$('div').not('div[data-id="'+anCounter+'"]').removeClass('active');
if(anState=="positive"){anCounter++;}else{anCounter--;}
if(!exit){
if(time <1000)
setTimeout(repeat, 300);
else if(time< 2000)
setTimeout(repeat, 100);
else setTimeout(repeat, 50);
}
}
repeat();
setTimeout(function(){
exit=true;
},time);
});
Once you know that you need to exit the flow (exit is true ) you can trigger some animation by creating a dorm linear serials of you code. Usually this animation should not last more than 2 sec.
You were kind of on the right track but it'd be easier to check the time you've passed by and increment accordingly at a fixed rate. I set it to increase by 50ms every iteration but you could change that to whatever you like.
Fiddle Demo
Javascript
$('button').on('click', function() {
var time = Math.floor(Math.random() * (5000 - 3000 + 1)) + 3000;
var anCounter = 1;
var anState = "positive";
var elapsed = 0;
var timer;
function repeat(timeAdded) {
if (anCounter > 7 && anState == "positive") {
anState = "negative"
}
if (anCounter < 2 && anState == "negative") {
anState = "positive"
}
$('div[data-id="' + anCounter + '"]').addClass('active');
$('div').not('div[data-id="' + anCounter + '"]').removeClass('active');
if (anState == "positive") {
anCounter++;
} else {
anCounter--;
}
if (elapsed < time) {
timer = setTimeout(function() {
repeat(timeAdded + 50);
}, timeAdded);
elapsed += timeAdded;
}
else {
clearTimeout(timer);
}
}
repeat(0);
});
You can add a parameter called intTime to your function repeat and inside that function you can adjust the next timeout and call the repeat function with the new timeout. each time it gets called it will take 20 ms longer. however you adjust the increment by changing the 20 in
var slowDown=20; to a different number.
var slowDown=20;
setTimeout ("repeat",50);
function repeat(intTime){
//my code
if(!exit){
intTime=Math.floor (intTime)+slowDown;
setTimeout(repeat(intTime), intTime);
}
}
And then you will need to create another timeout for the exit.
var time = Math.floor(Math.random() * (5000 - 3000 + 1)) + 3000;
var exit = false;
setTimeout ("stopSpinning",time);
function stopSpinning(){
exit = true;
}
so the whole thing should look something like this
var slowDown=20;
var time = Math.floor(Math.random() * (5000 - 3000 + 1)) + 3000;
var exit = false;
setTimeout ("stopSpinning",time);
setTimeout ("repeat",50);
function repeat(intTime){
//my code
if(!exit){
intTime=Math.floor (intTime)+20;
setTimeout(repeat(intTime), intTime);
}
}
function stopSpinning(){
exit = true;
}
Fiddle Demo
Linear deceleration: //values are just an example:
add a var slowDown = 0; inside the click event handler
add slowDown += 1; inside the repeat function
pass 50+slowDown to setTimeout
Curved deceleration:
add a var slowDown = 1;and a var curveIndex = 1.05 + Math.random() * (0.2); // [1.05-1.25)inside the click event handler
add slowDown *= curveIndex; inside the repeat function
pass 50+slowDown to setTimeout

JS : Using a setTimeout inside a setInterval, the setTimeout has no effect

I'm trying to make random images move upwards then return to their original position.
<script>
var looper = setInterval(animateAll, 2000, myArray);
function animateAll(myArray) {
// Gets a random image ID
var randomId = myArray[Math.floor(Math.random()*myArray.length)];
// Make that random icon bounce
bounce(randomId) ;
}
function bounce(randomId) {
var icon = document.getElementById(randomId)
var top = icon.offsetTop;
setTimeout ( icon.style.top = top-20 + "px", 500) ;
setTimeout ( icon.style.top = top + "px", 500) ;
}
</script>
Both setTimeout lines work fine. With only one line, well the images will move without returning to their original position. With both lines, images don't move at all, probably because there's no delay between each.
Thanks for your help !
The problem is that you're executing the code in your setTimeout calls immediately. You're effectively saying "execute the result of setting the icon.style.top = whatever in 500 milliseconds" ... which does nothing.
Try this instead:
icon.style.top = top-20 + "px";
setTimeout ( function() { icon.style.top = top + "px"; }, 500) ;
... and I just blew 15 minutes on this, lol:
var steps = 7;
var increment = (Math.PI) / steps;
var bounceHeight = 20;
function doStep(icon, start, major, minor, height) {
if (minor == steps) {
major--;
height /= 2;
minor = 0;
icon.style.top = start;
}
if (major < 0) {
return;
}
if (minor < steps) {
pos = Math.sin((minor + 1) * increment);
icon.style.top = (start - height * pos) + "px";
setTimeout( function() { doStep(icon, start, major, minor + 1, height); }, 500/steps );
}
}
function bounce(randomId) {
var icon = document.getElementById(randomId)
var top = icon.offsetTop;
setTimeout ( function() { doStep(icon, top, 3, 0, bounceHeight); }, 500/steps ) ;
}
How about moving the image up immediately when you call bounce and then returning it to the original position after a timeout?
function bounce(randomId) {
var icon = document.getElementById(randomId)
var top = icon.offsetTop;
icon.style.top = top-20 + "px";
setTimeout ( icon.style.top = top + "px", 500) ;
}

Javascript help, Trying to animate two different sprites at same time

I need some javascript help. I am trying to set up two sprite animations with different frame rates in two separate div.
Here is a fiddle I started and i am very much stuck.
How do I combine the two div IDs into one statement? OR Should I be using ClassName in the statement to run on both divs?
http://jsfiddle.net/akwilinski/3t7d6qbL/1/
<div id="animate" class="animation"></div>
<div id="animate2" class="animation2"></div>
onload = function startAnimation() {
var frameHeight = 400;
var frames = 27;
var frame = 0;
var div = document.getElementById("animate");
setInterval(function () {
var frameOffset = (++frame % frames) * -frameHeight;
div.style.backgroundPosition = "0px " + frameOffset + "px";
}, 100);
}
Thank you for any assistance!
Simplest way to do this using the method you have already started using is to define 2 new variables, 1 for the second div and one for the second frame count. Then you can just add the call to your function.
Updated js:
onload = function startAnimation() {
var frameHeight = 400;
var frames = 27;
var frame = 0;
var div = document.getElementById("animate");
var div2 = document.getElementById("animate2");
setInterval(function () {
var frameOffset = (++frame % frames) * -frameHeight;
var frameOffset2 = (++frame % frames) * -frameHeight - 10;
div.style.backgroundPosition = "0px " + frameOffset + "px";
div2.style.backgroundPosition = "0px " + frameOffset + "px";
}, 100);
}
Fiddle
http://jsfiddle.net/f4v1vy7x/5/
I made a Can object to store the configuration for each can (so you can have different frameHeights, frames and frameRates.
I used window.requestAnimationFrame because it's far more efficient than setInterval. On each available frame I check whether it's time to animate based on each Can's set frame rate:
var Can = function( selector, frameHeight, frames, frameRate )
{
this.domCan = document.getElementById( selector );
this.frameHeight = frameHeight;
this.frames = frames;
this.frameRate = frameRate;
this.frame = 0;
};
onload = function startAnimation() {
var can1 = new Can( 'animate', 400, 27, 20 );
var can2 = new Can( 'animate2', 400, 27, 100 );
var cans = [ can1, can2 ];
window.requestAnimationFrame( function() {
can1.start = can2.start = new Date();
animate( cans );
} );
};
var animate = function( cans ) {
for( var i = 0; i < cans.length; i++ ) {
var now = new Date();
var can = cans[i];
if( now - can.start >= 1000 / can.frameRate ) {
can.start = now;
var frameOffset = (++can.frame % can.frames) * -can.frameHeight;
can.domCan.style.backgroundPosition = "0px " + frameOffset + "px";
}
}
window.requestAnimationFrame( function() {
animate( cans );
} );
}
You could do something like this:
var div = document.getElementById("animate");
var div2 = document.getElementById("animate2");
function anim(div) {
setInterval(function () {
var frameOffset = (++frame % frames) * -frameHeight;
div.style.backgroundPosition = "0px " + frameOffset + "px";
}, 100);
}
anim(div);
anim(div2);
You could then pass in additional parameters like frameHeight, frame, and frames to further customize each animation.

Javascript gradual width increase

I'm trying to gradually increase the elements of 2 id's in javascript using a Timeout. I can get one working but when trying to call another element into the same function it only does one iteration then crashes after the first recursive call.
I'm passing two id's for the elements. and I want the left element to gradually increase while the right element gradually increases in width.
Heres what ive got
function grow(elementL, elementR)
{
var htL = parseInt(document.getElementById(elementL).style.width,10);
var htR = parseInt(document.getElementById(elementR).style.width,10);
var movementL = htL + 5;
var movementR = htR - 5;
document.getElementById(elementL).style.width = movementL + 'px';
document.getElementById(elementR).style.width = movementR + 'px';
if (movementL > 1000) {
clearTimeout(loopTimer);
return false;
}
var loopTimer = setTimeout('grow(\''+elementL+','+elementR+'\')',50);
}
You could simplify this (removing the script-generation) by using setInterval -- this repeats the function call until you cancel it.
function grow(elementL, elementR)
{
var loopTimer = setInterval(function() {
if (!growStep(elementL, elementR)) {
clearInterval(loopTimer);
}
}, 50);
}
function growStep(elementL, elementR) {
var htL = parseInt(document.getElementById(elementL).style.width,10);
var htR = parseInt(document.getElementById(elementR).style.width,10);
var movementL = htL + 5;
var movementR = htR - 5;
document.getElementById(elementL).style.width = movementL + 'px';
document.getElementById(elementR).style.width = movementR + 'px';
if (movementL > 1000) {
return false;
}
return true;
}
(Fiddle)
Edit
Yeah, I guess the only problem with the OP code is that it passes a string to setTimeout, rather than the function itself:
var loopTimer = setTimeout(function() {
grow(elementL, elementR);
},50);
setTimeout('grow(\''+elementL+','+elementR+'\')',50)
would need to be
setTimeout('grow(\''+elementL+'\',\''+elementR+'\')',50)
// ^^ ^^
to work. But don't do that. Pass a function expression to setTimeout:
setTimeout(function() {
grow(elementL, elementR);
}, 50)

Multiple JavaScript timeouts at the same moment

I'm developing an HTML5 application and I'm drawing a sequence of images on the canvas with a certain speed and a certain timeout between every animation.
Able to use it multiple times I've made a function for it.
var imgNumber = 0;
var lastImgNumber = 0;
var animationDone = true;
function startUpAnimation(context, imageArray, x, y, timeOut, refreshFreq) {
if (lastImgNumber == 0) lastImgNumber = imageArray.length-1;
if (animationDone) {
animationDone = false;
setTimeout(playAnimationSequence, timeOut, refreshFreq);
}
context.drawImage(imageArray[imgNumber], x, y);
}
function playAnimationSequence(interval) {
var timer = setInterval(function () {
if (imgNumber >= lastImgNumber) {
imgNumber = 0;
clearInterval(timer);
animationDone = true;
} else imgNumber++;
}, interval);
}
Now in my main code, every time startUpAnimation with the right parameters it works just fine. But when I want to draw multiple animations on the screen at the same time with each of them at a different interval and speed it doesnt work!
startUpAnimation(context, world.mushrooms, canvas.width / 3, canvas.height - (canvas.height / 5.3), 5000, 300);
startUpAnimation(context, world.clouds, 100, 200, 3000, 100);
It now displays both animations at the right location but they animate both at the interval and timeout of the first one called, so in my case 5000 timeout and 300 interval.
How do I fix this so that they all play independently? I think I need to make it a class or something but I have no idea how to fix this. In some cases I even need to display maybe 5 animations at the same time with this function.
Any help would be appreciated!
try something like this
var Class = function () {
this.imgNumber = 0;
this.lastImgNumber = 0;
this.animationDone = true;
}
Class.prototype.startUpAnimation = function(context, imageArray, x, y, timeOut, refreshFreq) {
// if (lastImgNumber == 0) lastImgNumber = imageArray.length-1;
if (this.animationDone) {
this.animationDone = false;
setTimeout(this.playAnimationSequence, timeOut, refreshFreq, this);
}
// context.drawImage(imageArray[imgNumber], x, y);
};
Class.prototype.playAnimationSequence = function (interval, object) {
var that = object; // to avoid flobal this in below function
var timer = setInterval(function () {
if (that.imgNumber >= that.lastImgNumber) {
that.imgNumber = 0;
clearInterval(timer);
that.animationDone = true;
console.log("xxx"+interval);
} else that.imgNumber++;
}, interval);
};
var d = new Class();
var d1 = new Class();
d.startUpAnimation(null, [], 300, 300, 5000, 50);
d1.startUpAnimation(null,[], 100, 200, 3000, 60);
It should work,
As far as I can see, each call to startUpAnimation will draw immediately to the canvas because this execution (your drawImage(..) call) hasn't been included in the callback of the setTimeout or setInterval.

Categories

Resources