How to approach a setInterval triggering a setInterval within the same function - javascript

I a rewards site. Users earn points when they watch videos. Users want to skip to the end of the video to quickly earn their points. Therefore, the mechanism of the video being marked as completed is managed by a JS timer.
The Problem: When a user pauses the video, the timer needs to pause. When the user clicks play again, a setInterval is used to reopen the function, where a setInterval within the existing function does the same thing, creating a huge problem...
This is the code that is set every second to update the timer...
var video_percent_count = 0;
function video_percent() {
var prize_video = document.getElementById("prize_video");
total_duration = Math.floor(prize_video.duration) + 1;
video_percent_count++;
percent = video_percent_count / total_duration;
convert_percent = (percent * 100).toFixed(2);
if(convert_percent == 100) {
clearInterval(video_percent_interval);
}
if(prize_video.paused) {
clearInterval(video_percent_interval);
}
if(prize_video.play) {
setInterval("video_percent()", 1000);
}
$(".percent_container").text(convert_percent);
}
function prize_video(count_prize) {
var back_count_prize = count_prize - 1;
$("#prize_video").get(back_count_prize).play();
video_percent_interval = setInterval("video_percent()", 1000);
}
How can I properly manage play and pause and still keep setInterval clean?

Related

Calling same function from 2 different events for simultaneous process

Please keep in mind that the answer could be very simple, since I'm not too advanced in JavaScript.
I need to call the same function or a class from different events to perform a simple animation while the process is in progress.
In particular: when a user clicks on a button, it engages AJAX request, so I want to animate this process by rotating an image (like a spinner). While the first process is in progress a user can click on another button to engage the similar AJAX request for another element, so the second spinner image suppose to start rotating while the first is still in progress.
The problem I'm having is that as soon as I call the same class second time the first one stops. That's because I'm changing the ID of the Updating.TheID but I thought Updating is a class that should initiate another instance every time it's called. I just learned that JavaScript is not a multithreaded language, so I suppose I could use an array or something to keep ID's separately, but I was looking for a more "elegant" approach with the minimal code. Please advice.
function UpdateData(id) { // This function called when a user clicks a button
Updating.TheID = id;
Updating.Begin();
}
var Updating = {
Degrees: 0,
TheID: 0,
ImageID: '',
Begin: function() {
this.ImageID = "ImgID" + this.TheID;
this.RotateImage();
// CallAJAXProcess(TheID); // This function should run in background
},
RotateImage: function() {
this.Degrees += 15;
document.getElementById(this.ImageID).style.transform = "rotate(" + this.Degrees + "deg)";
if (this.Degrees < 360) {
setTimeout(this.RotateImage.bind(this), 41);
} else {
this.Degrees = 0;
}
}
}
Even if you create a new UpdateData(), which will create a unique object, each one will be referring to that same Updating object; you're not creating multiple instances of that data.
It seems like in this case, you don't want to share any state between two update events. Try this pattern instead:
function updateData(id) { // This function called when a user clicks a button
var updateOperation = new UpdateOperation(id);
updateOperation.begin();
}
function UpdateOperation(id) {
this.id = id;
this.degrees = 0;
this.imageId = '';
}
UpdateOperation.prototype = {
begin: function() {
this.imageID = "ImgID" + this.id;
this.rotateImage();
// CallAJAXProcess(TheID); // This function should run in background
},
rotateImage: function() {
this.degrees += 15;
document.getElementById(this.imageID).style.transform = "rotate(" + this.degrees + "deg)";
if (this.degrees < 360) {
setTimeout(this.rotateImage.bind(this), 41);
} else {
this.degrees = 0;
}
}
};

Using javascript/jQuery, wait 3 seconds for click, then proceed

I've been trying to figure out how to run an infinite loop while pausing for user click, then allow for a break out.
When the loop starts, the user is presented with an image, and must choose the identical image from one of 4 displayed. If they successfully click the match within 5 seconds, they are presented another image, and the game goes on.
If they either choose an incorrect image, or 5 seconds elapses, the game ends.
I've got all of the functionality worked out, except this pause while waiting for a click or the time to expire.
Ideally, I'd also like the time to be adjustable on each iteration. Say start at 5 seconds, then shorten the time slightly (10ms) on each loop.
I believe it must be solvable using setTimeout() or setInterval(), but just can't wrap my head around it.
Here is a minimal concept of what I'm trying to accomplish.
$('#playnow').on('click',function(){
var speed = 5000;
var speed_reduce = 10;
var game_running = true;
/* create array of images */
var imgs = ['puppy.png','kitten.png','bunny.png','goldfish.png'];
var runnow = setInterval(
function(){
//get random image from loaded theme
rand_img = imgs[Math.floor(Math.random() * imgs.length) ];
//display chosen image
$('#goal_image').html('<img src="'+theme_dir+rand_img+'" />');
// wait up to 5 seconds for user to click or time to expire
if(*clicked and matched*){
//get new random image and reset timer (less 10ms)
}
if(*time expired*){
//bail out and game ends
}
/* reduce time */
speed -= speed_reduce;
},
speed);
});
You'll want something like this I think:
var speed = 5000, // the initial time
currentimage,
timer,
gamerunning;
function newimage(){
var imgs = ['puppy.png','kitten.png','bunny.png','goldfish.png'];
currentimage=Math.floor(Math.random() * imgs.length);
$('#goal_image').html('<img src="'+theme_dir+imgs[currentimage]+'" />');
timer = setTimeout(speed, lost)
}
function answer(id){
if(!gamerunning){return}
clearTimeout(timer)
if(id==currentimage){
speed -= 10; // time decrease every time.
newimage();
}else{
lost()
}
}
function lost(){
gamerunning=0;
speed=5000;
// what to do when lost.
}
$("#puppy").on("click",function(){answer(0)}); // here #puppy is the id of the answer image, and 0 the index in the imgs array.
$("#kitten").on("click",function(){answer(1)});
$("#bunny").on("click",function(){answer(2)});
$("#fish").on("click",function(){answer(3)});
$("#gamestartbutton").on("click",function(){gamerunning=1})
One way to solve this problem is to use setTimeout() and clearTimeout() rather than setInterval. Also, you need some event for the successful button click (I've pretended you have a special "#successfulmatch" button):
var speed = 5000;
var speed_reduce = 10;
var game_running = true;
var imgs = ['puppy.png','kitten.png','bunny.png','goldfish.png'];
var myTimeout;
function runNow(speed){
rand_img = imgs[Math.floor(Math.random() * imgs.length) ];
$('#goal_image').html('<img src="'+theme_dir+rand_img+'" />');
// Keep track of the timeout so we can cancel it later if the user clicks fast enough.
myTimeout = window.setTimeout(function(){
game_running = false;
gameEnds();
},speed);
}
$('#successfulmatch').on('click',function(){
if(game_running){
// Cancel the timeout because the user was fast enough
window.clearTimeout(myTimeout);
// Give the user less time than before
runNow(speed - speed_reduce);
}
else{
// Throw an error: you forgot to hide the clickable buttons when the game ended.
}
}
$('#playnow').on('click',function(){
runNow(speed);
}
Looks like you are mixing the logic for checking "has the user clicked the image? was it correct?" with the one for checking "has time expired?"
You can listen for onclick events on the images
and set a timeout event for the game over
so the user has to cancel that timer, to cancel imminent game over, by clicking on the images
if the right image is clicked the timer is reset
if not, it's game over
you can cancel a timeout event before it runs with cancelTimeout()
see W3C here for a reference.
here is a quick prototype:
$('#playnow').on('click', function() {
var speed = 5000;
var speed_reduce = 10;
var game_running = true;
/* create array of images */
var imgs = ['puppy.png', 'kitten.png', 'bunny.png', 'goldfish.png'];
// function that ends the game if it's called
function gameover() {
alert("GAME OVER");
game_running = false;
}
// in order to use clearTimeout() you must store the timer in a global variable
// setting a timeout that will end the game if it's not cleared before
window.timer = setTimeout(gameover, speed);
// function that is called whenever the user clicks on a image
function onclickimage(event) {
if (!game_running) return;
if ( /*clicked right img*/ ) {
// get random image from loaded theme
var rand_img = imgs[Math.floor(Math.random() * imgs.length)];
// display chosen image
$('#goal_image').html('<img src="' + theme_dir + rand_img + '" />');
// delete timer, user now has one more opportunity
clearTimeout(timer);
// speed is less 10ms
speed -= speed_reduce;
// launch timer again
window.gametimer = setTimeout(loop, speed);
} else { // if click did not match correct image
gameover();
}
}
});
Well, firstly, you need to clearInterval() when they either click or fail in order to stop the current interval. Then, you can restart an interval with the new speed. The interval seems to be working for.
Every 5 seconds a new picture is displayed. So, you want an onclick event for the picture that clears the interval and starts a new one. So, you may want to use setTimeout instead of setInterval since it is only a single iteration at a time.
You could use setInterval, I suppose, but there's no real benefit to it. This way also makes it relatively easy to reduce the speed each time.

Stop the execution of setInterval

I'm building a little fantasy game. In the game, the player has a endurance limit. For each click at the map, the endurance bar is increased:
angular.element('.click').click(function() {
var endurance_click = angular.element('.progressbar').height();
angular.element('.progressbar').css('height', endurance_click-20);
});
The thing I want to accomplish is that if you click around at the map, and stops before you have run out of endurance, and wait for 2sec, I want to fire an function that restores you endurance. When you decide to move on, I want to stop the function.
I have tried setInterval, but the function just keeps running.
Here is my code:
//Call a function that should restore the endurance
d = window.setInterval(restoreEndurance2, 2000);
function restoreEndurance2()
{
var endurance_restore = angular.element('.progressbar').height();
angular.element('.progressbar').css('height', endurance_restore+20);
if(endurance_restore >= 320) //If it's restored
{
alert(endurance_restore);
clearInterval(d);
}
}
How can I make the function stop when I start navigate through the map?

setInterval does not stop after second call

I am currently working on a battle system.
The health gets calculated every 200ms, and I'm using a Interval. It works pretty good, until I start the game - the Interval again. It doesn't stop anymore.
It is a lot of code - I have also an online live demo here http://wernersbacher.de/pro/coinerdev/
Like I said - works the first, but not the second.
So, just the main code:
var frameStop;
// Draws Startscreen
function showStartRaid(name) {
playerBTC = btc;
playerBTCs = btcs;
playerName = nick;
// Sets stats for called level
enemyBTC = dun[name]["buyer"]["btc"];
enemyBTCs = dun[name]["buyer"]["btcs"];
enemyName = dun[name]["buyer"]["label"];
enemyNum = dun[name]["meta"]["base"];
/* Reset everything in html */
}
var battle = false;
$(".raid_building").click(function() {
//Draws level
showStartRaid(name);
//Sets start BTC as fighting stats (they will decrease during battle)
fplayerBTC = playerBTC;
fenemyBTC = enemyBTC;
//Click on "Start"
$("#startRaid").click(function() {
function raiden() {
//Calculates fighting
fenemyBTC -= playerBTCs/frameMinus;
fplayerBTC -= enemyBTCs/frameMinus;
/*Draws stats and health here in html */
if(fplayerBTC >= 0 && fenemyBTC >= 0)
console.log("battle goes on")
else {
//If battle is over, stop it
clearInterval(frameStop);
}
}
//Start battle
frameStop = setInterval(raiden, frameRaid);
});
});
Thanks for any help, I'm helpless.
With your code, every time .raid_building is clicked, you hook up a new handler for clicks on #startRaid. So that means, if .raid_building is clicked twice, you'll have two handlers for clicks on #startRaid, both of which start a new interval timer. Your frameStop variable will only contain the handle of one of them; the other will continue. And of course, a third click will compound the problem (you'll have three click handlers, each of which fires up a new interval timer). And so on...
Move the code hooking click on #startRaid outside the click handler on .raid_building.

Easeljs sprite animation stuck on frame

I'm learning javascript by using the easeljs library to make a simple game, for school lessons.
I want to make a crosshair give some feedback to the player by showing a small animation while you are pointing at your target, using a hittest I made.
However, when the crosshair touches the target, the animation (should be two little triangles pointing to the middle of the crosshair) seems to be stuck on it's first frame.
Here is a bit of my code, I put both of these functions inside a ticker function. The functions do what they're supposed to do (I checked by sending a message to the console.log), but I think the animation is reset as soon as the variable "hitTestControle" is set to true, at every tick.
If you want to check out all of the code, here is a link to the "game":
http://athena.fhict.nl/users/i279907/achtergrond/achtergrond.html
function hitTest() {
if(distance(crossHair, block) < 60) {
hitTestControle = true;
} else {
hitTestControle = false;
console.log(hitTestControle);
}
}
function hitTestControl() {
if(hitTestControle == true) {
crossHair.gotoAndPlay("move");
console.log("hit");
} else {
crossHair.gotoAndPlay("stop");
}
}
PS: There also seems to be something wrong with this hittest I used.
function distance() {
var difx = blok.x - crossHair.x;
var dify = blok.y - crossHair.y;
return Math.sqrt( (difx * difx) + (dify * dify) );
}
It looks like you're starting the animation... setting it to the first frame and starting it... every time hitTestControle is true. Since hitTestControle will be true as long as you're hovering over the target, the animation will never reach the second frame.
What you need to do is start the animation when you transition from hitTestControle = false to hitTestControle = true, but once that happens you just let it play automatically.
Try changing your hitTestControl() function to something like this:
function hitTestControl() {
if(hitTestControle == true && alreadyOverHit == false) {
crossHair.gotoAndPlay("move");
alreadyOverHit = true;
console.log("hit");
} else {
crossHair.gotoAndPlay("stop");
alreadyOverHit = false;
}
}
In other words, only start the animation once, during the first frame you're detecting a hit, and then don't touch it unless you move off the target and back on.

Categories

Resources