setInterval time not getting correct value - javascript

I have two arrays. One of files and another for time I want to display them in an iframe. I loop through them the files appear correctly but the time is always set to the first element.
The iframe always loads for 10000 ms.
$(function() {
var urls = ['/uploads/presentations/1560837902.pdf', '/uploads/presentations/1560837925.mp4', '/uploads/presentations/1560837959.jpg', '/uploads/presentations/1560838138.docx', '/uploads/presentations/1560838215.ppt'];
var time = [10000, 40000, 10000, 20000, 10000];
var i = 0;
function loadIframe(url) {
$('#iframe').attr('src', url);
}
setInterval(function() {
// update the index
i = (i + 1) % urls.length;
loadIframe(urls[i]);
}, time[i]);
loadIframe(urls[i]);
});

The issue is because you only define a single interval, hence the delay is always the same.
To fix this you could use a recursive timeout instead, defining the next when the previous fires. Try this:
$(function() {
var urls = ['/uploads/presentations/1560837902.pdf', '/uploads/presentations/1560837925.mp4', '/uploads/presentations/1560837959.jpg', '/uploads/presentations/1560838138.docx', '/uploads/presentations/1560838215.ppt'];
var time = [10000, 40000, 10000, 20000, 10000];
function loadIframe(i = 0) {
$('#iframe').attr('src', urls[i % urls.length]);
console.log(`Changing src to ${urls[i % urls.length]}`);
setTimeout(function() {
loadIframe(++i);
}, time[i % time.length]);
}
loadIframe();
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

Related

Update live JavaScript Array while pushing elements to HTML ID

I am facing a slight dilemma as a JavaScript newbie. Let me explain the script:
I have implemented a JavaScript function rss() which pulls from an internet RSS news feed and saves the news headlines into an array newsArray[].
The function headlinesInsert() should push every item in the array to the HTML ID #headlineInsert, similarly to how it is shown here.
However, the linked example's textlist variable (which should be replaced with my local newsArray[]) does not seem to be 'compatible' for some reason as when replacing nothing shows on the HTML side.
The idea is that the rss() function updates the global newsArray[] with new headlines every 10 minutes while the headlinesInsert() pushes this data to the HTML ID constantly (as per the linked example).
With my limited knowledge of JavaScript, I am hoping someone could help me set the following code right and put the idea into action.
// Push RSS Headlines into HTML ID
var newsArray = [];
var listTicker = function headlinesInsert(options) {
var defaults = {
list: [],
startIndex:0,
interval: 8 * 1000,
}
var options = $.extend(defaults, options);
var listTickerInner = function headlinesInsert(index) {
if (options.list.length == 0) return;
if (!index || index < 0 || index > options.list.length) index = 0;
var value = options.list[index];
options.trickerPanel.fadeOut(function headlinesInsert() {
$(this).html(value).fadeIn();
});
var nextIndex = (index + 1) % options.list.length;
setTimeout(function headlinesInsert() {
listTickerInner(nextIndex);
}, options.interval);
};
listTickerInner(options.startIndex);
}
// The following line should hold the values of newsArray[]
var textlist = new Array("News Headline 1", "News Headline 2", "News Headline 3", "News Headline 4");
$(function headlinesInsert() {
listTicker({
list: textlist ,
startIndex:0,
trickerPanel: $('#headlineInsert'),
interval: 8 * 1000,
});
});
$(function slow(){
// Parse News Headlines into array
function rss() {
$.getJSON("https://api.rss2json.com/v1/api.json?rss_url=https%3A%2F%2Fwww.stuff.co.nz%2Frss", function(data) {
newsArray = [];
for (var i = 0; i < data.items.length; i++){
newsArray[i] = (data.items[i].title);
}
console.log(newsArray);
})}
// Refresh functions ever 10 minutes
rss()
setInterval(function slow() {
rss();
}, 600000); // 10 Minute refresh time
});
Check following code. You need to initialise listTicker once rss feed is loaded.
<script src='https://code.jquery.com/jquery-3.2.1.min.js'></script>
<script>
var listTicker = function(options) {
var defaults = {
list: [],
startIndex: 0,
interval: 3 * 1000,
}
var options = $.extend(defaults, options);
var listTickerInner = function(index) {
if (options.list.length == 0) return;
if (!index || index < 0 || index > options.list.length) index = 0;
var value = options.list[index];
options.trickerPanel.fadeOut(function() {
$(this).html(value).fadeIn();
});
var nextIndex = (index + 1) % options.list.length;
setTimeout(function() {
listTickerInner(nextIndex);
}, options.interval);
};
listTickerInner(options.startIndex);
}
var textlist = new Array("news1", "news2", "news3");
$(function() {
function rss() {
$.getJSON("https://api.rss2json.com/v1/api.json?rss_url=https%3A%2F%2Fwww.stuff.co.nz%2Frss", function(data) {
newsArray = [];
for (var i = 0; i < data.items.length; i++) {
newsArray[i] = (data.items[i].title);
}
console.log(newsArray);
listTicker({
list: newsArray,
startIndex: 0,
trickerPanel: $('#newsPanel'),
interval: 3 * 1000,
});
})
}
rss();
});
</script>
<div id='newsPanel' />

Changing images using delays

I’m trying to figure out how to make an animation using the order of three images.
The default image is “image1.png” that always shows when the page loads.
After every 5 seconds, the variable “back.src” must abruptly change
to image2.png, so without fading. The default is image1.png
And then after 0.5 seconds, the variable again changes but then to
image3.png.
0.5 seconds later it changes back to image2.png
and 0.5 later again back to image1.png.
This is to be repeated in a loop because I want to repeat the process again after 5 seconds.
My problem is, I don't know if structuring this code is the best way to go about it. How would my code need to look based on the requirement explained above?
Here's my code of what I've got so far:
var back = new Image();
back.src = "image1.png";
function wait(miliseconds) {
var currentTime = new Date().getTime();
while (currentTime + miliseconds >= new Date().getTime()) {
}
}
function image1() {
wait(5000);
back.src = "image2.png";
}
function image2() {
wait(500);
back.src = "image3.png";
}
function image3() {
wait(500);
back.src = "image2.png";
}
function animate(){
ctx.save();
ctx.clearRect(0, 0, cW, cH);
ctx.drawImage(back,0,0);
ctx.restore();
}
var animateInterval = setInterval(animate, 30);
There is no wait() operation in Javascript and usually trying to make one like you are doing causes bad things to happen (event loops get starved, user interfaces get locked up, etc...). Instead, you schedule things to run in the future with setTimeout(). This allows the JS engine to do other things (like service other events happening in the system) while you are waiting for your next loop iteration and is generally very important in Javascript.
I'd suggest you just put the sequence you want into a data structure and then use a timer that iterates through the data structure, wrapping when it gets to the end:
var data = [
["image1.png", 5000],
["image2.png", 500],
["image3.png", 500],
["image4.png", 500]
];
function runAnimation() {
var index = 0;
function animate(image){
ctx.save();
ctx.clearRect(0, 0, cW, cH);
ctx.drawImage(image,0,0);
ctx.restore();
}
function next() {
var img = new Image();
img.src = data[index][0];
animate(img);
// schedule next iteration
var t = data[index][1];
// increment and wrap index if past end
index = (index + 1) % data.length;
setTimeout(next, t);
}
next();
}
To make this work properly, you will need your images to be precached so they get loaded immediately. If you aren't going to precache the images, then you will need to add onload handlers so you can know when the images have finished loading and are ready for drawing.
There is info on precaching images here: How do you cache an image in Javascript
Or, to make sure your images are loaded before drawing with them, you can use an onload handler like this:
var data = [
["image1.png", 5000],
["image2.png", 500],
["image3.png", 500],
["image4.png", 500]
];
function runAnimation() {
var index = 0;
function animate(image){
ctx.save();
ctx.clearRect(0, 0, cW, cH);
ctx.drawImage(image,0,0);
ctx.restore();
}
function next() {
var img = new Image();
var nextSrc = data[index][0];
img.onload = function() {
animate(img);
// schedule next iteration
var t = data[index][1];
// increment and wrap index if past end
index = (index + 1) % data.length;
setTimeout(next, t);
};
img.src = nextSrc;
}
next();
}
You can achieve it using setIterval()
Read about JS Timers Here
Here is what you need : https://jsfiddle.net/nfe81zou/3/
var image1 = "https://pixabay.com/static/uploads/photo/2016/06/17/13/02/duck-1463317_960_720.jpg";
var image2 = "https://pixabay.com/static/uploads/photo/2013/09/22/16/56/duck-185014_960_720.jpg";
var image3 = "https://pixabay.com/static/uploads/photo/2013/11/02/03/23/ducks-204332_960_720.jpg";
$(function() {
$("#image").prop("src", image1);
setInterval(function() {
$("#image").prop("src", image2);
setTimeout(function() {
$("#image").prop("src", image1);
setTimeout(function() {
$("#image").prop("src", image3);
}, 500);
setTimeout(function() {
$("#image").prop("src", image2);
}, 1000);
}, 1500);
}, 5000);
});
#image {
border: 2px solid red;
width: 400px;
height: 200px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<img src="" id="image"/>
This would be my approach on this problem. The images are displayed in the order of
0 (5000ms) -> 1 (500ms) -> 2 (500ms) -> 1 (500ms) -> 0 (5000ms) ...
There is no setInterval() here. This code utilizes a pseudo recursive setTimeout() implementation.
var images = ["http://1.bp.blogspot.com/_ZRj5nlc3sp8/S-T7vOsypQI/AAAAAAAADBo/0kYfB8BM6zE/s320/beautiful+girl+pics+1.jpg",
"http://1.bp.blogspot.com/-w4UtHwbrqBE/ViNbJWhnmvI/AAAAAAAADtY/hGbRtz993Tg/s1600/face%2Bwallpapers%2B%25282%2529.jpg",
"http://2.bp.blogspot.com/-4kZ9Iu8QTws/ViNbL3S29fI/AAAAAAAADtw/QakqQE72N1w/s1600/face%2Bwallpapers%2B%25286%2529.jpg"],
imel = document.getElementById("photo");
function displayImages(idx,fwd){
var ms = 0;
idx = idx || 0; // we could also use the ES6 default value method.
if (idx === 0) {
fwd = !fwd; // when index becomes 0 switch the direction of the loop
ms = fwd ? 5000 // if backwards rearrange index and duration to 0.5 sec
: (idx = images.length - 2, 500);
} else ms = 500; // when index is not 0 set the duration to 0.5 sec
imel.src = images[idx];
fwd ? setTimeout(function(){displayImages(++idx % images.length,fwd)},ms)
: setTimeout(function(){displayImages(--idx,fwd)},ms);
}
displayImages();
<image id ="photo"></image>

Generating enemies while countdown is running

I'm currently trying to create a game during a gamejam using HTML CSS Javascript and jQuery.
I have a countdown that shows to the player how many time he has left, and that loses 2 seconds when the main character is getting hit by the enemies.
What I'd like to do is to find a way to generate enemies every x seconds as long as the timer hasn't reached 30s (where he wins) or 0s (where he loses)
I used two libraries so far, QuintusJS (link here) for the physic and jchavannes's countdown (link here), trying to make them work together.
So far, everything has been great, but I can't manage to make a jQuery loop that would look like the following,
while(currentTime < 30000){ //30s in milliseconds
stage.insert(new Q.Enemy({ x: 700, y: 0 })); //Allows ennemies to be generated by QuintusJS
//TODO : adding a delay between enemies generation
}
Because the canvas that Quintus creates becomes totally blank. I also tried to create a function that sets checkCurrentTime to true while currentTime isn't equal to 0, but changes it to false when it's the case, but it still doesn't work.
Am I doing this wrong? If so, how can I manage to have a loop that generates enemies properly?
You can create a looper that does something every N milliseconds. For example:
var looper = function (delay, callImmediately, callback) {
var self = this;
var deferred = $.Deferred();
if (callImmediately) {
callback.call(self);
}
var iv = setInterval(function () {
if (deferred.state() === "resolved" || deferred.state() === "rejected") {
clearInterval(iv);
} else {
callback.call(self);
}
}, delay);
return deferred;
};
You can then start and stop it whenever you like:
// Every second add a new enemy
var loop = looper(1000, false, function () {
console.log("Adding an enemy");
//stage.insert(new Q.Enemy({ x: 700, y: 0 }));
});
// Handle done and fail
loop.done(function () { console.log("All enemies were added!"); });
loop.fail(function () { console.log("The loop was stopped before all enemies were added!"); });
// Resolve the looper after 30 seconds (calls .done())
setTimeout(loop.resolve, 30000);
// Or if something went wrong you can also reject it (calls .fail())...
setTimeout(loop.reject, 5000);
Here is a small example:
var player = {
health: 100,
};
// Damage the player every second
var iv = setInterval(function () {
player.health -= Math.floor(Math.random() * 10);
if (player.health <= 0) {
clearInterval(iv);
}
}, 1000);
// Every second add a new enemy
var loop = looper(1000, false, function () {
if (player.health <= 0) {
loop.reject("The player is dead. Stopping.");
} else {
//stage.insert(new Q.Enemy({ x: 700, y: 0 }));
console.log("Inserting an enemy; health is", player.health);
}
});
loop.done(function () { console.log("All enemies were added, and player is still alive!"); });
loop.fail(function (message) { console.log(message) });
// Stop the looper after 30 seconds
setTimeout(loop.resolve, 30000);

How to setInterval() and removeInterval() in this situation?

I understand that in order to remove an interval you need a stored reference to it, so I figured I'll store function returns in a global array.
But why when I click on a cell the intervals keep going and only one stops (I saw first and last cell to stop flashing)?
What I wanted it to do is append a continuous fadeTo on all cells of a table row (excluding first one), and a listener that on clicking any of these cells would stop animations on all of them.
Here's my best effort so far (jsfiddle):
var intervals;
var target = $('#table');
var qty = target.find('td:contains(Price)');
var row = qty.closest('tr');
arr = new Array();
row.find('td').each( function() {
if ($(this).text() !== "Price" ) {
intervals = new Array();
addAnimation($(this));
}
});
function addAnimation(cell) {
var intv = setInterval(function() {
cell.fadeTo("slow", 0.3);
cell.fadeTo("slow", 1);
}, 1000);
intervals.push(intv);
cell.click(function() {
for (var i = 0; i < intervals.length; intervals++) {
window.clearInterval(intervals[i]);
}
});
}
You are instantiating the intervals array multiple times and incrementing the wrong parameter in the for loop:
var intervals = [],
target = $('#table'),
qty = target.find('td:contains(Price)'),
row = qty.closest('tr');
row.find('td').each( function() {
if ($(this).text() !== "Price" ) {
addAnimation($(this));
}
});
function addAnimation(cell) {
var intv = setInterval(function() {
cell.fadeTo("slow", 0.3);
cell.fadeTo("slow", 1);
}, 1000);
intervals.push(intv);
cell.click(function() {
for (var i = 0; i < intervals.length; i++) {
window.clearInterval(intervals[i]);
}
$(this).stop();
});
}
See: fiddle
Your other problem is here:
var intervals;
...
if ($(this).text() !== "Price" ) {
intervals = new Array();
addAnimation($(this));
That creates a new array each time. You should be initialising intervals when you declare it and delete the line creating a new array in the if block:
var intervals = [];
...
if ($(this).text() !== "Price" ) {
addAnimation($(this));
}
However, you may wish to run this more than once, so you should clear out the array when you clear the intervals, something like:
function addAnimation(cell) {
var intv = setInterval(function() {
cell.fadeTo("slow", 0.3);
cell.fadeTo("slow", 1);
}, 1000);
intervals.push(intv);
cell.click(function() {
for (var i = 0; i < intervals.length; intervals++) {
window.clearInterval(intervals[i]);
}
// reset the array
intervals = [];
});
}
or replace the for loop with something like:
while (intervals.length) {
window.clearInterval(intervals.pop());
}
which stops the intervals and clears the array in one go. :-)

javascript suppress error and continue in for loop

I'm trying to implement a speedtest by downloading 3 image files from the internet, and averaging the time it took to load them. If there is an error for whatever reason, I want to skip loading that image and proceed with the next one. If the error occurs on the last image, then I want to calculate the average speed at that point and return to the caller.
Right now, once an error occurs (I deliberately changed the url of an image so it doesn't exist), it won't go further. I've tried returning true from the .onerror function, but no luck. Any suggestions?
var images = [{
"url": 'http://<removed>250k.jpg?n=' + Math.random(),
"size": 256000
}, {
"url": 'http://<removed>500k.jpg?n=' + Math.random(),
"size": 512000
}, {
"url": '<removed>1000k.jpg?n=' + Math.random(),
"size": 1024000
}
];
function calculateBandwidth() {
var results = [];
for (var i = 0; i < images.length; i++) {
var startTime, endTime;
var downloadSize = images[i].size;
var download = new Image();
download.onload = function () {
endTime = (new Date()).getTime();
var duration = (endTime - startTime) / 1000;
var bitsLoaded = downloadSize * 8;
var speedBps = (bitsLoaded / duration).toFixed(2);
var speedKbps = (speedBps / 1024).toFixed(2);
var speedMbps = (speedKbps / 1024).toFixed(2);
results.push(speedMbps);
//Calculate the average speed
if (results.length == 3) {
var avg = (parseFloat(results[0]) + parseFloat(results[1]) + parseFloat(results[2])).toFixed(2);
return avg;
}
}
download.onerror = function (e) {
console.log("Failed to load image!");
return true;
};
startTime = (new Date()).getTime();
download.src = images[i].url;
}
}
I think what you're not doing is controlling the process as each event occurs. Each onerror and onload tells you that the process stopped, not that it should quit per se. You initialize one image, it doesn't load, maybe take note, otherwise continue.
The thing is, you do the same thing at the end of onload too. That is, do whatever measurements, then move to the next or, if no more, run the script that cleans up and reports or whatnot.
For instance:
var site = 'http://upload.wikimedia.org/',
imgs = [
'wikipedia/commons/6/6e/Brandenburger_Tor_2004.jpg',
'wikipedia/commons/a/ad/Cegonha_alsaciana.jpg',
'wikipe/Cegonha_alsaciana.jpg',
'wikipedia/commons/d/da/CrayonLogs.jpg',
'wikipedia/commons/1/17/Bobbahn_ep.jpg',
'wikipedia/commons/9/90/DS_Citro%C3%ABn.jpg',
'wikipedia/commons/f/f0/DeutzFahr_Ladewagen_K_7.39.jpg',
'wikipedia/commons/c/c7/DenglerSW-Peach-faced-Lovebird-20051026-1280x960.jpg',
'wikipedia/commons/4/4d/FA-18F_Breaking_SoundBarrier.jpg'
];
My array of images. Hint, wikipe/Cegonha_alsaciana.jpg won't load. Be forewarned, these are large and load slowly and I use a cache buster to keep them firing onloads.
getimg();
In the fiddle, this is all within a window.onload event handler, so when that gets called, this initializes the process. I don't have a setup phase, per se, or otherwise I might have called it init() or setup().
function getimg() {
var img = document.createElement('img'),
path = imgs.shift();
if (path) {
img.onload = loaded;
img.onerror = notloaded;
img.src = site + path + '?cach=bust' + Math.floor(Math.random() * 9999);
console.log('Loading ', img.src);
document.body.appendChild(img);
} else {
console.log('Finished loading images.');
}
function loaded(e) {
console.log('Loaded ', e.target.src);
next();
}
function notloaded(e) {
console.log('Not loaded ', e.target.src);
next();
}
function next() {
console.log(imgs.length, ' imgs left to load.');
getimg();
}
}
http://jsfiddle.net/userdude/vfTfP/
This does what you're trying to do, without the timing mechanism built-in. I can break things off into steps, actions and events, so that I can control the steps as necessary. In a basic way, your script should not run much differently.

Categories

Resources