Looping setTimeout stops before it should (creating a spinner) - javascript

I'm using a setTimeout function that calls itself in a loop in order to create the look of a spinner. Right now I'm just using a spinner image from Google and rotating the image within a setTimeout function and a global variable for the number of degrees to rotate. It looks like this:
var rotate = 0;
var rotateOn = false;
function rotateSpinner() {
if (rotateOn) {
setTimeout(function() {
rotate +=5
$('.spinner').css("transform", "rotate(" + rotate + "deg)");
rotateSpinner();
}, 30)
}
}
I call the spinner like this, which corresponds with a sending a socket.emit to the server:
socket.emit('someEmit', xyz, abc)
rotateOn = true;
rotateSpinner();
$('.spinner').show();
And when the response is received from the server, I reverse the code above, which should hide and then stop the spinner.
socket.on('someResponse', function() {
$('.spinner').hide();
rotateOn = false;
someOtherFunction();
})
I'm using this to fill the gap between the emit to the server and the response in several areas, however, in one of these instances, I have to go through a fairly large number of loops to build the page on the server response and it's causing the spinner to stop for a few seconds before the content actually shows up. It doesn't seem to matter how I write it out (including moving the statement to turn rotateOn to false to the end, adding a setTimeout function that doesn't turn rotateOn to false until well after the content should be done loading, etc.) - I still get the pause. Is there anything I can do to counteract this?

Related

How to added an automatic image slide show that does not clash with the update content function

I have some images that I am displaying through JSON. This file refreshes the content every 10 seconds so the new images added show without a page refresh.
I am struggling to add a slideshow code without the two refresh's clashing with each other.
I would really appreciate some help.
This is my current code.
function update_content() {
$.getJSON("showImages.php", function(data) {
$("#slides").html(
data.result.map(({image1}) => `<img class="slides" src="data:image/png;base64,${image1}" />`).join("")
);
setTimeout(update_content, 10000);
var index = 0;
slideshow();
function slideshow() {
var i;
var x = document.getElementsByClassName("slides");
for (i = 0; i < x.length; i++) {
x[i].style.display = "none";
}
index++;
if (index > x.length) {index = 1}
x[index-1].style.display = "block";
setTimeout(slideshow, 20000);
}
})
}
$(function() {
update_content()
})
The way this is written, there's no way the refreshes wouldn't clash with each other and cause a large mess in updating. What you have here will, every 10 seconds, do a ping back to the server for some json and then spawn what is essentially a thread (not in the technical sense, but in the behavior sense) that every 20 seconds hides all the slides and shows the first slide. By about 60 seconds into this page running, you now have six instances of the slideshow() function queued to run, the newly created one trying to show the first slide, the next most recently created two showing the second, the next two showing the third, etc. And because network lag is unpredictable, they'll all fire at slightly different times in an unpredictable order.
The main problem is setTimeout(slideshow, 20000). It's not needed as this is currently written. Slideshow() is being run every 10 seconds already from the outer function running every 10; it doesn't need to run separately in its own timeout. And if you're running it at that interval already, the slideshow function is useless anyway, and the server only needs to return one image in its json, and the whole slideshow function can be deleted.
Though I question why you need to do a network round-trip every 10 seconds to begin with. Unless this is some real-time snapshot of a camera feed or something, you can easily just give javascript a large array of images for it to cycle through and maybe do the server ping for new images every 10 minutes or so instead. If you go this route, instead move slideshow() out of the update_content() function and just call it once from the jquery onready function to set it running and leave it be. If you need to call slideshow() in the getJson callback, be sure to cancelTimeout on the previous setTimeout(slideshow, ...)'s return value, so you don't make pseudo-threads as described above.

Pull data from server onmousewheel?

I have a POST request that pulls data from a server, according to parameters that are adjustable by the user through number inputs. Simply listening to the change event is not enough, because I want the data to refresh while using the mousewheel to change the input value.
Calling my refresh function with something like that is clearly not optimal:
$('input').on('mousewheel', function(){
refresh_data();
});
There would be a lot of requests, and because they are asynchronous, I can never know for sure if the last request to be completed will be the last one I send.
What I currently use is a setInterval to watch an object containing intervals (rounded timestamps) where a refresh has been requested. Inside the mousewheel listener, I add the next interval to the object, as a property/key, to avoid duplicates.
Something like that:
var i = 100;
var obj = [];
$('input').on('mousewheel', function(){
// add next interval to obj;
obj[Math.ceil(Date.now()/i)*i] = true;
});
var check = setInterval(function(){
// refresh if current interval is in obj
var t = Math.floor(Date.now()/i)*i;
if(obj[t]){
delete obj[t]; // remove from obj
refresh_data();
}
}, i);
I tested this code, on my machine, with i=50, and the object is always empty, which is what we want, but with i=30, as soon as I go too fast with the wheel, I see some old intervals accumulating in the object. This is caused by the setInterval is skipping beats, so whatever value I choose for i, some user with less CPU could "skip the wrong beat", and end up finishing his mouse wheel movement seeing a result that does not match the parameters value.
I feel like maybe i'm making this more complicated than it has to be, so i'm asking:
What would be the best way to pull data from server onmousewheel, considering:
1: I want to constantly refresh the data as the user rolls the wheel over an input field.
2: I want to be 100% sure the presented data after the wheel movement is always accurate.
Try using the document offsetheight like the below code. This will work when user scrolls down and reaches the end of scroll. The behavior is kind of like a recycler view of android.
window.onscroll = function(ev) {
if ((window.innerHeight + window.pageYOffset ) >=
document.body.offsetHeight) {
alert("you're at the bottom of the page");
}
};

Javascript -- any way to force a draw of new elements before processing?

I have a javascript function or two that takes a few seconds to process. I want to show an activity indicator so users don't think it's hung. I tried bootstrap animated progress bars and spin.js -- the problem is, I put the code for them before the processing, but the browser doesn't seem to update to show the new elements until after the function has finished. Obviously, an activity indicator that shows up after processing isn't incredibly useful. Is there a way to force the redraw before the processing occurs?
EDIT: here's an example code. This is from an HTA application, hence the pulling of files and such. I know the forced "wait" portion is a bad idea, usually, but in this program, files are opened and closed immediately, this is to prevent a race conflict and I just want to give it a quick 10 retry timeout before it tells them something is wrong. So, the problem is that this.spin() doesn't display its good until after the alert pops up.
Database.prototype.tryDatabase = function(fileName, toEdit, table, keepAlive) {
if (typeof keepAlive === 'undefined') keepAlive = false;
for (timeInd=0; timeInd < 5; timeInd++) {
var complete = this._addToFile(fileName, toEdit, table, keepAlive);
if (complete === false) {
var until = moment().valueOf() + 2000;
this.spin();
while (moment().valueOf() <= until) {;}
}
else break;
}
if (complete === false) window.alert("Database was locked for an extended period, please try again");
else if (!keepAlive) $('#added_to_db_modal').modal('show');
return complete;
}

Ajax call makes sprite movement flicker javascript

i've just implemented movement for my main character sprite on my JavaScript/HTML5 game and all is well except for when i load existing users saved data.
In my main JavaScript file depending on whether the user clicked the new game button or load game button either a new game is loaded or the users data is loaded from a DB via PHP and Ajax.
If the user clicks new game it runs the code below and the player moves fine:
else{
//set beginning params
//Change screens
ui.new_game();
layer = scene.load("level_"+1);
layer = $.isArray(layer) ? layer : layer.layers;
layer.forEach(function(layer){
if (layer.hasOwnProperty("properties") && layer.properties.hasOwnProperty("collision"))
{
scene.collisionLayer(layer);
}
});
gameGUI.set_level(1);
gameGUI.set_location(20,20);
gameGUI.init_inv();
player.init_Player(20,20);
player.draw();
last_update = Date.now();
}
BUT if the player decides to load their previous game settings the code below is run and instead of the sprite moving fluidly in the canvas, when a key is pressed e.i right arrow key, the sprite will disappear, then flicker somewhere on the right side of the screen then disappear again.
function game_settings(state){
if(state == "load"){
ui.load_game();
//do ajax call to load user last save
var dataString = {"user_data": "true"};
$.ajax({
type:"POST",
url:"PHP/class.ajax.php",
data: dataString,
dataType: 'JSON',
async:false,
success: function(success) {
player_details_init(success);
},
error: function(){
alert("ERROR in User data");
}
});
layer = scene.load("level_"+level);
layer = $.isArray(layer) ? layer : layer.layers;
layer.forEach(function(layer){
if (layer.hasOwnProperty("properties") && layer.properties.hasOwnProperty("collision"))
{
scene.collisionLayer(layer);
}
});
player.init_Player(location_X,location_Y);
player.draw();
last_update = Date.now();
}
I know the Ajax is whats causing the problem because when i comment it out the Sprite moves like it should do, the only problem is i don't know why the Ajax is causing this weird behavior?
I have uploaded the current version of the game to HERE, so you can witness the weird behavior if you so wish.
To log in use
guest - username
guest - password
Thanks for your time
EDIT
Ok i have narrowed down the problem even further to the variables location_X, location_Y. Whne i stick in a hardcoded number say 20, 20 in the playr.init call the movement works fine, but when i use location_X, location_Y like you see in the example the problem still occurs as above.
I retrieve location_X and location_Y from a function called when the Ajax returns success called player_details_init.
Here is that function:
function player_details_init(success){
$.each(success, function(key,value){
if(level == null){
level = value.level;
}
if(location_X == null){
location_X = value.location_X;
}
if(location_Y == null){
location_Y = value.location_Y;
}
//items.push([value.game_item_id, value.quantity]);
gameGUI.set_level(level);
gameGUI.set_location(location_X,location_Y);
gameGUI.init_inv();
});
}
SO my guess is it is something to do with this function although when i do a console.log the location returns fine and the sprite does appear where it should it just doesnt move correctly
From the symptoms you describe, it looks like location_X and location_Y are strings instead of numbers. In this situation, computations will not work as you expect (addition will result in concatenation, for instance).
I would suggest you try applying parseInt() to these values (and possibly others as well) when reading your JSON payload.

Wait to execute code until after AJAX called image renders

I am trying to run some code that grabs the width and height of a div after it is loaded and filled with an image from an AJAX call.
The div is 0x0 until the image is placed so checking the dimensions before pretty much breaks everything.
I have tried .load() (Doesn't work because this is an AJAX call). I also tried the imagesLoaded js plugin. Here is what I have right now:
alert('outside;')
function startBubbles(){
alert('inside');
var space = new DisplaySpace($('#bubbleWrap').height(), $('#bubbleWrap').width());
var count = 1;
alert(space.getHeight());
var delay = (function(){
var timer = 0;
return function(afterResize, ms){
clearTimeout (timer);
timer = setTimeout(afterResize, ms);
};
})();
var spawnInterval = self.setInterval(function(){
var aBubble = new Bubble(count, space);
count++
aBubble.spawnimate(aBubble);
setTimeout(function() {aBubble.popBubble(aBubble)},Math.floor((Math.random()*30000)+10000));
}, 500);
});
My alerts all fire before the image is visible, then my 'space' object still has height and width of 0.
bubbleWrap is a div inside the loading zone that contains the image in question. I realize I could probably but a manual delay in here to solve the problem MOST of the time - however that doesn't seem optimal. What am I missing?
I'm now implementing the load of this particular state like this:
History.Adapter.bind(window,'popstate',function(){
var state = History.getState();
state = 'target='+state.data.state;
$.get('contents.php', state, function(data){
if(state == 'target=bubbles'){
$('#loading_zone').html(data).waitForImages(startBubbles());
}
else{
$('#loading_zone').html(data);
}
});
});
Unfortunately, on page reload, the height ends up as only 10. Everything seems great when I just navigate away and come back, though. Any thoughts?
I have a plugin that could do this nicely.
Simply call it after you inject the new HTML.
// Assume this function is the callback to the *success*.
function(html) {
$("#target").html(html).waitForImages(function() {
// All descendent images have now loaded, and the
// containing div will have the correct dimensions.
});
};

Categories

Resources