iterate through JSON object with pause before next iteration - javascript

I am trying to build a fake chat box. I can retrieve the data from the database using jQuery to return a JSON array ok. I then i want each line of text contained in the JSON object to display on the page one line at a time BUT the code must pause for the length of time it would normally take to type the line if text before it displays it. then the code must wait for it to be displayed before it iterated on to the next value in the JSON object.
I hope all that makes sense...
$.getJSON('includes/get-mentor-dialogue.php?e=' + new Date().getTime(), function(data){
var mainDialogue = data.item;
var l = mainDialogue.length;
$.each(mainDialogue, function(index, d){
var delay = Math.round(countWords(d.content) / WPS) * 1000;
setTimeout(function(){$('#chatBox #chatBody').append('<p>'+ d.content +'</p>');},delay);
});
});
This is what i have and it kinda works... countWords() is a function that returns the number of words in the sentence and WPS is a variable that contains the average "Words Per Second" value in it.
The issue is that all the lines of text are displayed out of sequence. i can't get it to wait for the previous line to be displayed before it moves on to the next one...
Really need help with this one guys...

You can use a function to iterate with timeout, without using the $.each, see below
$.getJSON('includes/get-mentor-dialogue.php?e=' + new Date().getTime(), function(data){
var mainDialogue = data.item;
var l = mainDialogue.length;
var actual = -1;
function showNextMessage(){
actual++;
if( actual >= l ) return;
var content = mainDialogue[actual].content;
var delay = Math.round(countWords(content) / WPS) * 1000;
setTimeout(function(){
$('#chatBox #chatBody').append('<p>'+ content +'</p>');
showNextMessage();
},delay);
}
showNextMessage();
});

I think this will work for you.
$.getJSON('includes/get-mentor-dialogue.php?e=' + new Date().getTime(), function(data){
var mainDialogue = data.item;
var l = mainDialogue.length;
var i = 0;
function appendText(content) {
var delay = Math.round(countWords(content) / WPS) * 1000;
if ( i < l - 1 ) {
setTimeout(function(){
$('#chatBox #chatBody').append('<p>'+ content +'</p>');
appendText(mainDialogue[i++].content);
}, delay);
}
};
appendText(mainDialogue[i++].content);
});

I usually create an Iterator class, somewhere along the lines of this:
Iterator = function (iterable) {
var keys = [];
for (var i in iterable)
keys.push(i);
var pointer = 0;
this.next = function () {
return keys[pointer++];
}
this.hasNext = function () {
return i < keys.length;
}
}
That way you don't have to directly iterate across the object, you can just easily keep track of your place in the object as you would in Java.
$.getJSON('includes/get-mentor-dialogue.php?e=' + new Date().getTime(), function(data){
var mainDialogue = data.item;
var l = mainDialogue.length;
var iter = new Iterator(mainDialogue);
(function appendChat () {
var d = iter.next();
var delay = Math.round(countWords(d.content) / WPS) * 1000;
$('#chatBox #chatBody').append('<p>'+ d.content + '</p>');
if (iter.hasNext())
setTimeout(appendChat, delay);
})();
});

I don't have a good way to test this, but you should look into doing something like this:
$.getJSON('includes/get-mentor-dialogue.php?e=' + new Date().getTime(), function(data){
var mainDialogue = data.item;
var l = mainDialogue.length;
var delay = 0;
$.each(mainDialogue, function(index, d){
var myDelay = Math.round(countWords(d.content) / WPS) * 1000;
myDelay+=delay; // how long it takes me to render + the guy before me
delay=myDelay + (Math.random()*2000); // this will give a random pause between each step
setTimeout(function(){$('#chatBox #chatBody').append('<p>'+ d.content +'</p>');},myDelay);
});
});
I added in that little random bit, to help give a more realistic variable timing to the chat sequence.

Related

How do I cycle through multiple lines with a javascript letter randomiser?

Ok so I have this code for a JavaScript letter randomiser that works perfectly fine, i'm just having trouble figuring out how i'd get it to produce more than just one line while still keeping my code relatively efficient.
Ideally id like it to say something like this and then cycle back to the start:
Hi, my name is Yeet
this is my website
I like making cool stuff
take a look around :)
Any help would be greatly appreciated :)))
window.onload = function() {
var theLetters = "abcdefghijklmnopqrstuvwxyz#%&^+=-";
var cntnt = "Hi, my name is Yeet";
var speed = 20; // ms per frame
var increment = 2; // frames per step
var clen = cntnt.length;
var si = 0;
var stri = 0;
var block = "";
var fixed = "";
//Call self x times, whole function wrapped in setTimeout
(function rustle(i) {
setTimeout(function() {
if (--i) {
rustle(i);
}
nextFrame(i);
si = si + 1;
}, speed);
})(clen * increment + 1);
function nextFrame(pos) {
for (var i = 0; i < clen - stri; i++) {
var num = Math.floor(theLetters.length * Math.random());
//Get random letter
var letter = theLetters.charAt(num);
block = block + letter;
}
if (si == (increment - 1)) {
stri++;
}
if (si == increment) {
// Add a letter;
// every speed*10 ms
fixed = fixed + cntnt.charAt(stri - 1);
si = 0;
}
$("#output").html(fixed + block);
block = "";
}
};
Make cntnt an array
var cntnt = ["Hi, my name is Yeet", "This is my website", "I like making cool stuff", "take a look around :)"];
and use pos % cntnt.length as the array index.
fixed = fixed + cntnt[pos % cntnt.length].charAt(stri - 1);

2 random images but not the same one

I have this function that shows two random images picked from a folder. Is there any chance I can modify the code so that I won't have the same image twice as result?
Thanks in advance.
var theImages = new Array()
theImages[0] = 'img/dyptichs/f-1.jpg'
theImages[1] = 'img/dyptichs/f-2.jpg'
theImages[2] = 'img/dyptichs/f-3.jpg'
theImages[3] = 'img/dyptichs/f-4.jpg'
theImages[4] = 'img/dyptichs/f-5.jpg'
var j = 0
var p = theImages.length;
var preBuffer = new Array()
for (i = 0; i < p; i++){
preBuffer[i] = new Image()
preBuffer[i].src = theImages[i]
}
var WI1 = Math.round(Math.random()*(p-1));
var WI2 = Math.round(Math.random()*(p-2));
function showImage1(){
document.write('<img src="'+theImages[WI1]+'">');
}
function showImage2(){
document.write('<img src="'+theImages[WI2]+'">');
}
You can do something like this:
var WI1 = Math.round(Math.random()*(p-1));
var WI2 = Math.round(Math.random()*(p-1));
while (WI2 === WI1) {
WI2 = Math.round(Math.random()*(p-1));
}
We keep generating a new number until it's different from WI1, ensuring it is unique.
The way I'd personally handle that is to randomise the array and then just grab the first 2 entries. That way you still pick 2 at random, but you guarantee not to get the same 2.
var theImages = new Array()
theImages[0] = 'img/dyptichs/f-1.jpg'
theImages[1] = 'img/dyptichs/f-2.jpg'
theImages[2] = 'img/dyptichs/f-3.jpg'
theImages[3] = 'img/dyptichs/f-4.jpg'
theImages[4] = 'img/dyptichs/f-5.jpg'
var randomImages = theImages
.concat()
.sort(function () {
return Math.random() > 0.5
? 1
: -1;
})
.slice(0, 2);
function showImage1() {
document.write('<img src="' + randomImages[0] + '">');
}
function showImage2() {
document.write('<img src="' + randomImages[1] + '">');
}
Edit: including the original array for a complete solution
var WI1 = Math.floor(Math.random()*p);
var WI2 = Math.floor(Math.random()*(p-1));
if (WI2 >= WI1) {
WI2 += 1;
}
Use floor instead of round and subtract 1, because with round you get twice less chance to get first or last element.
The if trick is slightly better than a loop in this case, though the loop is easier to apply to a more complex case.

JavaScript Loop Animation Interval

I'm trying to get this loop to return a value, for each value in an array every 1 second.
The returned value is a random value generated earlier, and each loop will add i to the number and output it. (ran[0]+i) (ran[1]+i) etc.
I need the output in the same order as my example, but with a 1 second interval and something to generate my returns/consolelogs instead of having to type in all 4, or however many I use (could be random).
The code is for an animation but I cannot get the SVG working here and its irrelevant to the problem I think.
var ran = [];
var qan = 4;
for(i=0;i<(qan);i++){
rd = Math.floor(Math.random()*360);
ran.push(rd);
};
for(i=0;i<10;i++){
/*need to have (random.length) number of console logs and interval loops by 1 second*/
console.log((ran[0]+i) + " loop " + (i));
console.log((ran[1]+i) + " loop " + (i));
console.log((ran[2]+i) + " loop " + (i));
console.log((ran[3]+i) + " loop " + (i));
};
You may do like this;
var ran = [];
var qan = 4;
for(let i=0;i<(qan);i++){
rd = Math.floor(Math.random()*360);
ran.push(rd);
setTimeout(function(){console.log(ran[i])},1000*i);
}
Or by using var instead of let you can still do like this by utilizing an IIFE to return a callback with an encapsulated i value.
var ran = [];
var qan = 4;
for(var i=0;i<(qan);i++){
rd = Math.floor(Math.random()*360);
ran.push(rd);
setTimeout((function(j){ return function(){ console.log(ran[j]) } })(i),1000*i);
}
Update: I hope i am getting closer.. I just console.log the array itself but the items increase by i in each round. (10 rounds / frames)
var randar = new Array(4).fill().map(e => ~~(Math.random()*360));
console.log("starting random array ",randar); // let's see how the random array starts
for (let i = 0; i < 10; i++){
setTimeout(function(){console.log(randar.map(e=> e+i))} ,1000*i);
}
Note: I use arrows mostly but if you have concerns about IE or Safari then it's best to replace them with conventional function calls.

Loop through array of images in javascript

I'm trying to loop through an array of images but can't seem to get past image 2.
The array should also loop back to 1 when the last image has passed...
var WorkArray = new Array('work/01.png', 'work/02.png', 'work/03.png', 'work/04.png');
var nelements = WorkArray.length;
preload_image_object = new Image();
var i = 0;
for(i=0; i<=nelements; i++) {
preload_image_object.src = WorkArray[i];
}
function cC() {
var nelements = WorkArray.length;
var i = 0;
for(i=0; i<=nelements; i++) {
nelements = WorkArray[i];
}
document.getElementById("work").style.backgroundImage="url('"+WorkArray[i]+"')";
}
You can save the current file and use modulo to run in cyclic manner.
It will look something like that:
var WorkArray = new Array('work/01.png', 'work/02.png', 'work/03.png', 'work/04.png');
var currentImage = 0
function nextImage(){
currentImage = (currentImage + 1) % WorkArray.length;
document.getElementById("work").style.backgroundImage="url('"+WorkArray[currentImage]+"')";
}
You are overwriting nelements with the current element of the loop:
nelements = WorkArray[i];
The following should fix your loops:
var WorkArray = new Array('work/01.png', 'work/02.png', 'work/03.png', 'work/04.png');
var preload_image_object = new Image();
/* Lets get rid of `nelements`, as its just confusing. Get the length here.
* If, for performace reasons you want to use elements, the best way is to reverse
* aka for(var i = WorkArray.length-1; i >= 0 ; i--)
* Also, its simpler to declare the var in your for-loop itself instead of outside of it.
*/
for(var i = 0; i <= WorkArray.length; i++){
preload_image_object.src = WorkArray[i];
}
Also, again for simplifications sake, your application of the background-image could be done inside your for loop as well, and can be made to look cleaner with some spaces and omitting the ' inside your url():
document.getElementById("work").style.backgroundImage = "url(" + WorkArray[i] + ")";

Javascript: Random number out of 5, no repeat until all have been used

I am using the below code to assign a random class (out of five) to each individual image on my page.
$(this).addClass('color-' + (Math.floor(Math.random() * 5) + 1));
It's working great but I want to make it so that there are never two of the same class in a row.
Even better would be if there were never two of the same in a row, and it also did not use any class more than once until all 5 had been used... As in, remove each used class from the array until all of them have been used, then start again, not allowing the last of the previous 5 and the first of the next 5 to be the same color.
Hope that makes sense, and thanks in advance for any help.
You need to create an array of the possible values and each time you retrieve a random index from the array to use one of the values, you remove it from the array.
Here's a general purpose random function that will not repeat until all values have been used. You can call this and then just add this index onto the end of your class name.
var uniqueRandoms = [];
var numRandoms = 5;
function makeUniqueRandom() {
// refill the array if needed
if (!uniqueRandoms.length) {
for (var i = 0; i < numRandoms; i++) {
uniqueRandoms.push(i);
}
}
var index = Math.floor(Math.random() * uniqueRandoms.length);
var val = uniqueRandoms[index];
// now remove that value from the array
uniqueRandoms.splice(index, 1);
return val;
}
Working demo: http://jsfiddle.net/jfriend00/H9bLH/
So, your code would just be this:
$(this).addClass('color-' + (makeUniqueRandom() + 1));
Here's an object oriented form that will allow more than one of these to be used in different places in your app:
// if only one argument is passed, it will assume that is the high
// limit and the low limit will be set to zero
// so you can use either r = new randomeGenerator(9);
// or r = new randomGenerator(0, 9);
function randomGenerator(low, high) {
if (arguments.length < 2) {
high = low;
low = 0;
}
this.low = low;
this.high = high;
this.reset();
}
randomGenerator.prototype = {
reset: function() {
this.remaining = [];
for (var i = this.low; i <= this.high; i++) {
this.remaining.push(i);
}
},
get: function() {
if (!this.remaining.length) {
this.reset();
}
var index = Math.floor(Math.random() * this.remaining.length);
var val = this.remaining[index];
this.remaining.splice(index, 1);
return val;
}
}
Sample Usage:
var r = new randomGenerator(1, 9);
var rand1 = r.get();
var rand2 = r.get();
Working demo: http://jsfiddle.net/jfriend00/q36Lk4hk/
You can do something like this using an array and the splice method:
var classes = ["color-1", "color-2", "color-3", "color-4", "color-5"];
for(i = 0;i < 5; i++){
var randomPosition = Math.floor(Math.random() * classes.length);
var selected = classes.splice(randomPosition,1);
console.log(selected);
alert(selected);
}
var used = [];
var range = [0, 5];
var generateColors = (function() {
var current;
for ( var i = range[0]; i < range[5]; i++ ) {
while ( used.indexOf(current = (Math.floor(Math.random() * 5) + 1)) != -1 ) ;
used.push(current);
$(" SELECTOR ").addClass('color-' + current);
}
});
Just to explain my comment to jfriend00's excellent answer, you can have a function that returns the members of a set in random order until all have been returned, then starts again, e.g.:
function RandomList(list) {
var original = list;
this.getOriginal = function() {
return original;
}
}
RandomList.prototype.getRandom = function() {
if (!(this.remainder && this.remainder.length)) {
this.remainder = this.getOriginal().slice();
}
return this.remainder.splice(Math.random() * this.remainder.length | 0,1);
}
var list = new RandomList([1,2,3]);
list.getRandom(); // returns a random member of list without repeating until all
// members have been returned.
If the list can be hard coded, you can keep the original in a closure, e.g.
var randomItem = (function() {
var original = [1,2,3];
var remainder;
return function() {
if (!(remainder && remainder.length)) {
remainder = original.slice();
}
return remainder.splice(Math.random() * remainder.length | 0, 1);
};
}());

Categories

Resources