Simon Game, Javascript settimeout and animation - javascript

Can anybody help me to simulate animation of four div tags?
Simply for loop should wait until opacity of div tags change in 1 second.
function animateDiv(ar) { // ar contains div tag indexes. ex:[0,3,2,3,1,0,1,2,3]
for (var i = 0; i < ar.length; i++) {
var ind = "";
if (ar[i] == 0) ind = ".red";
else if (ar[i] == 1) ind = ".blue";
else if (ar[i] == 2) ind = ".yellow";
else if (ar[i] = 3) ind = ".green";
var ok = false;
setTimeout(function () {
$(ind).css('opacity', 1);
console.log("waiting " + " index: " + i);
ok = true;
}, 1000);
if (ok == true) {
$(ind).css('opacity', 0.7);
console.log("Done!");
}
}
}
https://jsfiddle.net/z8y2v5u1/

A for loop can't wait for a timeout to finish, because the function you supply to setTimeout() is run asynchronously after the current function (and whatever called the current function) finishes. So the whole loop will run before the timeout happens.
You need to use a "pseudo-loop" that relies on setTimeout() to trigger the next iteration. Something like the following will work:
function animateDivs(ar, cb) {
var $divs = $(".red,.green,.blue,.yellow"),
i = 0;
(function next() {
if (i === ar.length) {
if (cb) cb();
} else {
var $div = $divs.eq(ar[i]).css('opacity', 1);
setTimeout(function() {
$div.css('opacity', 0.7);
i++;
next();
}, 1000);
}
})();
}
animateDivs([0,3,2,3,1,0,1,2,3], function() { console.log("Finished")});
div { width: 40px; height: 40px; display: inline-block; opacity: 0.7;}
.red { background-color: red;}
.green { background-color: green;}
.blue { background-color: blue;}
.yellow { background-color: yellow;}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<div class="red"></div><div class="green"></div>
<br/>
<div class="blue"></div><div class="yellow"></div>
The next() function changes the appropriate div to an opacity of 1, then uses setTimeout() to wait a second before changing the opacity back and then call next() again for the next index in the array.
I figured you might like some way to know when the animation had ended, so I've added a callback argument (cb) to animateDivs() that will be called after the whole array has been processed - in my example all it does is log something to the console, but you could use it to do something more interesting.

Related

Looping Arrays to Change Background Image of Div

I am trying to create a loop of an array that changes the background of a div only one single loop. The code is as follows;
var ImagesF = [];
ImagesF[0]="images/image1.png";
ImagesF[1]="images/image2.png";
ImagesF[2]="images/image3.png";
var i = 1;
var index = 0;
var iterations = 0;
var interval = setInterval(autoImgB(), 2000);
function autoImgB(arr1, id){
var url = 'url(' + arr1 [i] + ')';
if(index >= arr1.length) {
index = 0;
iterations++;
}
document.getElementById(id).style.backgroundImage = url;
if (iterations >= 2) {
clearInterval(interval);
} else {index++}
}
The code is being called like, onclick="autoImgB(ImagesF, 'DIV')"It seems to be trying to work and it does change the first image, however then it doesn't seem to be passing the arguments to the next iteration, what am I doing wrong?
-- UPDATE --
I have attempted to add the following code to pass the argument as originally passed during the function call autoImgB(ImagesF, 'DIV'), however I get an error that states, "arr1 is undefined".
var index = 0;
var iterations = 0;
var interval = setInterval(function() {
autoImgB(arr1, id);
}, 2000);
function autoImgB(arr1, id){
var url = 'url(' + arr1 [index] + ')';
if(index >= arr1.length) {
index = 0;
iterations++;
}
document.getElementById(id).style.backgroundImage = url;
if (iterations >= 2) {
clearInterval(interval);
} else {index++}
}
-- UPDATE 2 --
#Andy, requested that I post my nested DIV's for further help, the DIV structure within the contain are as depicted;
var ImagesF = [];
ImagesF[0]="image1.png";
ImagesF[1]="image2.png";
ImagesF[2]="image3.png";
var n = 0;
function autoImgB(arr1) {
var url = 'url(' + arr1 [n] + ')';
if (n < arr1.length) {
document.getElementById('DIV3').style.backgroundImage = url;
setTimeout(autoImgB, 2000, arr1, ++n);
console.log(url,n)
}
}
.DIV1{
position:absolute;
top: 0px;
left: 0px;
width:100%;
height:100%;
background-image:url('');
background-color: none;
display: none;
z-index: 2;
}
.DIV2{
position:absolute;
top: 0px;
left: 0px;
width:100%;
height:100%;
background-image:url('');
background-color: none;
display: none;
z-index: 1;
}
.DIV3{
position:absolute;
top: 40px;
left: 417px;
width:105px;
height:130px;
background-image:url('');
background-color: none;
display: block;
z-index: 2;
}
<div class="holder">
<div class="DIV1" id="DIV1"></div>
<div class="DIV2" id="DIV2"></div>
<div class="DIV3" id="DIV3"></div>
</div>
<button style="cursor:pointer" onclick="autoImgB(ImagesF)">Press</button>
What I would like for this to do is be able to call it by ID within the function, eg: autoImgB(ImagesF, 'DIV3').
OK. So the main issue is that you are calling the function immediately rather than passing a reference to the function to setInterval.
var interval = setInterval(autoImgB, 2000);
The other problem is that you aren't passing any arguments to the function. There are two ways to do that. The first is to pass them as additional arguments after the time:
var interval = setInterval(autoImgB, 2000, ImagesF, 0);
The second is to call the function itself within the setInterval callback:
var interval = setInterval(function () {
autoImgB(ImagesF, 0);
}, 2000);
The other issue is that it's not clear what your HTML looks like. You appear to be getting an element with a particular id but are passing in div as the argument. So either you have an element like <div id="div"></div> or something else is going on. You should probably take a closer look at that.
That said you can shorten your code considerably if you use setTimeout instead of setInterval, as you only need to do the minimum of checks, and there's no need to clear the timer at any point. Just check to see if the index is less than the array length and call the function again.
var div = document.querySelector('div');
function autoImgB(arr, i) {
if (i < arr.length) {
div.style.backgroundImage = 'url("' + arr[i] + '")';
setTimeout(autoImgB, 2000, arr, ++i);
}
}
autoImgB(ImagesF, 0);
Here's some working code with setTimeout.
setInterval expects a function as its first parameter
var interval = setInterval(function() {
autoImgB(ImagesF, 'DIV');
}, 2000);
The only thing which is wrong in the code you've created is that you pass the return value of the autoImgB function which is undefined into the setInterval function, but the setInterval function only accepts a function or a code string.
Documentation for setInterval
I've created a example based on your code to show you how it'll work.

jquery fadeIn/Out, custom slideshow glitches, fade memory? fade queue?

I am building a background img slideshow and running into glitches I can't comprehend.
I have several objects that contains a list of images. I have two functions that will take these images, create one div per each, and add the imgs as background of these divs, all within a container.
Then, as described in this website, I fadeout the first div,and fadeIn the second, then move the first child to last child position, and loop, creating a slideshow effect.
When I want this over i .empty() the container. Then the process can start again with the same or another object.
The first time I do this, it works, but second, third... times, it starts to glitch. Not only two, but all divs start to fade in and out, for I don't know what reason
This happens even if I am using the same object in the first, second, third... attempts.
It would seem as if although the divs are erased from DOM, apparently there is some memory of them? Could it be related to the fact that created divs share the name with previously created divs? maybe fadein out keep some kind of internal queue I am unaware of?
Here is an JsFiddle:
https://jsfiddle.net/93h51k9m/11/
and the code:
$(document).ready(function(){
var imgObject = {
imgs: ['http://lorempixel.com/400/200/sports/1/','http://lorempixel.com/400/200/sports/2/','http://lorempixel.com/400/200/sports/3/']
};
var imgObject2 = {
imgs: ['http://lorempixel.com/400/200/sports/4/','http://lorempixel.com/400/200/sports/5/','http://lorempixel.com/400/200/sports/6/']
};
var noImgObject = {
};
function prepare(index) {
if ($("#cover").css("display") != "none") {
console.log("cover is visible: hide it first");
console.log("fadeOut cover in 3000ms");
$("#cover").fadeOut(3000, function() {
console.log("then empty cover")
$("#cover").empty();
console.log("now for the images")
roll(index);
});
} else {
console.log("cover is already hidden: now for the images");
roll(index);
};
};
function roll(index) {
if (typeof index.imgs != "undefined") {
console.log("called object has images")
console.log("get them and their numbers")
var imgs = index.imgs;
var imgsLength = imgs.length;
console.log("create as many divs as imgs, and place each img as bg in each div")
for (i = 0; i < imgsLength; i++) {
$("#cover").append("<div class='imgdiv" + i + "'></div>");
$(".imgdiv" + i).css("background-image", "url('"+imgs[i]+"')");
};
console.log("now hide all but first div, fadeIn cover and start the carousel");
//as seen at http://snook.ca/archives/javascript/simplest-jquery-slideshow
$('#cover').fadeIn(3000);
$('#cover div:gt(0)').hide();
setInterval(function() {
console.log("fade and swap")
$('#cover :first-child').fadeOut(3000)
.next('div').fadeIn(3000)
.end().appendTo('#cover')
}, 6000);
} else {
console.log("index has no images, nothing to do");
};
};
$("#imgobj").click(function(){
console.log("imgObject called");
prepare(imgObject);
});
$("#imgobj2").click(function(){
console.log("imgObject2 called");
prepare(imgObject2);
});
$("#noimgobj").click(function(){
console.log("noImgObject called");
prepare(noImgObject);
});
});
Thank you
Every time click event is invoked, another interval is being started and that is the reason, actions are appended in the queue
Use global variable which will hold the setInterval instance and clear it every time you start new Interval.
var interval;
$(document).ready(function() {
var imgObject = {
imgs: ['http://lorempixel.com/400/200/sports/1/', 'http://lorempixel.com/400/200/sports/2/', 'http://lorempixel.com/400/200/sports/3/']
};
var imgObject2 = {
imgs: ['http://lorempixel.com/400/200/sports/4/', 'http://lorempixel.com/400/200/sports/5/', 'http://lorempixel.com/400/200/sports/6/']
};
var noImgObject = {};
function prepare(index) {
clearInterval(interval);
if ($("#cover").css("display") != "none") {
console.log("cover is visible: hide it first");
console.log("fadeOut cover in 3000ms");
$("#cover").fadeOut(3000, function() {
console.log("then empty cover")
$("#cover").empty();
console.log("now for the images")
roll(index);
});
} else {
console.log("cover is already hidden: now for the images");
roll(index);
};
};
function roll(index) {
if (typeof index.imgs != "undefined") {
console.log("called object has images")
console.log("get them and their numbers")
var imgs = index.imgs;
var imgsLength = imgs.length;
console.log("create as many divs as imgs, and place each img as bg in each div")
for (var i = 0; i < imgsLength; i++) {
$("#cover").append("<div class='imgdiv" + i + "'></div>");
$(".imgdiv" + i).css("background-image", "url('" + imgs[i] + "')");
};
console.log("now hide all but first div, fadeIn cover and start the carousel");
//as seen at http://snook.ca/archives/javascript/simplest-jquery-slideshow
$('#cover').fadeIn(3000);
$('#cover div:gt(0)').hide();
interval = setInterval(function() {
console.log("fade and swap")
$('#cover :first-child').fadeOut(3000)
.next('div').fadeIn(3000)
.end().appendTo('#cover')
}, 6000);
} else {
console.log("index has no images, nothing to do");
};
};
$("#imgobj").click(function() {
console.log("imgObject called");
prepare(imgObject);
});
$("#imgobj2").click(function() {
console.log("imgObject2 called");
prepare(imgObject2);
});
$("#noimgobj").click(function() {
console.log("noImgObject called");
prepare(noImgObject);
});
});
html {
color: black;
height: 100%;
padding: 0;
margin: 0;
overflow: hidden;
}
body {
height: 100%;
padding: 0;
margin: 0;
background: #f7fafa;
}
* {
box-sizing: border-box;
}
button {
cursor: pointer;
}
#buttons {
z-index: 1000;
}
#cover {
display: none;
position: fixed;
top: 5vh;
left: 0;
width: 100vw;
height: 95vh;
opacity: 0.5;
z-index: 0;
}
#cover div {
position: absolute;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
background-repeat: no-repeat;
background-size: cover;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js"></script>
<div id="buttons">
<button id="imgobj">imgObject</button>
<button id="imgobj2">imgObject2</button>
<button id="noimgobj">noImgObject</button>
</div>
<div id="cover"></div>

Perform click on one div after the other

I hope you understand my problem.
At the moment I have a JS-function that choses randomly a div of a specific Html-Class.
Now i would like to rewrite the function that it picks one div after the other, just like they are ordered in the HTML-content.
How can I do this?
For information: the random selection is made with jquery and looks like this:
function pickrandom() {
var elems = $(".classname");
if (elems.length) {
var keep = Math.floor(Math.random() * elems.length);
console.log(keep);
$(elems[keep]).click();
}
}
Thanks
$(document).on('click', '.classname', function(){
var self = $(this);
var total_items = $('.classname').length; // 10
var index = self.index(); //2 for 3rd element
if (index < total_items) {
setTimeout(function () {
$('.classname').eq(index+1).trigger('click');
}, 3000);
}
});
this will call the next clicks in 3 sec interval
i don't know why you are using a randomizer function.you can allow the user to make that click
Hopefully this helps you - can't see your markup, but it should get you on the right track. I've also changed your .click() to a .trigger('click') which should be quite a bit more dependable.
JavaScript
function pickrandom() {
var elems = $(".classname");
if (elems.length) {
var curTarget = Math.floor(Math.random() * elems.length);
console.log(curTarget);
$(elems[curTarget]).trigger('click');
// Find index of our next target - if we've reached
// the end, go back to beginning
var nextTarget = curTarget + 1;
if( nextTarget > elems.length ) {
nextTarget = 0;
}
// Wait 3 seconds and click the next div
setTimeout( function() { $(elems[nextTarget]).trigger('click'); }, 3000 );
}
}
$("div").click(function() {
var el = $(this);
setTimeout(function() {
console.log(el.text());
el.toggleClass("click");
}, 2000);
});
var random = Math.floor((Math.random() * $("div").length) + 1);
var index = random - 1;
console.log("Random number: ", random);
var clicker = setInterval(function() {
if (index === $("div").length) {
clearInterval(clicker);
console.log("cleared interval");
} else {
$("div").eq(index).click();
index++;
}
}, 2000)
div {
height: 50px;
width: 100%;
border: 2px solid black;
background-color: lightgreen;
margin-bottom: 10px;
text-align: center;
font-size: 30px;
}
.click {
background-color: lightblue;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div>
Div 1
</div>
<div>
Div 2
</div>
<div>
Div 3
</div>
<div>
Div 4
</div>
<div>
Div 5
</div>
<div>
Div 6
</div>

fadeIn and fadeOut in javascript

I'm trying to write my own animations using JavaScript.
I wrote a function for fadeIn() as below, it changes the display property followed by a change in value of opacity. But it doesn't seem to be working.
What am I doing wrong?
function fadeIn(obj, defDisp) {
obj.style.opacity = 0;
obj.style.display = defDisp;
var opVal = 0;
while (opVal < 1) {
obj.style.opacity = opVal;
opVal += 0.1;
}
}
defDisp = Default value for display property
Without a timing interval, this will likely execute too fast for you to see it. The while loop, without a timeout feature, will execute in far less than a second, and you won't see it happen. It's like asking a computer to count to 10, it will do it in less than a millisecond.
Try using a setTimeout
http://www.w3schools.com/jsref/met_win_settimeout.asp
while(opVal < 1) {
setTimeout(function(){
obj.style.opacity = opVal;
opVal += 0.1;
}, 3000);
}
Alter the timer (3000 in this case) to something that makes your fade work for you. Every 1000 is a one second and your loop runs 10 times, so in this case it would be 30 seconds, likely too slow.
I would probably stick with a CSS transition however, as they tend to render better on all browsers.
var el = document.getElementById('fadein');
fadeIn(el);
function fadeIn(ele, defDisp) {
ele.style.opacity = 0;
ele.style.display = defDisp;
var opVal = 0;
var t = setInterval(function(){
if(opVal >= 1){
clearInterval(t);
}
ele.style.opacity = opVal;
opVal += 0.1;
}, 100);
}
#fadein{ background: #ccc; border:1px solid #ddd; padding: 10px }
<div id="fadein">Hello</div>
Use a function that calls itself after a delay.
function fadeIn(obj, defDisp) {
obj.style.opacity = 0;
obj.style.display = defDisp;
var last = +new Date(); // Keep track of the time to calculate the opacity
var fadeStep = function () {
obj.style.opacity = +obj.style.opacity + (new Date() - last) / 800;
last = +new Date();
if (+obj.style.opacity < 1) {
setTimeout(fadeStep, 16);
}
};
fadeStep();
}
var el = document.getElementById('box');
fadeIn(el, 'block');
#box{ padding: 1em; background: #009afd; color: #ffffff; display: none; }
<div id="box">Hello</div>
If you want the fade to be faster, replace 800 by anything lower and vice-versa.
Because html render and for loop use the same thread, so when you doing the for-loop,you can't see any changes until the function complete. You have to use a setTimeout or setInterval (or requestAnimationFrame which is introduced from html5) so you browser can have the control to change the properties on the page:
You can see a example from the snippet, although the second that use a setTimeout is faster than the first one, which use for loop, the first one will not change its color as browser not able to change color during for-loop.
And if you choose to use requestAnimationFrame like I do in the snippets, you can have a smooth animation while the time can also be controlled precisely.
function fadeIn() {
this.style.opacity = 0;
this.style.display = 'block';
var opVal = 0;
console.time("count");
while(opVal < 1) {
this.style.opacity = opVal;
opVal += 0.000001;
}
console.timeEnd("count");
}
// Accept target as the target to apply anim, time is total anim time in ms.
function fadeInAlt(target, time) {
var opacity = 0;
var last = window.performance.now();
console.time("count2");
target.style.opacity = opacity;
target.style.display = 'block';
var fadeInFunc = function(timeStamp) {
if (opacity < 1) {
// Define the change by passed time.
var timePassed = timeStamp - last;
opacity += timePassed / time;
target.style.opacity = opacity;
last = timeStamp;
requestAnimationFrame(fadeInFunc);
} else {
console.timeEnd("count2");
return;
}
};
requestAnimationFrame(fadeInFunc);
}
var div = document.getElementById('test');
div.onclick = fadeIn;
var div2 = document.getElementById('test2');
div2.onclick = function() {
fadeInAlt(this, 3000);
};
#test {
background-color: red;
width: 30px;
height:30px;
}
#test2 {
background-color: blue;
width: 30px;
height:30px;
}
<div id="test"></div>
<div id="test2"></div>

JS Image Transition Not Working

I am unable to get the CSS opacity transition to work by adding it using JavaScript. Please let me know what is wrong with the code. http://jsfiddle.net/copperspeed/bvWbB
(function () {
var myImgs = document.getElementById('vz0');
var i = 0;
function cycle() {
if (i <= 3) {
var myArray = [
'http://jsrun.it/assets/t/r/U/O/trUOT.jpg',
'http://jsrun.it/assets/6/c/Y/s/6cYsH.jpg',
'http://jsrun.it/assets/w/M/r/i/wMriQ.jpg',
'http://jsrun.it/assets/5/Q/8/f/5Q8fW.jpg'
];
console.log(myArray[0]);
myImgs.setAttribute("src", myArray[i]);
if (myImgs.style.opacity === '0') {
console.log('trans');
myImgs.style.transitionProperty = 'opacity';
myImgs.style.transitionDuration = "1500ms";
}
if (myImgs.style.opacity === '1') {
console.log('opacity-0');
myImgs.style.opacity = '0';
}
i++;
setTimeout(function () {
cycle();
}, 3000);
There are a couple of issues with your script.
the opacity style doesn't exist on the element on initialization. You need to account for that in your logic
On the second pass through, the opacity style does exist and may be 0, so that condition also needs to be accounted for
Your second if statement immediately reverses what you did in the first conditional. That statement should be in an else-if
You are cycling only one image element in/out so your transition from one image to another won't work as expected. You either need to change to two elements or change your transitioning strategy to accommodate the single element.
Demo fiddle - items 1-3 above
Code changed for 1-3 above:
(function () {
var myImgs = document.getElementById('vz0');
var i = 0;
function cycle() {
if (i <= 3) {
var myArray = ['http://jsrun.it/assets/t/r/U/O/trUOT.jpg', 'http://jsrun.it/assets/6/c/Y/s/6cYsH.jpg', 'http://jsrun.it/assets/w/M/r/i/wMriQ.jpg', 'http://jsrun.it/assets/5/Q/8/f/5Q8fW.jpg'];
myImgs.setAttribute("src", myArray[i]);
if (myImgs.style.opacity === '' || myImgs.style.opacity == 0) {
console.log(myImgs.style.opacity + '0');
myImgs.style.transitionProperty = 'opacity';
myImgs.style.transitionDuration = "1500ms";
myImgs.style.opacity = 1;
} else if (myImgs.style.opacity == 1) {
console.log(myImgs.style.opacity + '2');
myImgs.style.opacity = 0;
}
i++;
setTimeout(function () {
cycle();
}, 3000);
if (i === 4) {
i = 0;
}
}
}
cycle();
}());
For item #4 above - here is a refactored version that uses two img elements to help manage the transition in and out:
Demo fiddle for 1-4 above
HTML:
<div class="imgWrapper">
<img src="http://jsrun.it/assets/t/r/U/O/trUOT.jpg" id="vz0" class="vzImage" alt="first" height="300" width="300" />
<img src="http://jsrun.it/assets/t/r/U/O/trUOT.jpg" id="vz1" class="vzImage" alt="first" height="300" width="300" />
</div>
CSS:
.imgWrapper {
position: relative;
height: 300px;
width: 300px;
}
.vzImage {
opacity:0;
position: absolute;
top: 0; left: 0;
bottom: 0; right: 0;
}
Script:
(function () {
var myImgs = document.getElementsByClassName('vzImage');
var myArray = ['http://jsrun.it/assets/t/r/U/O/trUOT.jpg',
'http://jsrun.it/assets/6/c/Y/s/6cYsH.jpg',
'http://jsrun.it/assets/w/M/r/i/wMriQ.jpg',
'http://jsrun.it/assets/5/Q/8/f/5Q8fW.jpg'];
// Consider moving this to .vsImage in stylesheet
for(var j = 0; j < myImgs.length; ++j) {
myImgs[j].style.transitionProperty = 'opacity';
myImgs[j].style.transitionDuration = "1500ms";
}
function cycle(i) {
var myArrayIdx = i % myArray.length;
var imgIdx = i % myImgs.length;
var prevImgIdx = (i-1) % myImgs.length;
myImgs[imgIdx].setAttribute("src", myArray[myArrayIdx]);
myImgs[imgIdx].style.opacity = 1;
if(myImgs[prevImgIdx]) {
myImgs[prevImgIdx].style.opacity = 0;
}
setTimeout(function () {
cycle(i+1);
}, 3000);
}
cycle(0);
}());
First rule of debugging. If something inside IF statement doesn't happen, look at the condition.
You check if myImgs.style.opacity equals 0 or 1. Use console.log(myImgs.style.opacity); and it'll show you that myImgs.style.opacity equals empty string. So none of your conditions ever fire.

Categories