I have an image carousel that by default rotates to the right. using setInterval() and changing the image every few seconds. I created two setInterval functions, one to rotate to the right and one to rotate to the left.
I then created an html arrow and added an onclick call to it to call the specific setInterval I wanted. The problem is the previous setIntervals dont turn off when new ones are called. I tried to use clearInterval but it wasnt working.
My code looks like:
const left_arrow = document.querySelector("#arrow");
var rotateRight = setInterval(()=>{
// do stuff
}, 3000)
var rotateLeft = setInterval(()=>{
// do stuff
}, 3000)
left_arrow.onclick = rotateLeft();
right_arrow.onclick = rotateRight();
I basically want to have the ability to click an arrow and have it rotate the flow of my images by calling different setInterval functions. I cant use any frameworks. Thank you.
SetInterval only executes an action every x milliseconds, so with your code you are going left and then right every three seconds. Here is my code that I think would work
direction = "right"
setInterval(()=>{
if(direction === "right") {
//do stuff to rotate right
} else {
//do stuff to rotate left
}
}, 3000)
left_arrow.onclick = () => {
direction = "left";
};
right_arrow.onclick = () => {
direction = "right";
};
So explaining what the code is doing the variable direction stores which direction the carousel should move, the code inside setInterval runs every 3 seconds, if the direction is right you run some code, otherwise you run other code. When you click the buttons it just sets the directions.
I think clearInterval will work on your scenary, may be you clearInterval on wrong time. if you're not sure,please paste more code.
secondly, const left_arrow = document.querySelector("#arrow"); #arrow element are named left_arrow, i don't know what is your element id for right_arrow, may be that's also a problem.
As #Dominik comments, the solution to stop the original interval is to use clearInterval
when you call setInterval it
const left_arrow = document.querySelector("#arrow");
let currentInterval = null;
function rotateRight(){
return setInterval(()=>{
clearInterval(currentInterval)
// do stuff
}, 3000)
}
function rotateLeft(){
return setInterval(()=>{
clearInterval(currentInterval)
// do stuff
}, 3000)
}
left_arrow.onclick = ()=> {currentInterval = rotateLeft();}
right_arrow.onclick = ()=> {currentInterval = rotateRight();}
Related
Im developing a game in JavaScript in which the user needs to give a key input (press spacebar) when a clock hand moves slightly more than usual.
Currently, I am using a setTimeout function that gives the user 1 second to give a key input after the clock hand has ticked (rotated by 10 degrees).
If the user correctly presses space when the clock hand moves more than usual (15 degrees), an indicator will flash green, otherwise it will flash red.
The problem I am running into is that once the user gives an input within 1 second of the hand moving, the indicator will not flash until AFTER that 1 second has passed (ie, if the user gives an input after 0.4 seconds, the indicator will not flash until 0.6 later)
I know this is because the indicator is set up in my setTimeout fuction, which will only execute the code after 1 second. I have tried to test for the user input outside of the setTimeout function but that way the user does not get 1 second to give a response.
I was wondering if there is a way around this problem or a better way to approach this?
//Get input after clock tick
setTimeout(() => {
if (irregular_tick && space_pressed) {
flashScreenGreen();
}
if (!(space_pressed) && irregular_tick) {
flashScreenRed();
}
},1000);
Thanks for any help!
You'll need to keep a reference to your timer outside of the setTimeout callback and add a listener for the keypress with an interrupt callback which will clear the timeout if all conditions are met.
let timer = null;
let space_pressed = false;
function reset() {
timer = null;
space_pressed = false;
}
function interruptHandler(e) {
if (timer !== null) { // only look for spacebar if timer is running
space_pressed = e.key === ' ';
if (irregular_tick && space_pressed) {
// clear timeOut if successful
clearTimeout(timer);
reset();
flashScreenGreen();
}
}
}
document.body.addEventListener('keyup', interruptHandler);
timer = setTimeout(() => {
if (!space_pressed && irregular_tick) {
flashScreenRed();
}
// reset timer at end of callback ready for next run
reset();
}, 1000);
As a side note it looks like you've defined two separate flashScreenGreen() and flashScreenRed() functions. I'm guessing that they have similar if not identical logic. If that is the case you might want to consider defining a single utility flashScreen() function which accepts a color as a parameter.
function flashScreen(color) {
// logic utilizing 'color'
}
// usage
flashScreen('green');
flashScreen('#FF0000'); // red as hex
I think the clearTimeout function will help you here
// Hold the reference to the timer
const timeoutId = setTimeout(() => {
if (irregular_tick && space_pressed) {
flashScreenGreen();
//You can use the clearTimeout function to end the timer
clearTimeout(timeoutId);
}
if (!(space_pressed) && irregular_tick) {
flashScreenRed();
//clear timeout, if you need it here too
clearTimeout(timeoutId);
}
},1000);
I have been working on my first major programming project and so far it has been going pretty well. My goal is to have an animation run until either an event is triggered or 15 seconds have passed. After the animation times out I want to animation to repeat. My current solution to the plan is shown below.
var animationSpeed = 40;
var animationTimout = 375;
var testTime = 1;
//some more set up variables
function onClick() {
startDrive();
setTimeout(startDrive, 15000); //****
}
function startDrive() {
var Driving = setInterval(carDriving, aniSpeed);
}
function carDriving() {
testLocation();
drawCar();
angleCalc();
newLocation();
getInfo();
}
function testLocation() {
//this code gets information on whether or not the animation should be stopped
testTime = testTime + 1
if(a === 1 || testTime > animationTimeout) {
//a test to cancel the animation, the other variables test to
clearInterval(Driving);
}
}
function drawCar() {
//draws the car
}
function angleCalc() {
//gets some info on how to move the car
}
function newLocation() {
//decides on new coords for the car based on angleCalc();
}
function getInfo() {
//gets some info I plan to use later in the project
}
When I run the code without the starred line, the whole thing works. The car animates as I want it to and stops if the conditions for stopping are met. The car freezes where it was on the canvas, and it seems as if the interval was cleared. When I add the starred line of code, the animation seems to work, but it runs twice as fast as before. I am utterly lost and nothing I try works. Please help.
The problem is likely due to the local variable defined here:
function startDrive() {
var Driving = setInterval(carDriving, aniSpeed);
}
The variable Driving is only defined in the function startDrive, it's a local variable because you are using var to define it inside the function. So when you attempt to access it inside testLocation() you are not accessing the same variable. In fact when you do clearInterval(Driving) the variable Driving isn't defined. A simple solution would be to make Driving global by removing the var:
function startDrive() {
Driving = setInterval(carDriving, aniSpeed);
}
Or you can pass the timer as a parameter inside the testLocation function. This way you will be clearing the interval properly.
I am working on a simple animation of a sprite in a little Javascript game, a mosquito that is changing state while flying towards right. These states correspond to two different images in my tile sheet.
So far I have been doing this, but the animation is very irregular:
for (var i = 0; i < mosquitos.length; i++) {
var mosquito = mosquitos[i];
setInterval (updateAnimation, 500);
mosquito.update();
// rest of code non-relevant to animation here...
and then, later:
function updateAnimation () {
next();
function next () {
mosquito.state = mosquito.FLYINGRIGHT1;
setTimeout (previous, 500);
function previous () {
mosquito.state = mosquito.FLYINGRIGHT;
}
}
}
The two states are of course FLYINGRIGHT and FLYINGRIGHT1...
problem is that mosquito starts animating very quickly and very irregularly. I would like it to change state, i.e. every half of a second. I tried with different time periods but it is always the same effect.
I can produce a jsfiddle of the whole thing, if what I am missing is not so obvious.
Thank you for any help and insights.
Here's the game in question, from my website:
http://www.retroinvaders.net/laurasworld/src/laurasTriviaLevels.html
I think you should rethink this part:
var mosquito = mosquitos[i];
setInterval (updateAnimation, 500);
I suppose you want every new 'updateAnimation' get a different mosquito (mosquitos[0], mosquitos[1] .. mosquitos[i]). But what really happens is something different. Every time you get the same mosquito (mosquitos[i]).
This is because of asynchronous behaviour of setInterval() function.
function updateAnimation () {
next();
function next() {
// every time, mosquito is equal to mosquitos[i]
// and NOT mosquitos[0], then mosquitos[1] ...
mosquito.state = mosquito.FLYINGRIGHT1;
setTimeout (previous, 500);
function previous()
{
mosquito.state = mosquito.FLYINGRIGHT;
}
}
}
This is originally from (Pause execution in while loop locks browser (updated with fiddles))
I have been at this all day and I can't figure out how to keep javascript from advancing to the next line and in essence executing all lines at once. I have tried every combination of delay / setTimeout I can think of to no avail.
I just want the elements in the array to flash once then pause, then do it again for another element in the array till all elements have been removed and the array is empty.
But because javascript is executing all lines at once I end up with the appearance of all elements flashing at the same time.
Here is the fiddle:
http://jsfiddle.net/ramjet/xgz52/7/
and the relevant code:
FlashElement: function () {
while (elementArray.length) {
alert('a ' + elementArray.length);
var $el = elementArray.eq(Math.floor(Math.random() * elementArray.length));
PageLoadAnimation.FlashBlast($el);
alert('delay complete');
elementArray = elementArray.not($el);
alert('array popped');
alert('z ' + elementArray.length);
}
},
ANSWER FOR THIS SITUATION. Hopefully it will help others.
As Zach Saucier points out the loop was really my problem...but not the only problem. I was the other problem(s).
Me first.
Fool that I am I was really causing my own complications with two things I was doing wrong.
First using jsfiddle my javascript would error due to syntax or some such thing but fiddle doesn't tell you that (to my knowledge) so my fiddle wouldn't run but I took it in pride as MY CODE IS FINE stupid javascript isn't working.
Second I was passing my function to setTimeout incorrectly. I was adding the function parens () and that is not correct either which would bring me back to issue one above.
WRONG: intervalTimer = setInterval(MyFunction(), 1500);
RIGHT: intervalTimer = setInterval(MyFunction, 1500);
As for the code. As Zach pointed out and I read here (http://javascript.info/tutorial/settimeout-setinterval) while he was responding setting a timeout in a loop is bad. The loop will iterate rapidly and with the timeout one of the steps in the loop we get into a circular firing squad.
Here is my implementation:
I created a couple variables but didn't want them polluting the global scope so I created them within the custom domain. One to hold the array of elements the other the handle to the setInterval object.
var PageLoadAnimation =
{
elementArray: null,
intervalTimer: null,
....
}
In my onReady function (the one the page calls to kick things off) I set my domain array variable and set the interval saving the handle for use later. Note that the interval timer is how long I want between images flashes.
onReady: function ()
{
elementArray = $('#PartialsContainer').children();
//black everything out just to be sure
PageLoadAnimation.BlackOutElements();
//flash & show
intervalTimer = setInterval(PageLoadAnimation.FlashElement, 1500);
},
Now instead of looping through the array I am executing a function at certain intervals and just tracking how many elements are left in the array to be flashed. Once there are zero elements in the array I kill the interval execution.
FlashElement: function ()
{
if(elementArray.length > 0) //check how many elements left to be flashed
{
var $el = PageLoadAnimation.GrabElement(); //get random element
PageLoadAnimation.FlashBlast($el); //flash it
PageLoadAnimation.RemoveElement($el); //remove that element
}
else
{
//done clear timer
clearInterval(intervalTimer);
intervalTimer = null;
}
},
So the whole thing is:
var PageLoadAnimation =
{
elementArray: null,
intervalTimer: null,
onReady: function () {
elementArray = $('#PartialsContainer').children();
//black everything out just to be sure
PageLoadAnimation.BlackOutElements();
//flash & show
intervalTimer = setInterval(PageLoadAnimation.FlashElement, 1500);
//NOT this PageLoadAnimation.FlashElement()
},
BlackOutElements: function () {
$('#PartialsContainer').children().hide();
},
FlashElement: function ()
{
if(elementArray.length > 0)
{
var $el = PageLoadAnimation.GrabElement();
PageLoadAnimation.FlashBlast($el);
PageLoadAnimation.RemoveElement($el);
}
else
{
//done clear timer
clearInterval(intervalTimer);
intervalTimer = null;
}
},
GrabElement: function()
{
return elementArray.eq(Math.floor(Math.random() * elementArray.length));
},
RemoveElement: function($el)
{ elementArray = elementArray.not($el); },
FlashBlast: function ($el) {
//flash background
$el.fadeIn(100, function () { $el.fadeOut(100) });
}
}
Hope that help others understand the way to go about pausing execution in javascript.
The reason why you were having trouble is because setTimeout function is non-blocking and will return immediately. Therefore the loop will iterate very quickly, initiating each of the timeouts within milliseconds of each other instead of including the previous one's delay
As a result, you need to create a custom function that will wait on the setInterval to finish before running again
FlashElement: function () { // Call it where you had the function originally
myLoop();
},
...
function myLoop() {
setTimeout(function () { // call a setTimeout when the loop is called
var $el = elementArray.eq(Math.floor(Math.random() * elementArray.length));
PageLoadAnimation.FlashBlast($el);
elementArray = elementArray.not($el);
if (0 < elementArray.length) { // if the counter < length, call the loop function
myLoop();
}
}, 1000)
}
Feel free to change the delay to whatever value you wish (3000ms to let each fade finish before the last at the moment). If you want to start the fade in of the next before the previous ends and keep them in their original positions you would have to animate the opacity using .css instead of using fadeIn and fadeOut
My answer is based on this answer from another SO question
I'm creating a simple game with a character that can jump, move right, and move left.
I'm having trouble with the jump function, which uses a setInterval.
Here is the function:
jumpUp: function (speed) {
setInterval(function () {
this.yPos += 10;
this.playerElement.css("top", '-=' + 10);
alert("dude, im not moving....so stop trying"); //for some reson, this line works and other dont.
}, 100);
}
I should add that the code works without the setInterval. I really don't have any idea why it's not working when I add the setInterval.
My questions:
What is stopping this code from running?
Is setInterval a good way to make a character look like it jumping and landing? Or should i use different method?
EDIT 1:
fiddle
The problem is your use of this. When the function you pass to setInterval is called, this will be the global object (window in browsers). You need to preserve the this value from when you call setInterval. One way of doing this is to store the value of this into a variable, which will then be closed over by the anonymous function (which is a closure):
jumpUp: function (speed) {
var self = this;
setInterval(function () {
self.yPos += 10;
self.playerElement.css("top", '-=' + 10);
}, 100);
}
EDIT:
To answer your second question, a better approach to animating a sprite (like your character) is to store the character's velocity, and then have a single game loop that will calculate the next position of the sprite based on that information. A very simple example would look like:
// Somewhere else in the code:
function tick() {
// move player by player.velocity.x and player.velocity.y
// code to decelerate player gradually, stop player when they hit a wall/floor, etc...
// A very simple example:
if (player.velocity.y > 0) {
player.velocity.y -= 1
}
// call next tick() (setTimeout, or preferably requestAnimationFrame)
}
// In the player code:
velocity: {x: 0, y: 0},
jumpUp: function () {
this.velocity.y -= 10;
},
moveRight: function () {
this.velocity.x += 10;
}
As darma and danronmoon pointed out, you have a scoping problem with this.
Try the following code:
jumpUp: function (speed) {
var that = this;
setInterval(function () {
that.yPos += 10;
that.playerElement.css("top", '-=' + 10);
}, 100);
}
I added a variable, that, to maintain the reference to whatever this is supposed to be.
In addition to your closure problem, this is probably going to cause choppy jumping.
Here's another pattern that watches the clock to see how much time has elapsed between each call to the function (setInterval is not consistent):
jumpUp: function (speed) // speed in pixels per second
{
var last = +new Date();
var c = this;
var jumptick = function ()
{
var interval = +new Date() - last;
c.yPos += speed * interval;
c.playerElement.css("top", c.yPos);
if (/* condition for reaching maximum height */) speed = -speed;
if (! /* condition for reaching ground */) setTimeout(jumptick);
// could add an interval but this will lead to fastest frame rate
};
jumptick();
}
Setinterval is not a good way to achieve this because it will use a lot a ressources.
You also need to consider the framerate when you are moving your character, otherwise he will move fast on a fast machine/browser and slow on a slow machine.
A good method is using the requestAnimationFrame method. You can find a javascript file on google that will make it crossbrowser compatible.
Then, everytime your function is called, you will need to check the time elapsed between to frame and move your sprites accordingly. It's more work but that way, your game will run at the same pace on any machine.