JavaScript - for loop iteration delay for stacked images - javascript

I have a "deck" of images, each one with a different Z-index. The top "cards" will go below the card with the lowest Z-index (let's call it "image1") in succession, until "image1" appears on top of the others. This is for an animation, but I can't get the for loop to iterate with a set time. I want to try 30 images, for now, in 1 second. Here is the code:
function placeImage(x) {
var div = document.getElementById("div_picture_right");
div.innerHTML = ""; // clear images
for (counter=1;counter<=x;counter++) {
var image=document.createElement("img");
image.src="borboleta/Borboleta"+counter+".png";
image.width="195";
image.height="390";
image.alt="borboleta"+counter;
image.id="imagem"+counter;
image.style.backgroundColor="rgb(255,255,255)"
image.style.position="absolute";
image.style.zIndex=counter;
div.appendChild(image);
}
};
var animaRight = function(x) {
var imageArray = [];
for (counter=1;counter<=x;counter++) {
imageArray[counter-1] = document.getElementById("imagem"+counter);
}
function delayLoop(x) {
for (counter=imageArray.length;counter>1;counter--) {
if (imageArray[counter-1].style.zIndex==counter) {
imageArray[counter-1].style.zIndex-=counter;
setTimeout("delayLoop()", 1000/x);
}
}
}
delayLoop(x);
};
Any help would me much appreciated! I tried some setTimeout functions, but I'm afraid I'm doing it wrong (placement, maybe?). If you need to, I'll edit the code with what I tried.

Is that what you want ?
var animaRight = function(x) {
var imageArray = [];
for (counter=1;counter<=x;counter++) {
imageArray.push(document.getElementById("imagem"+counter));
}
for (var i=0; i<x; i++) {
(function(i) {
setTimeout(function(){
imageArray[i%imageArray.length].style.zIndex=i*-1
}, i*1000);
})(i);
}
};
If it does what you want, I'll explain it more.

Related

Display a sequence of images in 1 position stored in an object js

I'd like to have an object of images and then loop through them to display each one with a certain interval of time between each one.
Store images in an object:
var images = [
{ name: 'image1', image: 'image1.jpg' },
{ name: 'image2', image: 'image2.jpg' },
{ name: 'image3', image: 'image3.jpg' }
]
Create a loop to go through each image in the object and each time it cycles through it changes the source of the div to the image.
Lastly i'd like the images to stop cycling through the loop of a button is clicked.
So far I have the above object setup along with a function containing a counter which loops through the items in the object.
var add = (function() {
var counter = images.length - 1;
return function() {
if(counter === images.length - 1) {
counter = 0;
} else {
counter+=1;
}
return counter;
}
})();
I then used setInterval to change the src every 0.5 seconds.
setInterval(
function() {
var imageDiv = document.getElementById('image');
var tester = images[add()];
imageDiv.src = tester.image;
}
, 500);
The above seems to console log the correct image OK, but its not changing the source and displaying one of the images once every 0.5 seconds.
Can anyone provide a pointer to help me update the src?
Thank you
A Loop is not necessary here, you can manually keep track of the Current Image like i did with the imgIndex variable and increment it each time the function changeImg() is called. I set that variable back to 0 after it reaches the last image, so it will repeat the Image-Order afterwards.
In the setInterval Function (2nd Argument) you can set the Time each Image should be displayed.
You dont need to worry about Images being loaded multiple times, since
we just replace the src attribute the Browser will handle it.
Any further questions?
images = [
{"name":"img1", "src":"https://phinest.com/wp-content/uploads/2017/02/Maya_Beano_Phinest_4-800x800.jpg"},
{"name":"img2", "src":"https://www.pixel-creation.com/wp-content/uploads/3d-nature-wallpapers-wallpaper-cave-800x800.jpg"},
{"name":"img3", "src":"https://img00.deviantart.net/5fdc/i/2009/162/9/0/nature_stock_14_by_dezzan_stock.jpg"},
{"name":"img4", "src":"https://www.secretescapes.com/inlombardia/img/content/link--nature.jpg"},
{"name":"img5", "src":"https://www.mappingmegan.com/wp-content/uploads/2017/04/sunset-2177324_1920-001.jpg"}
];
imgIndex = 0;
function changeImg(){
document.getElementById("slideshow").src = images[imgIndex].src;
if(images.length > imgIndex+1){
imgIndex++;
} else {
imgIndex = 0;
}
}
changeImg();
setInterval(changeImg, 2000)
<img id="slideshow" src=""/>
I think you have a problem with the counter fn. Please try this; it starts from 0, ends on images.length - 1 and then restarts to 0:
var add = (function(len) {
var counter = 0;
return function() {
if(counter === len) {
counter = 0;
}
return counter++;
}
})(images.length); // It's not required, but I also recommend you passing images.length as a parameter
Also, if you want to stop the interval, you just have to do something like this:
// Now myInterval is a numbered ID
var myInterval = setInterval(myFn, myTime);
// Something happens here and then..
// Your interval is stopped
clearInterval(myInterval);

Creating a closure for .setTimeout() inside a for loop

I am trying to write a javascript program which stores the value from an input element in an array when a button is clicked. The array is the split and each individual letter added to a span element and then appended to the document. The idea is to create a typing effect using setTimeout.
I am running into an issue creating a closure within the loop, so currently the setTimeout function always returns the final value of the iteration.
The function in question is at the bottom of the code block and called addTextToBoard();
var noteButton = document.querySelector('[data-js="button"]');
noteButton.addEventListener("click",function() {
var messageIn = document.querySelector('[data-js="input"]');
var message = messageIn.value;
postToBoard(message);
});
function postToBoard(val) {
var noteBoard = document.querySelector('[data-js="noteboard"]');
var newElement = document.createElement('div');
newElement.classList.add('noteboard__item');
noteBoard.appendChild(newElement);
setTimeout(function(){
newElement.classList.add('active');
}, 200);
addTextToBoard(newElement, val);
}
function addTextToBoard(el, val) {
var wordArray = val.split('');
for(i = 0; i < wordArray.length; i++) {
var letter = document.createElement('span');
letter.innerHTML = wordArray[i];
setTimeout(function(x){
return function() {}
el.appendChild(letter);
}(i),1000);
}
}
I believe I am close, I'm just not fully understanding the syntax for creating the closure. If someone could give poke in the right direction, without necessarily giving the full solution that would be great.
I essentially tried to paste in the following code snippet from here but I've missed something somehwere along the way!
setTimeout(function(x) { return function() { console.log(x); }; }(i), 1000*i);
Best,
Jack
You are close.
Since the "letter" variable changes, you'll add only the last letter over and over again. You need to "save" the current letter on the setTimeout() callback function, One way to go is like this:
function appendMyLetter(letter) {
return(function() {
el.append.Child(letter);
});
}
function addTextToBoard(el, val) {
var wordArray = val.split('');
for(i = 0; i < wordArray.length; i++) {
var letter = document.createElement('span');
letter.innerHTML = wordArray[i];
setTimeout(appendMyLetter(letter), 1000);
}
}
This way, the appendMyLetter() function gets called with a different parameter (one for each letter) and returns a function with the correct "stored" value to be called by setTimeout().
EDIT
Looking at your setTimeout() code closely
setTimeout(function(x){
return function() {}
el.appendChild(letter);
}(i),1000);
It would work fine, if you used the proper parameters and used the appendChild() inside the returned function, like so:
setTimeout(function(x){
return(function() {
el.appendChild(x);
});
}(letter),1000);
You can create an immediately-invoked function expression IIFE to create a closure
function addTextToBoard(el, val) {
var wordArray = val.split('');
for(i = 0; i < wordArray.length; i++) {
(function(index) {
var letter = document.createElement('span');
letter.innerHTML = wordArray[i];
setTimeout(function(){
el.appendChild(letter);
},1000);
})(i);
}
}
I dont know if this will work but here you go a slight change in operator:
letter.innerHTML += wordArray[i];
if you dont get the effect you imagined you will get you better try to increment the timer by i like this
setTimeout(function(){
...
},1000*i);

Cannot access array elements properly within setTimeout

Here what I'm trying to do.
I'm having an array like the following
var my_array = ['1', '2', '3' ... ,'1000000000000000'];
What I want to do is create a bunch of HTML elements for every element of that array, and since the array can contain a huge number of elements I attempted to do the following so the browser won't freeze.
for(var i in my_array)
{
if(my_array.hasOwnProperty(i))
{
setTimeout(function(){
do_something_with_data(my_array[i]);
});
}
}
What happens though is that the my_array[i] within the setTimeout doesn't have the value it should.
To be more accurate, when I try to console.log(my_array[i]) what I get is something like this:
"getUnique" function (){
var u = {}, a = [];
for(var i = 0, l = this.length; i < l; ++i){
if(u.hasOwnProperty(this[i])) {
continue;
}
a.push(this[i]);
u[this[i]] = 1;
}
return a;
}
getUnique is a function I've added to the Array prototype just like this:
Array.prototype.getUnique = function(){
var u = {}, a = [];
for(var i = 0, l = this.length; i < l; ++i){
if(u.hasOwnProperty(this[i])) {
continue;
}
a.push(this[i]);
u[this[i]] = 1;
}
return a;
};
Can please somebody help me with this issue?
the setTimeout is executed after the loop is done, and i is the last key or some garbage value at that point. You can capture the i like so:
for (var i in my_array) {
if (my_array.hasOwnProperty(i)) {
(function(capturedI) {
setTimeout(function() {
do_something_with_data(my_array[capturedI]);
});
})(i);
}
}
You should also not use for..in loops for arrays because it's an order of magnitude slower (especially so with the .hasOwnProperty check) than a for loop and the iteration order is not defined
If you have jQuery or willing to add some extra code for older browsers, you can do:
my_array.forEach( function( item ) {
setTimeout( function() {
do_something_with_data( item );
}, 1000);
});
With jQuery:
$.each( my_array, function( index, item ) {
setTimeout( function() {
do_something_with_data( item );
}, 1000);
});
See docs for [].forEach
The problem is that the functions you're creating have a reference to the i variable, not a copy of its value, and so when they run they see i as it is at that point in time (past the end of the array, presumably). (More: Closures are not complicated)
I'd recommend a completely different approach (below), but first, let's look at how to make your existing approach work.
To do what you were trying to do, with the for loop, you have to have the functions close over something that won't change. The usual way to do that is to use a factory function that creates the timeout functions such that they close over the argument to the factory. Or actually, you can pass in the array element's value rather than the index variable.
for(var i in my_array)
{
if(my_array.hasOwnProperty(i))
{
setTimeout(makeFunction(my_array[i]));
}
}
function makeFunction(entry) {
return function(){
do_something_with_data(entry);
};
}
But, I would probably restructure the code so you're not creating masses and masses of function objects unnecessarily. Instead, use one function, and have it close over an index that it increments:
// Assumes `my_array` exists at this point, and that it
// has at least one entry
var i = 0;
setTimeout(tick, 0);
function tick() {
// Process this entry
if (my_array.hasOwnProperty(i)) {
do_something_with_data(my_array[i]);
}
// Move to next
++i;
// If there are any left, schedule the next tick
if (i < my_array.length) {
setTimeout(tick, 0);
}
}
Its just a guess. Try it like:
for(var i in my_array)
{
if(my_array.hasOwnProperty(i))
setTimeout("do_something_with_data('"+my_array[i]+"')", 500);
}

Loop through js array continually with pauses

I want to loop over an array continuously on click. Extra props if you can work in a delay between class switches :-)
I got this far:
// Define word
var text = "textthing";
// Define canvas
var canvas = 'section';
// Split word into parts
text.split();
// Loop over text
$(canvas).click(function() {
$.each(text, function(key, val) {
$(canvas).removeAttr('class').addClass(val);
});
});
Which is not too far at all :-)
Any tips?
The following will wait until you click the selected element(s) in the var el. In this example var el = $('section') will select all <section>...</section> elements in your document.
Then it will start cycling through the values in cssClassNames, using each, in turn as the css class name on the selected element(s). A delay of delayInMillis will be used between each class change.
var cssClassNames = ['c1', 'c2', 'c3'];
var el = $('section');
var delayInMillis = 1000;
// Loop over text
el.click(function() {
var i = 0;
function f() {
if( i >= cssClassNames.length ) {
i = 0;
}
var currentClass = cssClassNames[i];
i += 1;
el.removeClass().addClass(currentClass);
setTimeout(f, delayInMillis);
}
f();
});
I believe you want a delay of X milliseconds between removing a class and adding a class. I'm not sure that you have to have the lines marked // ? or even that they do the job, but what you do have to have is a way to get the value's into the function. Also, the setTimeout anon function might not actually need the parameters, but it should give you an idea.
$(canvas).click(function() {
$.each(text, function(key, val) {
$(canvas).removeAttr('class')
var $canvas = $(canvas) //?
var class_val = val //?
setTimeout(function ($canvas, class_val) {
$canvas.addClass(class_val);
}, 2000);
});
});
Edit: I'd do this instead
function modify_element($element, class_name){
$element.removeClass('class');
setTimeout(function ($element) {
$element.addClass(class_name);
}, 1000);
//adds the class 1 second after removing it
}
$(canvas).click(function() {
$.each(text, function(key, val) {
setTimeout(modify_element($(canvas), val),2000);
//this will loop over the elements with 2 seconds between elements
});
});
"loop over an array continuously" this sounds like a infinite loop, I don't think you want that. About pausing the loop, this is possible, you can use this

Having some issues with my non-blocking Javascript

this.plotted = [jQuery('#img1'), jQuery('#img2'), jQuery('#img3')];
Blah.prototype.animate = function()
{
if (!this.plotted.length)
throw 'Blah::animate - No points have been plotted';
// fix the scope
var _this = this;
var animateOn = function(image)
{
image.attr('src', _this.options.pointActive);
setTimeout(function() { animateOff(point); }, 700);
}
var animateOff = function(image)
{
image.attr('src', _this.options.pointDefault);
setTimeout(function() { animateOn(point); }, 700);
}
for (var i = 0; i < this.plotted.length; i++)
{
var point = this.plotted[i];
setTimeout(function() { animateOn(point); }, 700);
}
}
I'm trying to animate these 3 images by switching their src between an 'on' and 'off' image. I don't want this to be 'sequential'. Ie, I don't want to see the first image change, then the second and then the third.
I'm using setTimeout to accomplish this. Well, I'm trying to...
Firstly, the problem I'm having is with the setTimeout inside the for loop.
for (var i = 0; i < this.plotted.length; i++)
{
var point = this.plotted[i];
console.log(point); // this correctly shows each image point
setTimeout(function()
{
console.log(point); // this shows the same, first, point
animateOn(point);
}, 700);
}
I have no idea what the inner point isn't matching the outer point :/
Also, I would like to know if this method is, well, stupid. Will these nested function calls continually build onto the stack and eventually cause me to run out of RAM? Is there a better way to approach this?
This doesn't work because of how closures work.
I'd do it like this:
var makeAnimateStarter = function(point) {
return function() {
animateOn(point);
};
};
for (var i = 0; i < this.plotted.length; i++)
{
var point = this.plotted[i];
setTimeout(makeAnimateStarter(point), 700);
}
And it's not a problem from a stack point of view. Every time a timeout is executed, it's in a new call stack. That's why you require _this. setTimeout() is not suspending the thread at that point and then resuming it's executing the function fresh.

Categories

Resources